Merge "Don't register the binned brightness sensor for disabled displays" into tm-qpr-dev
diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
index 7393bcd..21ed1eb 100644
--- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java
+++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
@@ -37,7 +37,9 @@
 import android.os.HandlerExecutor;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.Process;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.os.WorkSource;
 import android.text.TextUtils;
 import android.util.Log;
@@ -91,6 +93,14 @@
 public class AlarmManager {
     private static final String TAG = "AlarmManager";
 
+    /**
+     * Prefix used by {{@link #makeTag(long, WorkSource)}} to make a tag on behalf of the caller
+     * when the {@link #set(int, long, long, long, OnAlarmListener, Handler, WorkSource)} API is
+     * used. This prefix is a unique sequence of characters to differentiate with other tags that
+     * apps may provide to other APIs that accept a listener callback.
+     */
+    private static final String GENERATED_TAG_PREFIX = "$android.alarm.generated";
+
     /** @hide */
     @IntDef(prefix = { "RTC", "ELAPSED" }, value = {
             RTC_WAKEUP,
@@ -861,6 +871,24 @@
     }
 
     /**
+     * This is only used to make an identifying tag for the deprecated
+     * {@link #set(int, long, long, long, OnAlarmListener, Handler, WorkSource)} API which doesn't
+     * accept a tag. For all other APIs, the tag provided by the app is used, even if it is
+     * {@code null}.
+     */
+    private static String makeTag(long triggerMillis, WorkSource ws) {
+        final StringBuilder tagBuilder = new StringBuilder(GENERATED_TAG_PREFIX);
+
+        tagBuilder.append(":");
+        final int attributionUid =
+                (ws == null || ws.isEmpty()) ? Process.myUid() : ws.getAttributionUid();
+        tagBuilder.append(UserHandle.formatUid(attributionUid));
+        tagBuilder.append(":");
+        tagBuilder.append(triggerMillis);
+        return tagBuilder.toString();
+    }
+
+    /**
      * Direct callback version of {@link #set(int, long, long, long, PendingIntent, WorkSource)}.
      * Note that repeating alarms must use the PendingIntent variant, not an OnAlarmListener.
      * <p>
@@ -875,8 +903,8 @@
     public void set(@AlarmType int type, long triggerAtMillis, long windowMillis,
             long intervalMillis, OnAlarmListener listener, Handler targetHandler,
             WorkSource workSource) {
-        setImpl(type, triggerAtMillis, windowMillis, intervalMillis, 0, null, listener, null,
-                targetHandler, workSource, null);
+        setImpl(type, triggerAtMillis, windowMillis, intervalMillis, 0, null, listener,
+                makeTag(triggerAtMillis, workSource), targetHandler, workSource, null);
     }
 
     /**
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 37ce0d2..f1a3931 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -4739,8 +4739,14 @@
                             }
                             final ArraySet<Pair<String, Integer>> triggerPackages =
                                     new ArraySet<>();
+                            final SparseIntArray countsPerUid = new SparseIntArray();
+                            final SparseIntArray wakeupCountsPerUid = new SparseIntArray();
                             for (int i = 0; i < triggerList.size(); i++) {
                                 final Alarm a = triggerList.get(i);
+                                increment(countsPerUid, a.uid);
+                                if (a.wakeup) {
+                                    increment(wakeupCountsPerUid, a.uid);
+                                }
                                 if (mConstants.USE_TARE_POLICY) {
                                     if (!isExemptFromTare(a)) {
                                         triggerPackages.add(Pair.create(
@@ -4761,7 +4767,8 @@
                             }
                             rescheduleKernelAlarmsLocked();
                             updateNextAlarmClockLocked();
-                            MetricsHelper.pushAlarmBatchDelivered(triggerList.size(), wakeUps);
+                            logAlarmBatchDelivered(
+                                    triggerList.size(), wakeUps, countsPerUid, wakeupCountsPerUid);
                         }
                     }
 
@@ -4776,6 +4783,32 @@
         }
     }
 
+    private static void increment(SparseIntArray array, int key) {
+        final int index = array.indexOfKey(key);
+        if (index >= 0) {
+            array.setValueAt(index, array.valueAt(index) + 1);
+        } else {
+            array.put(key, 1);
+        }
+    }
+
+    private void logAlarmBatchDelivered(
+            int alarms,
+            int wakeups,
+            SparseIntArray countsPerUid,
+            SparseIntArray wakeupCountsPerUid) {
+        final int[] uids = new int[countsPerUid.size()];
+        final int[] countsArray = new int[countsPerUid.size()];
+        final int[] wakeupCountsArray = new int[countsPerUid.size()];
+        for (int i = 0; i < countsPerUid.size(); i++) {
+            uids[i] = countsPerUid.keyAt(i);
+            countsArray[i] = countsPerUid.valueAt(i);
+            wakeupCountsArray[i] = wakeupCountsPerUid.get(uids[i], 0);
+        }
+        MetricsHelper.pushAlarmBatchDelivered(
+                alarms, wakeups, uids, countsArray, wakeupCountsArray);
+    }
+
     /**
      * Attribute blame for a WakeLock.
      *
@@ -5695,12 +5728,7 @@
     }
 
     private void incrementAlarmCount(int uid) {
-        final int uidIndex = mAlarmsPerUid.indexOfKey(uid);
-        if (uidIndex >= 0) {
-            mAlarmsPerUid.setValueAt(uidIndex, mAlarmsPerUid.valueAt(uidIndex) + 1);
-        } else {
-            mAlarmsPerUid.put(uid, 1);
-        }
+        increment(mAlarmsPerUid, uid);
     }
 
     /**
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/MetricsHelper.java b/apex/jobscheduler/service/java/com/android/server/alarm/MetricsHelper.java
index 75ed616..2923cfd 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/MetricsHelper.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/MetricsHelper.java
@@ -111,10 +111,14 @@
                 ActivityManager.processStateAmToProto(callerProcState));
     }
 
-    static void pushAlarmBatchDelivered(int numAlarms, int wakeups) {
+    static void pushAlarmBatchDelivered(
+            int numAlarms, int wakeups, int[] uids, int[] alarmsPerUid, int[] wakeupAlarmsPerUid) {
         FrameworkStatsLog.write(
                 FrameworkStatsLog.ALARM_BATCH_DELIVERED,
                 numAlarms,
-                wakeups);
+                wakeups,
+                uids,
+                alarmsPerUid,
+                wakeupAlarmsPerUid);
     }
 }
diff --git a/core/api/current.txt b/core/api/current.txt
index 487e57d1..c451049 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -30978,6 +30978,7 @@
     field public static final int S = 31; // 0x1f
     field public static final int S_V2 = 32; // 0x20
     field public static final int TIRAMISU = 33; // 0x21
+    field public static final int UPSIDE_DOWN_CAKE = 10000; // 0x2710
   }
 
   public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 8c00c6a..c0e89d2 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -3452,7 +3452,7 @@
 
   public class WindowOrganizer {
     ctor public WindowOrganizer();
-    method @RequiresPermission(value=android.Manifest.permission.MANAGE_ACTIVITY_TASKS, conditional=true) public int applySyncTransaction(@NonNull android.window.WindowContainerTransaction, @NonNull android.window.WindowContainerTransactionCallback);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public int applySyncTransaction(@NonNull android.window.WindowContainerTransaction, @NonNull android.window.WindowContainerTransactionCallback);
     method @RequiresPermission(value=android.Manifest.permission.MANAGE_ACTIVITY_TASKS, conditional=true) public void applyTransaction(@NonNull android.window.WindowContainerTransaction);
   }
 
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 0c08735..d6f44e6 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -318,6 +318,20 @@
             "android:activity.applyActivityFlagsForBubbles";
 
     /**
+     * Indicates to apply {@link Intent#FLAG_ACTIVITY_MULTIPLE_TASK} to the launching shortcut.
+     * @hide
+     */
+    private static final String KEY_APPLY_MULTIPLE_TASK_FLAG_FOR_SHORTCUT =
+            "android:activity.applyMultipleTaskFlagForShortcut";
+
+    /**
+     * Indicates to apply {@link Intent#FLAG_ACTIVITY_NO_USER_ACTION} to the launching shortcut.
+     * @hide
+     */
+    private static final String KEY_APPLY_NO_USER_ACTION_FLAG_FOR_SHORTCUT =
+            "android:activity.applyNoUserActionFlagForShortcut";
+
+    /**
      * For Activity transitions, the calling Activity's TransitionListener used to
      * notify the called Activity when the shared element and the exit transitions
      * complete.
@@ -449,6 +463,8 @@
     private boolean mLockTaskMode = false;
     private boolean mDisallowEnterPictureInPictureWhileLaunching;
     private boolean mApplyActivityFlagsForBubbles;
+    private boolean mApplyMultipleTaskFlagForShortcut;
+    private boolean mApplyNoUserActionFlagForShortcut;
     private boolean mTaskAlwaysOnTop;
     private boolean mTaskOverlay;
     private boolean mTaskOverlayCanResume;
@@ -1246,6 +1262,10 @@
                 KEY_DISALLOW_ENTER_PICTURE_IN_PICTURE_WHILE_LAUNCHING, false);
         mApplyActivityFlagsForBubbles = opts.getBoolean(
                 KEY_APPLY_ACTIVITY_FLAGS_FOR_BUBBLES, false);
+        mApplyMultipleTaskFlagForShortcut = opts.getBoolean(
+                KEY_APPLY_MULTIPLE_TASK_FLAG_FOR_SHORTCUT, false);
+        mApplyNoUserActionFlagForShortcut = opts.getBoolean(
+                KEY_APPLY_NO_USER_ACTION_FLAG_FOR_SHORTCUT, false);
         if (opts.containsKey(KEY_ANIM_SPECS)) {
             Parcelable[] specs = opts.getParcelableArray(KEY_ANIM_SPECS);
             mAnimSpecs = new AppTransitionAnimationSpec[specs.length];
@@ -1815,6 +1835,26 @@
         return mApplyActivityFlagsForBubbles;
     }
 
+    /** @hide */
+    public void setApplyMultipleTaskFlagForShortcut(boolean apply) {
+        mApplyMultipleTaskFlagForShortcut = apply;
+    }
+
+    /** @hide */
+    public boolean isApplyMultipleTaskFlagForShortcut() {
+        return mApplyMultipleTaskFlagForShortcut;
+    }
+
+    /** @hide */
+    public void setApplyNoUserActionFlagForShortcut(boolean apply) {
+        mApplyNoUserActionFlagForShortcut = apply;
+    }
+
+    /** @hide */
+    public boolean isApplyNoUserActionFlagForShortcut() {
+        return mApplyNoUserActionFlagForShortcut;
+    }
+
     /**
      * Sets a launch cookie that can be used to track the activity and task that are launch as a
      * result of this option. If the launched activity is a trampoline that starts another activity
@@ -2143,6 +2183,13 @@
         if (mApplyActivityFlagsForBubbles) {
             b.putBoolean(KEY_APPLY_ACTIVITY_FLAGS_FOR_BUBBLES, mApplyActivityFlagsForBubbles);
         }
+        if (mApplyMultipleTaskFlagForShortcut) {
+            b.putBoolean(KEY_APPLY_MULTIPLE_TASK_FLAG_FOR_SHORTCUT,
+                    mApplyMultipleTaskFlagForShortcut);
+        }
+        if (mApplyNoUserActionFlagForShortcut) {
+            b.putBoolean(KEY_APPLY_NO_USER_ACTION_FLAG_FOR_SHORTCUT, true);
+        }
         if (mAnimSpecs != null) {
             b.putParcelableArray(KEY_ANIM_SPECS, mAnimSpecs);
         }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 46d8481..0fd80c5 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -6185,10 +6185,8 @@
         private RemoteViews generateActionButton(Action action, boolean emphasizedMode,
                 StandardTemplateParams p) {
             final boolean tombstone = (action.actionIntent == null);
-            RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
-                    emphasizedMode ? getEmphasizedActionLayoutResource()
-                            : tombstone ? getActionTombstoneLayoutResource()
-                                    : getActionLayoutResource());
+            final RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
+                    getActionButtonLayoutResource(emphasizedMode, tombstone));
             if (!tombstone) {
                 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
             }
@@ -6200,6 +6198,12 @@
                 // change the background bgColor
                 CharSequence title = action.title;
                 int buttonFillColor = getColors(p).getSecondaryAccentColor();
+                if (tombstone) {
+                    buttonFillColor = setAlphaComponentByFloatDimen(mContext,
+                            ContrastColorUtil.resolveSecondaryColor(
+                                    mContext, getColors(p).getBackgroundColor(), mInNightMode),
+                            R.dimen.notification_action_disabled_container_alpha);
+                }
                 if (isLegacy()) {
                     title = ContrastColorUtil.clearColorSpans(title);
                 } else {
@@ -6215,8 +6219,14 @@
                     title = ensureColorSpanContrast(title, buttonFillColor);
                 }
                 button.setTextViewText(R.id.action0, processTextSpans(title));
-                final int textColor = ContrastColorUtil.resolvePrimaryColor(mContext,
+                int textColor = ContrastColorUtil.resolvePrimaryColor(mContext,
                         buttonFillColor, mInNightMode);
+                if (tombstone) {
+                    textColor = setAlphaComponentByFloatDimen(mContext,
+                            ContrastColorUtil.resolveSecondaryColor(
+                                    mContext, getColors(p).getBackgroundColor(), mInNightMode),
+                            R.dimen.notification_action_disabled_content_alpha);
+                }
                 button.setTextColor(R.id.action0, textColor);
                 // We only want about 20% alpha for the ripple
                 final int rippleColor = (textColor & 0x00ffffff) | 0x33000000;
@@ -6246,6 +6256,26 @@
             return button;
         }
 
+        private int getActionButtonLayoutResource(boolean emphasizedMode, boolean tombstone) {
+            if (emphasizedMode) {
+                return tombstone ? getEmphasizedTombstoneActionLayoutResource()
+                        : getEmphasizedActionLayoutResource();
+            } else {
+                return tombstone ? getActionTombstoneLayoutResource()
+                        : getActionLayoutResource();
+            }
+        }
+
+        /**
+         * Set the alpha component of {@code color} to be {@code alphaDimenResId}.
+         */
+        private static int setAlphaComponentByFloatDimen(Context context, @ColorInt int color,
+                @DimenRes int alphaDimenResId) {
+            final TypedValue alphaValue = new TypedValue();
+            context.getResources().getValue(alphaDimenResId, alphaValue, true);
+            return ColorUtils.setAlphaComponent(color, Math.round(alphaValue.getFloat() * 255));
+        }
+
         /**
          * Extract the color from a full-length span from the text.
          *
@@ -6725,6 +6755,10 @@
             return R.layout.notification_material_action_emphasized;
         }
 
+        private int getEmphasizedTombstoneActionLayoutResource() {
+            return R.layout.notification_material_action_emphasized_tombstone;
+        }
+
         private int getActionTombstoneLayoutResource() {
             return R.layout.notification_material_action_tombstone;
         }
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index f6d27ad..37a90de 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -317,7 +317,10 @@
 
     /**
      * Intent that is broadcast when the state of {@link #getEffectsSuppressor()} changes.
-     * This broadcast is only sent to registered receivers.
+     *
+     * <p>This broadcast is only sent to registered receivers and (starting from
+     * {@link Build.VERSION_CODES#Q}) receivers in packages that have been granted Do Not
+     * Disturb access (see {@link #isNotificationPolicyAccessGranted()}).
      *
      * @hide
      */
@@ -337,7 +340,10 @@
 
     /**
      * Intent that is broadcast when the state of getNotificationPolicy() changes.
-     * This broadcast is only sent to registered receivers.
+     *
+     * <p>This broadcast is only sent to registered receivers and (starting from
+     * {@link Build.VERSION_CODES#Q}) receivers in packages that have been granted Do Not
+     * Disturb access (see {@link #isNotificationPolicyAccessGranted()}).
      */
     @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_NOTIFICATION_POLICY_CHANGED
@@ -345,7 +351,10 @@
 
     /**
      * Intent that is broadcast when the state of getCurrentInterruptionFilter() changes.
-     * This broadcast is only sent to registered receivers.
+     *
+     * <p>This broadcast is only sent to registered receivers and (starting from
+     * {@link Build.VERSION_CODES#Q}) receivers in packages that have been granted Do Not
+     * Disturb access (see {@link #isNotificationPolicyAccessGranted()}).
      */
     @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_INTERRUPTION_FILTER_CHANGED
diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java
index 067a4c3..a34a50c 100644
--- a/core/java/android/app/WallpaperColors.java
+++ b/core/java/android/app/WallpaperColors.java
@@ -27,6 +27,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.SystemProperties;
 import android.util.Log;
 import android.util.MathUtils;
 import android.util.Size;
@@ -101,11 +102,13 @@
     // Decides when dark theme is optimal for this wallpaper
     private static final float DARK_THEME_MEAN_LUMINANCE = 0.3f;
     // Minimum mean luminosity that an image needs to have to support dark text
-    private static final float BRIGHT_IMAGE_MEAN_LUMINANCE = 0.7f;
+    private static final float BRIGHT_IMAGE_MEAN_LUMINANCE = SystemProperties.getInt(
+            "persist.wallpapercolors.threshold", 70) / 100f;
     // We also check if the image has dark pixels in it,
     // to avoid bright images with some dark spots.
     private static final float DARK_PIXEL_CONTRAST = 5.5f;
-    private static final float MAX_DARK_AREA = 0.05f;
+    private static final float MAX_DARK_AREA = SystemProperties.getInt(
+            "persist.wallpapercolors.max_dark_area", 5) / 100f;
 
     private final List<Color> mMainColors;
     private final Map<Integer, Integer> mAllColors;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index bab2061..097f622 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6638,6 +6638,13 @@
     public static final int KEYGUARD_DISABLE_IRIS = 1 << 8;
 
     /**
+     * Disable all keyguard shortcuts.
+     *
+     * @hide
+     */
+    public static final int KEYGUARD_DISABLE_SHORTCUTS_ALL = 1 << 9;
+
+    /**
      * NOTE: Please remember to update the DevicePolicyManagerTest's testKeyguardDisabledFeatures
      * CTS test when adding to the list above.
      */
@@ -6680,7 +6687,8 @@
      */
     public static final int ORG_OWNED_PROFILE_KEYGUARD_FEATURES_PARENT_ONLY =
             DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA
-                    | DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS;
+                    | DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS
+                    | DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL;
 
     /**
      * Keyguard features that when set on a normal or organization-owned managed profile, have
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 3daee1f..809dc3c4 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -5741,14 +5741,13 @@
 
     /**
      * Optional argument to be used with {@link #ACTION_CHOOSER}.
-     * A {@link android.app.PendingIntent} to be sent when the user wants to do payload reselection
-     * in the sharesheet.
-     * A reselection action allows the user to return to the source app to change the content being
-     * shared.
+     * A {@link android.app.PendingIntent} to be sent when the user wants to modify the content that
+     * they're sharing. This can be used to allow the user to return to the source app to, for
+     * example, select different media.
      * @hide
      */
-    public static final String EXTRA_CHOOSER_PAYLOAD_RESELECTION_ACTION =
-            "android.intent.extra.CHOOSER_PAYLOAD_RESELECTION_ACTION";
+    public static final String EXTRA_CHOOSER_MODIFY_SHARE_ACTION =
+            "android.intent.extra.CHOOSER_MODIFY_SHARE_ACTION";
 
     /**
      * An {@code ArrayList} of {@code String} annotations describing content for
diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java
index b1252fd..49d3cac 100644
--- a/core/java/android/content/IntentSender.java
+++ b/core/java/android/content/IntentSender.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityManager.PendingIntentInfo;
+import android.app.ActivityOptions;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Bundle;
 import android.os.Handler;
@@ -158,7 +159,7 @@
      */
     public void sendIntent(Context context, int code, Intent intent,
             OnFinished onFinished, Handler handler) throws SendIntentException {
-        sendIntent(context, code, intent, onFinished, handler, null);
+        sendIntent(context, code, intent, onFinished, handler, null, null /* options */);
     }
 
     /**
@@ -190,6 +191,42 @@
     public void sendIntent(Context context, int code, Intent intent,
             OnFinished onFinished, Handler handler, String requiredPermission)
             throws SendIntentException {
+        sendIntent(context, code, intent, onFinished, handler, requiredPermission,
+                null /* options */);
+    }
+
+    /**
+     * Perform the operation associated with this IntentSender, allowing the
+     * caller to specify information about the Intent to use and be notified
+     * when the send has completed.
+     *
+     * @param context The Context of the caller.  This may be null if
+     * <var>intent</var> is also null.
+     * @param code Result code to supply back to the IntentSender's target.
+     * @param intent Additional Intent data.  See {@link Intent#fillIn
+     * Intent.fillIn()} for information on how this is applied to the
+     * original Intent.  Use null to not modify the original Intent.
+     * @param onFinished The object to call back on when the send has
+     * completed, or null for no callback.
+     * @param handler Handler identifying the thread on which the callback
+     * should happen.  If null, the callback will happen from the thread
+     * pool of the process.
+     * @param requiredPermission Name of permission that a recipient of the PendingIntent
+     * is required to hold.  This is only valid for broadcast intents, and
+     * corresponds to the permission argument in
+     * {@link Context#sendBroadcast(Intent, String) Context.sendOrderedBroadcast(Intent, String)}.
+     * If null, no permission is required.
+     * @param options Additional options the caller would like to provide to modify the sending
+     * behavior.  May be built from an {@link ActivityOptions} to apply to an activity start.
+     *
+     * @throws SendIntentException Throws CanceledIntentException if the IntentSender
+     * is no longer allowing more intents to be sent through it.
+     * @hide
+     */
+    public void sendIntent(Context context, int code, Intent intent,
+            OnFinished onFinished, Handler handler, String requiredPermission,
+            @Nullable Bundle options)
+            throws SendIntentException {
         try {
             String resolvedType = intent != null ?
                     intent.resolveTypeIfNeeded(context.getContentResolver())
@@ -199,7 +236,7 @@
                     onFinished != null
                             ? new FinishedDispatcher(this, onFinished, handler)
                             : null,
-                    requiredPermission, null);
+                    requiredPermission, options);
             if (res < 0) {
                 throw new SendIntentException();
             }
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 2e3b5d2..bbe99f5 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1058,6 +1058,17 @@
     public static final long ALWAYS_SANDBOX_DISPLAY_APIS = 185004937L; // buganizer id
 
     /**
+     * This change id excludes the packages it is applied to from ignoreOrientationRequest behaviour
+     * that can be enabled by the device manufacturers for the com.android.server.wm.DisplayArea
+     * or for the whole display.
+     * @hide
+     */
+    @ChangeId
+    @Overridable
+    @Disabled
+    public static final long OVERRIDE_RESPECT_REQUESTED_ORIENTATION = 236283604L; // buganizer id
+
+    /**
      * This change id excludes the packages it is applied to from the camera compat force rotation
      * treatment. See com.android.server.wm.DisplayRotationCompatPolicy for context.
      * @hide
@@ -1187,6 +1198,91 @@
     @Overridable
     public static final long OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS = 263259275L;
 
+    // Compat framework that per-app overrides rely on only supports booleans. That's why we have
+    // multiple OVERRIDE_*_ORIENTATION_* change ids below instead of just one override with
+    // the integer value.
+
+    /**
+     * Enables {@link #SCREEN_ORIENTATION_PORTRAIT}. Unless OVERRIDE_ANY_ORIENTATION
+     * is enabled, this override is used only when no other fixed orientation was specified by the
+     * activity.
+     * @hide
+     */
+    @ChangeId
+    @Disabled
+    @Overridable
+    public static final long OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT = 265452344L;
+
+    /**
+     * Enables {@link #SCREEN_ORIENTATION_NOSENSOR}. Unless OVERRIDE_ANY_ORIENTATION
+     * is enabled, this override is used only when no other fixed orientation was specified by the
+     * activity.
+     * @hide
+     */
+    @ChangeId
+    @Disabled
+    @Overridable
+    public static final long OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR = 265451093L;
+
+    /**
+     * Enables {@link #SCREEN_ORIENTATION_REVERSE_LANDSCAPE}. Unless OVERRIDE_ANY_ORIENTATION
+     * is enabled, this override is used only when activity specify landscape orientation.
+     * This can help apps that assume that landscape display orientation corresponds to {@link
+     * android.view.Surface#ROTATION_90}, while on some devices it can be {@link
+     * android.view.Surface#ROTATION_270}.
+     * @hide
+     */
+    @ChangeId
+    @Disabled
+    @Overridable
+    public static final long OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE = 266124927L;
+
+    /**
+     * When enabled, allows OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE,
+     * OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR and OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT
+     * to override any orientation requested by the activity.
+     * @hide
+     */
+    @ChangeId
+    @Disabled
+    @Overridable
+    public static final long OVERRIDE_ANY_ORIENTATION = 265464455L;
+
+    /**
+     * When enabled, activates OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE,
+     * OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR and OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT
+     * only when an app is connected to the camera. See
+     * com.android.server.wm.DisplayRotationCompatPolicy for more context.
+     * @hide
+     */
+    @ChangeId
+    @Disabled
+    @Overridable
+    public static final long OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA = 265456536L;
+
+    /**
+     * This override fixes display orientation to landscape natural orientation when a task is
+     * fullscreen. While display rotation is fixed to landscape, the orientation requested by the
+     * activity will be still respected by bounds resolution logic. For instance, if an activity
+     * requests portrait orientation and this override is set, then activity will appear in the
+     * letterbox mode for fixed orientation with the display rotated to the lanscape natural
+     * orientation.
+     *
+     * <p>This override is applicable only when natural orientation of the device is
+     * landscape and display ignores orientation requestes.
+     *
+     * <p>Main use case for this override are camera-using activities that are portrait-only and
+     * assume alignment with natural device orientation. Such activities can automatically be
+     * rotated with com.android.server.wm.DisplayRotationCompatPolicy but not all of them can
+     * handle dynamic rotation and thus can benefit from this override.
+     *
+     * @hide
+     */
+    @ChangeId
+    @Disabled
+    @Overridable
+    public static final long OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION = 255940284L;
+
     /**
      * Compares activity window layout min width/height with require space for multi window to
      * determine if it can be put into multi window mode.
@@ -1405,8 +1501,19 @@
      * @hide
      */
     public boolean isFixedOrientation() {
-        return isFixedOrientationLandscape() || isFixedOrientationPortrait()
-                || screenOrientation == SCREEN_ORIENTATION_LOCKED;
+        return isFixedOrientation(screenOrientation);
+    }
+
+    /**
+     * Returns true if the passed activity's orientation is fixed.
+     * @hide
+     */
+    public static boolean isFixedOrientation(@ScreenOrientation int orientation) {
+        return orientation == SCREEN_ORIENTATION_LOCKED
+                // Orientation is fixed to natural display orientation
+                || orientation == SCREEN_ORIENTATION_NOSENSOR
+                || isFixedOrientationLandscape(orientation)
+                || isFixedOrientationPortrait(orientation);
     }
 
     /**
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index df1c0d7..e5243ee 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -878,8 +878,8 @@
      * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV / PRIV}</td><td id="rb">{@code s1440p}</td><td id="rb">{@code VIDEO_CALL}</td> <td colspan="3" id="rb"></td> <td>Preview with video call</td> </tr>
      * <tr> <td>{@code YUV / PRIV}</td><td id="rb">{@code s1440p}</td><td id="rb">{@code PREVIEW_VIDEO_STILL}</td> <td>{@code YUV / JPEG}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code STILL_CAPTURE}</td> <td colspan="3" id="rb"></td> <td>Multi-purpose stream with JPEG or YUV still capture</td> </tr>
      * <tr> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code STILL_CAPTURE}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code STILL_CAPTURE}</td> <td colspan="3" id="rb"></td> <td>YUV and JPEG concurrent still image capture (for testing)</td> </tr>
-     * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV / PRIV}</td><td id="rb">{@code RECORD}</td><td id="rb">{@code VIDEO_RECORD}</td> <td>{@code YUV / JPEG}</td><td id="rb">{@code RECORD}</td><td id="rb">{@code STILL_CAPTURE}</td> <td>Preview, video record and JPEG or YUV video snapshot</td> </tr>
-     * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV / JPEG}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code STILL_CAPTURE}</td> <td>Preview, in-application image processing, and JPEG or YUV still image capture</td> </tr>
+     * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV / PRIV}</td><td id="rb">{@code RECORD}</td><td id="rb">{@code VIDEO_RECORD}</td> <td>{@code JPEG}</td><td id="rb">{@code RECORD}</td><td id="rb">{@code STILL_CAPTURE}</td> <td>Preview, video record and JPEG video snapshot</td> </tr>
+     * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code STILL_CAPTURE}</td> <td>Preview, in-application image processing, and JPEG still image capture</td> </tr>
      * </table><br>
      * </p>
      *
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 4dc6e93..32cf0a7 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -2353,6 +2353,15 @@
                 final AvailabilityCallback callback = mCallbackMap.keyAt(i);
 
                 postSingleUpdate(callback, executor, id, null /*physicalId*/, status);
+
+                // Send the NOT_PRESENT state for unavailable physical cameras
+                if (isAvailable(status) && mUnavailablePhysicalDevices.containsKey(id)) {
+                    ArrayList<String> unavailableIds = mUnavailablePhysicalDevices.get(id);
+                    for (String unavailableId : unavailableIds) {
+                        postSingleUpdate(callback, executor, id, unavailableId,
+                                ICameraServiceListener.STATUS_NOT_PRESENT);
+                    }
+                }
             }
         } // onStatusChangedLocked
 
@@ -2372,9 +2381,8 @@
             }
 
             //TODO: Do we need to treat this as error?
-            if (!mDeviceStatus.containsKey(id) || !isAvailable(mDeviceStatus.get(id))
-                    || !mUnavailablePhysicalDevices.containsKey(id)) {
-                Log.e(TAG, String.format("Camera %s is not available. Ignore physical camera "
+            if (!mDeviceStatus.containsKey(id) || !mUnavailablePhysicalDevices.containsKey(id)) {
+                Log.e(TAG, String.format("Camera %s is not present. Ignore physical camera "
                         + "status change", id));
                 return;
             }
@@ -2399,6 +2407,12 @@
                 return;
             }
 
+            if (!isAvailable(mDeviceStatus.get(id))) {
+                Log.i(TAG, String.format("Camera %s is not available. Ignore physical camera "
+                        + "status change callback(s)", id));
+                return;
+            }
+
             final int callbackCount = mCallbackMap.size();
             for (int i = 0; i < callbackCount; i++) {
                 Executor executor = mCallbackMap.valueAt(i);
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index e5b9cdb..754472f 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -1223,14 +1223,6 @@
         new StreamCombinationTemplate(new StreamTemplate [] {
                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
                         STREAM_USE_CASE_PREVIEW),
-                new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD,
-                        STREAM_USE_CASE_RECORD),
-                new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD,
-                        STREAM_USE_CASE_STILL_CAPTURE)},
-                "Preview, video record and YUV video snapshot"),
-        new StreamCombinationTemplate(new StreamTemplate [] {
-                new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
-                        STREAM_USE_CASE_PREVIEW),
                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD,
                         STREAM_USE_CASE_RECORD),
                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.RECORD,
@@ -1239,27 +1231,11 @@
         new StreamCombinationTemplate(new StreamTemplate [] {
                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
                         STREAM_USE_CASE_PREVIEW),
-                new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD,
-                        STREAM_USE_CASE_RECORD),
-                new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD,
-                        STREAM_USE_CASE_STILL_CAPTURE)},
-                "Preview, in-application video processing and YUV video snapshot"),
-        new StreamCombinationTemplate(new StreamTemplate [] {
-                new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
-                        STREAM_USE_CASE_PREVIEW),
                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW,
                         STREAM_USE_CASE_PREVIEW),
                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM,
                         STREAM_USE_CASE_STILL_CAPTURE)},
                 "Preview, in-application image processing, and JPEG still image capture"),
-        new StreamCombinationTemplate(new StreamTemplate [] {
-                new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
-                        STREAM_USE_CASE_PREVIEW),
-                new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW,
-                        STREAM_USE_CASE_PREVIEW),
-                new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM,
-                        STREAM_USE_CASE_STILL_CAPTURE)},
-                "Preview, in-application image processing, and YUV still image capture"),
     };
 
     private static StreamCombinationTemplate sPreviewStabilizedStreamCombinations[] = {
diff --git a/core/java/android/hardware/fingerprint/IUdfpsHbmListener.aidl b/core/java/android/hardware/fingerprint/IUdfpsHbmListener.aidl
index 9c2aa66..a36ccf6 100644
--- a/core/java/android/hardware/fingerprint/IUdfpsHbmListener.aidl
+++ b/core/java/android/hardware/fingerprint/IUdfpsHbmListener.aidl
@@ -39,5 +39,15 @@
      *        {@link android.view.Display#getDisplayId()}.
      */
     void onHbmDisabled(int displayId);
+
+    /**
+     * To avoid delay in switching refresh rate when activating LHBM, allow screens to request
+     * higher refersh rate if auth is possible on particular screen
+     *
+     * @param displayId The displayId for which the refresh rate should be unset. See
+     *        {@link android.view.Display#getDisplayId()}.
+     * @param isPossible If authentication is possible on particualr screen
+     */
+    void onAuthenticationPossible(int displayId, boolean isPossible);
 }
 
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 3d5c34c..76475f2 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -649,8 +649,11 @@
             return Uid.PROCESS_STATE_NONEXISTENT;
         } else if (procState == ActivityManager.PROCESS_STATE_TOP) {
             return Uid.PROCESS_STATE_TOP;
-        } else if (ActivityManager.isForegroundService(procState)) {
-            // State when app has put itself in the foreground.
+        } else if (procState == ActivityManager.PROCESS_STATE_BOUND_TOP) {
+            return Uid.PROCESS_STATE_BACKGROUND;
+        } else if (procState == ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+            return Uid.PROCESS_STATE_FOREGROUND_SERVICE;
+        } else if (procState == ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
             return Uid.PROCESS_STATE_FOREGROUND_SERVICE;
         } else if (procState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
             // Persistent and other foreground states go here.
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 0b956f8..dbd602f 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1167,6 +1167,11 @@
          * Tiramisu.
          */
         public static final int TIRAMISU = 33;
+
+        /**
+         * Upside Down Cake.
+         */
+        public static final int UPSIDE_DOWN_CAKE = CUR_DEVELOPMENT;
     }
 
     /** The type of build, like "user" or "eng". */
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index ac2156e..ca34337 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -109,7 +109,8 @@
     public static final long TRACE_TAG_THERMAL = 1L << 27;
 
     private static final long TRACE_TAG_NOT_READY = 1L << 63;
-    private static final int MAX_SECTION_NAME_LEN = 127;
+    /** @hide **/
+    public static final int MAX_SECTION_NAME_LEN = 127;
 
     // Must be volatile to avoid word tearing.
     // This is only kept in case any apps get this by reflection but do not
diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java
index 91d231e..787b609 100644
--- a/core/java/android/os/UidBatteryConsumer.java
+++ b/core/java/android/os/UidBatteryConsumer.java
@@ -51,8 +51,7 @@
     }
 
     /**
-     * The state of an application when it is either running a foreground (top) activity
-     * or a foreground service.
+     * The state of an application when it is either running a foreground (top) activity.
      */
     public static final int STATE_FOREGROUND = 0;
 
@@ -64,7 +63,8 @@
      * {@link android.app.ActivityManager#PROCESS_STATE_TRANSIENT_BACKGROUND},
      * {@link android.app.ActivityManager#PROCESS_STATE_BACKUP},
      * {@link android.app.ActivityManager#PROCESS_STATE_SERVICE},
-     * {@link android.app.ActivityManager#PROCESS_STATE_RECEIVER}.
+     * {@link android.app.ActivityManager#PROCESS_STATE_RECEIVER},
+     * {@link android.app.ActivityManager#PROCESS_STATE_FOREGROUND_SERVICE}.
      */
     public static final int STATE_BACKGROUND = 1;
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ce4a735..10e1633 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9921,6 +9921,28 @@
                 "active_unlock_on_unlock_intent_when_biometric_enrolled";
 
         /**
+         * If active unlock triggers on unlock intents, then also request active unlock on
+         * these wake-up reasons. See {@link PowerManager.WakeReason} for value mappings.
+         * WakeReasons should be separated by a pipe. For example: "0|3" or "0". If this
+         * setting should be disabled, then this should be set to an empty string. A null value
+         * will use the system default value (WAKE_REASON_UNFOLD_DEVICE).
+         * @hide
+         */
+        public static final String ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS =
+                "active_unlock_wakeups_considered_unlock_intents";
+
+        /**
+         * If active unlock triggers and succeeds on these wakeups, force dismiss keyguard on
+         * these wake reasons. See {@link PowerManager#WakeReason} for value mappings.
+         * WakeReasons should be separated by a pipe. For example: "0|3" or "0". If this
+         * setting should be disabled, then this should be set to an empty string. A null value
+         * will use the system default value (WAKE_REASON_UNFOLD_DEVICE).
+         * @hide
+         */
+        public static final String ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD =
+                "active_unlock_wakeups_to_force_dismiss_keyguard";
+
+        /**
          * Whether the assist gesture should be enabled.
          *
          * @hide
@@ -11036,6 +11058,13 @@
                 "extra_automatic_power_save_mode";
 
         /**
+         * Whether lockscreen weather is enabled.
+         *
+         * @hide
+         */
+        public static final String LOCK_SCREEN_WEATHER_ENABLED = "lockscreen_weather_enabled";
+
+        /**
          * These entries are considered common between the personal and the managed profile,
          * since the managed profile doesn't get to change them.
          */
diff --git a/core/java/android/service/dreams/DreamOverlayService.java b/core/java/android/service/dreams/DreamOverlayService.java
index bf5b970..6e4535b 100644
--- a/core/java/android/service/dreams/DreamOverlayService.java
+++ b/core/java/android/service/dreams/DreamOverlayService.java
@@ -36,39 +36,101 @@
 public abstract class DreamOverlayService extends Service {
     private static final String TAG = "DreamOverlayService";
     private static final boolean DEBUG = false;
-    private boolean mShowComplications;
-    private ComponentName mDreamComponent;
 
-    private IDreamOverlay mDreamOverlay = new IDreamOverlay.Stub() {
+    // The last client that started dreaming and hasn't ended
+    private OverlayClient mCurrentClient;
+
+    // An {@link IDreamOverlayClient} implementation that identifies itself when forwarding
+    // requests to the {@link DreamOverlayService}
+    private static class OverlayClient extends IDreamOverlayClient.Stub {
+        private final DreamOverlayService mService;
+        private boolean mShowComplications;
+        private ComponentName mDreamComponent;
+        IDreamOverlayCallback mDreamOverlayCallback;
+
+        OverlayClient(DreamOverlayService service) {
+            mService = service;
+        }
+
         @Override
-        public void startDream(WindowManager.LayoutParams layoutParams,
-                IDreamOverlayCallback callback, String dreamComponent,
-                boolean shouldShowComplications) {
-            mDreamOverlayCallback = callback;
+        public void startDream(WindowManager.LayoutParams params, IDreamOverlayCallback callback,
+                String dreamComponent, boolean shouldShowComplications) throws RemoteException {
             mDreamComponent = ComponentName.unflattenFromString(dreamComponent);
             mShowComplications = shouldShowComplications;
-            onStartDream(layoutParams);
+            mDreamOverlayCallback = callback;
+            mService.startDream(this, params);
+        }
+
+
+
+        @Override
+        public void wakeUp() {
+            mService.wakeUp(this, () -> {
+                try {
+                    mDreamOverlayCallback.onWakeUpComplete();
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Could not notify dream of wakeUp", e);
+                }
+            });
         }
 
         @Override
         public void endDream() {
-            onEndDream();
+            mService.endDream(this);
         }
 
+        private void onExitRequested() {
+            try {
+                mDreamOverlayCallback.onExitRequested();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Could not request exit:" + e);
+            }
+        }
+
+        private boolean shouldShowComplications() {
+            return mShowComplications;
+        }
+
+        private ComponentName getComponent() {
+            return mDreamComponent;
+        }
+    }
+
+    private void startDream(OverlayClient client, WindowManager.LayoutParams params) {
+        endDream(mCurrentClient);
+        mCurrentClient = client;
+        onStartDream(params);
+    }
+
+    private void endDream(OverlayClient client) {
+        if (client == null || client != mCurrentClient) {
+            return;
+        }
+
+        onEndDream();
+        mCurrentClient = null;
+    }
+
+    private void wakeUp(OverlayClient client, Runnable callback) {
+        if (mCurrentClient != client) {
+            return;
+        }
+
+        onWakeUp(callback);
+    }
+
+    private IDreamOverlay mDreamOverlay = new IDreamOverlay.Stub() {
         @Override
-        public void wakeUp() {
-            onWakeUp(() -> {
-                try {
-                    mDreamOverlayCallback.onWakeUpComplete();
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Could not notify dream of wakeUp:" + e);
-                }
-            });
+        public void getClient(IDreamOverlayClientCallback callback) {
+            try {
+                callback.onDreamOverlayClient(
+                        new OverlayClient(DreamOverlayService.this));
+            } catch (RemoteException e) {
+                Log.e(TAG, "could not send client to callback", e);
+            }
         }
     };
 
-    IDreamOverlayCallback mDreamOverlayCallback;
-
     public DreamOverlayService() {
     }
 
@@ -110,18 +172,23 @@
      * This method is invoked to request the dream exit.
      */
     public final void requestExit() {
-        try {
-            mDreamOverlayCallback.onExitRequested();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Could not request exit:" + e);
+        if (mCurrentClient == null) {
+            throw new IllegalStateException("requested exit with no dream present");
         }
+
+        mCurrentClient.onExitRequested();
     }
 
     /**
      * Returns whether to show complications on the dream overlay.
      */
     public final boolean shouldShowComplications() {
-        return mShowComplications;
+        if (mCurrentClient == null) {
+            throw new IllegalStateException(
+                    "requested if should show complication when no dream active");
+        }
+
+        return mCurrentClient.shouldShowComplications();
     }
 
     /**
@@ -129,6 +196,10 @@
      * @hide
      */
     public final ComponentName getDreamComponent() {
-        return mDreamComponent;
+        if (mCurrentClient == null) {
+            throw new IllegalStateException("requested dream component when no dream active");
+        }
+
+        return mCurrentClient.getComponent();
     }
 }
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index d378886..d79ea89 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -234,6 +234,7 @@
     private boolean mCanDoze;
     private boolean mDozing;
     private boolean mWindowless;
+    private boolean mOverlayFinishing;
     private int mDozeScreenState = Display.STATE_UNKNOWN;
     private int mDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
 
@@ -248,25 +249,39 @@
     private OverlayConnection mOverlayConnection;
 
     private static class OverlayConnection extends PersistentServiceConnection<IDreamOverlay> {
-        // Overlay set during onBind.
-        private IDreamOverlay mOverlay;
+        // Retrieved Client
+        private IDreamOverlayClient mClient;
+
         // A list of pending requests to execute on the overlay.
-        private final ArrayList<Consumer<IDreamOverlay>> mConsumers = new ArrayList<>();
+        private final ArrayList<Consumer<IDreamOverlayClient>> mConsumers = new ArrayList<>();
+
+        private final IDreamOverlayClientCallback mClientCallback =
+                new IDreamOverlayClientCallback.Stub() {
+            @Override
+            public void onDreamOverlayClient(IDreamOverlayClient client) {
+                mClient = client;
+
+                for (Consumer<IDreamOverlayClient> consumer : mConsumers) {
+                    consumer.accept(mClient);
+                }
+            }
+        };
 
         private final Callback<IDreamOverlay> mCallback = new Callback<IDreamOverlay>() {
             @Override
             public void onConnected(ObservableServiceConnection<IDreamOverlay> connection,
                     IDreamOverlay service) {
-                mOverlay = service;
-                for (Consumer<IDreamOverlay> consumer : mConsumers) {
-                    consumer.accept(mOverlay);
+                try {
+                    service.getClient(mClientCallback);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "could not get DreamOverlayClient", e);
                 }
             }
 
             @Override
             public void onDisconnected(ObservableServiceConnection<IDreamOverlay> connection,
                     int reason) {
-                mOverlay = null;
+                mClient = null;
             }
         };
 
@@ -296,16 +311,16 @@
             super.unbind();
         }
 
-        public void addConsumer(Consumer<IDreamOverlay> consumer) {
+        public void addConsumer(Consumer<IDreamOverlayClient> consumer) {
             execute(() -> {
                 mConsumers.add(consumer);
-                if (mOverlay != null) {
-                    consumer.accept(mOverlay);
+                if (mClient != null) {
+                    consumer.accept(mClient);
                 }
             });
         }
 
-        public void removeConsumer(Consumer<IDreamOverlay> consumer) {
+        public void removeConsumer(Consumer<IDreamOverlayClient> consumer) {
             execute(() -> mConsumers.remove(consumer));
         }
 
@@ -1037,6 +1052,7 @@
         // We must unbind from any overlay connection if we are unbound before finishing.
         if (mOverlayConnection != null) {
             mOverlayConnection.unbind();
+            mOverlayConnection = null;
         }
 
         return super.onUnbind(intent);
@@ -1050,6 +1066,26 @@
      * </p>
      */
     public final void finish() {
+        // If there is an active overlay connection, signal that the dream is ending before
+        // continuing. Note that the overlay cannot rely on the unbound state, since another dream
+        // might have bound to it in the meantime.
+        if (mOverlayConnection != null && !mOverlayFinishing) {
+            // Set mOverlayFinish to true to only allow this consumer to be added once.
+            mOverlayFinishing = true;
+            mOverlayConnection.addConsumer(overlay -> {
+                try {
+                    overlay.endDream();
+                    mOverlayConnection.unbind();
+                    mOverlayConnection = null;
+                    finish();
+                } catch (RemoteException e) {
+                    Log.e(mTag, "could not inform overlay of dream end:" + e);
+                }
+            });
+            mOverlayConnection.clearConsumers();
+            return;
+        }
+
         if (mDebug) Slog.v(mTag, "finish(): mFinished=" + mFinished);
 
         Activity activity = mActivity;
@@ -1066,10 +1102,6 @@
         }
         mFinished = true;
 
-        if (mOverlayConnection != null) {
-            mOverlayConnection.unbind();
-        }
-
         if (mDreamToken == null) {
             if (mDebug) Slog.v(mTag, "finish() called when not attached.");
             stopSelf();
@@ -1272,9 +1304,10 @@
      * Must run on mHandler.
      *
      * @param dreamToken Token for this dream service.
-     * @param started A callback that will be invoked once onDreamingStarted has completed.
+     * @param started    A callback that will be invoked once onDreamingStarted has completed.
      */
-    private void attach(IBinder dreamToken, boolean canDoze, IRemoteCallback started) {
+    private void attach(IBinder dreamToken, boolean canDoze, boolean isPreviewMode,
+            IRemoteCallback started) {
         if (mDreamToken != null) {
             Slog.e(mTag, "attach() called when dream with token=" + mDreamToken
                     + " already attached");
@@ -1322,7 +1355,8 @@
             i.putExtra(DreamActivity.EXTRA_CALLBACK, new DreamActivityCallbacks(mDreamToken));
             final ServiceInfo serviceInfo = fetchServiceInfo(this,
                     new ComponentName(this, getClass()));
-            i.putExtra(DreamActivity.EXTRA_DREAM_TITLE, fetchDreamLabel(this, serviceInfo));
+            i.putExtra(DreamActivity.EXTRA_DREAM_TITLE,
+                    fetchDreamLabel(this, serviceInfo, isPreviewMode));
 
             try {
                 if (!ActivityTaskManager.getService().startDreamActivity(i)) {
@@ -1365,7 +1399,7 @@
 
         mWindow.getDecorView().addOnAttachStateChangeListener(
                 new View.OnAttachStateChangeListener() {
-                    private Consumer<IDreamOverlay> mDreamStartOverlayConsumer;
+                    private Consumer<IDreamOverlayClient> mDreamStartOverlayConsumer;
 
                     @Override
                     public void onViewAttachedToWindow(View v) {
@@ -1389,17 +1423,6 @@
 
                     @Override
                     public void onViewDetachedFromWindow(View v) {
-                        if (mOverlayConnection != null) {
-                            mOverlayConnection.addConsumer(overlay -> {
-                                try {
-                                    overlay.endDream();
-                                } catch (RemoteException e) {
-                                    Log.e(mTag, "could not inform overlay of dream end:" + e);
-                                }
-                            });
-                            mOverlayConnection.clearConsumers();
-                        }
-
                         if (mActivity == null || !mActivity.isChangingConfigurations()) {
                             // Only stop the dream if the view is not detached by relaunching
                             // activity for configuration changes. It is important to also clear
@@ -1408,6 +1431,10 @@
                             mActivity = null;
                             finish();
                         }
+
+                        if (mOverlayConnection != null && mDreamStartOverlayConsumer != null) {
+                            mOverlayConnection.removeConsumer(mDreamStartOverlayConsumer);
+                        }
                     }
                 });
     }
@@ -1445,10 +1472,18 @@
 
     @Nullable
     private static CharSequence fetchDreamLabel(Context context,
-            @Nullable ServiceInfo serviceInfo) {
-        if (serviceInfo == null) return null;
+            @Nullable ServiceInfo serviceInfo,
+            boolean isPreviewMode) {
+        if (serviceInfo == null) {
+            return null;
+        }
         final PackageManager pm = context.getPackageManager();
-        return serviceInfo.loadLabel(pm);
+        final CharSequence dreamLabel = serviceInfo.loadLabel(pm);
+        if (!isPreviewMode || dreamLabel == null) {
+            return dreamLabel;
+        }
+        // When in preview mode, return a special label indicating the dream is in preview.
+        return context.getResources().getString(R.string.dream_preview_title, dreamLabel);
     }
 
     @Nullable
@@ -1504,8 +1539,9 @@
     final class DreamServiceWrapper extends IDreamService.Stub {
         @Override
         public void attach(final IBinder dreamToken, final boolean canDoze,
-                IRemoteCallback started) {
-            mHandler.post(() -> DreamService.this.attach(dreamToken, canDoze, started));
+                final boolean isPreviewMode, IRemoteCallback started) {
+            mHandler.post(
+                    () -> DreamService.this.attach(dreamToken, canDoze, isPreviewMode, started));
         }
 
         @Override
diff --git a/core/java/android/service/dreams/IDreamOverlay.aidl b/core/java/android/service/dreams/IDreamOverlay.aidl
index 0e4bd3b..7ec75a5 100644
--- a/core/java/android/service/dreams/IDreamOverlay.aidl
+++ b/core/java/android/service/dreams/IDreamOverlay.aidl
@@ -16,8 +16,7 @@
 
 package android.service.dreams;
 
-import android.service.dreams.IDreamOverlayCallback;
-import android.view.WindowManager.LayoutParams;
+import android.service.dreams.IDreamOverlayClientCallback;
 
 /**
 * {@link IDreamOverlay} provides a way for a component to annotate a dream with additional view
@@ -28,20 +27,7 @@
 */
 interface IDreamOverlay {
     /**
-    * @param params The {@link LayoutParams} for the associated DreamWindow, including the window
-                    token of the Dream Activity.
-    * @param callback The {@link IDreamOverlayCallback} for requesting actions such as exiting the
-    *                dream.
-    * @param dreamComponent The component name of the dream service requesting overlay.
-    * @param shouldShowComplications Whether the dream overlay should show complications, e.g. clock
-    *                and weather.
+    * Retrieves a client the caller can use to interact with the dream overlay.
     */
-    void startDream(in LayoutParams params, in IDreamOverlayCallback callback,
-        in String dreamComponent, in boolean shouldShowComplications);
-
-    /** Called when the dream is waking, to do any exit animations */
-    void wakeUp();
-
-    /** Called when the dream has ended. */
-    void endDream();
+    void getClient(in IDreamOverlayClientCallback callback);
 }
diff --git a/core/java/android/service/dreams/IDreamOverlayClient.aidl b/core/java/android/service/dreams/IDreamOverlayClient.aidl
new file mode 100644
index 0000000..78b7280
--- /dev/null
+++ b/core/java/android/service/dreams/IDreamOverlayClient.aidl
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.dreams;
+
+import android.service.dreams.IDreamOverlayCallback;
+import android.view.WindowManager.LayoutParams;
+
+/**
+* {@link IDreamOverlayClient} allows {@link DreamService} instances to act upon the dream overlay.
+*
+* @hide
+*/
+interface IDreamOverlayClient {
+    /**
+    * @param params The {@link LayoutParams} for the associated DreamWindow, including the window
+                    token of the Dream Activity.
+    * @param callback The {@link IDreamOverlayCallback} for requesting actions such as exiting the
+    *                dream.
+    * @param dreamComponent The component name of the dream service requesting overlay.
+    * @param shouldShowComplications Whether the dream overlay should show complications, e.g. clock
+    *                and weather.
+    */
+    void startDream(in LayoutParams params, in IDreamOverlayCallback callback,
+        in String dreamComponent, in boolean shouldShowComplications);
+
+    /** Called when the dream is waking, to do any exit animations */
+    void wakeUp();
+
+    /** Called when the dream has ended. */
+    void endDream();
+}
diff --git a/core/java/android/service/dreams/IDreamOverlayClientCallback.aidl b/core/java/android/service/dreams/IDreamOverlayClientCallback.aidl
new file mode 100644
index 0000000..244d999
--- /dev/null
+++ b/core/java/android/service/dreams/IDreamOverlayClientCallback.aidl
@@ -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 android.service.dreams;
+
+import android.service.dreams.IDreamOverlayClient;
+
+/**
+* {@link IDreamOverlayClientCallback} allows receiving a requested {@link IDreamOverlayClient}.
+* @hide
+*/
+interface IDreamOverlayClientCallback {
+    /**
+    * Called with a unique {@link IDreamOverlayClient}.
+    */
+    void onDreamOverlayClient(in IDreamOverlayClient client);
+}
diff --git a/core/java/android/service/dreams/IDreamService.aidl b/core/java/android/service/dreams/IDreamService.aidl
index ce04354..8b5d875 100644
--- a/core/java/android/service/dreams/IDreamService.aidl
+++ b/core/java/android/service/dreams/IDreamService.aidl
@@ -22,7 +22,7 @@
  * @hide
  */
 oneway interface IDreamService {
-    void attach(IBinder windowToken, boolean canDoze, IRemoteCallback started);
+    void attach(IBinder windowToken, boolean canDoze, boolean isPreviewMode, IRemoteCallback started);
     void detach();
     void wakeUp();
 }
diff --git a/core/java/android/service/quickaccesswallet/WalletCard.java b/core/java/android/service/quickaccesswallet/WalletCard.java
index b09d2e9..7aacb9b 100644
--- a/core/java/android/service/quickaccesswallet/WalletCard.java
+++ b/core/java/android/service/quickaccesswallet/WalletCard.java
@@ -16,6 +16,7 @@
 
 package android.service.quickaccesswallet;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.PendingIntent;
@@ -24,28 +25,73 @@
 import android.os.Parcelable;
 import android.text.TextUtils;
 
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+
 /**
  * A {@link WalletCard} can represent anything that a user might carry in their wallet -- a credit
  * card, library card, transit pass, etc. Cards are identified by a String identifier and contain a
- * card image, card image content description, and a {@link PendingIntent} to be used if the user
- * clicks on the card. Cards may be displayed with an icon and label, though these are optional.
+ * card type, card image, card image content description, and a {@link PendingIntent} to be used if
+ * the user clicks on the card. Cards may be displayed with an icon and label, though these are
+ * optional. Valuable cards will also have a second image that will be displayed when the card is
+ * tapped.
  */
+
 public final class WalletCard implements Parcelable {
 
+    /**
+     * Unknown cards refer to cards whose types are unspecified.
+     * @hide
+     */
+    public static final int CARD_TYPE_UNKNOWN = 0;
+
+    /**
+     * Payment cards refer to credit cards, debit cards or any other cards in the wallet used to
+     * make cash-equivalent payments.
+     * @hide
+     */
+    public static final int CARD_TYPE_PAYMENT = 1;
+
+    /**
+     * Valuable cards refer to any cards that are not used for cash-equivalent payment.
+     * This includes event tickets, flights, offers, loyalty cards, gift cards and transit tickets.
+     * @hide
+     */
+    public static final int CARD_TYPE_VALUABLE = 2;
+
     private final String mCardId;
+    private final int mCardType;
     private final Icon mCardImage;
     private final CharSequence mContentDescription;
     private final PendingIntent mPendingIntent;
     private final Icon mCardIcon;
     private final CharSequence mCardLabel;
+    private final Icon mValuableCardSecondaryImage;
 
     private WalletCard(Builder builder) {
         this.mCardId = builder.mCardId;
+        this.mCardType = builder.mCardType;
         this.mCardImage = builder.mCardImage;
         this.mContentDescription = builder.mContentDescription;
         this.mPendingIntent = builder.mPendingIntent;
         this.mCardIcon = builder.mCardIcon;
         this.mCardLabel = builder.mCardLabel;
+        this.mValuableCardSecondaryImage = builder.mValuableCardSecondaryImage;
+    }
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"CARD_TYPE_"}, value = {
+            CARD_TYPE_UNKNOWN,
+            CARD_TYPE_PAYMENT,
+            CARD_TYPE_VALUABLE
+    })
+    public @interface CardType {
     }
 
     @Override
@@ -56,29 +102,44 @@
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeString(mCardId);
+        dest.writeInt(mCardType);
         mCardImage.writeToParcel(dest, flags);
         TextUtils.writeToParcel(mContentDescription, dest, flags);
         PendingIntent.writePendingIntentOrNullToParcel(mPendingIntent, dest);
-        if (mCardIcon == null) {
+        writeIconIfNonNull(mCardIcon, dest, flags);
+        TextUtils.writeToParcel(mCardLabel, dest, flags);
+        writeIconIfNonNull(mValuableCardSecondaryImage, dest, flags);
+
+    }
+
+    /** Utility function called by writeToParcel
+     */
+    private void writeIconIfNonNull(Icon icon,  Parcel dest, int flags) {
+        if (icon == null) {
             dest.writeByte((byte) 0);
         } else {
             dest.writeByte((byte) 1);
-            mCardIcon.writeToParcel(dest, flags);
+            icon.writeToParcel(dest, flags);
         }
-        TextUtils.writeToParcel(mCardLabel, dest, flags);
     }
 
     private static WalletCard readFromParcel(Parcel source) {
         String cardId = source.readString();
+        int cardType = source.readInt();
         Icon cardImage = Icon.CREATOR.createFromParcel(source);
         CharSequence contentDesc = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
         PendingIntent pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(source);
         Icon cardIcon = source.readByte() == 0 ? null : Icon.CREATOR.createFromParcel(source);
         CharSequence cardLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
-        return new Builder(cardId, cardImage, contentDesc, pendingIntent)
+        Icon valuableCardSecondaryImage = source.readByte() == 0 ? null :
+                Icon.CREATOR.createFromParcel(source);
+        Builder builder = new Builder(cardId, cardType, cardImage, contentDesc, pendingIntent)
                 .setCardIcon(cardIcon)
-                .setCardLabel(cardLabel)
-                .build();
+                .setCardLabel(cardLabel);
+
+        return cardType == CARD_TYPE_VALUABLE
+                ? builder.setValuableCardSecondaryImage(valuableCardSecondaryImage).build() :
+                 builder.build();
     }
 
     @NonNull
@@ -104,6 +165,16 @@
     }
 
     /**
+     * Returns the card type.
+     * @hide
+     */
+    @NonNull
+    @CardType
+    public int getCardType() {
+        return mCardType;
+    }
+
+    /**
      * The visual representation of the card. If the card image Icon is a bitmap, it should have a
      * width of {@link GetWalletCardsRequest#getCardWidthPx()} and a height of {@link
      * GetWalletCardsRequest#getCardHeightPx()}.
@@ -158,23 +229,37 @@
     }
 
     /**
-     * Builder for {@link WalletCard} objects. You must to provide cardId, cardImage,
+    * Visual representation of the card when it is tapped. Includes a barcode to scan the card in
+     * addition to the information in the primary image.
+     * @hide
+    */
+    @Nullable
+    public Icon getValuableCardSecondaryImage() {
+        return mValuableCardSecondaryImage;
+    }
+
+    /**
+     * Builder for {@link WalletCard} objects. You must provide cardId, cardImage,
      * contentDescription, and pendingIntent. If the card is opaque and should be shown with
      * elevation, set hasShadow to true. cardIcon and cardLabel are optional.
      */
     public static final class Builder {
         private String mCardId;
+        private int mCardType;
         private Icon mCardImage;
         private CharSequence mContentDescription;
         private PendingIntent mPendingIntent;
         private Icon mCardIcon;
         private CharSequence mCardLabel;
+        private Icon mValuableCardSecondaryImage;
 
         /**
          * @param cardId             The card id must be non-null and unique within the list of
          *                           cards returned. <b>Note:
          *                           </b> this card ID should <b>not</b> contain PII (Personally
          *                           Identifiable Information, such as username or email address).
+         * @param cardType           Integer representing the card type. The card type must be
+         *                           non-null. If not provided, it defaults to unknown.
          * @param cardImage          The visual representation of the card. If the card image Icon
          *                           is a bitmap, it should have a width of {@link
          *                           GetWalletCardsRequest#getCardWidthPx()} and a height of {@link
@@ -193,15 +278,30 @@
          *                           request device unlock before sending the pending intent. It is
          *                           recommended that the pending intent be immutable (use {@link
          *                           PendingIntent#FLAG_IMMUTABLE}).
+         * @hide
+         */
+        public Builder(@NonNull String cardId,
+                @NonNull @CardType int cardType,
+                @NonNull Icon cardImage,
+                @NonNull CharSequence contentDescription,
+                @NonNull PendingIntent pendingIntent
+        ) {
+            mCardId = cardId;
+            mCardType = cardType;
+            mCardImage = cardImage;
+            mContentDescription = contentDescription;
+            mPendingIntent = pendingIntent;
+        }
+
+        /**
+         * Called when a card type is not provided.
          */
         public Builder(@NonNull String cardId,
                 @NonNull Icon cardImage,
                 @NonNull CharSequence contentDescription,
                 @NonNull PendingIntent pendingIntent) {
-            mCardId = cardId;
-            mCardImage = cardImage;
-            mContentDescription = contentDescription;
-            mPendingIntent = pendingIntent;
+            this(cardId, WalletCard.CARD_TYPE_UNKNOWN, cardImage, contentDescription,
+                    pendingIntent);
         }
 
         /**
@@ -236,6 +336,19 @@
         }
 
         /**
+         * Visual representation of the card when it is tapped. Includes a barcode to scan the card
+         * in addition to the information in the primary image.
+         * @hide
+         */
+        @NonNull
+        public Builder setValuableCardSecondaryImage(@Nullable Icon valuableCardSecondaryImage) {
+            Preconditions.checkState(mCardType == CARD_TYPE_VALUABLE,
+                    "This field can only be set on valuable cards");
+            mValuableCardSecondaryImage = valuableCardSecondaryImage;
+            return this;
+        }
+
+        /**
          * Builds a new {@link WalletCard} instance.
          *
          * @return A built response.
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 2d1a41e..d53ad17 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -61,6 +61,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
@@ -170,6 +171,7 @@
             Float.NEGATIVE_INFINITY);
 
     private static final int NOTIFY_COLORS_RATE_LIMIT_MS = 1000;
+    private static final int PROCESS_LOCAL_COLORS_INTERVAL_MS = 1000;
 
     private static final boolean ENABLE_WALLPAPER_DIMMING =
             SystemProperties.getBoolean("persist.debug.enable_wallpaper_dimming", true);
@@ -179,6 +181,9 @@
     private final ArrayList<Engine> mActiveEngines
             = new ArrayList<Engine>();
 
+    private Handler mBackgroundHandler;
+    private HandlerThread mBackgroundThread;
+
     static final class WallpaperCommand {
         String action;
         int x;
@@ -197,14 +202,6 @@
      */
     public class Engine {
         IWallpaperEngineWrapper mIWallpaperEngine;
-        final ArraySet<RectF> mLocalColorAreas = new ArraySet<>(4);
-        final ArraySet<RectF> mLocalColorsToAdd = new ArraySet<>(4);
-
-        // 2D matrix [x][y] to represent a page of a portion of a window
-        EngineWindowPage[] mWindowPages = new EngineWindowPage[0];
-        Bitmap mLastScreenshot;
-        int mLastWindowPage = -1;
-        private boolean mResetWindowPages;
 
         // Copies from mIWallpaperEngine.
         HandlerCaller mCaller;
@@ -266,21 +263,37 @@
 
         final Object mLock = new Object();
         boolean mOffsetMessageEnqueued;
+
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         float mPendingXOffset;
         float mPendingYOffset;
         float mPendingXOffsetStep;
         float mPendingYOffsetStep;
+
+        /**
+         * local color extraction related fields
+         * to be used by the background thread only (except the atomic boolean)
+         */
+        final ArraySet<RectF> mLocalColorAreas = new ArraySet<>(4);
+        final ArraySet<RectF> mLocalColorsToAdd = new ArraySet<>(4);
+        private long mLastProcessLocalColorsTimestamp;
+        private AtomicBoolean mProcessLocalColorsPending = new AtomicBoolean(false);
+        private int mPixelCopyCount = 0;
+        // 2D matrix [x][y] to represent a page of a portion of a window
+        EngineWindowPage[] mWindowPages = new EngineWindowPage[0];
+        Bitmap mLastScreenshot;
+        private boolean mResetWindowPages;
+
         boolean mPendingSync;
         MotionEvent mPendingMove;
         boolean mIsInAmbientMode;
 
-        // Needed for throttling onComputeColors.
+        // used to throttle onComputeColors
         private long mLastColorInvalidation;
         private final Runnable mNotifyColorsChanged = this::notifyColorsChanged;
+
         private final Supplier<Long> mClockFunction;
         private final Handler mHandler;
-
         private Display mDisplay;
         private Context mDisplayContext;
         private int mDisplayState;
@@ -820,7 +833,7 @@
                             + "was not established.");
                 }
                 mResetWindowPages = true;
-                processLocalColors(mPendingXOffset, mPendingXOffsetStep);
+                processLocalColors();
             } catch (RemoteException e) {
                 Log.w(TAG, "Can't notify system because wallpaper connection was lost.", e);
             }
@@ -1359,7 +1372,7 @@
                             resetWindowPages();
                             mSession.finishDrawing(mWindow, null /* postDrawTransaction */,
                                                    Integer.MAX_VALUE);
-                            processLocalColors(mPendingXOffset, mPendingXOffsetStep);
+                            processLocalColors();
                         }
                         reposition();
                         reportEngineShown(shouldWaitForEngineShown());
@@ -1504,7 +1517,7 @@
             if (!mDestroyed) {
                 mVisible = visible;
                 reportVisibility();
-                if (mReportedVisible) processLocalColors(mPendingXOffset, mPendingXOffsetStep);
+                if (mReportedVisible) processLocalColors();
             } else {
                 AnimationHandler.requestAnimatorsEnabled(visible, this);
             }
@@ -1588,12 +1601,41 @@
             }
 
             // setup local color extraction data
-            processLocalColors(xOffset, xOffsetStep);
+            processLocalColors();
         }
 
-        private void processLocalColors(float xOffset, float xOffsetStep) {
+        /**
+         * Thread-safe util to call {@link #processLocalColorsInternal} with a minimum interval of
+         * {@link #PROCESS_LOCAL_COLORS_INTERVAL_MS} between two calls.
+         */
+        private void processLocalColors() {
+            if (mProcessLocalColorsPending.compareAndSet(false, true)) {
+                final long now = mClockFunction.get();
+                final long timeSinceLastColorProcess = now - mLastProcessLocalColorsTimestamp;
+                final long timeToWait = Math.max(0,
+                        PROCESS_LOCAL_COLORS_INTERVAL_MS - timeSinceLastColorProcess);
+
+                mBackgroundHandler.postDelayed(() -> {
+                    mLastProcessLocalColorsTimestamp = now + timeToWait;
+                    mProcessLocalColorsPending.set(false);
+                    processLocalColorsInternal();
+                }, timeToWait);
+            }
+        }
+
+        private void processLocalColorsInternal() {
             // implemented by the wallpaper
             if (supportsLocalColorExtraction()) return;
+            assertBackgroundThread();
+            float xOffset;
+            float xOffsetStep;
+            float wallpaperDimAmount;
+            synchronized (mLock) {
+                xOffset = mPendingXOffset;
+                xOffsetStep = mPendingXOffsetStep;
+                wallpaperDimAmount = mWallpaperDimAmount;
+            }
+
             if (DEBUG) {
                 Log.d(TAG, "processLocalColors " + xOffset + " of step "
                         + xOffsetStep);
@@ -1625,40 +1667,39 @@
 
             float finalXOffsetStep = xOffsetStep;
             float finalXOffset = xOffset;
-            mHandler.post(() -> {
-                Trace.beginSection("WallpaperService#processLocalColors");
-                resetWindowPages();
-                int xPage = xCurrentPage;
-                EngineWindowPage current;
-                if (mWindowPages.length == 0 || (mWindowPages.length != xPages)) {
-                    mWindowPages = new EngineWindowPage[xPages];
-                    initWindowPages(mWindowPages, finalXOffsetStep);
+
+            Trace.beginSection("WallpaperService#processLocalColors");
+            resetWindowPages();
+            int xPage = xCurrentPage;
+            EngineWindowPage current;
+            if (mWindowPages.length == 0 || (mWindowPages.length != xPages)) {
+                mWindowPages = new EngineWindowPage[xPages];
+                initWindowPages(mWindowPages, finalXOffsetStep);
+            }
+            if (mLocalColorsToAdd.size() != 0) {
+                for (RectF colorArea : mLocalColorsToAdd) {
+                    if (!isValid(colorArea)) continue;
+                    mLocalColorAreas.add(colorArea);
+                    int colorPage = getRectFPage(colorArea, finalXOffsetStep);
+                    EngineWindowPage currentPage = mWindowPages[colorPage];
+                    currentPage.setLastUpdateTime(0);
+                    currentPage.removeColor(colorArea);
                 }
-                if (mLocalColorsToAdd.size() != 0) {
-                    for (RectF colorArea : mLocalColorsToAdd) {
-                        if (!isValid(colorArea)) continue;
-                        mLocalColorAreas.add(colorArea);
-                        int colorPage = getRectFPage(colorArea, finalXOffsetStep);
-                        EngineWindowPage currentPage = mWindowPages[colorPage];
-                        currentPage.setLastUpdateTime(0);
-                        currentPage.removeColor(colorArea);
-                    }
-                    mLocalColorsToAdd.clear();
+                mLocalColorsToAdd.clear();
+            }
+            if (xPage >= mWindowPages.length) {
+                if (DEBUG) {
+                    Log.e(TAG, "error xPage >= mWindowPages.length page: " + xPage);
+                    Log.e(TAG, "error on page " + xPage + " out of " + xPages);
+                    Log.e(TAG,
+                            "error on xOffsetStep " + finalXOffsetStep
+                                    + " xOffset " + finalXOffset);
                 }
-                if (xPage >= mWindowPages.length) {
-                    if (DEBUG) {
-                        Log.e(TAG, "error xPage >= mWindowPages.length page: " + xPage);
-                        Log.e(TAG, "error on page " + xPage + " out of " + xPages);
-                        Log.e(TAG,
-                                "error on xOffsetStep " + finalXOffsetStep
-                                        + " xOffset " + finalXOffset);
-                    }
-                    xPage = mWindowPages.length - 1;
-                }
-                current = mWindowPages[xPage];
-                updatePage(current, xPage, xPages, finalXOffsetStep);
-                Trace.endSection();
-            });
+                xPage = mWindowPages.length - 1;
+            }
+            current = mWindowPages[xPage];
+            updatePage(current, xPage, xPages, wallpaperDimAmount);
+            Trace.endSection();
         }
 
         private void initWindowPages(EngineWindowPage[] windowPages, float step) {
@@ -1677,16 +1718,23 @@
             }
         }
 
+        /**
+         * Must be called with the surface lock held.
+         * Must not be called if the surface is not valid.
+         * Will unlock the surface when done using it.
+         */
         void updatePage(EngineWindowPage currentPage, int pageIndx, int numPages,
-                float xOffsetStep) {
+                float wallpaperDimAmount) {
+
+            assertBackgroundThread();
+
             // in case the clock is zero, we start with negative time
             long current = SystemClock.elapsedRealtime() - DEFAULT_UPDATE_SCREENSHOT_DURATION;
             long lapsed = current - currentPage.getLastUpdateTime();
             // Always update the page when the last update time is <= 0
             // This is important especially when the device first boots
-            if (lapsed < DEFAULT_UPDATE_SCREENSHOT_DURATION) {
-                return;
-            }
+            if (lapsed < DEFAULT_UPDATE_SCREENSHOT_DURATION) return;
+
             Surface surface = mSurfaceHolder.getSurface();
             if (!surface.isValid()) return;
             boolean widthIsLarger = mSurfaceSize.x > mSurfaceSize.y;
@@ -1702,33 +1750,42 @@
             Bitmap screenShot = Bitmap.createBitmap(width, height,
                     Bitmap.Config.ARGB_8888);
             final Bitmap finalScreenShot = screenShot;
-            Trace.beginSection("WallpaperService#pixelCopy");
-            PixelCopy.request(surface, screenShot, (res) -> {
-                Trace.endSection();
-                if (DEBUG) Log.d(TAG, "result of pixel copy is " + res);
-                if (res != PixelCopy.SUCCESS) {
-                    Bitmap lastBitmap = currentPage.getBitmap();
-                    // assign the last bitmap taken for now
-                    currentPage.setBitmap(mLastScreenshot);
-                    Bitmap lastScreenshot = mLastScreenshot;
-                    if (lastScreenshot != null && !lastScreenshot.isRecycled()
-                            && !Objects.equals(lastBitmap, lastScreenshot)) {
-                        updatePageColors(currentPage, pageIndx, numPages, xOffsetStep);
+            final String pixelCopySectionName = "WallpaperService#pixelCopy";
+            final int pixelCopyCount = mPixelCopyCount++;
+            Trace.beginAsyncSection(pixelCopySectionName, pixelCopyCount);
+            try {
+                PixelCopy.request(surface, screenShot, (res) -> {
+                    Trace.endAsyncSection(pixelCopySectionName, pixelCopyCount);
+                    if (DEBUG) Log.d(TAG, "result of pixel copy is " + res);
+                    if (res != PixelCopy.SUCCESS) {
+                        Bitmap lastBitmap = currentPage.getBitmap();
+                        // assign the last bitmap taken for now
+                        currentPage.setBitmap(mLastScreenshot);
+                        Bitmap lastScreenshot = mLastScreenshot;
+                        if (lastScreenshot != null && !lastScreenshot.isRecycled()
+                                && !Objects.equals(lastBitmap, lastScreenshot)) {
+                            updatePageColors(currentPage, pageIndx, numPages, wallpaperDimAmount);
+                        }
+                    } else {
+                        mLastScreenshot = finalScreenShot;
+                        // going to hold this lock for a while
+                        currentPage.setBitmap(finalScreenShot);
+                        currentPage.setLastUpdateTime(current);
+                        updatePageColors(currentPage, pageIndx, numPages, wallpaperDimAmount);
                     }
-                } else {
-                    mLastScreenshot = finalScreenShot;
-                    // going to hold this lock for a while
-                    currentPage.setBitmap(finalScreenShot);
-                    currentPage.setLastUpdateTime(current);
-                    updatePageColors(currentPage, pageIndx, numPages, xOffsetStep);
-                }
-            }, mHandler);
-
+                }, mBackgroundHandler);
+            } catch (IllegalArgumentException e) {
+                // this can potentially happen if the surface is invalidated right between the
+                // surface.isValid() check and the PixelCopy operation.
+                // in this case, stop: we'll compute colors on the next processLocalColors call.
+                Log.i(TAG, "Cancelling processLocalColors: exception caught during PixelCopy");
+            }
         }
         // locked by the passed page
-        private void updatePageColors(EngineWindowPage page, int pageIndx, int numPages,
-                float xOffsetStep) {
+        private void updatePageColors(
+                EngineWindowPage page, int pageIndx, int numPages, float wallpaperDimAmount) {
             if (page.getBitmap() == null) return;
+            assertBackgroundThread();
             Trace.beginSection("WallpaperService#updatePageColors");
             if (DEBUG) {
                 Log.d(TAG, "updatePageColorsLocked for page " + pageIndx + " with areas "
@@ -1750,7 +1807,7 @@
                     Log.e(TAG, "Error creating page local color bitmap", e);
                     continue;
                 }
-                WallpaperColors color = WallpaperColors.fromBitmap(target, mWallpaperDimAmount);
+                WallpaperColors color = WallpaperColors.fromBitmap(target, wallpaperDimAmount);
                 target.recycle();
                 WallpaperColors currentColor = page.getColors(area);
 
@@ -1767,17 +1824,26 @@
                                 + " local color callback for area" + area + " for page " + pageIndx
                                 + " of " + numPages);
                     }
-                    try {
-                        mConnection.onLocalWallpaperColorsChanged(area, color,
-                                mDisplayContext.getDisplayId());
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Error calling Connection.onLocalWallpaperColorsChanged", e);
-                    }
+                    mHandler.post(() -> {
+                        try {
+                            mConnection.onLocalWallpaperColorsChanged(area, color,
+                                    mDisplayContext.getDisplayId());
+                        } catch (RemoteException e) {
+                            Log.e(TAG, "Error calling Connection.onLocalWallpaperColorsChanged", e);
+                        }
+                    });
                 }
             }
             Trace.endSection();
         }
 
+        private void assertBackgroundThread() {
+            if (!mBackgroundHandler.getLooper().isCurrentThread()) {
+                throw new IllegalStateException(
+                        "ProcessLocalColors should be called from the background thread");
+            }
+        }
+
         private RectF generateSubRect(RectF in, int pageInx, int numPages) {
             float minLeft = (float) (pageInx) / (float) (numPages);
             float maxRight = (float) (pageInx + 1) / (float) (numPages);
@@ -1802,7 +1868,6 @@
             if (supportsLocalColorExtraction()) return;
             if (!mResetWindowPages) return;
             mResetWindowPages = false;
-            mLastWindowPage = -1;
             for (int i = 0; i < mWindowPages.length; i++) {
                 mWindowPages[i].setLastUpdateTime(0L);
             }
@@ -1828,12 +1893,10 @@
             if (DEBUG) {
                 Log.d(TAG, "addLocalColorsAreas adding local color areas " + regions);
             }
-            mHandler.post(() -> {
+            mBackgroundHandler.post(() -> {
                 mLocalColorsToAdd.addAll(regions);
-                processLocalColors(mPendingXOffset, mPendingYOffset);
+                processLocalColors();
             });
-
-
         }
 
         /**
@@ -1843,7 +1906,7 @@
          */
         public void removeLocalColorsAreas(@NonNull List<RectF> regions) {
             if (supportsLocalColorExtraction()) return;
-            mHandler.post(() -> {
+            mBackgroundHandler.post(() -> {
                 float step = mPendingXOffsetStep;
                 mLocalColorsToAdd.removeAll(regions);
                 mLocalColorAreas.removeAll(regions);
@@ -2474,6 +2537,9 @@
     @Override
     public void onCreate() {
         Trace.beginSection("WPMS.onCreate");
+        mBackgroundThread = new HandlerThread("DefaultWallpaperLocalColorExtractor");
+        mBackgroundThread.start();
+        mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
         super.onCreate();
         Trace.endSection();
     }
@@ -2486,6 +2552,7 @@
             mActiveEngines.get(i).detach();
         }
         mActiveEngines.clear();
+        mBackgroundThread.quitSafely();
         Trace.endSection();
     }
 
diff --git a/core/java/android/util/SparseSetArray.java b/core/java/android/util/SparseSetArray.java
index b7873b7..61f29a4 100644
--- a/core/java/android/util/SparseSetArray.java
+++ b/core/java/android/util/SparseSetArray.java
@@ -139,4 +139,9 @@
     public T valueAt(int intIndex, int valueIndex) {
         return mData.valueAt(intIndex).valueAt(valueIndex);
     }
+
+    /** @return The set of values for key at position {@code intIndex}. */
+    public ArraySet<T> valuesAt(int intIndex) {
+        return mData.valueAt(intIndex);
+    }
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 333efad..d7480e5 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import static android.content.res.Resources.ID_NULL;
+import static android.os.Trace.TRACE_TAG_APP;
 import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP;
 import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
 import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_BOUNDS;
@@ -985,6 +986,22 @@
     private static boolean sAcceptZeroSizeDragShadow;
 
     /**
+     * When true, measure and layout passes of all the newly attached views will be logged with
+     * {@link Trace}, so we can better debug jank due to complex view hierarchies.
+     */
+    private static boolean sTraceLayoutSteps;
+
+    /**
+     * When not null, emits a {@link Trace} instant event and the stacktrace every time a relayout
+     * of a class having this name happens.
+     */
+    private static String sTraceRequestLayoutClass;
+
+    /** Used to avoid computing the full strings each time when layout tracing is enabled. */
+    @Nullable
+    private ViewTraversalTracingStrings mTracingStrings;
+
+    /**
      * Prior to R, {@link #dispatchApplyWindowInsets} had an issue:
      * <p>The modified insets changed by {@link #onApplyWindowInsets} were passed to the
      * entire view hierarchy in prefix order, including siblings as well as siblings of parents
@@ -3532,6 +3549,8 @@
      *                  1               PFLAG4_HAS_TRANSLATION_TRANSIENT_STATE
      *                 1                PFLAG4_DRAG_A11Y_STARTED
      *                1                 PFLAG4_AUTO_HANDWRITING_INITIATION_ENABLED
+     *             1                    PFLAG4_TRAVERSAL_TRACING_ENABLED
+     *            1                     PFLAG4_RELAYOUT_TRACING_ENABLED
      * |-------|-------|-------|-------|
      */
 
@@ -3612,6 +3631,19 @@
      * Indicates that the view enables auto handwriting initiation.
      */
     private static final int PFLAG4_AUTO_HANDWRITING_ENABLED = 0x000010000;
+
+    /**
+     * When set, measure and layout passes of this view will be logged with {@link Trace}, so we
+     * can better debug jank due to complex view hierarchies.
+     */
+    private static final int PFLAG4_TRAVERSAL_TRACING_ENABLED = 0x000040000;
+
+    /**
+     * When set, emits a {@link Trace} instant event and stacktrace every time a requestLayout of
+     * this class happens.
+     */
+    private static final int PFLAG4_RELAYOUT_TRACING_ENABLED = 0x000080000;
+
     /* End of masks for mPrivateFlags4 */
 
     /** @hide */
@@ -6537,6 +6569,15 @@
         out.append(mRight);
         out.append(',');
         out.append(mBottom);
+        appendId(out);
+        if (mAutofillId != null) {
+            out.append(" aid="); out.append(mAutofillId);
+        }
+        out.append("}");
+        return out.toString();
+    }
+
+    void appendId(StringBuilder out) {
         final int id = getId();
         if (id != NO_ID) {
             out.append(" #");
@@ -6568,11 +6609,6 @@
                 }
             }
         }
-        if (mAutofillId != null) {
-            out.append(" aid="); out.append(mAutofillId);
-        }
-        out.append("}");
-        return out.toString();
     }
 
     /**
@@ -20767,6 +20803,14 @@
         if (isFocused()) {
             notifyFocusChangeToImeFocusController(true /* hasFocus */);
         }
+
+        if (sTraceLayoutSteps) {
+            setTraversalTracingEnabled(true);
+        }
+        if (sTraceRequestLayoutClass != null
+                && sTraceRequestLayoutClass.equals(getClass().getSimpleName())) {
+            setRelayoutTracingEnabled(true);
+        }
     }
 
     /**
@@ -23666,6 +23710,30 @@
         return o instanceof ViewGroup && ((ViewGroup) o).isLayoutModeOptical();
     }
 
+    /**
+     * Enable measure/layout debugging on traces.
+     *
+     * @see Trace
+     * @hide
+     */
+    public static void setTraceLayoutSteps(boolean traceLayoutSteps) {
+        sTraceLayoutSteps = traceLayoutSteps;
+    }
+
+    /**
+     * Enable request layout tracing classes with {@code s} simple name.
+     * <p>
+     * When set, a {@link Trace} instant event and a log with the stacktrace is emitted every
+     * time a requestLayout of a class matching {@code s} name happens.
+     * This applies only to views attached from this point onwards.
+     *
+     * @see Trace#instant(long, String)
+     * @hide
+     */
+    public static void setTracedRequestLayoutClassClass(String s) {
+        sTraceRequestLayoutClass = s;
+    }
+
     private boolean setOpticalFrame(int left, int top, int right, int bottom) {
         Insets parentInsets = mParent instanceof View ?
                 ((View) mParent).getOpticalInsets() : Insets.NONE;
@@ -23700,7 +23768,13 @@
     @SuppressWarnings({"unchecked"})
     public void layout(int l, int t, int r, int b) {
         if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
+            if (isTraversalTracingEnabled()) {
+                Trace.beginSection(mTracingStrings.onMeasureBeforeLayout);
+            }
             onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
+            if (isTraversalTracingEnabled()) {
+                Trace.endSection();
+            }
             mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
         }
 
@@ -23713,7 +23787,13 @@
                 setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
 
         if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
+            if (isTraversalTracingEnabled()) {
+                Trace.beginSection(mTracingStrings.onLayout);
+            }
             onLayout(changed, l, t, r, b);
+            if (isTraversalTracingEnabled()) {
+                Trace.endSection();
+            }
 
             if (shouldDrawRoundScrollbar()) {
                 if(mRoundScrollbarRenderer == null) {
@@ -26270,6 +26350,25 @@
         return (viewRoot != null && viewRoot.isInLayout());
     }
 
+    /** To be used only for debugging purposes. */
+    private void printStackStrace(String name) {
+        Log.d(VIEW_LOG_TAG, "---- ST:" + name);
+
+        StringBuilder sb = new StringBuilder();
+        StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
+        int startIndex = 1;
+        int endIndex = Math.min(stackTraceElements.length, startIndex + 20); // max 20 entries.
+        for (int i = startIndex; i < endIndex; i++) {
+            StackTraceElement s = stackTraceElements[i];
+            sb.append(s.getMethodName())
+                    .append("(")
+                    .append(s.getFileName())
+                    .append(":")
+                    .append(s.getLineNumber())
+                    .append(") <- ");
+        }
+        Log.d(VIEW_LOG_TAG, name + ": " + sb);
+    }
     /**
      * Call this when something has changed which has invalidated the
      * layout of this view. This will schedule a layout pass of the view
@@ -26283,6 +26382,12 @@
      */
     @CallSuper
     public void requestLayout() {
+        if (isRelayoutTracingEnabled()) {
+            Trace.instantForTrack(TRACE_TAG_APP, "requestLayoutTracing",
+                    mTracingStrings.classSimpleName);
+            printStackStrace(mTracingStrings.requestLayoutStacktracePrefix);
+        }
+
         if (mMeasureCache != null) mMeasureCache.clear();
 
         if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
@@ -26376,8 +26481,14 @@
 
             int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
             if (cacheIndex < 0 || sIgnoreMeasureCache) {
+                if (isTraversalTracingEnabled()) {
+                    Trace.beginSection(mTracingStrings.onMeasure);
+                }
                 // measure ourselves, this should set the measured dimension flag back
                 onMeasure(widthMeasureSpec, heightMeasureSpec);
+                if (isTraversalTracingEnabled()) {
+                    Trace.endSection();
+                }
                 mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
             } else {
                 long value = mMeasureCache.valueAt(cacheIndex);
@@ -31547,6 +31658,38 @@
                 == PFLAG4_AUTO_HANDWRITING_ENABLED;
     }
 
+    private void setTraversalTracingEnabled(boolean enabled) {
+        if (enabled) {
+            if (mTracingStrings == null) {
+                mTracingStrings = new ViewTraversalTracingStrings(this);
+            }
+            mPrivateFlags4 |= PFLAG4_TRAVERSAL_TRACING_ENABLED;
+        } else {
+            mPrivateFlags4 &= ~PFLAG4_TRAVERSAL_TRACING_ENABLED;
+        }
+    }
+
+    private boolean isTraversalTracingEnabled() {
+        return (mPrivateFlags4 & PFLAG4_TRAVERSAL_TRACING_ENABLED)
+                == PFLAG4_TRAVERSAL_TRACING_ENABLED;
+    }
+
+    private void setRelayoutTracingEnabled(boolean enabled) {
+        if (enabled) {
+            if (mTracingStrings == null) {
+                mTracingStrings = new ViewTraversalTracingStrings(this);
+            }
+            mPrivateFlags4 |= PFLAG4_RELAYOUT_TRACING_ENABLED;
+        } else {
+            mPrivateFlags4 &= ~PFLAG4_RELAYOUT_TRACING_ENABLED;
+        }
+    }
+
+    private boolean isRelayoutTracingEnabled() {
+        return (mPrivateFlags4 & PFLAG4_RELAYOUT_TRACING_ENABLED)
+                == PFLAG4_RELAYOUT_TRACING_ENABLED;
+    }
+
     /**
      * Collects a {@link ViewTranslationRequest} which represents the content to be translated in
      * the view.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 43bbcfb..953f17a 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -78,7 +78,6 @@
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
-import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
@@ -712,6 +711,7 @@
 
     // These are accessed by multiple threads.
     final Rect mWinFrame; // frame given by window manager.
+    private final Rect mLastLayoutFrame;
     Rect mOverrideInsetsFrame;
 
     final Rect mPendingBackDropFrame = new Rect();
@@ -932,6 +932,7 @@
         mHeight = -1;
         mDirty = new Rect();
         mWinFrame = new Rect();
+        mLastLayoutFrame = new Rect();
         mWindow = new W(this);
         mLeashToken = new Binder();
         mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
@@ -1113,6 +1114,8 @@
         // Update the last resource config in case the resource configuration was changed while
         // activity relaunched.
         updateLastConfigurationFromResources(getConfiguration());
+        // Make sure to report the completion of draw for relaunch with preserved window.
+        reportNextDraw("rebuilt");
     }
 
     private Configuration getConfiguration() {
@@ -1303,7 +1306,7 @@
                         UNSPECIFIED_LENGTH, UNSPECIFIED_LENGTH,
                         mInsetsController.getRequestedVisibilities(), 1f /* compactScale */,
                         mTmpFrames);
-                setFrame(mTmpFrames.frame);
+                setFrame(mTmpFrames.frame, true /* withinRelayout */);
                 registerBackCallbackOnWindow();
                 if (!WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) {
                     // For apps requesting legacy back behavior, we add a compat callback that
@@ -1388,6 +1391,8 @@
                                 listener, listener.data, mHandler, true /*waitForPresentTime*/);
                         mAttachInfo.mThreadedRenderer.addObserver(mHardwareRendererObserver);
                     }
+                    // Update unbuffered request when set the root view.
+                    mUnbufferedInputSource = mView.mUnbufferedInputSource;
                 }
 
                 view.assignParent(this);
@@ -1824,7 +1829,7 @@
             onMovedToDisplay(displayId, mLastConfigurationFromResources);
         }
 
-        setFrame(frame);
+        setFrame(frame, false /* withinRelayout */);
         mTmpFrames.displayFrame.set(displayFrame);
         if (mTmpFrames.attachedFrame != null && attachedFrame != null) {
             mTmpFrames.attachedFrame.set(attachedFrame);
@@ -5740,7 +5745,7 @@
                         mTmpFrames.frame.right = l + w;
                         mTmpFrames.frame.top = t;
                         mTmpFrames.frame.bottom = t + h;
-                        setFrame(mTmpFrames.frame);
+                        setFrame(mTmpFrames.frame, false /* withinRelayout */);
                         maybeHandleWindowMove(mWinFrame);
                     }
                     break;
@@ -8210,7 +8215,7 @@
             // If the position and the size of the frame are both changed, it will trigger a BLAST
             // sync, and we still need to call relayout to obtain the syncSeqId. Otherwise, we just
             // need to send attributes via relayoutAsync.
-            final Rect oldFrame = mWinFrame;
+            final Rect oldFrame = mLastLayoutFrame;
             final Rect newFrame = mTmpFrames.frame;
             final boolean positionChanged =
                     newFrame.top != oldFrame.top || newFrame.left != oldFrame.left;
@@ -8340,7 +8345,7 @@
             params.restore();
         }
 
-        setFrame(mTmpFrames.frame);
+        setFrame(mTmpFrames.frame, true /* withinRelayout */);
         return relayoutResult;
     }
 
@@ -8375,8 +8380,18 @@
         mIsSurfaceOpaque = opaque;
     }
 
-    private void setFrame(Rect frame) {
+    /**
+     * Set the mWinFrame of this window.
+     * @param frame the new frame of this window.
+     * @param withinRelayout {@code true} if this setting is within the relayout, or is the initial
+     *                       setting. That will make sure in the relayout process, we always compare
+     *                       the window frame with the last processed window frame.
+     */
+    private void setFrame(Rect frame, boolean withinRelayout) {
         mWinFrame.set(frame);
+        if (withinRelayout) {
+            mLastLayoutFrame.set(frame);
+        }
 
         final WindowConfiguration winConfig = getCompatWindowConfiguration();
         mPendingBackDropFrame.set(mPendingDragResizing && !winConfig.useWindowFrameForBackdrop()
@@ -8617,6 +8632,8 @@
 
         mInsetsController.dump(prefix, writer);
 
+        mOnBackInvokedDispatcher.dump(prefix, writer);
+
         writer.println(prefix + "View Hierarchy:");
         dumpViewHierarchy(innerPrefix, writer, mView);
     }
diff --git a/core/java/android/view/ViewTraversalTracingStrings.java b/core/java/android/view/ViewTraversalTracingStrings.java
new file mode 100644
index 0000000..7dde87b
--- /dev/null
+++ b/core/java/android/view/ViewTraversalTracingStrings.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.os.Trace;
+
+/**
+ * Keeps and caches strings used to trace {@link View} traversals.
+ * <p>
+ * This is done to avoid expensive computations of them every time, which can improve performance.
+ */
+class ViewTraversalTracingStrings {
+
+    /** {@link Trace} tag used to mark {@link View#onMeasure(int, int)}. */
+    public final String onMeasure;
+
+    /** {@link Trace} tag used to mark {@link View#onLayout(boolean, int, int, int, int)}. */
+    public final String onLayout;
+
+    /** Caches the view simple name to avoid re-computations. */
+    public final String classSimpleName;
+
+    /** Prefix for request layout stacktraces output in logs. */
+    public final String requestLayoutStacktracePrefix;
+
+    /** {@link Trace} tag used to mark {@link View#onMeasure(int, int)} happening before layout. */
+    public final String onMeasureBeforeLayout;
+
+    /**
+     * @param v {@link View} from where to get the class name.
+     */
+    ViewTraversalTracingStrings(View v) {
+        String className = v.getClass().getSimpleName();
+        classSimpleName = className;
+        onMeasureBeforeLayout = getTraceName("onMeasureBeforeLayout", className, v);
+        onMeasure = getTraceName("onMeasure", className, v);
+        onLayout = getTraceName("onLayout", className, v);
+        requestLayoutStacktracePrefix = "requestLayout " + className;
+    }
+
+    private String getTraceName(String sectionName, String className, View v) {
+        StringBuilder out = new StringBuilder();
+        out.append(sectionName);
+        out.append(" ");
+        out.append(className);
+        v.appendId(out);
+        return out.substring(0, Math.min(out.length() - 1, Trace.MAX_SECTION_NAME_LEN - 1));
+    }
+}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index a37c244..17df585 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -814,8 +814,8 @@
     }
 
     /**
-     * Activity level {@link android.content.pm.PackageManager.Property PackageManager
-     * .Property} for an app to inform the system that the activity can be opted-in or opted-out
+     * Application level {@link android.content.pm.PackageManager.Property PackageManager
+     * .Property} for an app to inform the system that the app can be opted-in or opted-out
      * from the compatibility treatment that avoids {@link
      * android.app.Activity#setRequestedOrientation} loops. The loop can be trigerred by
      * ignoreRequestedOrientation display setting enabled on the device or by the landscape natural
@@ -833,17 +833,17 @@
      *     <li>Camera compatibility force rotation treatment is active for the package.
      * </ul>
      *
-     * <p>Setting this property to {@code false} informs the system that the activity must be
+     * <p>Setting this property to {@code false} informs the system that the app must be
      * opted-out from the compatibility treatment even if the device manufacturer has opted the app
      * into the treatment.
      *
      * <p><b>Syntax:</b>
      * <pre>
-     * &lt;activity&gt;
+     * &lt;application&gt;
      *   &lt;property
      *     android:name="android.window.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION"
      *     android:value="true|false"/&gt;
-     * &lt;/activity&gt;
+     * &lt;/application&gt;
      * </pre>
      *
      * @hide
@@ -853,8 +853,45 @@
             "android.window.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION";
 
     /**
-     * Activity level {@link android.content.pm.PackageManager.Property PackageManager
-     * .Property} for an app to inform the system that the activity should be excluded from the
+     * Application level {@link android.content.pm.PackageManager.Property PackageManager
+     * .Property} for an app to inform the system that the application can be opted-in or opted-out
+     * from the compatibility treatment that enables sending a fake focus event for unfocused
+     * resumed split screen activities. This is needed because some game engines wait to get
+     * focus before drawing the content of the app which isn't guaranteed by default in multi-window
+     * modes.
+     *
+     * <p>Device manufacturers can enable this treatment using their discretion on a per-device
+     * basis to improve display compatibility. The treatment also needs to be specifically enabled
+     * on a per-app basis afterwards. This can either be done by device manufacturers or developers.
+     *
+     * <p>With this property set to {@code true}, the system will apply the treatment only if the
+     * device manufacturer had previously enabled it on the device. A fake focus event will be sent
+     * to the app after it is resumed only if the app is in split-screen.
+     *
+     * <p>Setting this property to {@code false} informs the system that the activity must be
+     * opted-out from the compatibility treatment even if the device manufacturer has opted the app
+     * into the treatment.
+     *
+     * <p>If the property remains unset the system will apply the treatment only if it had
+     * previously been enabled both at the device and app level by the device manufacturer.
+     *
+     * <p><b>Syntax:</b>
+     * <pre>
+     * &lt;application&gt;
+     *   &lt;property
+     *     android:name="android.window.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS"
+     *     android:value="true|false"/&gt;
+     * &lt;/application&gt;
+     * </pre>
+     *
+     * @hide
+     */
+    // TODO(b/263984287): Make this public API.
+    String PROPERTY_COMPAT_ENABLE_FAKE_FOCUS = "android.window.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS";
+
+    /**
+     * Application level {@link android.content.pm.PackageManager.Property PackageManager
+     * .Property} for an app to inform the system that the app should be excluded from the
      * camera compatibility force rotation treatment.
      *
      * <p>The camera compatibility treatment aligns orientations of portrait app window and natural
@@ -879,11 +916,11 @@
      *
      * <p><b>Syntax:</b>
      * <pre>
-     * &lt;activity&gt;
+     * &lt;application&gt;
      *   &lt;property
      *     android:name="android.window.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION"
      *     android:value="true|false"/&gt;
-     * &lt;/activity&gt;
+     * &lt;/application&gt;
      * </pre>
      *
      * @hide
@@ -893,8 +930,8 @@
             "android.window.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION";
 
     /**
-     * Activity level {@link android.content.pm.PackageManager.Property PackageManager
-     * .Property} for an app to inform the system that the activity should be excluded
+     * Application level {@link android.content.pm.PackageManager.Property PackageManager
+     * .Property} for an app to inform the system that the app should be excluded
      * from the activity "refresh" after the camera compatibility force rotation treatment.
      *
      * <p>The camera compatibility treatment aligns orientations of portrait app window and natural
@@ -926,11 +963,11 @@
      *
      * <p><b>Syntax:</b>
      * <pre>
-     * &lt;activity&gt;
+     * &lt;application&gt;
      *   &lt;property
      *     android:name="android.window.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH"
      *     android:value="true|false"/&gt;
-     * &lt;/activity&gt;
+     * &lt;/application&gt;
      * </pre>
      *
      * @hide
@@ -940,7 +977,7 @@
             "android.window.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH";
 
     /**
-     * Activity level {@link android.content.pm.PackageManager.Property PackageManager
+     * Application level {@link android.content.pm.PackageManager.Property PackageManager
      * .Property} for an app to inform the system that the activity should be or shouldn't be
      * "refreshed" after the camera compatibility force rotation treatment using "paused ->
      * resumed" cycle rather than "stopped -> resumed".
@@ -976,11 +1013,11 @@
      *
      * <p><b>Syntax:</b>
      * <pre>
-     * &lt;activity&gt;
+     * &lt;application&gt;
      *   &lt;property
      *     android:name="android.window.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE"
      *     android:value="true|false"/&gt;
-     * &lt;/activity&gt;
+     * &lt;/application&gt;
      * </pre>
      *
      * @hide
@@ -990,6 +1027,77 @@
             "android.window.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE";
 
     /**
+     * Application level {@link android.content.pm.PackageManager.Property PackageManager
+     * .Property} for an app to inform the system that the app should be excluded from the
+     * compatibility override for orientation set by the device manufacturer.
+     *
+     * <p>With this property set to {@code true} or unset, device manufacturers can override
+     * orientation for the app using their discretion to improve display compatibility.
+     *
+     * <p>With this property set to {@code false}, device manufactured per-app override for
+     * orientation won't be applied.
+     *
+     * <p><b>Syntax:</b>
+     * <pre>
+     * &lt;application&gt;
+     *   &lt;property
+     *     android:name="android.window.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE"
+     *     android:value="true|false"/&gt;
+     * &lt;/application&gt;
+     * </pre>
+     *
+     * @hide
+     */
+    // TODO(b/263984287): Make this public API.
+    String PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE =
+            "android.window.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE";
+
+    /**
+     * Application level {@link android.content.pm.PackageManager.Property PackageManager
+     * .Property} for an app to inform the system that the app should be opted-out from the
+     * compatibility override that fixes display orientation to landscape natural orientation when
+     * an activity is fullscreen.
+     *
+     * <p>When this compat override is enabled and while display is fixed to the landscape natural
+     * orientation, the orientation requested by the activity will be still respected by bounds
+     * resolution logic. For instance, if an activity requests portrait orientation, then activity
+     * will appear in the letterbox mode for fixed orientation with the display rotated to the
+     * lanscape natural orientation.
+     *
+     * <p>The treatment is disabled by default but device manufacturers can enable the treatment
+     * using their discretion to improve display compatibility on the displays that have
+     * ignoreOrientationRequest display setting enabled (enables compatibility mode for fixed
+     * orientation, see <a href="https://developer.android.com/guide/practices/enhanced-letterboxing">Enhanced letterboxing</a>
+     * for more details).
+     *
+     * <p>With this property set to {@code true} or unset, the system wiil use landscape display
+     * orientation when the following conditions are met:
+     * <ul>
+     *     <li>Natural orientation of the display is landscape
+     *     <li>ignoreOrientationRequest display setting is enabled
+     *     <li>Activity is fullscreen.
+     *     <li>Device manufacturer enabled the treatment.
+     * </ul>
+     *
+     * <p>With this property set to {@code false}, device manufactured per-app override for
+     * display orientation won't be applied.
+     *
+     * <p><b>Syntax:</b>
+     * <pre>
+     * &lt;application&gt;
+     *   &lt;property
+     *     android:name="android.window.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE"
+     *     android:value="true|false"/&gt;
+     * &lt;/application&gt;
+     * </pre>
+     *
+     * @hide
+     */
+    // TODO(b/263984287): Make this public API.
+    String PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE =
+            "android.window.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE";
+
+    /**
      * @hide
      */
     public static final String PARCEL_KEY_SHORTCUTS_ARRAY = "shortcuts_array";
diff --git a/core/java/android/window/BackEvent.java b/core/java/android/window/BackEvent.java
index 4a4f561..940b133 100644
--- a/core/java/android/window/BackEvent.java
+++ b/core/java/android/window/BackEvent.java
@@ -99,7 +99,21 @@
     }
 
     /**
-     * Returns a value between 0 and 1 on how far along the back gesture is.
+     * Returns a value between 0 and 1 on how far along the back gesture is. This value is
+     * driven by the horizontal location of the touch point, and should be used as the fraction to
+     * seek the predictive back animation with. Specifically,
+     * <ol>
+     * <li>The progress is 0 when the touch is at the starting edge of the screen (left or right),
+     * and animation should seek to its start state.
+     * <li>The progress is approximately 1 when the touch is at the opposite side of the screen,
+     * and animation should seek to its end state. Exact end value may vary depending on
+     * screen size.
+     * </ol>
+     * <li> After the gesture finishes in cancel state, this method keeps getting invoked until the
+     * progress value animates back to 0.
+     * </ol>
+     * In-between locations are linearly interpolated based on horizontal distance from the starting
+     * edge and smooth clamped to 1 when the distance exceeds a system-wide threshold.
      */
     public float getProgress() {
         return mProgress;
diff --git a/core/java/android/window/BackProgressAnimator.java b/core/java/android/window/BackProgressAnimator.java
index 38c52e7..b22f967 100644
--- a/core/java/android/window/BackProgressAnimator.java
+++ b/core/java/android/window/BackProgressAnimator.java
@@ -16,8 +16,10 @@
 
 package android.window;
 
+import android.annotation.NonNull;
 import android.util.FloatProperty;
 
+import com.android.internal.dynamicanimation.animation.DynamicAnimation;
 import com.android.internal.dynamicanimation.animation.SpringAnimation;
 import com.android.internal.dynamicanimation.animation.SpringForce;
 
@@ -123,6 +125,27 @@
         mProgress = 0;
     }
 
+    /**
+     * Animate the back progress animation from current progress to start position.
+     * This should be called when back is cancelled.
+     *
+     * @param finishCallback the callback to be invoked when the progress is reach to 0.
+     */
+    public void onBackCancelled(@NonNull Runnable finishCallback) {
+        final DynamicAnimation.OnAnimationEndListener listener =
+                new DynamicAnimation.OnAnimationEndListener() {
+            @Override
+            public void onAnimationEnd(DynamicAnimation animation, boolean canceled, float value,
+                    float velocity) {
+                mSpring.removeEndListener(this);
+                finishCallback.run();
+                reset();
+            }
+        };
+        mSpring.addEndListener(listener);
+        mSpring.animateToFinalPosition(0);
+    }
+
     private void updateProgressValue(float progress) {
         if (mLastBackEvent == null || mCallback == null || !mStarted) {
             return;
diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.java b/core/java/android/window/ImeOnBackInvokedDispatcher.java
index a0bd7f7..34b75a4 100644
--- a/core/java/android/window/ImeOnBackInvokedDispatcher.java
+++ b/core/java/android/window/ImeOnBackInvokedDispatcher.java
@@ -211,6 +211,12 @@
         IOnBackInvokedCallback getIOnBackInvokedCallback() {
             return mIOnBackInvokedCallback;
         }
+
+        @Override
+        public String toString() {
+            return "ImeCallback=ImeOnBackInvokedCallback@" + mId
+                    + " Callback=" + mIOnBackInvokedCallback;
+        }
     }
 
     /**
diff --git a/core/java/android/window/ProxyOnBackInvokedDispatcher.java b/core/java/android/window/ProxyOnBackInvokedDispatcher.java
index 49acde9..eb3bcae 100644
--- a/core/java/android/window/ProxyOnBackInvokedDispatcher.java
+++ b/core/java/android/window/ProxyOnBackInvokedDispatcher.java
@@ -179,16 +179,7 @@
                 return;
             }
             clearCallbacksOnDispatcher();
-            if (actualDispatcher instanceof ProxyOnBackInvokedDispatcher) {
-                // We don't want to nest ProxyDispatchers, so if we are given on, we unwrap its
-                // actual dispatcher.
-                // This can happen when an Activity is recreated but the Window is preserved (e.g.
-                // when going from split-screen back to single screen)
-                mActualDispatcher =
-                        ((ProxyOnBackInvokedDispatcher) actualDispatcher).mActualDispatcher;
-            } else {
-                mActualDispatcher = actualDispatcher;
-            }
+            mActualDispatcher = actualDispatcher;
             transferCallbacksToDispatcher();
         }
     }
diff --git a/core/java/android/window/TaskFragmentAnimationParams.java b/core/java/android/window/TaskFragmentAnimationParams.java
index 12ad914..c8f6327 100644
--- a/core/java/android/window/TaskFragmentAnimationParams.java
+++ b/core/java/android/window/TaskFragmentAnimationParams.java
@@ -33,6 +33,13 @@
     public static final TaskFragmentAnimationParams DEFAULT =
             new TaskFragmentAnimationParams.Builder().build();
 
+    /**
+     * The default value for animation background color, which means to use the theme window
+     * background color.
+     */
+    @ColorInt
+    public static final int DEFAULT_ANIMATION_BACKGROUND_COLOR = 0;
+
     @ColorInt
     private final int mAnimationBackgroundColor;
 
@@ -104,12 +111,13 @@
     public static final class Builder {
 
         @ColorInt
-        private int mAnimationBackgroundColor = 0;
+        private int mAnimationBackgroundColor = DEFAULT_ANIMATION_BACKGROUND_COLOR;
 
         /**
          * Sets the {@link ColorInt} to use for the background during the animation with this
          * TaskFragment if the animation requires a background. The default value is
-         * {@code 0}, which is to use the theme window background.
+         * {@link #DEFAULT_ANIMATION_BACKGROUND_COLOR}, which is to use the theme window background
+         * color.
          *
          * @param color a packed color int, {@code AARRGGBB}, for the animation background color.
          * @return this {@link Builder}.
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index dd9483a..2b5e16f 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -27,6 +27,7 @@
 import android.view.IWindow;
 import android.view.IWindowSession;
 
+import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -221,6 +222,26 @@
     @NonNull
     private static final BackProgressAnimator mProgressAnimator = new BackProgressAnimator();
 
+    /**
+     * Dump information about this WindowOnBackInvokedDispatcher
+     * @param prefix the prefix that will be prepended to each line of the produced output
+     * @param writer the writer that will receive the resulting text
+     */
+    public void dump(String prefix, PrintWriter writer) {
+        String innerPrefix = prefix + "    ";
+        writer.println(prefix + "WindowOnBackDispatcher:");
+        if (mAllCallbacks.isEmpty()) {
+            writer.println(prefix + "<None>");
+            return;
+        }
+
+        writer.println(innerPrefix + "Top Callback: " + getTopCallback());
+        writer.println(innerPrefix + "Callbacks: ");
+        mAllCallbacks.forEach((callback, priority) -> {
+            writer.println(innerPrefix + "  Callback: " + callback + " Priority=" + priority);
+        });
+    }
+
     static class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub {
         private final WeakReference<OnBackInvokedCallback> mCallback;
 
@@ -255,11 +276,12 @@
         @Override
         public void onBackCancelled() {
             Handler.getMain().post(() -> {
-                mProgressAnimator.reset();
-                final OnBackAnimationCallback callback = getBackAnimationCallback();
-                if (callback != null) {
-                    callback.onBackCancelled();
-                }
+                mProgressAnimator.onBackCancelled(() -> {
+                    final OnBackAnimationCallback callback = getBackAnimationCallback();
+                    if (callback != null) {
+                        callback.onBackCancelled();
+                    }
+                });
             });
         }
 
diff --git a/core/java/android/window/WindowOrganizer.java b/core/java/android/window/WindowOrganizer.java
index 2a80d02..740fbac 100644
--- a/core/java/android/window/WindowOrganizer.java
+++ b/core/java/android/window/WindowOrganizer.java
@@ -61,9 +61,7 @@
      * Apply multiple WindowContainer operations at once.
      *
      * Note that using this API requires the caller to hold
-     * {@link android.Manifest.permission#MANAGE_ACTIVITY_TASKS}, unless the caller is using
-     * {@link TaskFragmentOrganizer}, in which case it is allowed to change TaskFragment that is
-     * created by itself.
+     * {@link android.Manifest.permission#MANAGE_ACTIVITY_TASKS}.
      *
      * @param t The transaction to apply.
      * @param callback This transaction will use the synchronization scheme described in
@@ -72,8 +70,7 @@
      * @return An ID for the sync operation which will later be passed to transactionReady callback.
      *         This lets the caller differentiate overlapping sync operations.
      */
-    @RequiresPermission(value = android.Manifest.permission.MANAGE_ACTIVITY_TASKS,
-            conditional = true)
+    @RequiresPermission(value = android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
     public int applySyncTransaction(@NonNull WindowContainerTransaction t,
             @NonNull WindowContainerTransactionCallback callback) {
         try {
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 43be031..1b901f5 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -21,6 +21,7 @@
 import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
 
 import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.getTargets;
+import static com.android.internal.os.RoSystemProperties.SUPPORT_ONE_HANDED_MODE;
 import static com.android.internal.util.ArrayUtils.convertToLongArray;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
@@ -147,11 +148,13 @@
                             Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
                             "1" /* Value to enable */, "0" /* Value to disable */,
                             R.string.color_correction_feature_name));
-            featuresMap.put(ONE_HANDED_COMPONENT_NAME,
-                    new ToggleableFrameworkFeatureInfo(
-                            Settings.Secure.ONE_HANDED_MODE_ACTIVATED,
-                            "1" /* Value to enable */, "0" /* Value to disable */,
-                            R.string.one_handed_mode_feature_name));
+            if (SUPPORT_ONE_HANDED_MODE) {
+                featuresMap.put(ONE_HANDED_COMPONENT_NAME,
+                        new ToggleableFrameworkFeatureInfo(
+                                Settings.Secure.ONE_HANDED_MODE_ACTIVATED,
+                                "1" /* Value to enable */, "0" /* Value to disable */,
+                                R.string.one_handed_mode_feature_name));
+            }
             featuresMap.put(REDUCE_BRIGHT_COLORS_COMPONENT_NAME,
                     new ToggleableFrameworkFeatureInfo(
                             Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
index fc2c8cc..2d87745 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
@@ -25,6 +25,7 @@
 import static com.android.internal.accessibility.AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME;
 import static com.android.internal.accessibility.util.AccessibilityUtils.getAccessibilityServiceFragmentType;
 import static com.android.internal.accessibility.util.ShortcutUtils.isShortcutContained;
+import static com.android.internal.os.RoSystemProperties.SUPPORT_ONE_HANDED_MODE;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.AccessibilityShortcutInfo;
@@ -209,6 +210,7 @@
                         context.getString(R.string.accessibility_magnification_chooser_text),
                         context.getDrawable(R.drawable.ic_accessibility_magnification),
                         Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
+        targets.add(magnification);
 
         final ToggleAllowListingFeatureTarget daltonizer =
                 new ToggleAllowListingFeatureTarget(context,
@@ -219,6 +221,7 @@
                         context.getString(R.string.color_correction_feature_name),
                         context.getDrawable(R.drawable.ic_accessibility_color_correction),
                         Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED);
+        targets.add(daltonizer);
 
         final ToggleAllowListingFeatureTarget colorInversion =
                 new ToggleAllowListingFeatureTarget(context,
@@ -229,16 +232,20 @@
                         context.getString(R.string.color_inversion_feature_name),
                         context.getDrawable(R.drawable.ic_accessibility_color_inversion),
                         Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
+        targets.add(colorInversion);
 
-        final ToggleAllowListingFeatureTarget oneHandedMode =
-                new ToggleAllowListingFeatureTarget(context,
-                        shortcutType,
-                        isShortcutContained(context, shortcutType,
-                                ONE_HANDED_COMPONENT_NAME.flattenToString()),
-                        ONE_HANDED_COMPONENT_NAME.flattenToString(),
-                        context.getString(R.string.one_handed_mode_feature_name),
-                        context.getDrawable(R.drawable.ic_accessibility_one_handed),
-                        Settings.Secure.ONE_HANDED_MODE_ACTIVATED);
+        if (SUPPORT_ONE_HANDED_MODE) {
+            final ToggleAllowListingFeatureTarget oneHandedMode =
+                    new ToggleAllowListingFeatureTarget(context,
+                            shortcutType,
+                            isShortcutContained(context, shortcutType,
+                                    ONE_HANDED_COMPONENT_NAME.flattenToString()),
+                            ONE_HANDED_COMPONENT_NAME.flattenToString(),
+                            context.getString(R.string.one_handed_mode_feature_name),
+                            context.getDrawable(R.drawable.ic_accessibility_one_handed),
+                            Settings.Secure.ONE_HANDED_MODE_ACTIVATED);
+            targets.add(oneHandedMode);
+        }
 
         final ToggleAllowListingFeatureTarget reduceBrightColors =
                 new ToggleAllowListingFeatureTarget(context,
@@ -249,11 +256,6 @@
                         context.getString(R.string.reduce_bright_colors_feature_name),
                         context.getDrawable(R.drawable.ic_accessibility_reduce_bright_colors),
                         Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED);
-
-        targets.add(magnification);
-        targets.add(daltonizer);
-        targets.add(colorInversion);
-        targets.add(oneHandedMode);
         targets.add(reduceBrightColors);
 
         return targets;
diff --git a/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java b/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java
index e3cc4f1..d0b5811 100644
--- a/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java
+++ b/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java
@@ -47,7 +47,9 @@
                 /* num_app_provided_app_targets = 6 */ appProvidedApp,
                 /* is_workprofile = 7 */ isWorkprofile,
                 /* previewType = 8 */ typeFromPreviewInt(previewType),
-                /* intentType = 9 */ typeFromIntentString(intent));
+                /* intentType = 9 */ typeFromIntentString(intent),
+                /* num_provided_custom_actions = 10 */ 0,
+                /* reselection_action_provided = 11 */ false);
     }
 
     @Override
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 4f7f8ba..3303c0e 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -567,24 +567,12 @@
     public static final String VOLUME_SEPARATE_NOTIFICATION = "volume_separate_notification";
 
     /**
-     * (boolean) Whether the clipboard overlay is enabled.
-     */
-    public static final String CLIPBOARD_OVERLAY_ENABLED = "clipboard_overlay_enabled";
-
-    /**
      * (boolean) Whether widget provider info would be saved to / loaded from system persistence
      * layer as opposed to individual manifests in respective apps.
      */
     public static final String PERSISTS_WIDGET_PROVIDER_INFO = "persists_widget_provider_info";
 
     /**
-     * (boolean) Whether the clipboard overlay shows an edit button (as opposed to requiring tapping
-     * the preview to send an edit intent).
-     */
-    public static final String CLIPBOARD_OVERLAY_SHOW_EDIT_BUTTON =
-            "clipboard_overlay_show_edit_button";
-
-    /**
      * (boolean) Whether to show smart chips (based on TextClassifier) in the clipboard overlay.
      */
     public static final String CLIPBOARD_OVERLAY_SHOW_ACTIONS = "clipboard_overlay_show_actions";
diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
new file mode 100644
index 0000000..c946db1
--- /dev/null
+++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
@@ -0,0 +1,188 @@
+/**
+ * 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.internal.config.sysui;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Build;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Provides a central definition of debug SystemUI's SystemProperties flags, and their defaults.
+ *
+ * The main feature of this class is that it encodes a system-wide default for each flag which can
+ *  be updated by engineers with a single-line CL.
+ *
+ * NOTE: Because flag values returned by this class are not cached, it is important that developers
+ *  understand the intricacies of changing values and how that applies to their own code.
+ *  Generally, the best practice is to set the property, and then restart the device so that any
+ *  processes with stale state can be updated.  However, if your code has no state derived from the
+ *  flag value and queries it any time behavior is relevant, then it may be safe to change the flag
+ *  and not immediately reboot.
+ *
+ * To enable flags in debuggable builds, use the following commands:
+ *
+ * $ adb shell setprop persist.sysui.whatever_the_flag true
+ * $ adb reboot
+ *
+ * @hide
+ */
+public class SystemUiSystemPropertiesFlags {
+
+    /** The teamfood flag allows multiple features to be opted into at once. */
+    public static final Flag TEAMFOOD = devFlag("persist.sysui.teamfood");
+
+    /**
+     * Flags related to notification features
+     */
+    public static final class NotificationFlags {
+
+        /**
+         * FOR DEVELOPMENT / TESTING ONLY!!!
+         * Forcibly demote *ALL* FSI notifications as if no apps have the app op permission.
+         * NOTE: enabling this implies SHOW_STICKY_HUN_FOR_DENIED_FSI in SystemUI
+         */
+        public static final Flag FSI_FORCE_DEMOTE =
+                devFlag("persist.sysui.notification.fsi_force_demote");
+
+        /** Gating the feature which shows FSI-denied notifications as Sticky HUNs */
+        public static final Flag SHOW_STICKY_HUN_FOR_DENIED_FSI =
+                devFlag("persist.sysui.notification.show_sticky_hun_for_denied_fsi");
+
+        /** Gating the ability for users to dismiss ongoing event notifications */
+        public static final Flag ALLOW_DISMISS_ONGOING =
+                devFlag("persist.sysui.notification.ongoing_dismissal");
+
+        /** Gating the redaction of OTP notifications on the lockscreen */
+        public static final Flag OTP_REDACTION =
+                devFlag("persist.sysui.notification.otp_redaction");
+
+    }
+
+    //// == End of flags.  Everything below this line is the implementation. == ////
+
+    /** The interface used for resolving SystemUI SystemProperties Flags to booleans. */
+    public interface FlagResolver {
+        /** Is the flag enabled? */
+        boolean isEnabled(Flag flag);
+    }
+
+    /** The primary, immutable resolver returned by getResolver() */
+    private static final FlagResolver
+            MAIN_RESOLVER =
+            Build.IS_DEBUGGABLE ? new DebugResolver() : new ProdResolver();
+
+    /**
+     * On debuggable builds, this can be set to override the resolver returned by getResolver().
+     * This can be useful to override flags when testing components that do not allow injecting the
+     * SystemUiPropertiesFlags resolver they use.
+     * Always set this to null when tests tear down.
+     */
+    @VisibleForTesting
+    public static FlagResolver TEST_RESOLVER = null;
+
+    /** Get the resolver for this device configuration. */
+    public static FlagResolver getResolver() {
+        if (Build.IS_DEBUGGABLE && TEST_RESOLVER != null) {
+            Log.i("SystemUiSystemPropertiesFlags", "Returning debug resolver " + TEST_RESOLVER);
+            return TEST_RESOLVER;
+        }
+        return MAIN_RESOLVER;
+    }
+
+    /**
+     * Creates a flag that is enabled by default in debuggable builds.
+     * It can be enabled by setting this flag's SystemProperty to 1.
+     *
+     * This flag is ALWAYS disabled in release builds.
+     */
+    @VisibleForTesting
+    public static Flag devFlag(String name) {
+        return new Flag(name, false, null);
+    }
+
+    /**
+     * Creates a flag that is disabled by default in debuggable builds.
+     * It can be enabled or force-disabled by setting this flag's SystemProperty to 1 or 0.
+     * If this flag's SystemProperty is not set, the flag can be enabled by setting the
+     * TEAMFOOD flag's SystemProperty to 1.
+     *
+     * This flag is ALWAYS disabled in release builds.
+     */
+    @VisibleForTesting
+    public static Flag teamfoodFlag(String name) {
+        return new Flag(name, false, TEAMFOOD);
+    }
+
+    /**
+     * Creates a flag that is enabled by default in debuggable builds.
+     * It can be enabled by setting this flag's SystemProperty to 0.
+     *
+     * This flag is ALWAYS enabled in release builds.
+     */
+    @VisibleForTesting
+    public static Flag releasedFlag(String name) {
+        return new Flag(name, true, null);
+    }
+
+    /** Represents a developer-switchable gate for a feature. */
+    public static final class Flag {
+        public final String mSysPropKey;
+        public final boolean mDefaultValue;
+        @Nullable
+        public final Flag mDebugDefault;
+
+        /** constructs a new flag.  only visible for testing the class */
+        @VisibleForTesting
+        public Flag(@NonNull String sysPropKey, boolean defaultValue, @Nullable Flag debugDefault) {
+            mSysPropKey = sysPropKey;
+            mDefaultValue = defaultValue;
+            mDebugDefault = debugDefault;
+        }
+    }
+
+    /** Implementation of the interface used in release builds. */
+    @VisibleForTesting
+    public static final class ProdResolver implements
+            FlagResolver {
+        @Override
+        public boolean isEnabled(Flag flag) {
+            return flag.mDefaultValue;
+        }
+    }
+
+    /** Implementation of the interface used in debuggable builds. */
+    @VisibleForTesting
+    public static class DebugResolver implements FlagResolver {
+        @Override
+        public final boolean isEnabled(Flag flag) {
+            if (flag.mDebugDefault == null) {
+                return getBoolean(flag.mSysPropKey, flag.mDefaultValue);
+            }
+            return getBoolean(flag.mSysPropKey, isEnabled(flag.mDebugDefault));
+        }
+
+        /** Look up the value; overridable for tests to avoid needing to set SystemProperties */
+        @VisibleForTesting
+        public boolean getBoolean(String key, boolean defaultValue) {
+            return SystemProperties.getBoolean(key, defaultValue);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
index 09e409b..ac4976f 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
@@ -298,18 +298,16 @@
                 BatteryStats.Uid.PROCESS_STATE_FOREGROUND, realtimeUs,
                 BatteryStats.STATS_SINCE_CHARGED);
 
-        totalForegroundDurationUs += uid.getProcessStateTime(
-                BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE, realtimeUs,
-                BatteryStats.STATS_SINCE_CHARGED);
-
         return totalForegroundDurationUs / 1000;
     }
 
     private long getProcessBackgroundTimeMs(BatteryStats.Uid uid, long realtimeUs) {
-        return uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_BACKGROUND, realtimeUs,
-                BatteryStats.STATS_SINCE_CHARGED) / 1000;
+        return (uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_BACKGROUND,
+                realtimeUs, BatteryStats.STATS_SINCE_CHARGED)
+                + uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE,
+                realtimeUs, BatteryStats.STATS_SINCE_CHARGED))
+                / 1000;
     }
-
     private BatteryUsageStats getAggregatedBatteryUsageStats(BatteryUsageStatsQuery query) {
         final boolean includePowerModels = (query.getFlags()
                 & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0;
diff --git a/core/java/com/android/internal/os/RoSystemProperties.java b/core/java/com/android/internal/os/RoSystemProperties.java
index 98d81c9..cccd80e 100644
--- a/core/java/com/android/internal/os/RoSystemProperties.java
+++ b/core/java/com/android/internal/os/RoSystemProperties.java
@@ -31,6 +31,8 @@
             SystemProperties.getInt("ro.factorytest", 0);
     public static final String CONTROL_PRIVAPP_PERMISSIONS =
             SystemProperties.get("ro.control_privapp_permissions");
+    public static final boolean SUPPORT_ONE_HANDED_MODE =
+            SystemProperties.getBoolean("ro.support_one_handed_mode", /* def= */ false);
 
     // ------ ro.hdmi.* -------- //
     /**
diff --git a/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java b/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java
index 205c5fd..f1ed3be 100644
--- a/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java
+++ b/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java
@@ -56,6 +56,9 @@
         }
     };
 
+    /**
+     * Registers the observer for all users.
+     */
     public void register() {
         ContentResolver r = mContext.getContentResolver();
         r.registerContentObserver(
@@ -73,6 +76,26 @@
                 mOnPropertiesChangedListener);
     }
 
+    /**
+     * Registers the observer for the calling user.
+     */
+    public void registerForCallingUser() {
+        ContentResolver r = mContext.getContentResolver();
+        r.registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.BACK_GESTURE_INSET_SCALE_LEFT),
+                false, this);
+        r.registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT),
+                false, this);
+        r.registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE),
+                false, this);
+        DeviceConfig.addOnPropertiesChangedListener(
+                DeviceConfig.NAMESPACE_SYSTEMUI,
+                runnable -> mMainHandler.post(runnable),
+                mOnPropertiesChangedListener);
+    }
+
     public void unregister() {
         mContext.getContentResolver().unregisterContentObserver(this);
         DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
@@ -86,12 +109,46 @@
         }
     }
 
+    /**
+     * Returns the left sensitivity for the current user.  To be used in code that runs primarily
+     * in one user's process.
+     */
     public int getLeftSensitivity(Resources userRes) {
-        return getSensitivity(userRes, Settings.Secure.BACK_GESTURE_INSET_SCALE_LEFT);
+        final float scale = Settings.Secure.getFloatForUser(mContext.getContentResolver(),
+                Settings.Secure.BACK_GESTURE_INSET_SCALE_LEFT, 1.0f, UserHandle.USER_CURRENT);
+        return (int) (getUnscaledInset(userRes) * scale);
     }
 
+    /**
+     * Returns the left sensitivity for the calling user.  To be used in code that runs in a
+     * per-user process.
+     */
+    @SuppressWarnings("NonUserGetterCalled")
+    public int getLeftSensitivityForCallingUser(Resources userRes) {
+        final float scale = Settings.Secure.getFloat(mContext.getContentResolver(),
+                Settings.Secure.BACK_GESTURE_INSET_SCALE_LEFT, 1.0f);
+        return (int) (getUnscaledInset(userRes) * scale);
+    }
+
+    /**
+     * Returns the right sensitivity for the current user.  To be used in code that runs primarily
+     * in one user's process.
+     */
     public int getRightSensitivity(Resources userRes) {
-        return getSensitivity(userRes, Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT);
+        final float scale = Settings.Secure.getFloatForUser(mContext.getContentResolver(),
+                Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT, 1.0f, UserHandle.USER_CURRENT);
+        return (int) (getUnscaledInset(userRes) * scale);
+    }
+
+    /**
+     * Returns the right sensitivity for the calling user.  To be used in code that runs in a
+     * per-user process.
+     */
+    @SuppressWarnings("NonUserGetterCalled")
+    public int getRightSensitivityForCallingUser(Resources userRes) {
+        final float scale = Settings.Secure.getFloat(mContext.getContentResolver(),
+                Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT, 1.0f);
+        return (int) (getUnscaledInset(userRes) * scale);
     }
 
     public boolean areNavigationButtonForcedVisible() {
@@ -99,7 +156,7 @@
                 Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) == 0;
     }
 
-    private int getSensitivity(Resources userRes, String side) {
+    private float getUnscaledInset(Resources userRes) {
         final DisplayMetrics dm = userRes.getDisplayMetrics();
         final float defaultInset = userRes.getDimension(
                 com.android.internal.R.dimen.config_backGestureInset) / dm.density;
@@ -110,8 +167,6 @@
                 : defaultInset;
         final float inset = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, backGestureInset,
                 dm);
-        final float scale = Settings.Secure.getFloatForUser(
-                mContext.getContentResolver(), side, 1.0f, UserHandle.USER_CURRENT);
-        return (int) (inset * scale);
+        return inset;
     }
 }
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index fb38bba..bb69192f 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -379,8 +379,12 @@
             // window, as we'll be skipping the addView in handleResumeActivity(), and
             // the token will not be updated as for a new window.
             getAttributes().token = preservedWindow.getAttributes().token;
-            mProxyOnBackInvokedDispatcher.setActualDispatcher(
-                    preservedWindow.getOnBackInvokedDispatcher());
+            final ViewRootImpl viewRoot = mDecor.getViewRootImpl();
+            if (viewRoot != null) {
+                // Clear the old callbacks and attach to the new window.
+                viewRoot.getOnBackInvokedDispatcher().clear();
+                onViewRootImplSet(viewRoot);
+            }
         }
         // Even though the device doesn't support picture-in-picture mode,
         // an user can force using it through developer options.
diff --git a/core/java/com/android/internal/util/ScreenshotRequest.java b/core/java/com/android/internal/util/ScreenshotRequest.java
index 1902f80..c8b7def 100644
--- a/core/java/com/android/internal/util/ScreenshotRequest.java
+++ b/core/java/com/android/internal/util/ScreenshotRequest.java
@@ -173,6 +173,9 @@
         public Builder(
                 @WindowManager.ScreenshotType int type,
                 @WindowManager.ScreenshotSource int source) {
+            if (type != TAKE_SCREENSHOT_FULLSCREEN && type != TAKE_SCREENSHOT_PROVIDED_IMAGE) {
+                throw new IllegalArgumentException("Invalid screenshot type requested!");
+            }
             mType = type;
             mSource = source;
         }
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index b1610d7..8952f37 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -428,7 +428,7 @@
         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
         return 0;
     } else if (err != NO_ERROR) {
-        jniThrowException(env, OutOfResourcesException, NULL);
+        jniThrowException(env, OutOfResourcesException, statusToString(err).c_str());
         return 0;
     }
 
diff --git a/core/proto/android/service/notification.proto b/core/proto/android/service/notification.proto
index 8e4006a..e029af4 100644
--- a/core/proto/android/service/notification.proto
+++ b/core/proto/android/service/notification.proto
@@ -110,11 +110,20 @@
     // All of this type/caption enabled for current profiles.
     repeated android.content.ComponentNameProto enabled = 3;
 
-
     repeated ManagedServiceInfoProto live_services = 4;
 
+    // Was: repeated ComponentNameProto, when snoozed services were not per-user-id.
+    reserved 5;
+
+    message SnoozedServices {
+        option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+        optional int32 user_id = 1;
+        repeated android.content.ComponentNameProto snoozed = 2;
+    }
+
     // Snoozed for current profiles.
-    repeated android.content.ComponentNameProto snoozed = 5;
+    repeated SnoozedServices snoozed = 6;
 }
 
 message RankingHelperProto {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 6a80d1c..31903e2 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -315,6 +315,7 @@
     <protected-broadcast android:name="android.media.MASTER_BALANCE_CHANGED_ACTION" />
     <protected-broadcast android:name="android.media.SCO_AUDIO_STATE_CHANGED" />
     <protected-broadcast android:name="android.media.ACTION_SCO_AUDIO_STATE_UPDATED" />
+    <protected-broadcast android:name="com.android.server.audio.action.CHECK_MUSIC_ACTIVE" />
 
     <protected-broadcast android:name="android.intent.action.MEDIA_REMOVED" />
     <protected-broadcast android:name="android.intent.action.MEDIA_UNMOUNTED" />
@@ -3893,7 +3894,7 @@
          <p>Should only be requested by the System, should be required by
          TileService declarations.-->
     <permission android:name="android.permission.BIND_QUICK_SETTINGS_TILE"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|recents" />
 
     <!-- Allows SystemUI to request third party controls.
          <p>Should only be requested by the System and required by
diff --git a/core/res/res/layout/notification_material_action_emphasized_tombstone.xml b/core/res/res/layout/notification_material_action_emphasized_tombstone.xml
new file mode 100644
index 0000000..60f10db
--- /dev/null
+++ b/core/res/res/layout/notification_material_action_emphasized_tombstone.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<com.android.internal.widget.EmphasizedNotificationButton
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    style="@style/NotificationEmphasizedAction"
+    android:id="@+id/action0"
+    android:layout_width="wrap_content"
+    android:layout_height="match_parent"
+    android:layout_marginStart="12dp"
+    android:drawablePadding="6dp"
+    android:enabled="false"
+    android:gravity="center"
+    android:singleLine="true"
+    android:ellipsize="end"
+/>
diff --git a/core/res/res/layout/resolve_grid_item.xml b/core/res/res/layout/resolve_grid_item.xml
index 50e6f33..a5ff470 100644
--- a/core/res/res/layout/resolve_grid_item.xml
+++ b/core/res/res/layout/resolve_grid_item.xml
@@ -17,6 +17,7 @@
 */
 -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:id="@+id/item"
               android:orientation="vertical"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index b49f8c2..7372a7e 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Kan gebare vasvang wat op die toestel se vingerafdruksensor uitgevoer word."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Neem skermkiekie"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Kan \'n skermkiekie neem."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"deaktiveer of verander statusbalk"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Laat die program toe om die statusbalk te deaktiveer en stelselikone by te voeg of te verwyder."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"wees die statusbalk"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Bekyk tans volskerm"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Swiep van bo na onder as jy wil uitgaan."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Het dit"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Draai vir ’n beter aansig"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Verlaat gedeelde skerm vir ’n beter aansig"</string>
     <string name="done_label" msgid="7283767013231718521">"Klaar"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Ure se sirkelglyer"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Minute se sirkelglyer"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index d5d8fe5..3310cec 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"በመሣáˆȘያው á‹šáŒŁá‰” አሻራ á‹łáˆłáˆœ ላይ ዹተኹናወኑ á‹šáŒŁá‰” ምልክቶቜን መያዝ á‹­á‰œáˆ‹áˆáą"</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"á‰…áŒœá‰ á‰łá‹Š ገጜ ኄይታን á‹«áŠáˆłáˆ"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"á‹šáˆ›áˆłá‹«á‹ á‰…áŒœá‰ á‰łá‹Š ገጜ ኄይታን áˆ›áŠ•áˆłá‰” á‹­á‰œáˆ‹áˆáą"</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"ዹሁኔቮ አሞሌ አቩዝን ወይም ቀይር"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"ዚሔርዓቔ አዶዎቜን ወደ ሁኔታ አሞሌ ላለማሔቻል ወይም ለማኹል ኄና ለማሔወገዔ ለመተግበáˆȘያው ይፈቅዳሉ፡፡"</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"ዹሁኔታ አሞሌ መሆን"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"ሙሉ ገጜ á‰ áˆ›áˆłá‹šá‰” ላይ"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"áˆˆáˆ˜á‹áŒŁá‰”áŁ ኹላይ á‹ˆá‹°á‰łá‰œ ጠሹግ á‹«á‹”áˆ­áŒ‰áą"</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"ገባኝ"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ለተሻለ ዕይታ ያሜኚርክሩ"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"ለተሻለ ዕይታ ዹተኹፈለ ማያ ገጜን ቔተው ይውጡ"</string>
     <string name="done_label" msgid="7283767013231718521">"ተኹናውኗል"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"á‹šáˆ°á‹“á‰łá‰” ክቄ á‰°áŠ•áˆžáˆ«á‰łá‰œ"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"ዚደቂቃዎቜ ክቄ á‰°áŠ•áˆžáˆ«á‰łá‰œ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index cbf18251..980986e 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -346,6 +346,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"يمكن ŰŁÙ† ŰȘلŰȘÙ‚Ű· Ű§Ù„Ű„ÙŠÙ…Ű§ŰĄŰ§ŰȘ من ۣۯۧ۩ ۧ۳ŰȘŰŽŰčۧ۱ ŰšŰ”Ù…Ű© Ű§Ù„Ű„Ű”ŰšŰč في Ű§Ù„ŰŹÙ‡Ű§ŰČ."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"ۣ۟۰ Ù„Ù‚Ű·Ű© ێۧێ۩"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"يمكن ۣ۟۰ Ù„Ù‚Ű·Ű© ێۧێ۩."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"Ű„ÙŠÙ‚Ű§Ù ŰŽŰ±ÙŠŰ· Ű§Ù„Ű­Ű§Ù„Ű© ŰŁÙˆ ŰȘŰčŰŻÙŠÙ„Ù‡"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Ù„Ù„ŰłÙ…Ű§Ű­ للŰȘŰ·ŰšÙŠÙ‚ ŰšŰ„ÙŠÙ‚Ű§Ù ŰŽŰ±ÙŠŰ· Ű§Ù„Ű­Ű§Ù„Ű© ŰŁÙˆ Ű„Ű¶Ű§ÙŰ© Ű±Ù…ÙˆŰČ Ù†ŰžŰ§Ù… ÙˆŰ„ŰČŰ§Ù„ŰȘÙ‡Ű§."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"Ű§Ù„Űčمل ÙƒŰŽŰ±ÙŠŰ· Ù„Ù„Ű­Ű§Ù„Ű©"</string>
@@ -1844,6 +1846,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"ŰŹŰ§Ű±Ù Ű§Ù„Űč۱۶ ŰšÙ…Ù„ŰĄ Ű§Ù„ŰŽŰ§ŰŽŰ©"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Ù„Ù„ŰźŰ±ÙˆŰŹŰŒ Ù…Ű±Ű± ۚ۳۱ŰčŰ© من ŰŁŰčلى Ű„Ù„Ù‰ ŰŁŰłÙÙ„."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Ű­ŰłÙ†Ù‹Ű§"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"يمكنك ŰȘŰŻÙˆÙŠŰ± Ű§Ù„ŰŹÙ‡Ű§ŰČ Ù„Ű±Ű€ÙŠŰ© ێۧێ۩ مŰčŰ§ÙŠÙ†Ű© Ű§Ù„ÙƒŰ§Ù…ÙŠŰ±Ű§ ŰšŰŽÙƒÙ„ ŰŁÙˆŰ¶Ű­."</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"يمكنك Ű§Ù„ŰźŰ±ÙˆŰŹ من ÙˆŰ¶Űč \"ŰȘÙ‚ŰłÙŠÙ… Ű§Ù„ŰŽŰ§ŰŽŰ©\" Ù„Ű±Ű€ÙŠŰ© ێۧێ۩ مŰčŰ§ÙŠÙ†Ű© Ű§Ù„ÙƒŰ§Ù…ÙŠŰ±Ű§ ŰšŰŽÙƒÙ„ ŰŁÙˆŰ¶Ű­."</string>
     <string name="done_label" msgid="7283767013231718521">"ŰȘم"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"ŰŽŰ±ÙŠŰ· Ű§Ù„ŰȘÙ…Ű±ÙŠŰ± Ű§Ù„ŰŻŰ§ŰŠŰ±ÙŠ Ù„Ù„ŰłŰ§ŰčۧŰȘ"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"ŰŽŰ±ÙŠŰ· Ű§Ù„ŰȘÙ…Ű±ÙŠŰ± Ű§Ù„ŰŻŰ§ŰŠŰ±ÙŠ Ù„Ù„ŰŻÙ‚Ű§ŰŠÙ‚"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index e544de5..77f2305 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"àŠĄàŠżàŠ­àŠŸàŠ‡àŠšàŠŸà§‹à§° àŠ«àŠżàŠ‚àŠ—àŠŸà§°àŠȘà§à§°àŠżàŠŁà§àŠŸ àŠ›à§‡àŠšà§àŠžà§°àŠ€ àŠŠàŠżàŠŻàŠŒàŠŸ àŠšàŠżà§°à§àŠŠà§‡àŠ¶ àŠŹà§àŠœàŠżàŠŹ àŠȘàŠŸà§°à§‡à„€"</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"àŠžà§àŠ•à§à§°à§€àŠšàŠ¶à§àŠŹàŠŸ àŠČàŠ“àŠ•"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"àŠĄàŠżàŠ›àŠȘ্àŠČে’àŠ–àŠšà§° àŠàŠŸàŠŸ àŠžà§àŠ•à§à§°à§€àŠšàŠ¶à§àŠŹàŠŸ àŠČ\'àŠŹ àŠȘàŠŸà§°à§‡à„€"</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"àŠžà§àŠ„àŠżàŠ€àŠż àŠŠàŠŁà§àŠĄ àŠ…àŠ•à§àŠ·àŠź àŠ•à§°àŠ• àŠŹàŠŸ àŠžàŠČàŠšàŠż àŠ•à§°àŠ•"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"àŠžà§àŠ„àŠżàŠ€àŠż àŠŠàŠŁà§àŠĄ àŠ…àŠ•à§àŠ·àŠź àŠ•à§°àŠżàŠŹàŠČৈ àŠŹàŠŸ àŠ›àŠżàŠ·à§àŠŸà§‡àŠź àŠ†àŠ‡àŠ•àŠš àŠ†àŠàŠ€à§°àŠŸàŠŹàŠČৈ àŠàŠȘ্‌àŠŸà§‹àŠ• àŠ…àŠšà§àŠźàŠ€àŠż àŠŠàŠżàŠŻàŠŒà§‡à„€"</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"àŠžà§àŠ„àŠżàŠ€àŠż àŠŠàŠŁà§àŠĄ àŠč\'àŠŹ àŠȘàŠŸà§°à§‡"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"àŠžà§àŠ•à§à§°à§€àŠš àŠȘà§‚à§°à§àŠŁà§°à§‚àŠȘàŠ€ àŠšàŠŸàŠ‡ àŠ†àŠ›à§‡"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"àŠŹàŠŸàŠčàŠżà§° àŠč\'àŠŹàŠČৈ àŠ“àŠȘà§°à§°àŠȘà§°àŠŸ àŠ€àŠČàŠČৈ àŠ›à§‹à§±àŠŸàŠ‡àŠȘ àŠ•à§°àŠ•à„€"</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"àŠŹà§àŠœàŠż àŠȘàŠŸàŠČà§‹àŠ"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"àŠ­àŠŸàŠČàŠ•à§ˆ àŠšàŠŸàŠŹàŠČৈ àŠ˜à§‚à§°àŠŸàŠ“àŠ•"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"àŠ­àŠŸàŠČàŠ•à§ˆ àŠšàŠŸàŠŹàŠČৈ àŠŹàŠżàŠ­àŠŸàŠœàŠżàŠ€ àŠžà§àŠ•à§à§°à§€àŠšà§° àŠȘà§°àŠŸ àŠŹàŠŸàŠčàŠżà§° àŠčàŠ“àŠ•"</string>
     <string name="done_label" msgid="7283767013231718521">"àŠžàŠźà§àŠȘàŠšà§àŠš àŠ•à§°àŠŸ àŠč’àŠČ"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"àŠ˜àŠĄàŠŒà§€à§° àŠŹà§ƒàŠ€à§àŠ€àŠŸàŠ•àŠŸà§° àŠ¶à§àŠČàŠŸàŠ‡àŠĄàŠŸà§°"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"àŠźàŠżàŠšàŠżàŠŸà§° àŠŹà§ƒàŠ€à§àŠ€àŠŸàŠ•àŠŸà§° àŠ¶à§àŠČàŠŸàŠ‡àŠĄàŠŸà§°"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 9a4e5ce..534f1a0e 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Cihazların barmaq izi sensorunda olan ißarələri əldə edə bilər."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Ekran Ɵəkli çəkin"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Ekran Ɵəkli çəkilə bilər."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"status panelini deaktivləƟdir və ya dəyiƟdir"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Tətbiqə status panelini deaktiv etməyə və ya sistem ikonalarını əlavə etmək və ya silmək imkanı verir."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"status paneli edin"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Tam ekrana baxıß"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Çıxmaq üçün yuxarıdan aßağı sürüßdürün."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Anladım"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Daha yaxßı görünüß üçün fırladın"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Daha yaxßı görünüß üçün bölünmüß ekrandan çıxın"</string>
     <string name="done_label" msgid="7283767013231718521">"Hazırdır"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Dairəvi saat slayderi"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Dairəvi dəqiqə slayderi"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index c183da8..ae8c663 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -343,6 +343,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"MoĆŸe da registruje pokrete na senzoru za otisak prsta na uređaju."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Napravi snimak ekrana"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"MoĆŸe da napravi snimak ekrana."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"onemogućavanje ili izmena statusne trake"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Dozvoljava aplikaciji da onemogući statusnu traku ili da dodaje i uklanja sistemske ikone."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"funkcionisanje kao statusna traka"</string>
@@ -1841,6 +1843,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Prikazuje se ceo ekran"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Da biste izašli, prevucite nadole odozgo."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"VaĆŸi"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rotirajte radi boljeg prikaza"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Izađite iz podeljenog ekrana radi boljeg prikaza"</string>
     <string name="done_label" msgid="7283767013231718521">"Gotovo"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"KruĆŸni klizač za sate"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"KruĆŸni klizač za minute"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 2d8f21d..85bd8f9 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -344,6 +344,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"ĐœĐŸĐ¶Đ° Ń€Đ°ŃĐżĐ°Đ·ĐœĐ°ĐČаць Đ¶ŃŃŃ‚Ń‹ ĐœĐ° сĐșĐ°ĐœĐ”Ń€Ń‹ аЎбітĐșаў ĐżĐ°Đ»ŃŒŃ†Đ°Ńž прылаЎы."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Đ—Ń€Đ°Đ±Ń–Ń†ŃŒ Đ·ĐŽŃ‹ĐŒĐ°Đș эĐșŃ€Đ°ĐœĐ°"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"ĐœĐŸĐ¶ĐœĐ° Đ·Ń€Đ°Đ±Ń–Ń†ŃŒ Đ·ĐŽŃ‹ĐŒĐ°Đș эĐșŃ€Đ°ĐœĐ°."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"аЎĐșĐ»ŃŽŃ‡Đ°Ń†ŃŒ ці Đ·ĐŒŃĐœŃŃ†ŃŒ Ń€Đ°ĐŽĐŸĐș ŃŃ‚Đ°ĐœŃƒ"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"ДазĐČĐ°Đ»ŃĐ” прыĐșĐ»Đ°ĐŽĐ°ĐœĐœŃĐŒ аЎĐșĐ»ŃŽŃ‡Đ°Ń†ŃŒ Ń€Đ°ĐŽĐŸĐș ŃŃ‚Đ°ĐœŃƒ Đ°Đ±ĐŸ ЎаЎаĐČаць і ĐČŃ‹ĐŽĐ°Đ»ŃŃ†ŃŒ ŃŃ–ŃŃ‚ŃĐŒĐœŃ‹Ń Đ·ĐœĐ°Ń‡Đșі."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"Đ±Ń‹Ń†ŃŒ ĐżĐ°ĐœŃĐ»Đ»ŃŽ ŃŃ‚Đ°ĐœŃƒ"</string>
@@ -1842,6 +1844,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"ĐŸŃ€Đ°ĐłĐ»ŃĐŽ у ĐżĐŸŃžĐœĐ°ŃĐșŃ€Đ°ĐœĐœŃ‹ĐŒ Ń€ŃĐ¶Ń‹ĐŒĐ”"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Đ”Đ»Ń ĐČыхаЮу праĐČŃĐŽĐ·Ń–Ń†Đ” Đ·ĐČĐ”Ń€Ń…Ńƒ ŃžĐœŃ–Đ·."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Đ—Ń€Đ°Đ·ŃƒĐŒĐ”Đ»Đ°"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ПаĐČŃŃ€ĐœŃƒŃ†ŃŒ ĐŽĐ»Ń лДпшага ĐżŃ€Đ°ĐłĐ»ŃĐŽŃƒ"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"ВыĐčсці Đ· Ń€ŃĐ¶Ń‹ĐŒŃƒ ĐżĐ°ĐŽĐ·Đ”Đ»Đ”ĐœĐ°ĐłĐ° эĐșŃ€Đ°ĐœĐ° ĐŽĐ»Ń лДпшага ĐżŃ€Đ°ĐłĐ»ŃĐŽŃƒ"</string>
     <string name="done_label" msgid="7283767013231718521">"Đ“Đ°Ń‚ĐŸĐČа"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"КругаĐČы ĐżĐ°ŃžĐ·ŃƒĐœĐŸĐș ĐłĐ°ĐŽĐ·Ń–Đœ"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"КругаĐČы ĐżĐ°ŃžĐ·ŃƒĐœĐŸĐș хĐČŃ–Đ»Ń–Đœ"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index d418409..424f2cf 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"ĐœĐŸĐ¶Đ” Ўа улаĐČя Đ¶Đ”ŃŃ‚ĐŸĐČДтД, ОзĐČŃŠŃ€ŃˆĐ”ĐœĐž ĐČърху ŃĐ”ĐœĐ·ĐŸŃ€Đ° за ĐŸŃ‚ĐżĐ”Ń‡Đ°Ń‚ŃŠŃ†Đž ĐœĐ° ŃƒŃŃ‚Ń€ĐŸĐčстĐČĐŸŃ‚ĐŸ."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"ХъзЎаĐČĐ°ĐœĐ” ĐœĐ° Đ”ĐșŃ€Đ°ĐœĐœĐ° ŃĐœĐžĐŒĐșа"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"ĐœĐŸĐ¶Đ” Ўа съзЎаĐČа Đ”ĐșŃ€Đ°ĐœĐœĐž ŃĐœĐžĐŒĐșĐž."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"ЎДаĐșтоĐČĐžŃ€Đ°ĐœĐ” ОлО ĐżŃ€ĐŸĐŒŃĐœĐ° ĐœĐ° Đ»Đ”ĐœŃ‚Đ°Ń‚Đ° ĐœĐ° ŃŃŠŃŃ‚ĐŸŃĐœĐžĐ”Ń‚ĐŸ"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Đ Đ°Đ·Ń€Đ”ŃˆĐ°ĐČа ĐœĐ° ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ”Ń‚ĐŸ Ўа ЎДаĐșтоĐČора Đ»Đ”ĐœŃ‚Đ°Ń‚Đ° ĐœĐ° ŃŃŠŃŃ‚ĐŸŃĐœĐžĐ”Ń‚ĐŸ ОлО Ўа ĐŽĐŸĐ±Đ°ĐČя Đž ĐżŃ€Đ”ĐŒĐ°Ń…ĐČа ŃĐžŃŃ‚Đ”ĐŒĐœĐž ĐžĐșĐŸĐœĐž."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"ĐžĐ·ĐżŃŠĐ»ĐœŃĐČĐ°ĐœĐ” ĐœĐ° Ń€ĐŸĐ»ŃŃ‚Đ° ĐœĐ° Đ»Đ”ĐœŃ‚Đ° ĐœĐ° ŃŃŠŃŃ‚ĐŸŃĐœĐžĐ”Ń‚ĐŸ"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"ИзглДЎ ĐœĐ° Ń†ŃĐ» Đ”ĐșŃ€Đ°Đœ"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"За ĐžĐ·Ń…ĐŸĐŽ ĐżĐ»ŃŠĐ·ĐœĐ”Ń‚Đ” пръст ĐœĐ°ĐŽĐŸĐ»Ńƒ ĐŸŃ‚ ĐłĐŸŃ€ĐœĐ°Ń‚Đ° част."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Разбрах"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ЗаĐČъртДтД за ĐżĐŸ-ĐŽĐŸĐ±ŃŠŃ€ ОзглДЎ"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Đ˜Đ·Đ»Đ”Đ·Ń‚Đ” ĐŸŃ‚ Ń€Đ°Đ·ĐŽĐ”Đ»Đ”ĐœĐžŃ Đ”ĐșŃ€Đ°Đœ за ĐżĐŸ-ĐŽĐŸĐ±ŃŠŃ€ ОзглДЎ"</string>
     <string name="done_label" msgid="7283767013231718521">"Đ“ĐŸŃ‚ĐŸĐČĐŸ"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"ĐšŃ€ŃŠĐłĐŸĐČ ĐżĐ»ŃŠĐ·ĐłĐ°Ń‡ за Ń‡Đ°ŃĐŸĐČДтД"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"ĐšŃ€ŃŠĐłĐŸĐČ ĐżĐ»ŃŠĐ·ĐłĐ°Ń‡ за ĐŒĐžĐœŃƒŃ‚ĐžŃ‚Đ”"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index c8ec80f..cc1e616 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"àŠĄàŠżàŠ­àŠŸàŠ‡àŠžà§‡àŠ° àŠ†àŠ™à§àŠ—à§àŠČà§‡àŠ° àŠ›àŠŸàŠȘà§‡àŠ° àŠžà§‡àŠšà§àŠžàŠ°à§‡àŠ° àŠ‰àŠȘàŠ°à§‡ àŠ‡àŠ™à§àŠ—àŠżàŠ€ àŠ•àŠ°àŠČে àŠŹà§àŠàŠ€à§‡ àŠȘàŠŸàŠ°à§‡à„€"</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"àŠžà§àŠ•à§àŠ°àŠżàŠšàŠ¶àŠŸ àŠšàŠżàŠš"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"àŠĄàŠżàŠžàŠȘ্àŠČà§‡àŠ° àŠàŠ•àŠŸàŠż àŠžà§àŠ•à§àŠ°àŠżàŠšàŠ¶àŠŸ àŠšàŠżàŠ€à§‡ àŠȘàŠŸàŠ°à§‡àŠšà„€"</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"àŠžà§àŠŸà§àŠŻàŠŸàŠŸàŠŸàŠž àŠŹàŠŸàŠ° àŠšàŠżàŠ·à§àŠ•à§àŠ°àŠżàŠŻàŠŒ àŠ…àŠ„àŠŹàŠŸ àŠžàŠ‚àŠ¶à§‹àŠ§àŠš àŠ•àŠ°à§‡"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"àŠ…à§àŠŻàŠŸàŠȘ্àŠČàŠżàŠ•à§‡àŠ¶àŠšàŠ•à§‡ àŠžà§àŠŸà§àŠŻàŠŸàŠŸàŠŸàŠž àŠŹàŠŸàŠ° àŠ…àŠ•à§àŠ·àŠź àŠ•àŠ°àŠ€à§‡ àŠàŠŹàŠ‚ àŠžàŠżàŠžà§àŠŸà§‡àŠź àŠ†àŠ‡àŠ•àŠšàŠ—à§àŠČàŠż àŠžàŠ°àŠŸàŠ€à§‡ àŠŠà§‡àŠŻàŠŒà§·"</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"àŠžà§àŠ„àŠżàŠ€àŠż àŠŠàŠšà§àŠĄà§‡ àŠ„àŠŸàŠ•à§àŠš"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"àŠȘà§‚àŠ°à§àŠŁ àŠžà§àŠ•à§àŠ°àŠżàŠšà§‡ àŠŠà§‡àŠ–àŠŸ àŠčàŠšà§àŠ›à§‡"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"àŠȘà§àŠ°àŠžà§àŠ„àŠŸàŠš àŠ•àŠ°àŠ€à§‡ àŠ‰àŠȘàŠ° àŠ„à§‡àŠ•à§‡ àŠšàŠżàŠšà§‡àŠ° àŠŠàŠżàŠ•à§‡ àŠžà§‹àŠŻàŠŒàŠŸàŠ‡àŠȘ àŠ•àŠ°à§àŠš"</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"àŠŹà§àŠà§‡àŠ›àŠż"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"àŠ†àŠ°àŠ“ àŠ­àŠŸàŠČ àŠ•à§àŠŻàŠŸàŠźà§‡àŠ°àŠŸ àŠ­àŠżàŠ‰ àŠȘàŠŸàŠ“àŠŻàŠŒàŠŸàŠ° àŠœàŠšà§àŠŻ àŠ˜à§‹àŠ°àŠŸàŠš"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"àŠ†àŠ°àŠ“ àŠ­àŠŸàŠČ àŠ•à§àŠŻàŠŸàŠźà§‡àŠ°àŠŸ àŠ­àŠżàŠ‰ àŠȘàŠŸàŠ“àŠŻàŠŒàŠŸàŠ° àŠœàŠšà§àŠŻ àŠžà§àŠȘ্àŠČàŠżàŠŸ àŠžà§àŠ•à§àŠ°àŠżàŠš àŠ„à§‡àŠ•à§‡ àŠŹà§‡àŠ°àŠżàŠŻàŠŒà§‡ àŠ†àŠžà§àŠš"</string>
     <string name="done_label" msgid="7283767013231718521">"àŠžàŠźà§àŠȘàŠšà§àŠš àŠčàŠŻàŠŒà§‡àŠ›à§‡"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"àŠŹà§ƒàŠ€à§àŠ€àŠŸàŠ•àŠŸàŠ° àŠ˜àŠŁà§àŠŸàŠŸ àŠšàŠżàŠ°à§àŠŹàŠŸàŠšàŠ•à§‡àŠ° àŠžà§àŠČàŠŸàŠ‡àŠĄàŠŸàŠ°"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"àŠŹà§ƒàŠ€à§àŠ€àŠŸàŠ•àŠŸàŠ° àŠźàŠżàŠšàŠżàŠŸ àŠšàŠżàŠ°à§àŠŹàŠŸàŠšàŠ•à§‡àŠ° àŠžà§àŠČàŠŸàŠ‡àŠĄàŠŸàŠ°"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 46b3f6c..34b2046 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -343,6 +343,7 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"MoĆŸe zabiljeĆŸiti pokrete na senzoru za otisak prsta uređaja."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"praviti snimke ekrana"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"MoĆŸe napraviti snimak ekrana."</string>
+    <string name="dream_preview_title" msgid="5570751491996100804">"Pregled, <xliff:g id="DREAM_NAME">%1$s</xliff:g>"</string>
     <string name="permlab_statusBar" msgid="8798267849526214017">"onemogućavanje ili mijenjanje statusne trake"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Dozvoljava aplikaciji onemogućavanje statusne trake ili dodavanje i uklanjanje sistemskih ikona."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"funkcioniranje u vidu statusne trake"</string>
@@ -1841,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Prikazuje se cijeli ekran"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Da izađete, prevucite odozgo nadolje."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Razumijem"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rotirajte za bolji prikaz"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Izađite iz podijeljenog ekrana za bolji prikaz"</string>
     <string name="done_label" msgid="7283767013231718521">"Gotovo"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"KruĆŸni klizač za odabir sata"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"KruĆŸni klizač za minute"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index f7745f0..4744f44 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -52,7 +52,7 @@
     <string name="needPuk2" msgid="7032612093451537186">"Escriviu el PUK2 per desbloquejar la targeta SIM."</string>
     <string name="enablePin" msgid="2543771964137091212">"No és correcte; activa el bloqueig de RUIM/SIM."</string>
     <plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
-      <item quantity="many">You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts before SIM is locked.</item>
+      <item quantity="many">Et queden <xliff:g id="NUMBER_1">%d</xliff:g> intents; si no l\'encertes, la SIM es bloquejarà.</item>
       <item quantity="other">Et queden <xliff:g id="NUMBER_1">%d</xliff:g> intents; si no l\'encertes, la SIM es bloquejarà.</item>
       <item quantity="one">Et queda <xliff:g id="NUMBER_0">%d</xliff:g> intent; si no l\'encertes, la SIM es bloquejarà.</item>
     </plurals>
@@ -182,7 +182,7 @@
     <string name="low_memory" product="watch" msgid="3479447988234030194">"L\'emmagatzematge del rellotge està ple. Suprimeix uns quants fitxers per alliberar espai."</string>
     <string name="low_memory" product="tv" msgid="6663680413790323318">"L\'espai d\'emmagatzematge del dispositiu Android TV és ple. Suprimeix alguns fitxers per alliberar espai."</string>
     <string name="low_memory" product="default" msgid="2539532364144025569">"L\'emmagatzematge del telèfon és ple. Suprimeix uns quants fitxers per alliberar espai."</string>
-    <string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{L\'autoritat de certificació s\'ha instal·lat}many{Certificate authorities installed}other{Les autoritats de certificació s\'han instal·lat}}"</string>
+    <string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{L\'autoritat de certificació s\'ha instal·lat}many{Les autoritats de certificació s\'han instal·lat}other{Les autoritats de certificació s\'han instal·lat}}"</string>
     <string name="ssl_ca_cert_noti_by_unknown" msgid="4961102218216815242">"Per un tercer desconegut"</string>
     <string name="ssl_ca_cert_noti_by_administrator" msgid="4564941950768783879">"Per l\'administrador del teu perfil de treball"</string>
     <string name="ssl_ca_cert_noti_managed" msgid="217337232273211674">"Per <xliff:g id="MANAGING_DOMAIN">%s</xliff:g>"</string>
@@ -256,7 +256,7 @@
     <string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"Utilitza aquesta opció en la majoria de circumstàncies. Et permet fer un seguiment del progrés de l\'informe, introduir més dades sobre el problema i fer captures de pantalla. És possible que ometi seccions poc utilitzades que requereixen molt de temps."</string>
     <string name="bugreport_option_full_title" msgid="7681035745950045690">"Informe complet"</string>
     <string name="bugreport_option_full_summary" msgid="1975130009258435885">"Utilitza aquesta opció perquè la interferència en el sistema sigui mínima si el dispositiu no respon o va massa lent, o bé si necessites totes les seccions de l\'informe. No et permet introduir més dades ni fer més captures de pantalla."</string>
-    <string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{Es farà una captura de pantalla de l\'informe d\'errors d\'aquí a # segon.}many{Taking screenshot for bug report in # seconds.}other{Es farà una captura de pantalla de l\'informe d\'errors d\'aquí a # segons.}}"</string>
+    <string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{Es farà una captura de pantalla de l\'informe d\'errors d\'aquí a # segon.}many{Es farà una captura de pantalla de l\'informe d\'errors d\'aquí a # segons.}other{Es farà una captura de pantalla de l\'informe d\'errors d\'aquí a # segons.}}"</string>
     <string name="bugreport_screenshot_success_toast" msgid="7986095104151473745">"S\'ha fet la captura de pantalla amb l\'informe d\'errors"</string>
     <string name="bugreport_screenshot_failure_toast" msgid="6736320861311294294">"No s\'ha pogut fer la captura de pantalla amb l\'informe d\'errors"</string>
     <string name="global_action_toggle_silent_mode" msgid="8464352592860372188">"Mode silenciós"</string>
@@ -343,6 +343,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Pot capturar els gestos fets en el sensor d\'empremtes digitals del dispositiu."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Fer una captura de pantalla"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Pot fer una captura de la pantalla."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"desactivar o modificar la barra d\'estat"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Permet que l\'aplicació desactivi la barra d\'estat o afegeixi i elimini icones del sistema."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"aparèixer a la barra d\'estat"</string>
@@ -1090,7 +1092,7 @@
     <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> vol activar l\'exploració tàctil. Quan l\'exploració per tàctil està activada, pots escoltar o veure les descripcions del contingut seleccionat o utilitzar gestos per interaccionar amb el telèfon."</string>
     <string name="oneMonthDurationPast" msgid="4538030857114635777">"Fa 1 mes"</string>
     <string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"Fa més d\'1 mes"</string>
-    <string name="last_num_days" msgid="2393660431490280537">"{count,plural, =1{Darrer dia (#)}many{Last # days}other{# darrers dies}}"</string>
+    <string name="last_num_days" msgid="2393660431490280537">"{count,plural, =1{Darrer dia (#)}many{# darrers dies}other{# darrers dies}}"</string>
     <string name="last_month" msgid="1528906781083518683">"Darrer mes"</string>
     <string name="older" msgid="1645159827884647400">"Més antigues"</string>
     <string name="preposition_for_date" msgid="2780767868832729599">"el <xliff:g id="DATE">%s</xliff:g>"</string>
@@ -1117,14 +1119,14 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"d\'aquí a <xliff:g id="COUNT">%d</xliff:g> h"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"d\'aquí a <xliff:g id="COUNT">%d</xliff:g> d"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"d\'aquí a <xliff:g id="COUNT">%d</xliff:g> a"</string>
-    <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Fa # minut}many{# minutes ago}other{Fa # minuts}}"</string>
-    <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Fa # hora}many{# hours ago}other{Fa # hores}}"</string>
-    <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Fa # dia}many{# days ago}other{Fa # dies}}"</string>
-    <string name="duration_years_relative" msgid="8731202348869424370">"{count,plural, =1{Fa # any}many{# years ago}other{Fa # anys}}"</string>
-    <string name="duration_minutes_relative_future" msgid="5259574171747708115">"{count,plural, =1{# minut}many{# minutes}other{# minuts}}"</string>
-    <string name="duration_hours_relative_future" msgid="6670440478481140565">"{count,plural, =1{# hora}many{# hours}other{# hores}}"</string>
-    <string name="duration_days_relative_future" msgid="8870658635774250746">"{count,plural, =1{# dia}many{# days}other{# dies}}"</string>
-    <string name="duration_years_relative_future" msgid="8855853883925918380">"{count,plural, =1{# any}many{# years}other{# anys}}"</string>
+    <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Fa # minut}many{Fa # minuts}other{Fa # minuts}}"</string>
+    <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Fa # hora}many{Fa # hores}other{Fa # hores}}"</string>
+    <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Fa # dia}many{Fa # dies}other{Fa # dies}}"</string>
+    <string name="duration_years_relative" msgid="8731202348869424370">"{count,plural, =1{Fa # any}many{Fa # anys}other{Fa # anys}}"</string>
+    <string name="duration_minutes_relative_future" msgid="5259574171747708115">"{count,plural, =1{# minut}many{# minuts}other{# minuts}}"</string>
+    <string name="duration_hours_relative_future" msgid="6670440478481140565">"{count,plural, =1{# hora}many{# hores}other{# hores}}"</string>
+    <string name="duration_days_relative_future" msgid="8870658635774250746">"{count,plural, =1{# dia}many{# dies}other{# dies}}"</string>
+    <string name="duration_years_relative_future" msgid="8855853883925918380">"{count,plural, =1{# any}many{# anys}other{# anys}}"</string>
     <string name="VideoView_error_title" msgid="5750686717225068016">"Problema amb el vídeo"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3782449246085134720">"Aquest vídeo no és vàlid per a la reproducció en aquest dispositiu."</string>
     <string name="VideoView_error_text_unknown" msgid="7658683339707607138">"No es pot reproduir aquest vídeo."</string>
@@ -1511,7 +1513,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Omet"</string>
     <string name="no_matches" msgid="6472699895759164599">"No s\'ha trobat cap coincidència"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Troba-ho a la pàgina"</string>
-    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# coincidència}many{# of {total}}other{# de {total}}}"</string>
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# coincidència}many{# de {total}}other{# de {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Fet"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"S\'està esborrant l\'emmagatzematge compartit…"</string>
     <string name="share" msgid="4157615043345227321">"Comparteix"</string>
@@ -1841,6 +1843,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Mode de pantalla completa"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Per sortir, llisca cap avall des de la part superior."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Entesos"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Gira per a una millor visualització"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Surt de la pantalla dividida per a una millor visualització"</string>
     <string name="done_label" msgid="7283767013231718521">"Fet"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Control circular de les hores"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Control circular dels minuts"</string>
@@ -1864,14 +1868,14 @@
     <string name="data_saver_description" msgid="4995164271550590517">"Per reduir l\'ús de dades, la funció Estalvi de dades evita que determinades aplicacions enviïn o rebin dades en segon pla. L\'aplicació que estiguis fent servir podrà accedir a les dades, però menys sovint. Això vol dir, per exemple, que les imatges no es mostraran fins que no les toquis."</string>
     <string name="data_saver_enable_title" msgid="7080620065745260137">"Vols activar l\'Estalvi de dades?"</string>
     <string name="data_saver_enable_button" msgid="4399405762586419726">"Activa"</string>
-    <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Durant 1 minut (fins a les {formattedTime})}many{For # minutes (until {formattedTime})}other{Durant # minuts (fins a les {formattedTime})}}"</string>
-    <string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{Durant 1 min (fins a les {formattedTime})}many{For # min (until {formattedTime})}other{Durant # min (fins a les {formattedTime})}}"</string>
-    <string name="zen_mode_duration_hours_summary" msgid="3866333100793277211">"{count,plural, =1{Durant 1 hora (fins a les {formattedTime})}many{For # hours (until {formattedTime})}other{Durant # hores (fins a les {formattedTime})}}"</string>
-    <string name="zen_mode_duration_hours_summary_short" msgid="687919813833347945">"{count,plural, =1{Durant 1 h (fins a les {formattedTime})}many{For # hr (until {formattedTime})}other{Durant # h (fins a les {formattedTime})}}"</string>
-    <string name="zen_mode_duration_minutes" msgid="2340007982276569054">"{count,plural, =1{Durant 1 minut}many{For # minutes}other{Durant # minuts}}"</string>
-    <string name="zen_mode_duration_minutes_short" msgid="2435756450204526554">"{count,plural, =1{Durant 1 min}many{For # min}other{Durant # min}}"</string>
-    <string name="zen_mode_duration_hours" msgid="7841806065034711849">"{count,plural, =1{Durant 1 hora}many{For # hours}other{Durant # hores}}"</string>
-    <string name="zen_mode_duration_hours_short" msgid="3666949653933099065">"{count,plural, =1{Durant 1 h}many{For # hr}other{Durant # h}}"</string>
+    <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Durant 1 minut (fins a les {formattedTime})}many{Durant # minuts (fins a les {formattedTime})}other{Durant # minuts (fins a les {formattedTime})}}"</string>
+    <string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{Durant 1 min (fins a les {formattedTime})}many{Durant # min (fins a les {formattedTime})}other{Durant # min (fins a les {formattedTime})}}"</string>
+    <string name="zen_mode_duration_hours_summary" msgid="3866333100793277211">"{count,plural, =1{Durant 1 hora (fins a les {formattedTime})}many{Durant # hores (fins a les {formattedTime})}other{Durant # hores (fins a les {formattedTime})}}"</string>
+    <string name="zen_mode_duration_hours_summary_short" msgid="687919813833347945">"{count,plural, =1{Durant 1 h (fins a les {formattedTime})}many{Durant # h (fins a les {formattedTime})}other{Durant # h (fins a les {formattedTime})}}"</string>
+    <string name="zen_mode_duration_minutes" msgid="2340007982276569054">"{count,plural, =1{Durant 1 minut}many{Durant # minuts}other{Durant # minuts}}"</string>
+    <string name="zen_mode_duration_minutes_short" msgid="2435756450204526554">"{count,plural, =1{Durant 1 min}many{Durant # min}other{Durant # min}}"</string>
+    <string name="zen_mode_duration_hours" msgid="7841806065034711849">"{count,plural, =1{Durant 1 hora}many{Durant # hores}other{Durant # hores}}"</string>
+    <string name="zen_mode_duration_hours_short" msgid="3666949653933099065">"{count,plural, =1{Durant 1 h}many{Durant # h}other{Durant # h}}"</string>
     <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Finalitza: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_until" msgid="2250286190237669079">"Fins a les <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="7046911727540499275">"Fins a les <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (propera alarma)"</string>
@@ -2002,7 +2006,7 @@
     <string name="autofill_save_accessibility_title" msgid="1523225776218450005">"Desa per a emplenament automàtic"</string>
     <string name="autofill_error_cannot_autofill" msgid="6528827648643138596">"El contingut no es pot emplenar automàticament"</string>
     <string name="autofill_picker_no_suggestions" msgid="1076022650427481509">"Cap suggeriment d\'emplenament automàtic"</string>
-    <string name="autofill_picker_some_suggestions" msgid="5560549696296202701">"{count,plural, =1{1 suggeriment d\'emplenament automàtic}many{# autofill suggestions}other{# suggeriments d\'emplenament automàtic}}"</string>
+    <string name="autofill_picker_some_suggestions" msgid="5560549696296202701">"{count,plural, =1{1 suggeriment d\'emplenament automàtic}many{# suggeriments d\'emplenament automàtic}other{# suggeriments d\'emplenament automàtic}}"</string>
     <string name="autofill_save_title" msgid="7719802414283739775">"Vols desar-ho a "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
     <string name="autofill_save_title_with_type" msgid="3002460014579799605">"Vols desar <xliff:g id="TYPE">%1$s</xliff:g> a "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
     <string name="autofill_save_title_with_2types" msgid="3783270967447869241">"Vols desar <xliff:g id="TYPE_0">%1$s</xliff:g> i <xliff:g id="TYPE_1">%2$s</xliff:g> a "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
@@ -2113,7 +2117,7 @@
     <string name="mime_type_presentation_ext" msgid="8761049335564371468">"Presentació <xliff:g id="EXTENSION">%1$s</xliff:g>"</string>
     <string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"El Bluetooth es mantindrà activat durant el mode d\'avió"</string>
     <string name="car_loading_profile" msgid="8219978381196748070">"S\'està carregant"</string>
-    <string name="file_count" msgid="3220018595056126969">"{count,plural, =1{{file_name} i # fitxer}many{{file_name} + # files}other{{file_name} i # fitxers}}"</string>
+    <string name="file_count" msgid="3220018595056126969">"{count,plural, =1{{file_name} i # fitxer}many{{file_name} i # fitxers}other{{file_name} i # fitxers}}"</string>
     <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"No hi ha cap suggeriment de persones amb qui compartir"</string>
     <string name="chooser_all_apps_button_label" msgid="3230427756238666328">"Llista d\'aplicacions"</string>
     <string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Aquesta aplicació no té permís de gravació, però pot capturar àudio a través d\'aquest dispositiu USB."</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index ea61fea..9968691 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -344,6 +344,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"DokáĆŸe rozpoznat gesta zadaná na snímači otiskĆŻ prstĆŻ."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Poƙídit snímek obrazovky"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"MĆŻĆŸe poƙídit snímek obrazovky."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"zakázání či změny stavového ƙádku"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"UmoĆŸĆˆuje aplikaci zakázat stavový ƙádek nebo pƙidat či odebrat systémové ikony."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"vydávání se za stavový ƙádek"</string>
@@ -1842,6 +1844,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Zobrazení celé obrazovky"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"ReĆŸim ukončíte pƙejetím prstem shora dolĆŻ."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Rozumím"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Otočte obrazovku, abyste lépe viděli"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Ukončete reĆŸim rozdělené obrazovky, abyste lépe viděli"</string>
     <string name="done_label" msgid="7283767013231718521">"Hotovo"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Kruhový posuvník hodin"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Kruhový posuvník minut"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index ca619b3..7b04bc9 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -217,7 +217,7 @@
     <string name="silent_mode" msgid="8796112363642579333">"Lydløs"</string>
     <string name="turn_on_radio" msgid="2961717788170634233">"Slå trådløs til"</string>
     <string name="turn_off_radio" msgid="7222573978109933360">"Slå trådløs fra"</string>
-    <string name="screen_lock" msgid="2072642720826409809">"Skærmlås"</string>
+    <string name="screen_lock" msgid="2072642720826409809">"Skærm­lås"</string>
     <string name="power_off" msgid="4111692782492232778">"Sluk"</string>
     <string name="silent_mode_silent" msgid="5079789070221150912">"Ringeren er deaktiveret"</string>
     <string name="silent_mode_vibrate" msgid="8821830448369552678">"Ringervibrering"</string>
@@ -241,7 +241,7 @@
     <string name="global_actions" product="tablet" msgid="4412132498517933867">"Valgmuligheder for tabletcomputeren"</string>
     <string name="global_actions" product="tv" msgid="3871763739487450369">"Valgmuligheder for Android TV"</string>
     <string name="global_actions" product="default" msgid="6410072189971495460">"Indstillinger for telefon"</string>
-    <string name="global_action_lock" msgid="6949357274257655383">"Skærmlås"</string>
+    <string name="global_action_lock" msgid="6949357274257655383">"Skærm­lås"</string>
     <string name="global_action_power_off" msgid="4404936470711393203">"Sluk"</string>
     <string name="global_action_power_options" msgid="1185286119330160073">"Afbryderknap"</string>
     <string name="global_action_restart" msgid="4678451019561687074">"Genstart"</string>
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Kan registrere bevægelser, der foretages på enhedens fingeraftrykssensor."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Tag screenshot"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Kan tage et screenshot af skærmen."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"deaktivere eller redigere statuslinje"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Tillader, at appen kan deaktivere statusbjælken eller tilføje og fjerne systemikoner."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"vær statusbjælken"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Visning i fuld skærm"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Stryg ned fra toppen for at afslutte."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"OK, det er forstået"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Roter, og få en bedre visning"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Afslut opdelt skærm, og få en bedre visning"</string>
     <string name="done_label" msgid="7283767013231718521">"Udfør"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Cirkulær timevælger"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Cirkulær minutvælger"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 876d3a1..7b78645 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Erfasst Touch-Gesten auf dem Fingerabdrucksensor des Geräts."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Screenshot erstellen"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Es kann ein Screenshot des Displays erstellt werden."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"Statusleiste deaktivieren oder ändern"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Ermöglicht der App, die Statusleiste zu deaktivieren oder Systemsymbole hinzuzufügen oder zu entfernen"</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"Statusleiste darstellen"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Vollbildmodus wird aktiviert"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Zum Beenden von oben nach unten wischen"</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Ok"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Drehen, um die Ansicht zu verbessern"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Modus für geteilten Bildschirm beenden, um die Ansicht zu verbessern"</string>
     <string name="done_label" msgid="7283767013231718521">"Fertig"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Kreisförmiger Schieberegler für Stunden"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Kreisförmiger Schieberegler für Minuten"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 172365f..93128aa 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -305,7 +305,7 @@
     <string name="permgrouplab_calendar" msgid="6426860926123033230">"Ημερολόγιο"</string>
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"έχει πρόσβαση στο ημερολόγιό σας"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
-    <string name="permgroupdesc_sms" msgid="5726462398070064542">"στέλνει και να διαβÎŹζει μηνύματα SMS"</string>
+    <string name="permgroupdesc_sms" msgid="5726462398070064542">"στέλνει και διαβÎŹζει μηνύματα SMS"</string>
     <string name="permgrouplab_storage" msgid="17339216290379241">"ΑρχεÎŻα"</string>
     <string name="permgroupdesc_storage" msgid="5378659041354582769">"πρόσβαση στα αρχεÎŻα της συσκευÎźς σας"</string>
     <string name="permgrouplab_readMediaAural" msgid="1858331312624942053">"ΜουσικÎź και Îźχος"</string>
@@ -313,7 +313,7 @@
     <string name="permgrouplab_readMediaVisual" msgid="4724874717811908660">"ΦωτογραφÎŻες και βÎŻντεο"</string>
     <string name="permgroupdesc_readMediaVisual" msgid="4080463241903508688">"πρόσβαση στις φωτογραφÎŻες και τα βÎŻντεο στη συσκευÎź σας"</string>
     <string name="permgrouplab_microphone" msgid="2480597427667420076">"Μικρόφωνο"</string>
-    <string name="permgroupdesc_microphone" msgid="1047786732792487722">"ηχογραφεÎŻ"</string>
+    <string name="permgroupdesc_microphone" msgid="1047786732792487722">"ηχογρÎŹφηση"</string>
     <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"ΣωματικÎź δραστ/τητα"</string>
     <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"πρόσβαση στη σωματικÎź σας δραστηριότητα"</string>
     <string name="permgrouplab_camera" msgid="9090413408963547706">"ΚÎŹμερα"</string>
@@ -323,7 +323,7 @@
     <string name="permgrouplab_calllog" msgid="7926834372073550288">"ΑρχεÎŻα καταγρ. κλÎźσ."</string>
     <string name="permgroupdesc_calllog" msgid="2026996642917801803">"ανÎŹγνωση και εγγραφÎź αρχεÎŻου καταγραφÎźς τηλεφωνικών κλÎźσεων"</string>
     <string name="permgrouplab_phone" msgid="570318944091926620">"Τηλέφωνο"</string>
-    <string name="permgroupdesc_phone" msgid="270048070781478204">"πραγματοποιεÎŻ και να διαχειρÎŻζεται τηλ/κές κλÎźσεις"</string>
+    <string name="permgroupdesc_phone" msgid="270048070781478204">"πραγματοποιεÎŻ και διαχειρÎŻζεται τηλ/κές κλÎźσεις"</string>
     <string name="permgrouplab_sensors" msgid="9134046949784064495">"ΑισθητÎźρες σώματος"</string>
     <string name="permgroupdesc_sensors" msgid="2610631290633747752">"πρόσβαση στα δεδομένα αισθητÎźρα σχετικÎŹ με τις ζωτικές ενδεÎŻξεις σας"</string>
     <string name="permgrouplab_notifications" msgid="5472972361980668884">"ΕιδοποιÎźσεις"</string>
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"ΜπορεÎŻ να αναγνωρÎŻσει κινÎźσεις που εκτελούνται στον αισθητÎźρα δακτυλικού αποτυπώματος της συσκευÎźς."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"ΛÎźψη στιγμιότυπου οθόνης"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"ΜπορεÎŻ να τραβÎźξει στιγμιότυπο της οθόνης."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"απενεργοποιεÎŻ Îź να τροποποιεÎŻ την γραμμÎź κατÎŹστασης"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Επιτρέπει στην εφαρμογÎź να απενεργοποιεÎŻ τη γραμμÎź κατÎŹστασης Îź να προσθέτει και να αφαιρεÎŻ εικονÎŻδια συστÎźματος."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"ορÎŻζεται ως γραμμÎź κατÎŹστασης"</string>
@@ -370,7 +372,7 @@
     <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Επιτρέπει στην εφαρμογÎź την ανÎŹγνωση μηνυμÎŹτων που έχουν μεταδοθεÎŻ μέσω κινητού τηλεφώνου και έχουν ληφθεÎŻ από τη συσκευÎź σας. ΕιδοποιÎźσεις που μεταδÎŻδονται μέσω κινητού παραδÎŻδονται σε ορισμένες τοποθεσÎŻες για να σας προειδοποιÎźσουν για καταστÎŹσεις έκτακτης ανÎŹγκης. Κακόβουλες εφαρμογές ενδέχεται να παρεμποδÎŻσουν την απόδοση Îź τη λειτουργÎŻα της συσκευÎźς σας κατÎŹ τη λÎźψη μετÎŹδοσης μέσω κινητού σχετικÎŹ με μια επεÎŻγουσα κατÎŹσταση."</string>
     <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"διαβÎŹζει ροές δεδομένων στις οποÎŻες έχετε εγγραφεÎŻ"</string>
     <string name="permdesc_subscribedFeedsRead" msgid="6911349196661811865">"Επιτρέπει στην εφαρμογÎź τη λÎźψη λεπτομερειών σχετικÎŹ με τις τρέχουσες συγχρονισμένες ροές δεδομένων."</string>
-    <string name="permlab_sendSms" msgid="7757368721742014252">"στέλνει και να διαβÎŹζει μηνύματα SMS"</string>
+    <string name="permlab_sendSms" msgid="7757368721742014252">"στέλνει και διαβÎŹζει μηνύματα SMS"</string>
     <string name="permdesc_sendSms" msgid="6757089798435130769">"Επιτρέπει στην εφαρμογÎź των αποστολÎź μηνυμÎŹτων SMS. Αυτό μπορεÎŻ να προκαλέσει μη αναμενόμενες χρεώσεις. Οι κακόβουλες εφαρμογές ενδέχεται να σας κοστÎŻσουν χρÎźματα, αποστέλλοντας μηνύματα χωρÎŻς την έγκρισÎź σας."</string>
     <string name="permlab_readSms" msgid="5164176626258800297">"διαβÎŹζει τα μηνύματα κειμένου (SMS Îź MMS)"</string>
     <string name="permdesc_readSms" product="tablet" msgid="7912990447198112829">"ΑυτÎź η εφαρμογÎź μπορεÎŻ να διαβÎŹσει όλα τα μηνύματα SMS (κειμένου) που εÎŻναι αποθηκευμένα στο tablet που χρησιμοποιεÎŻτε."</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"ΠροβολÎź σε πλÎźρη οθόνη"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Για έξοδο, σύρετε προς τα κÎŹτω από το επÎŹνω μέρος."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Το κατÎŹλαβα"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Περιστρέψτε την οθόνη για καλύτερη προβολÎź"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Εξέλθετε από τον διαχωρισμό οθόνης για καλύτερη προβολÎź"</string>
     <string name="done_label" msgid="7283767013231718521">"Τέλος"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Κυκλικό ρυθμιστικό ωρών"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Κυκλικό ρυθμιστικό λεπτών"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index f42a577..d1da0fb 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -342,6 +342,7 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Can capture gestures performed on the device\'s fingerprint sensor."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Take screenshot"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Can take a screenshot of the display."</string>
+    <string name="dream_preview_title" msgid="5570751491996100804">"Preview, <xliff:g id="DREAM_NAME">%1$s</xliff:g>"</string>
     <string name="permlab_statusBar" msgid="8798267849526214017">"disable or modify status bar"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Allows the app to disable the status bar or add and remove system icons."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"be the status bar"</string>
@@ -1840,6 +1841,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Viewing full screen"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"To exit, swipe down from the top."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Got it"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rotate for a better view"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Exit split screen for a better view"</string>
     <string name="done_label" msgid="7283767013231718521">"Done"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Hours circular slider"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Minutes circular slider"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 76ca7cf..8c6285c 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -342,6 +342,7 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Can capture gestures performed on the device\'s fingerprint sensor."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Take screenshot"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Can take a screenshot of the display."</string>
+    <string name="dream_preview_title" msgid="5570751491996100804">"Preview, <xliff:g id="DREAM_NAME">%1$s</xliff:g>"</string>
     <string name="permlab_statusBar" msgid="8798267849526214017">"disable or modify status bar"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Allows the app to disable the status bar or add and remove system icons."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"be the status bar"</string>
@@ -1840,6 +1841,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Viewing full screen"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"To exit, swipe down from the top."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Got it"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rotate for a better view"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Exit split screen for a better view"</string>
     <string name="done_label" msgid="7283767013231718521">"Done"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Hours circular slider"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Minutes circular slider"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index e627764..861393f 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -342,6 +342,7 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Can capture gestures performed on the device\'s fingerprint sensor."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Take screenshot"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Can take a screenshot of the display."</string>
+    <string name="dream_preview_title" msgid="5570751491996100804">"Preview, <xliff:g id="DREAM_NAME">%1$s</xliff:g>"</string>
     <string name="permlab_statusBar" msgid="8798267849526214017">"disable or modify status bar"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Allows the app to disable the status bar or add and remove system icons."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"be the status bar"</string>
@@ -1840,6 +1841,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Viewing full screen"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"To exit, swipe down from the top."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Got it"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rotate for a better view"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Exit split screen for a better view"</string>
     <string name="done_label" msgid="7283767013231718521">"Done"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Hours circular slider"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Minutes circular slider"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 565cbd9..a0a9436 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -342,6 +342,7 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Can capture gestures performed on the device\'s fingerprint sensor."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Take screenshot"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Can take a screenshot of the display."</string>
+    <string name="dream_preview_title" msgid="5570751491996100804">"Preview, <xliff:g id="DREAM_NAME">%1$s</xliff:g>"</string>
     <string name="permlab_statusBar" msgid="8798267849526214017">"disable or modify status bar"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Allows the app to disable the status bar or add and remove system icons."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"be the status bar"</string>
@@ -1840,6 +1841,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Viewing full screen"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"To exit, swipe down from the top."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Got it"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rotate for a better view"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Exit split screen for a better view"</string>
     <string name="done_label" msgid="7283767013231718521">"Done"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Hours circular slider"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Minutes circular slider"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 2718d42..b3551cf 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -342,6 +342,7 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‏‎‏‎‎‏‎‎‎‎‎‏‏‏‎‎‏‏‏‎‏‎‎‎‎‎‏‎‎‏‎‎‏‏‎‎‎‎‎‎‏‎‎‎‎‎‎‏‏‎‏‎‎Can capture gestures performed on the device\'s fingerprint sensor.‎‏‎‎‏‎"</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‎‏‎‎‎‎‏‎‏‏‎‏‎‏‎‎‏‎‏‏‏‏‎‏‏‏‎‏‏‎‎‏‏‎‎‏‏‏‏‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‎Take screenshot‎‏‎‎‏‎"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‏‎‎‏‎‎‏‏‏‎‎‎‎‏‎‏‎‏‎‏‏‏‏‎‎‏‎‏‎‎‏‎‏‎‏‎‏‏‏‏‎‏‎‏‏‏‏‎‎‏‎‎‎Can take a screenshot of the display.‎‏‎‎‏‎"</string>
+    <string name="dream_preview_title" msgid="5570751491996100804">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‎‎‏‏‏‏‎‏‎‎‏‎‎‎‏‏‏‏‎‏‏‏‏‎‎‎‎‏‎‎‎‏‏‎‎‏‏‏‏‎‏‎‏‎‎‎‏‏‎‎‎‏‎‎‎Preview, ‎‏‎‎‏‏‎<xliff:g id="DREAM_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="permlab_statusBar" msgid="8798267849526214017">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‏‎‎‏‏‎‏‏‏‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‎‏‎disable or modify status bar‎‏‎‎‏‎"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‎‏‏‏‏‎‎‏‎‎‏‎‏‎‏‏‎‎‎‎‏‎‏‎‎‎‎‎‏‏‏‏‎‏‏‏‏‎‎‏‏‎‎‏‎‏‎‏‏‏‏‎‏‎‎Allows the app to disable the status bar or add and remove system icons.‎‏‎‎‏‎"</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‎‎‎‏‎‎‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‏‏‏‎‏‏‎‎‏‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‎‏‏‏‎‏‎be the status bar‎‏‎‎‏‎"</string>
@@ -1840,6 +1841,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‎‎‎‎‏‎‎‎‎‏‏‏‎‏‏‎‏‏‏‏‎‎‎‏‎‏‏‎‎‏‏‏‏‏‏‎‎‎‎‏‎‎‏‏‏‏‎‏‎‎‏‏‏‏‏‎Viewing full screen‎‏‎‎‏‎"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‎‏‏‏‎‎‏‏‏‎‏‏‎‏‏‏‎‎‎‎‏‏‏‎‎‏‏‎‏‏‎‏‎‏‎‎‎‏‎‎‎‏‏‏‎‎‏‏‎‎‎‎‎‎To exit, swipe down from the top.‎‏‎‎‏‎"</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‎‏‏‎‏‏‎‏‏‏‏‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‎‏‎‎‎‎‎‏‏‎‎‎‎‎‏‏‎‏‏‎Got it‎‏‎‎‏‎"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‏‎‏‏‏‏‎‎‏‎‏‎‏‎‎‏‎‏‏‏‎‎‎‎‏‏‏‎‎‏‏‏‏‏‎‏‏‎‎‏‏‏‎‏‏‏‎‏‏‏‏‎‎Rotate for a better view‎‏‎‎‏‎"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‏‏‎‎‎‎‎‎‎‎‎‎‎‏‏‏‎‏‎‏‎‎‏‎‏‎‎‏‎‏‏‏‏‎‎‏‏‎‏‏‏‎‎‏‎‏‏‎‏‎‎Exit split screen for a better view‎‏‎‎‏‎"</string>
     <string name="done_label" msgid="7283767013231718521">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‏‎‏‎‏‎‎‏‎‎‎‏‏‏‎‎‎‎‎‎‏‏‏‏‎‏‎‏‎‎‏‏‎‎‎‏‎‎‏‏‏‎‎‎‎‎‏‏‏‏‎‎‏‎Done‎‏‎‎‏‎"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‎‎‎‎‏‎‏‏‏‎‏‎‎‏‏‎‎‏‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‏‎‏‎‎‏‎‎‎‏‏‏‏‎‏‎‎‏‏‎‏‏‎Hours circular slider‎‏‎‎‏‎"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‎‎‏‏‎‏‎‎‎‏‎‎‏‎‏‏‎‏‏‏‎‏‎‏‎‏‎‎‎‏‎‏‏‏‏‏‏‎‎Minutes circular slider‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 4e5c8f7..817f00e 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -52,7 +52,7 @@
     <string name="needPuk2" msgid="7032612093451537186">"Escribir PUK2 para desbloquear la tarjeta SIM."</string>
     <string name="enablePin" msgid="2543771964137091212">"Error; habilita el bloqueo de SIM/RUIM."</string>
     <plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
-      <item quantity="many">You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts before SIM is locked.</item>
+      <item quantity="many">Tienes <xliff:g id="NUMBER_1">%d</xliff:g> intentos más antes de que se bloquee la tarjeta SIM.</item>
       <item quantity="other">Tienes <xliff:g id="NUMBER_1">%d</xliff:g> intentos más antes de que se bloquee la tarjeta SIM.</item>
       <item quantity="one">Tienes <xliff:g id="NUMBER_0">%d</xliff:g> un intento más antes de que se bloquee la tarjeta SIM.</item>
     </plurals>
@@ -343,6 +343,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Capturará los gestos que se hacen en el sensor de huellas dactilares del dispositivo."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Tomar captura de pantalla"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Puede tomar capturas de pantalla."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"desactivar o modificar la barra de estado"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Permite que la aplicación inhabilite la barra de estado o que agregue y elimine íconos del sistema."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"aparecer en la barra de estado"</string>
@@ -1841,6 +1843,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Visualización en pantalla completa"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Para salir, desliza el dedo hacia abajo desde la parte superior."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Entendido"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Gira la pantalla para obtener una mejor vista"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Sal de la pantalla dividida para obtener una mejor vista"</string>
     <string name="done_label" msgid="7283767013231718521">"Listo"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Control deslizante circular de horas"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Control deslizante circular de minutos"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 54e22d3..ba30d42 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -52,7 +52,7 @@
     <string name="needPuk2" msgid="7032612093451537186">"Introduce el código PUK2 para desbloquear la tarjeta SIM."</string>
     <string name="enablePin" msgid="2543771964137091212">"Error, habilitar bloqueo de SIM/RUIM."</string>
     <plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
-      <item quantity="many">You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts before SIM is locked.</item>
+      <item quantity="many">Te quedan <xliff:g id="NUMBER_1">%d</xliff:g> intentos para bloquear la tarjeta SIM.</item>
       <item quantity="other">Te quedan <xliff:g id="NUMBER_1">%d</xliff:g> intentos para bloquear la tarjeta SIM.</item>
       <item quantity="one">Te queda <xliff:g id="NUMBER_0">%d</xliff:g> intento para bloquear la tarjeta SIM.</item>
     </plurals>
@@ -343,6 +343,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Puede capturar los gestos realizados en el sensor de huellas digitales del dispositivo."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Hacer captura"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Puede hacer capturas de la pantalla."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"inhabilitar o modificar la barra de estado"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Permite que la aplicación inhabilite la barra de estado o añada y elimine iconos del sistema."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"aparecer en la barra de estado"</string>
@@ -1841,6 +1843,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Modo de pantalla completa"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Para salir, desliza el dedo de arriba abajo."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Entendido"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Gira la pantalla para verlo mejor"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Sal de la pantalla dividida para verlo mejor"</string>
     <string name="done_label" msgid="7283767013231718521">"Hecho"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Control deslizante circular de horas"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Control deslizante circular de minutos"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index c727137..18539df 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Teil on võimalik jäädvustada seadme sõrmejäljeanduril tehtud liigutused."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Jäädvusta ekraanipilt"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Saab jäädvustada ekraanipildi."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"keela või muuda olekuriba"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Võimaldab rakendusel keelata olekuriba või lisada ja eemaldada süsteemiikoone."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"olekuribana kuvamine"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Kuvamine täisekraanil"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Väljumiseks pühkige ülevalt alla."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Selge"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Pöörake parema vaate jaoks"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Parema vaate jaoks väljuge jagatud ekraanikuvast"</string>
     <string name="done_label" msgid="7283767013231718521">"Valmis"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Ringikujuline tunniliugur"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Ringikujuline minutiliugur"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index bba9c9c..858b656 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Gailuaren hatz-marken sentsorean egindako keinuak atzeman ditzake."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Pantaila-argazkiak atera."</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Pantaila-argazkiak atera ditzake."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"desgaitu edo aldatu egoera-barra"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Egoera-barra desgaitzea edo sistema-ikonoak gehitzea edo kentzea baimentzen die aplikazioei."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"bihurtu egoera-barra"</string>
@@ -427,9 +429,9 @@
     <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Android TV gailuko deien erregistroa aldatzeko baimena ematen die aplikazioei, jasotako eta egindako deiei buruzko datuak barne. Baliteke asmo txarreko aplikazioek deien erregistroa ezabatzea edo aldatzea."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Telefonoaren deien erregistroa aldatzeko baimena ematen die aplikazioei, sarrerako eta irteerako deiei buruzko datuak barne. Asmo txarreko aplikazioek deien erregistroa ezabatzeko edo aldatzeko erabil dezakete."</string>
     <string name="permlab_bodySensors" msgid="662918578601619569">"Atzitu gorputz-sentsoreen datuak (esaterako, bihotz-maiztasuna) aplikazioa erabili bitartean"</string>
-    <string name="permdesc_bodySensors" product="default" msgid="7652650410295512140">"Aplikazioak erabiltzen diren bitartean, gorputz-sentsoreen datuak (besteak beste, bihotz-maiztasuna, tenperatura eta odolean dagoen oxigenoaren ehunekoa) atzitzeko baimena ematen die aplikazio horiei."</string>
+    <string name="permdesc_bodySensors" product="default" msgid="7652650410295512140">"Aplikazioak erabiltzen diren bitartean, gorputz-sentsoreen datuak (besteak beste, bihotz-maiztasuna, tenperatura eta odolean dagoen oxigenoaren ehunekoa) erabiltzeko baimena ematen die aplikazio horiei."</string>
     <string name="permlab_bodySensors_background" msgid="4912560779957760446">"Atzitu gorputz-sentsoreen datuak (adib., bihotz-maiztasunarenak) atzeko planoan"</string>
-    <string name="permdesc_bodySensors_background" product="default" msgid="8870726027557749417">"Aplikazioak atzeko planoan egon bitartean, gorputz-sentsoreen datuak (besteak beste, bihotz-maiztasuna, tenperatura eta odolean dagoen oxigenoaren ehunekoa) atzitzeko baimena ematen die aplikazio horiei."</string>
+    <string name="permdesc_bodySensors_background" product="default" msgid="8870726027557749417">"Aplikazioak atzeko planoan egon bitartean, gorputz-sentsoreen datuak (besteak beste, bihotz-maiztasuna, tenperatura eta odolean dagoen oxigenoaren ehunekoa) erabiltzeko baimena ematen die aplikazio horiei."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"irakurri egutegiko gertaerak eta xehetasunak"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Aplikazioak tabletan gordetako egutegiko gertaerak irakur ditzake eta egutegiko datuak parteka eta gorde ditzake."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"Aplikazioak Android TV gailuan gordeta dituzun egutegiko gertaerak irakur ditzake, baita egutegiko datuak partekatu eta gorde ere."</string>
@@ -439,7 +441,7 @@
     <string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"Android TV gailuan egutegiko gertaerak gehitzeko eta gehitutakoak kentzeko edo aldatzeko aukera dute aplikazioek. Gainera, egutegien jabeenak diruditen mezuak bidal ditzakete, edo gertaerak aldatu jabeei ezer esan gabe."</string>
     <string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"Telefonoko gertaerak gehitzeko, kentzeko edo aldatzeko aukera du aplikazioak. Gainera, egutegien jabeenak diruditen mezuak bidal ditzake, eta gertaerak alda ditzake jabeei beraiei jakinarazi gabe."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"atzitu kokapen-hornitzaileen komando gehigarriak"</string>
-    <string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"Kokapen-hornitzailearen agindu gehigarriak atzitzeko baimena ematen die aplikazioei. Horrela, agian aplikazioek GPSaren edo bestelako kokapenaren iturburuen funtzionamenduan eragina izan dezakete."</string>
+    <string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"Kokapen-hornitzailearen agindu gehigarriak erabiltzeko baimena ematen die aplikazioei. Horrela, agian aplikazioek GPSaren edo bestelako kokapenaren iturburuen funtzionamenduan eragina izan dezakete."</string>
     <string name="permlab_accessFineLocation" msgid="6426318438195622966">"lortu kokapen zehatza aurreko planoan bakarrik"</string>
     <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Abian denean, aplikazioak kokapen zehatza lor dezake kokapen-zerbitzuen bidez. Aplikazioak kokapena lortu ahal izateko, kokapen-zerbitzuek aktibatuta egon behar dute gailuan. Bateria-erabilera areagotzen du horrek."</string>
     <string name="permlab_accessCoarseLocation" msgid="1561042925407799741">"atzitu gutxi gorabeherako kokapena aurreko planoan bakarrik"</string>
@@ -460,21 +462,21 @@
     <string name="permdesc_camera" msgid="5240801376168647151">"Aplikazioak abian den bitartean erabil dezake kamera argazkiak ateratzeko eta bideoak grabatzeko."</string>
     <string name="permlab_backgroundCamera" msgid="7549917926079731681">"Argazkiak atera eta bideoak grabatu atzeko planoan."</string>
     <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Aplikazioak edonoiz erabil dezake kamera argazkiak ateratzeko eta bideoak grabatzeko."</string>
-    <string name="permlab_systemCamera" msgid="3642917457796210580">"eman sistemako kamerak atzitzeko baimena aplikazio edo zerbitzu bati argazkiak ateratzeko eta bideoak grabatzeko"</string>
+    <string name="permlab_systemCamera" msgid="3642917457796210580">"eman sistemako kamerak erabiltzeko baimena aplikazio edo zerbitzu bati argazkiak ateratzeko eta bideoak grabatzeko"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Pribilegioa duen edo sistemakoa den aplikazio honek edonoiz erabil dezake kamera argazkiak ateratzeko eta bideoak grabatzeko. Halaber, android.permission.CAMERA baimena izan behar du aplikazioak."</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"eman jakinarazpenak jasotzeko baimena aplikazioari edo zerbitzuari kamerak ireki edo ixten direnean."</string>
     <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Kamera ireki edo itxi dela (eta zer aplikaziorekin) dioten jakinarazpenak jaso ditzake aplikazio honek."</string>
     <string name="permlab_vibrate" msgid="8596800035791962017">"kontrolatu dardara"</string>
     <string name="permdesc_vibrate" msgid="8733343234582083721">"Bibragailua kontrolatzeko baimena ematen die aplikazioei."</string>
-    <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Dardara-egoera atzitzeko baimena ematen die aplikazioei."</string>
+    <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Dardara-egoera erabiltzeko baimena ematen die aplikazioei."</string>
     <string name="permlab_callPhone" msgid="1798582257194643320">"deitu zuzenean telefono-zenbakietara"</string>
     <string name="permdesc_callPhone" msgid="5439809516131609109">"Telefono-zenbakietara zuk esku hartu gabe deitzeko baimena ematen die aplikazioei. Horrela, ustekabeko gastuak edo deiak eragin daitezke. Asmo txarreko aplikazioek erabil dezakete zuk berretsi gabeko deiak eginda gastuak eragiteko."</string>
     <string name="permlab_accessImsCallService" msgid="442192920714863782">"atzitu IMS dei-zerbitzua"</string>
     <string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Zuk ezer egin beharrik gabe deiak egiteko IMS zerbitzua erabiltzeko baimena ematen die aplikazioei."</string>
     <string name="permlab_readPhoneState" msgid="8138526903259297969">"irakurri telefonoaren egoera eta identitatea"</string>
-    <string name="permdesc_readPhoneState" msgid="7229063553502788058">"Gailuaren telefono-eginbideak atzitzeko baimena ematen die aplikazioei. Baimen horrek aplikazioari telefono-zenbakia eta gailu IDak zein diren, deirik aktibo dagoen eta deia zer zenbakirekin konektatuta dagoen zehazteko baimena ematen die aplikazioei."</string>
+    <string name="permdesc_readPhoneState" msgid="7229063553502788058">"Gailuaren telefono-eginbideak erabiltzeko baimena ematen die aplikazioei. Baimen horrek aplikazioari telefono-zenbakia eta gailu IDak zein diren, deirik aktibo dagoen eta deia zer zenbakirekin konektatuta dagoen zehazteko baimena ematen die aplikazioei."</string>
     <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"irakurri oinarrizko egoera telefonikoa eta identitatea"</string>
-    <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Gailuaren oinarrizko eginbide telefonikoak atzitzeko baimena ematen dio aplikazioari."</string>
+    <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Gailuaren oinarrizko eginbide telefonikoak erabiltzeko baimena ematen dio aplikazioari."</string>
     <string name="permlab_manageOwnCalls" msgid="9033349060307561370">"bideratu deiak sistemaren bidez"</string>
     <string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Deiak sistemaren bidez bideratzea baimentzen die aplikazioei, deien zerbitzua ahal bezain ona izan dadin."</string>
     <string name="permlab_callCompanionApp" msgid="3654373653014126884">"ikusi eta kontrolatu deiak sistemaren bidez."</string>
@@ -484,7 +486,7 @@
     <string name="permlab_acceptHandover" msgid="2925523073573116523">"jarraitu beste aplikazio batean hasitako deia"</string>
     <string name="permdesc_acceptHandovers" msgid="7129026180128626870">"Beste aplikazio batean hasitako dei batekin jarraitzeko baimena ematen die aplikazioei."</string>
     <string name="permlab_readPhoneNumbers" msgid="5668704794723365628">"irakurri telefono-zenbakiak"</string>
-    <string name="permdesc_readPhoneNumbers" msgid="7368652482818338871">"Gailuaren telefono-zenbakiak atzitzeko baimena ematen die aplikazioei."</string>
+    <string name="permdesc_readPhoneNumbers" msgid="7368652482818338871">"Gailuaren telefono-zenbakiak erabiltzeko baimena ematen die aplikazioei."</string>
     <string name="permlab_wakeLock" product="automotive" msgid="1904736682319375676">"mantendu piztuta autoko pantaila"</string>
     <string name="permlab_wakeLock" product="tablet" msgid="1527660973931694000">"eragotzi tableta inaktibo ezartzea"</string>
     <string name="permlab_wakeLock" product="tv" msgid="2856941418123343518">"Android TV gailua inaktibo ezar dadin eragotzi"</string>
@@ -631,7 +633,7 @@
     <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Sakatu hau aurpegi-eredua ezabatzeko eta, gero, gehitu aurpegia berriro"</string>
     <string name="face_setup_notification_title" msgid="8843461561970741790">"Konfiguratu aurpegi bidez desblokeatzeko eginbidea"</string>
     <string name="face_setup_notification_content" msgid="5463999831057751676">"Telefonoa desblokeatzeko, begira iezaiozu"</string>
-    <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Aurpegi bidez desblokeatzeko aukera erabiltzeko, aktibatu "<b>"kamera atzitzeko baimena"</b>" Ezarpenak &gt; Pribatutasuna atalean"</string>
+    <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Aurpegi bidez desblokeatzeko aukera erabiltzeko, aktibatu "<b>"kamera erabiltzeko baimena"</b>" Ezarpenak &gt; Pribatutasuna atalean"</string>
     <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Konfiguratu telefonoa desblokeatzeko modu gehiago"</string>
     <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Sakatu hau hatz-marka bat gehitzeko"</string>
     <string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Hatz-marka bidez desblokeatzea"</string>
@@ -1471,14 +1473,14 @@
     <string name="ime_action_default" msgid="8265027027659800121">"Abiarazi"</string>
     <string name="dial_number_using" msgid="6060769078933953531">"Markatu zenbakia \n<xliff:g id="NUMBER">%s</xliff:g> erabilita"</string>
     <string name="create_contact_using" msgid="6200708808003692594">"Sortu kontaktu bat\n<xliff:g id="NUMBER">%s</xliff:g> erabilita"</string>
-    <string name="grant_credentials_permission_message_header" msgid="5365733888842570481">"Aplikazio hauetako bat edo gehiago kontua orain eta etorkizunean atzitzeko baimena eskatzen ari dira."</string>
+    <string name="grant_credentials_permission_message_header" msgid="5365733888842570481">"Aplikazio hauetako bat edo gehiago kontua orain eta etorkizunean erabiltzeko baimena eskatzen ari dira."</string>
     <string name="grant_credentials_permission_message_footer" msgid="1886710210516246461">"Eskaera onartu nahi duzu?"</string>
     <string name="grant_permissions_header_text" msgid="3420736827804657201">"Sarbide-eskaera"</string>
     <string name="allow" msgid="6195617008611933762">"Eman baimena"</string>
     <string name="deny" msgid="6632259981847676572">"Ukatu"</string>
     <string name="permission_request_notification_title" msgid="1810025922441048273">"Baimena eskatu da"</string>
     <string name="permission_request_notification_with_subtitle" msgid="3743417870360129298">"Baimena eskatu da \n<xliff:g id="ACCOUNT">%s</xliff:g> konturako."</string>
-    <string name="permission_request_notification_for_app_with_subtitle" msgid="1298704005732851350">"<xliff:g id="APP">%1$s</xliff:g> aplikazioak <xliff:g id="ACCOUNT">%2$s</xliff:g> kontua atzitzeko baimena\neskatu du."</string>
+    <string name="permission_request_notification_for_app_with_subtitle" msgid="1298704005732851350">"<xliff:g id="APP">%1$s</xliff:g> aplikazioak <xliff:g id="ACCOUNT">%2$s</xliff:g> kontua erabiltzeko baimena\neskatu du."</string>
     <string name="forward_intent_to_owner" msgid="4620359037192871015">"Laneko profiletik kanpo ari zara aplikazioa erabiltzen"</string>
     <string name="forward_intent_to_work" msgid="3620262405636021151">"Laneko profilean ari zara aplikazioa erabiltzen"</string>
     <string name="input_method_binding_label" msgid="1166731601721983656">"Idazketa-metodoa"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Pantaila osoko ikuspegia"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Irteteko, pasatu hatza goitik behera."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Ados"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Biratu pantaila ikuspegi hobea lortzeko"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Irten pantaila zatitutik ikuspegi hobea lortzeko"</string>
     <string name="done_label" msgid="7283767013231718521">"Eginda"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Ordua aukeratzeko ikuspegi zirkularra"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Minutuak aukeratzeko ikuspegi zirkularra"</string>
@@ -2048,11 +2052,11 @@
     <string name="harmful_app_warning_uninstall" msgid="6472912975664191772">"DESINSTALATU"</string>
     <string name="harmful_app_warning_open_anyway" msgid="5963657791740211807">"IREKI, HALA ERE"</string>
     <string name="harmful_app_warning_title" msgid="8794823880881113856">"Aplikazio kaltegarri bat hauteman da"</string>
-    <string name="log_access_confirmation_title" msgid="2343578467290592708">"Gailuko erregistro guztiak atzitzeko baimena eman nahi diozu <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> aplikazioari?"</string>
+    <string name="log_access_confirmation_title" msgid="2343578467290592708">"Gailuko erregistro guztiak erabiltzeko baimena eman nahi diozu <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> aplikazioari?"</string>
     <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Eman behin erabiltzeko baimena"</string>
     <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Ez eman baimenik"</string>
-    <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Gailuko erregistroetan gailuan gertatzen den guztia gordetzen da. Arazoak bilatu eta konpontzeko erabil ditzakete aplikazioek erregistro horiek.\n\nBaliteke erregistro batzuek kontuzko informazioa edukitzea. Beraz, eman gailuko erregistro guztiak atzitzeko baimena fidagarritzat jotzen dituzun aplikazioei bakarrik. \n\nNahiz eta gailuko erregistro guztiak atzitzeko baimena ez eman aplikazio honi, aplikazioak hari dagozkion erregistroak atzitu ahalko ditu. Gainera, baliteke gailuaren fabrikatzaileak gailuko erregistro edo datu batzuk atzitu ahal izatea."</string>
-    <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"Gailuko erregistroetan gailuan gertatzen den guztia gordetzen da. Arazoak bilatu eta konpontzeko erabil ditzakete aplikazioek erregistro horiek.\n\nBaliteke erregistro batzuek kontuzko informazioa edukitzea. Beraz, eman gailuko erregistro guztiak atzitzeko baimena fidagarritzat jotzen dituzun aplikazioei bakarrik. \n\nNahiz eta gailuko erregistro guztiak atzitzeko baimena ez eman aplikazio honi, aplikazioak hari dagozkion erregistroak atzitu ahalko ditu. Gainera, baliteke gailuaren fabrikatzaileak gailuko erregistro edo datu batzuk atzitu ahal izatea.\n\nLortu informazio gehiago g.co/android/devicelogs helbidean."</string>
+    <string name="log_access_confirmation_body" product="default" msgid="1806692062668620735">"Gailuko erregistroetan gailuan gertatzen den guztia gordetzen da. Arazoak bilatu eta konpontzeko erabil ditzakete aplikazioek erregistro horiek.\n\nBaliteke erregistro batzuek kontuzko informazioa edukitzea. Beraz, eman gailuko erregistro guztiak erabiltzeko baimena fidagarritzat jotzen dituzun aplikazioei bakarrik. \n\nNahiz eta gailuko erregistro guztiak erabiltzeko baimena ez eman aplikazio honi, aplikazioak hari dagozkion erregistroak atzitu ahalko ditu. Gainera, baliteke gailuaren fabrikatzaileak gailuko erregistro edo datu batzuk atzitu ahal izatea."</string>
+    <string name="log_access_confirmation_body" product="tv" msgid="7379536536425265262">"Gailuko erregistroetan gailuan gertatzen den guztia gordetzen da. Arazoak bilatu eta konpontzeko erabil ditzakete aplikazioek erregistro horiek.\n\nBaliteke erregistro batzuek kontuzko informazioa edukitzea. Beraz, eman gailuko erregistro guztiak erabiltzeko baimena fidagarritzat jotzen dituzun aplikazioei bakarrik. \n\nNahiz eta gailuko erregistro guztiak erabiltzeko baimena ez eman aplikazio honi, aplikazioak hari dagozkion erregistroak atzitu ahalko ditu. Gainera, baliteke gailuaren fabrikatzaileak gailuko erregistro edo datu batzuk atzitu ahal izatea.\n\nLortu informazio gehiago g.co/android/devicelogs helbidean."</string>
     <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Ez erakutsi berriro"</string>
     <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> aplikazioak <xliff:g id="APP_2">%2$s</xliff:g> aplikazioaren zatiak erakutsi nahi ditu"</string>
     <string name="screenshot_edit" msgid="7408934887203689207">"Editatu"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index f9fa48d..5b5d5e4 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"می‌ŰȘÙˆŰ§Ù†ŰŻ Ű§ŰŽŰ§Ű±Ù‡‌Ù‡Ű§ÛŒ ۧۏ۱ۧ‌ŰŽŰŻÙ‡ Ű±ÙˆÛŒ ۭ۳گ۱ Ű§Ű«Ű±Ű§Ù†ÚŻŰŽŰȘ ŰŻŰłŰȘÚŻŰ§Ù‡ ۱ۧ ۫ۚŰȘ Ú©Ù†ŰŻ."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"ÚŻŰ±ÙŰȘن Ù†Ù…Ű§ÚŻŰ±ÙŰȘ"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"می‌ŰȘÙˆŰ§Ù†ŰŻ ۧŰČ Ù†Ù…Ű§ÛŒŰŽÚŻŰ± Ù†Ù…Ű§ÚŻŰ±ÙŰȘ ŰšÚŻÛŒŰ±ŰŻ."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"ŰșÛŒŰ±ÙŰčŰ§Ù„ Ú©Ű±ŰŻÙ† ÛŒŰ§ ŰȘŰșÛŒÛŒŰ± Ù†ÙˆŰ§Ű± ÙˆŰ¶ŰčیŰȘ"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"‏ŰšÙ‡ ŰšŰ±Ù†Ű§Ù…Ù‡ ۧۏۧŰČه می‎ŰŻÙ‡ŰŻ ŰȘۧ Ù†ÙˆŰ§Ű± ÙˆŰ¶ŰčیŰȘ ۱ۧ ŰșÛŒŰ±ÙŰčŰ§Ù„ Ú©Ù†ŰŻ ÛŒŰ§ Ù†Ù…Ű§ŰŻÙ‡Ű§ÛŒ ŰłÛŒŰłŰȘم ۱ۧ Ű§Ű¶Ű§ÙÙ‡ ÛŒŰ§ Ű­Ű°Ù Ú©Ù†ŰŻ."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"Ù†ÙˆŰ§Ű± ÙˆŰ¶ŰčیŰȘ ۚۧێۯ"</string>
@@ -575,7 +577,7 @@
     <string name="permdesc_mediaLocation" msgid="597912899423578138">"ŰšÙ‡ ŰšŰ±Ù†Ű§Ù…Ù‡ ۧۏۧŰČه می‌ŰŻÙ‡ŰŻ Ù…Ú©Ű§Ù†‌Ù‡Ű§ ۱ۧ ۧŰČ Ù…ŰŹÙ…ÙˆŰčه Ű±ŰłŰ§Ù†Ù‡‌ŰȘŰ§Ù† ŰšŰźÙˆŰ§Ù†ŰŻ."</string>
     <string name="biometric_app_setting_name" msgid="3339209978734534457">"ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ ŰČÛŒŰłŰȘ‌ŰłÙ†ŰŹŰŽÛŒ"</string>
     <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ ŰČÛŒŰłŰȘ‌ŰłÙ†ŰŹŰŽÛŒ ÛŒŰ§ قفل Ű”ÙŰ­Ù‡"</string>
-    <string name="biometric_dialog_default_title" msgid="55026799173208210">"ŰȘŰŁÛŒÛŒŰŻ Ú©Ù†ÛŒŰŻ Ű§ÛŒÙ† ŰŽÙ…Ű§ Ù‡ŰłŰȘÛŒŰŻ"</string>
+    <string name="biometric_dialog_default_title" msgid="55026799173208210">"ŰȘŰŁÛŒÛŒŰŻ Ú©Ù†ÛŒŰŻ Ű§ÛŒÙ† ŰŽÙ…Ű§ÛŒÛŒŰŻ"</string>
     <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"ŰšŰ±Ű§ÛŒ Ű§ŰŻŰ§Ù…Ù‡ŰŒ ۧŰČ ŰČÛŒŰłŰȘ‌ŰłÙ†ŰŹŰŽÛŒ ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ú©Ù†ÛŒŰŻ"</string>
     <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"ŰšŰ±Ű§ÛŒ Ű§ŰŻŰ§Ù…Ù‡ŰŒ ۧŰČ ŰČÛŒŰłŰȘ‌ŰłÙ†ŰŹŰŽÛŒ ÛŒŰ§ قفل Ű”ÙŰ­Ù‡ ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ú©Ù†ÛŒŰŻ"</string>
     <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"۳۟ŰȘ‌Ű§ÙŰČۧ۱ ŰČÛŒŰłŰȘ‌ŰłÙ†ŰŹÛŒ ۯ۱ۯ۳ŰȘ۱۳ Ù†ÛŒŰłŰȘ"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Ù…ŰŽŰ§Ù‡ŰŻÙ‡ ۯ۱ Ű­Ű§Ù„ŰȘ ŰȘÙ…Ű§Ù… Ű”ÙŰ­Ù‡"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"ŰšŰ±Ű§ÛŒ ŰźŰ±ÙˆŰŹŰŒ Ű§Ù†ÚŻŰŽŰȘŰȘŰ§Ù† ۱ۧ ۧŰČ ŰšŰ§Ù„Ű§ÛŒ Ű”ÙŰ­Ù‡ ŰšÙ‡ ÙŸŰ§ÛŒÛŒÙ† ŰšÚ©ŰŽÛŒŰŻ."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"مŰȘÙˆŰŹÙ‡ ŰŽŰŻÙ…"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ŰšŰ±Ű§ÛŒ ŰŻÛŒŰŻ ŰšÙ‡ŰȘŰ±ŰŒ ŰŻŰłŰȘÚŻŰ§Ù‡ ۱ۧ ŰšÚ†Ű±ŰźŰ§Ù†ÛŒŰŻ"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"ŰšŰ±Ű§ÛŒ ŰŻÛŒŰŻ ŰšÙ‡ŰȘŰ±ŰŒ ۧŰČ Ű”ÙŰ­Ù‡Ù” ŰŻÙˆÙ†ÛŒÙ…Ù‡ ۟ۧ۱ۏ ŰŽÙˆÛŒŰŻ"</string>
     <string name="done_label" msgid="7283767013231718521">"ŰȘÙ…Ű§Ù…"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"لŰșŰČÙ†ŰŻÙ‡ ŰŻŰ§ÛŒŰ±Ù‡‌Ű§ÛŒ ۳ۧŰčŰȘ"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"لŰșŰČÙ†ŰŻÙ‡ ŰŻŰ§ÛŒŰ±Ù‡‌Ű§ÛŒ ŰŻÙ‚ÛŒÙ‚Ù‡"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index c81bf00..e2b8fcc 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Voi tallentaa laitteen sormenjälkitunnistimelle tehtyjä eleitä."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Ota kuvakaappaus"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Voi ottaa kuvakaappauksen näytöstä."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"poista tilapalkki käytöstä tai muokkaa tilapalkkia"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Antaa sovelluksen poistaa tilapalkin käytöstä ja lisätä tai poistaa järjestelmäkuvakkeita."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"sijaita tilapalkissa"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Koko ruudun tilassa"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Sulje palkki pyyhkäisemällä alas ruudun ylälaidasta."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Selvä"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Kierrä, niin saat paremman näkymän"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Poistu jaetulta näytöltä, niin saat paremman näkymän"</string>
     <string name="done_label" msgid="7283767013231718521">"Valmis"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Tuntien ympyränmuotoinen liukusäädin"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Minuuttien ympyränmuotoinen liukusäädin"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 40b265a..3e29326 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -53,7 +53,7 @@
     <string name="enablePin" msgid="2543771964137091212">"Opération infructueuse. Activez le verrouillage SIM/RUIM."</string>
     <plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
       <item quantity="one">Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentative avant que votre carte SIM soit verrouillée.</item>
-      <item quantity="many">You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts before SIM is locked.</item>
+      <item quantity="many">Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentatives avant que votre carte SIM soit verrouillée.</item>
       <item quantity="other">Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentatives avant que votre carte SIM soit verrouillée.</item>
     </plurals>
     <string name="imei" msgid="2157082351232630390">"Code IIEM"</string>
@@ -343,6 +343,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Peut capturer des gestes effectués sur le capteur d\'empreintes digitales de l\'appareil."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Prendre une capture d\'écran"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Peut prendre une capture de l\'écran."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"désactiver ou modifier la barre d\'état"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Permet à l\'application de désactiver la barre d\'état, ou d\'ajouter et de supprimer des icônes système."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"servir de barre d\'état"</string>
@@ -1841,6 +1843,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Affichage plein écran"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Pour quitter, balayez vers le bas à partir du haut."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Faire pivoter pour obtenir un meilleur affichage"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Quitter l\'écran partagé pour obtenir un meilleur affichage"</string>
     <string name="done_label" msgid="7283767013231718521">"Terminé"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Curseur circulaire des heures"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Curseur circulaire des minutes"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index c2a8460..c5bf6a94 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -53,7 +53,7 @@
     <string name="enablePin" msgid="2543771964137091212">"Échec de l\'opération. Veuillez activer le verrouillage de la carte SIM/RUIM."</string>
     <plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
       <item quantity="one">Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentative avant que votre carte SIM ne soit verrouillée.</item>
-      <item quantity="many">You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts before SIM is locked.</item>
+      <item quantity="many">Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentatives avant que votre carte SIM ne soit verrouillée.</item>
       <item quantity="other">Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentatives avant que votre carte SIM ne soit verrouillée.</item>
     </plurals>
     <string name="imei" msgid="2157082351232630390">"Code IMEI"</string>
@@ -343,6 +343,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Peut enregistrer des gestes effectués sur le lecteur d\'empreinte digitale de l\'appareil."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Prendre une capture d\'écran"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Peut prendre des captures d\'écran."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"Désactivation ou modification de la barre d\'état"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Permet à l\'application de désactiver la barre d\'état, ou d\'ajouter et de supprimer des icônes système."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"remplacer la barre d\'état"</string>
@@ -1841,6 +1843,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Affichage en plein écran"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Pour quitter, balayez l\'écran du haut vers le bas."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Faites pivoter pour mieux voir"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Quittez l\'écran partagé pour mieux voir"</string>
     <string name="done_label" msgid="7283767013231718521">"OK"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Curseur circulaire des heures"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Curseur circulaire des minutes"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 9353cf8..b89bd3f 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Pode rexistrar os xestos realizados no sensor de impresión dixital do dispositivo."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Facer captura de pantalla"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Pode facer capturas de pantalla."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"desactivar ou modificar a barra de estado"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Permite á aplicación desactivar a barra de estado ou engadir e quitar as iconas do sistema."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"actuar como a barra de estado"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Vendo pantalla completa"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Para saír, pasa o dedo cara abaixo desde a parte superior."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Entendido"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Xira a pantalla para que se vexa mellor"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Sae da pantalla dividida para que se vexa mellor"</string>
     <string name="done_label" msgid="7283767013231718521">"Feito"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Control desprazable circular das horas"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Control desprazable circular dos minutos"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index ad006c6..c775021 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"àȘĄàȘżàȘ”àȘŸàȘ‡àȘžàȘšàȘŸ àȘ«àȘżàȘ‚àȘ—àȘ°àȘȘ્àȘ°àȘżàȘšà«àȘŸ àȘžà«‡àȘšà«àȘžàȘ° àȘȘàȘ° àȘ•àȘ°àȘ”àȘŸàȘźàȘŸàȘ‚ àȘ†àȘ”ેàȘČàȘŸ àȘžàȘ‚àȘ•ેàȘ€à«‹ àȘ•à«…àȘȘ્àȘšàȘ° àȘ•àȘ°à«€ àȘ¶àȘ•ે àȘ›à«‡."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"àȘžà«àȘ•્àȘ°à«€àȘšàȘ¶à«‰àȘŸ àȘČો"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"àȘĄàȘżàȘžà«àȘȘ્àȘČેàȘšà«‹ àȘžà«àȘ•્àȘ°à«€àȘšàȘ¶à«‰àȘŸ àȘČàȘˆ àȘ¶àȘ•ે àȘ›à«‡."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"àȘžà«àȘŸà«‡àȘŸàȘž àȘŹàȘŸàȘ°àȘšà«‡ àȘ…àȘ•્àȘ·àȘź àȘ•àȘ°à«‹ àȘ…àȘ„àȘ”àȘŸ àȘ€à«‡àȘźàȘŸàȘ‚ àȘ«à«‡àȘ°àȘ«àȘŸàȘ° àȘ•àȘ°à«‹"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"àȘàȘȘ્àȘČàȘżàȘ•ેàȘ¶àȘšàȘšà«‡ àȘžà«àȘŸà«‡àȘŸàȘž àȘŹàȘŸàȘ° àȘ…àȘ•્àȘ·àȘź àȘ•àȘ°àȘ”àȘŸàȘšà«€ àȘ…àȘ„àȘ”àȘŸ àȘžàȘżàȘžà«àȘŸàȘź àȘ†àȘŻàȘ•àȘšà«àȘž àȘ‰àȘźà«‡àȘ°àȘ”àȘŸ àȘ…àȘšà«‡ àȘŠà«‚àȘ° àȘ•àȘ°àȘ”àȘŸàȘšà«€ àȘźàȘ‚àȘœà«‚àȘ°à«€ àȘ†àȘȘે àȘ›à«‡."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"àȘžà«àȘŸà«‡àȘŸàȘž àȘŹàȘŸàȘ°àȘźàȘŸàȘ‚ àȘŹàȘ€àȘŸàȘ”ો"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"àȘȘૂàȘ°à«àȘŁ àȘžà«àȘ•્àȘ°à«€àȘš àȘȘàȘ° àȘœà«àȘ“"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"àȘŹàȘčàȘŸàȘ° àȘšà«€àȘ•àȘłàȘ”àȘŸ àȘźàȘŸàȘŸà«‡, àȘŸà«‹àȘš àȘȘàȘ°àȘ„ી àȘšà«€àȘšà«‡ àȘžà«àȘ”àȘŸàȘ‡àȘȘ àȘ•àȘ°à«‹."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"àȘžàȘźàȘœàȘŸàȘˆ àȘ—àȘŻà«àȘ‚"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"àȘŹàȘčેàȘ€àȘ° àȘ”્àȘŻà«‚ àȘźàȘŸàȘŸà«‡ àȘ«à«‡àȘ°àȘ”ો"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"àȘŹàȘčેàȘ€àȘ° àȘ”્àȘŻà«‚ àȘźàȘŸàȘŸà«‡, àȘ”àȘżàȘ­àȘŸàȘœàȘżàȘ€ àȘžà«àȘ•્àȘ°à«€àȘšàȘźàȘŸàȘ‚àȘ„ી àȘŹàȘčàȘŸàȘ° àȘšà«€àȘ•àȘłà«‹"</string>
     <string name="done_label" msgid="7283767013231718521">"àȘ„àȘˆ àȘ—àȘŻà«àȘ‚"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"àȘ•àȘČàȘŸàȘ•àȘšà«àȘ‚ àȘ”àȘ°à«àȘ€à«àȘłàȘŸàȘ•àȘŸàȘ° àȘžà«àȘČàȘŸàȘ‡àȘĄàȘ°"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"àȘźàȘżàȘšàȘżàȘŸàȘšà«àȘ‚ àȘ”àȘ°à«àȘ€à«àȘłàȘŸàȘ•àȘŸàȘ° àȘžà«àȘČàȘŸàȘ‡àȘĄàȘ°"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 2a472f8..1951a38 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"à€Ąà€żà€”à€Ÿà€‡à€ž à€•à„‡ à€«à€Œà€żà€‚à€—à€°à€Șà„à€°à€żà€‚à€Ÿ à€žà„‡à€‚à€žà€° à€Șà€° à€•à€żà€ à€—à€ à€čà€Ÿà€„ à€•à„‡ à€œà„‡à€žà„à€šà€° à€•à„ˆà€Șà„à€šà€° à€•à€żà€ à€œà€Ÿ à€žà€•à€€à„‡ à€čà„ˆà€‚."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"à€žà„à€•à„à€°à„€à€šà€¶à„‰à€Ÿ à€Čà„‡à€‚"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"à€Ąà€żà€žà€Șà„à€Čà„‡ à€•à€Ÿ à€žà„à€•à„à€°à„€à€šà€¶à„‰à€Ÿ à€Čà€żà€Żà€Ÿ à€œà€Ÿ à€žà€•à€€à€Ÿ à€čà„ˆ."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"à€žà„à€Ÿà„‡à€Ÿà€ž à€Źà€Ÿà€° à€•à„‹ à€…à€•à„à€·à€ź à€•à€°à„‡à€‚ à€Żà€Ÿ à€Źà€Šà€Čà„‡à€‚"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"à€à€Ș à€•à„‹, à€žà„à€Ÿà„‡à€Ÿà€ž à€Źà€Ÿà€° à€•à„‹ à€Źà€‚à€Š à€•à€°à€šà„‡ à€Żà€Ÿ à€žà€żà€žà„‍à€Ÿà€ź à€†à€‡à€•à„‰à€š à€•à„‹ à€œà„‹à€Ąà€Œà€šà„‡ à€”à€° à€šà€żà€•à€Ÿà€Čà€šà„‡ à€•à„€ à€…à€šà„à€źà€€à€ż à€Šà„‡à€€à€Ÿ à€čà„ˆ."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"à€žà„à€Ÿà„‡à€Ÿà€ž à€Źà€Ÿà€° à€•à„‹ à€°à€čà€šà„‡ à€Šà„‡à€‚"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"à€†à€Ș à€Șà„‚à€°à„‡ à€žà„à€•à„à€°à„€à€š à€Șà€° à€Šà„‡à€– à€°à€čà„‡ à€čà„ˆà€‚"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"à€Źà€Ÿà€čà€° à€šà€żà€•à€Čà€šà„‡ à€•à„‡ à€Čà€żà€, à€Šà€Șà€° à€žà„‡ à€šà„€à€šà„‡ à€žà„à€”à€Ÿ‍à€‡à€Ș à€•à€°à„‡à€‚."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"à€ à„€à€• à€čà„ˆ"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"à€Źà„‡à€čà€€à€° à€”à„à€Żà„‚ à€Șà€Ÿà€šà„‡ à€•à„‡ à€Čà€żà€, à€Ąà€żà€”à€Ÿà€‡à€ž à€•à„€ à€žà„à€•à„à€°à„€à€š à€•à„‹ à€˜à„à€źà€Ÿà€à€‚"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"à€Źà„‡à€čà€€à€° à€”à„à€Żà„‚ à€Șà€Ÿà€šà„‡ à€•à„‡ à€Čà€żà€, à€žà„à€Șà„à€Čà€żà€Ÿ à€žà„à€•à„à€°à„€à€š à€źà„‹à€Ą à€Źà€‚à€Š à€•à€°à„‡à€‚"</string>
     <string name="done_label" msgid="7283767013231718521">"à€čà„‹ à€—à€Żà€Ÿ"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"à€˜à€‚à€Ÿà„‹ à€•à€Ÿ à€šà€•à„à€°à€Ÿà€•à€Ÿà€° à€žà„à€Čà€Ÿà€‡à€Ąà€°"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"à€źà€żà€šà€Ÿà„‹à€‚ à€•à€Ÿ à€šà€•à„à€°à€Ÿà€•à€Ÿà€° à€žà„à€Čà€Ÿà€‡à€Ąà€°"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 72480a4..f380cb4 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -343,6 +343,7 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"MoĆŸe snimati pokrete izvršene na senzoru otiska prsta na uređaju."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Snimi zaslon"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"MoĆŸete napraviti snimku zaslona."</string>
+    <string name="dream_preview_title" msgid="5570751491996100804">"Pregled, <xliff:g id="DREAM_NAME">%1$s</xliff:g>"</string>
     <string name="permlab_statusBar" msgid="8798267849526214017">"onemogućavanje ili izmjena trake statusa"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Aplikaciji omogućuje onemogućavanje trake statusa ili dodavanje i uklanjanje sistemskih ikona."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"biti traka statusa"</string>
@@ -1841,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Gledanje preko cijelog zaslona"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Za izlaz prijeđite prstom od vrha prema dolje."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Shvaćam"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Zakrenite kako biste bolje vidjeli"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Zatvorite podijeljeni zaslon kako biste bolje vidjeli"</string>
     <string name="done_label" msgid="7283767013231718521">"Gotovo"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"KruĆŸni klizač sati"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"KruĆŸni klizač minuta"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index f9a93a8..3d98bb4 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Érzékeli az eszköz ujjlenyomat-érzékelƑjén végzett kézmozdulatokat."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"KépernyƑkép készítése"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Készíthet képernyƑképet a kijelzƑrƑl."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"állapotsor kikapcsolása vagy módosítása"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"LehetƑvé teszi az alkalmazás számára az állapotsor kikapcsolását, illetve rendszerikonok hozzáadását és eltávolítását."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"az állapotsor szerepének átvétele"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Megtekintése teljes képernyƑn"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Kilépéshez csúsztassa ujját fentrƑl lefelé."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Értem"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Forgassa el a jobb élmény érdekében"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Lépjen ki az osztott képernyƑs módból a jobb élmény érdekében"</string>
     <string name="done_label" msgid="7283767013231718521">"Kész"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Óra kör alakú csúszkája"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Perc kör alakú csúszkája"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 138d810..5d60c5d 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"ԿարվŐČ Ő§ ŐĄÖ€Ő±ŐĄŐ¶ŐĄŐŁÖ€Ő„ŐŹ ŐŽŐĄŐżŐ¶ŐĄŐ°Ő„ŐżÖ„Ő„Ö€Ő« ŐœŐŻŐĄŐ¶Ő„Ö€Ő« ŐŸÖ€ŐĄ ŐŻŐĄŐżŐĄÖ€ŐŸŐžŐČ ŐȘŐ„ŐœŐżŐ„Ö€Őš"</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"ŐÖ„Ö€Ő«Ő¶Ő·ŐžŐ©Ő« ŐœŐżŐ„ŐČռվւծ"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"ԿարվŐČ Ő§ ŐœŐżŐ„ŐČŐźŐ„ŐŹ Ő§ŐŻÖ€ŐĄŐ¶Ő« ŐœÖ„Ö€Ő«Ő¶Ő·ŐžŐ©Ö‰"</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"ŐĄŐ¶Ő»ŐĄŐżŐ„ŐŹ ŐŻŐĄŐŽ փ՞փ՞խՄՏ ŐŻŐĄÖ€ŐŁŐĄŐŸŐ«ŐłŐĄŐŻŐ« ŐŁŐžŐżŐ«Ő¶"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"ÔčŐžÖ‚Ő”ŐŹ Ő§ ŐżŐĄŐŹŐ«Őœ Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐ«Ő¶ ŐĄŐ¶Ő»ŐĄŐżŐ„ŐŹ ŐŻŐĄÖ€ŐŁŐĄŐŸŐ«ŐłŐĄŐŻŐ« ŐŁŐžŐżŐ«Ő¶ ŐŻŐĄŐŽ ŐĄŐŸŐ„ŐŹŐĄÖŐ¶Ő„ŐŹ վւ Ő°Ő„ŐŒŐĄÖŐ¶Ő„ŐŹ Ő°ŐĄŐŽŐĄŐŻŐĄÖ€ŐŁŐ« ŐșŐĄŐżŐŻŐ„Ö€ŐĄŐŻŐ¶Ő„Ö€Őš:"</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"ŐŹŐ«Ő¶Ő„ŐŹ ŐŻŐĄÖ€ŐŁŐĄŐŸŐ«ŐłŐĄŐŻŐ« ŐŁŐžŐżŐ«"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"ÔŒŐ«ŐĄŐ§ŐŻÖ€ŐĄŐ¶ Ő€Ő«ŐżŐžÖ‚ŐŽ"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"ÔŽŐžÖ‚Ö€Őœ գալվւ հածար ŐŸŐ„Ö€Ö‡Ő«Ö ŐœŐĄŐ°ŐĄŐ°ŐĄÖ€ŐŸŐĄŐźŐ„Ö„ Ő€Ő„ŐșŐ« Ő¶Ő„Ö€Ö„Ö‡:"</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Պարթ է"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ŐŠŐżŐżŐ„Ö„Ő Ő€Ő«ŐżŐĄŐŻŐ„Ö€ŐșŐš ŐŹŐĄŐŸŐĄÖŐ¶Ő„ŐŹŐžÖ‚ հածար"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"ÔŽŐžÖ‚Ö€Őœ Ő„ŐŻŐ„Ö„ ŐŻŐ«ŐœŐŸŐĄŐź Ő§ŐŻÖ€ŐĄŐ¶Ő« ŐŒŐ„ŐȘŐ«ŐŽŐ«ÖŐ Ő€Ő«ŐżŐĄŐŻŐ„Ö€ŐșŐš ŐŹŐĄŐŸŐĄÖŐ¶Ő„ŐŹŐžÖ‚ հածար"</string>
     <string name="done_label" msgid="7283767013231718521">"ŐŠŐĄŐżÖ€ŐĄŐœŐż Ő§"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"ÔșŐĄŐŽŐ„Ö€Ő« ŐšŐ¶ŐżÖ€ŐžÖ‚Ő©Ő”ŐžÖ‚Ő¶ Ő©ŐŸŐĄŐżŐĄŐ­ŐżŐĄŐŻŐ«Ö"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Ր՞ŐșŐ„Ő¶Ő„Ö€Ő« ŐšŐ¶ŐżÖ€ŐžÖ‚Ő©Ő”ŐžÖ‚Ő¶ Ő©ŐŸŐĄŐżŐĄŐ­ŐżŐĄŐŻŐ«Ö"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index b02345a..2173eb9 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Dapat merekam gestur yang dilakukan di sensor sidik jari perangkat."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Ambil screenshot"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Dapat mengambil screenshot tampilan."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"nonaktifkan atau ubah bilah status"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Mengizinkan apl menonaktifkan bilah status atau menambah dan menghapus ikon sistem."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"jadikan bilah status"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Melihat layar penuh"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Untuk keluar, geser layar ke bawah dari atas."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Mengerti"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Putar posisi layar untuk mendapatkan tampilan yang lebih baik"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Keluar dari layar terpisah untuk mendapatkan tampilan yang lebih baik"</string>
     <string name="done_label" msgid="7283767013231718521">"Selesai"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Penggeser putar jam"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Penggeser putar menit"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 77df331..d9df620 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Getur fangað bendingar sem eru gerðar á fingrafaralesara tækisins."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Taka skjámynd"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Getur tekið skjámynd af skjánum."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"slökkva á eða breyta stöðustiku"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Leyfir forriti að slökkva á stöðustikunni eða bæta við og fjarlægja kerfistákn."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"vera stöðustikan"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Notar allan skjáinn"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Strjúktu niður frá efri brún til að hætta."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Ég skil"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Snúðu til að sjá betur"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Lokaðu skjáskiptingu til að sjá betur"</string>
     <string name="done_label" msgid="7283767013231718521">"Lokið"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Valskífa fyrir klukkustundir"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Valskífa fyrir mínútur"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 13f265e..dd1b8de 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -52,7 +52,7 @@
     <string name="needPuk2" msgid="7032612093451537186">"Digita il PUK2 per sbloccare la SIM."</string>
     <string name="enablePin" msgid="2543771964137091212">"Operazione non riuscita; attiva blocco SIM/RUIM."</string>
     <plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
-      <item quantity="many">You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts before SIM is locked.</item>
+      <item quantity="many">Hai ancora <xliff:g id="NUMBER_1">%d</xliff:g> tentativi a disposizione prima che la SIM venga bloccata.</item>
       <item quantity="other">Hai ancora <xliff:g id="NUMBER_1">%d</xliff:g> tentativi a disposizione prima che la SIM venga bloccata.</item>
       <item quantity="one">Hai ancora <xliff:g id="NUMBER_0">%d</xliff:g> tentativo a disposizione prima che la SIM venga bloccata.</item>
     </plurals>
@@ -343,6 +343,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"È in grado di rilevare i gesti compiuti con il sensore di impronte dei dispositivi."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Acquisire screenshot"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Può acquisire uno screenshot del display."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"disattivazione o modifica della barra di stato"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Consente all\'applicazione di disattivare la barra di stato o di aggiungere e rimuovere icone di sistema."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"ruolo di barra di stato"</string>
@@ -992,7 +994,7 @@
     <string name="keyguard_accessibility_user_selector" msgid="1466067610235696600">"Selettore utente"</string>
     <string name="keyguard_accessibility_status" msgid="6792745049712397237">"Stato"</string>
     <string name="keyguard_accessibility_camera" msgid="7862557559464986528">"Fotocamera"</string>
-    <string name="keygaurd_accessibility_media_controls" msgid="2267379779900620614">"Controlli media"</string>
+    <string name="keygaurd_accessibility_media_controls" msgid="2267379779900620614">"Controlli multimediali"</string>
     <string name="keyguard_accessibility_widget_reorder_start" msgid="7066213328912939191">"Riordino dei widget iniziato."</string>
     <string name="keyguard_accessibility_widget_reorder_end" msgid="1083806817600593490">"Riordino dei widget terminato."</string>
     <string name="keyguard_accessibility_widget_deleted" msgid="1509738950119878705">"Widget <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> eliminato."</string>
@@ -1841,6 +1843,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Visualizzazione a schermo intero"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Per uscire, scorri dall\'alto verso il basso."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Ruota per migliorare l\'anteprima"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Esci dallo schermo diviso per migliorare l\'anteprima"</string>
     <string name="done_label" msgid="7283767013231718521">"Fine"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Dispositivo di scorrimento circolare per le ore"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Dispositivo di scorrimento circolare per i minuti"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 30426c9..a6c46a2 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -343,6 +343,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"ŚŚ€Ś©ŚšŚ•ŚȘ ŚœŚ–Ś”Ś•ŚȘ ŚȘŚ Ś•ŚąŚ•ŚȘ Ś‘Ś–ŚžŚŸ Ś Ś’Ś™ŚąŚ” Ś‘Ś—Ś™Ś™Ś©ŚŸ Ś˜Ś‘Ś™ŚąŚ•ŚȘ Ś”ŚŚŠŚ‘Śą کڜ Ś”ŚžŚ›Ś©Ś™Śš."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"ŚŠŚ™ŚœŚ•Ś Ś”ŚžŚĄŚš"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Ś Ś™ŚȘڟ ŚœŚŠŚœŚ ŚŠŚ™ŚœŚ•Ś ŚžŚĄŚš کڜ Ś”ŚȘŚŠŚ•Ś’Ś”."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"ڔکڑŚȘŚ” ڐڕ کڙڠڕڙ کڜ Ś©Ś•ŚšŚȘ Ś”ŚĄŚ˜Ś˜Ś•ŚĄ"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"ŚžŚŚ€Ś©ŚšŚȘ ŚœŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś” ŚœŚ”Ś©Ś‘Ś™ŚȘ ڐŚȘ Ś©Ś•ŚšŚȘ Ś”ŚĄŚ˜Ś˜Ś•ŚĄ ڐڕ ŚœŚ”Ś•ŚĄŚ™ŚŁ Ś•ŚœŚ”ŚĄŚ™Śš ŚĄŚžŚœŚ™ ŚžŚąŚšŚ›ŚȘ."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"ŚœŚ”Ś™Ś•ŚȘ Ś©Ś•ŚšŚȘ Ś”ŚĄŚ˜Ś˜Ś•ŚĄ"</string>
@@ -1841,6 +1843,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"ŚŠŚ€Ś™Ś™Ś” Ś‘ŚžŚĄŚš ŚžŚœŚ"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"ڛړڙ ŚœŚŠŚŚȘ, Ś€Ś©Ś•Ś˜ ŚžŚ—ŚœŚ™Ś§Ś™Ś ŚŚŠŚ‘Śą ŚžŚœŚžŚąŚœŚ” ŚœŚžŚ˜Ś”."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"ڔڑڠŚȘŚ™"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ŚžŚĄŚ•Ś‘Ś‘Ś™Ś ڛړڙ ŚœŚšŚŚ•ŚȘ Ś˜Ś•Ś‘ ڙڕŚȘŚš"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"ŚŠŚšŚ™Śš ŚœŚŠŚŚȘ ŚžŚ”ŚžŚĄŚš Ś”ŚžŚ€Ś•ŚŠŚœ ڛړڙ ŚœŚšŚŚ•ŚȘ Ś˜Ś•Ś‘ ڙڕŚȘŚš"</string>
     <string name="done_label" msgid="7283767013231718521">"ŚĄŚ™Ś•Ś"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"ŚžŚ—Ś•Ś•ŚŸ Ś©ŚąŚ•ŚȘ ŚžŚąŚ’ŚœŚ™"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"ŚžŚ—Ś•Ś•ŚŸ ړڧڕŚȘ ŚžŚąŚ’ŚœŚ™"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 2700f02..1879cc6 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"ăƒ‡ăƒă‚€ă‚čăźæŒ‡çŽ‹èȘèšŒă‚»ăƒłă‚”ăƒŒă§èĄŒă‚ă‚ŒăŸæ“äœœă‚’ă‚­ăƒŁăƒ—ăƒăƒŁă§ăăŸă™ă€‚"</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"ă‚čクăƒȘăƒŒăƒłă‚·ăƒ§ăƒƒăƒˆăźæ’źćœ±"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"ディă‚čăƒ—ăƒŹă‚€ăźă‚čクăƒȘăƒŒăƒłă‚·ăƒ§ăƒƒăƒˆă‚’æ’źćœ±ă§ăăŸă™ă€‚"</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"ă‚čăƒ†ăƒŒă‚żă‚čăƒăƒŒăźç„ĄćŠčćŒ–ă‚„ć€‰æ›Ž"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"ă‚čăƒ†ăƒŒă‚żă‚čăƒăƒŒăźç„ĄćŠčćŒ–ă€ă‚·ă‚čăƒ†ăƒ ă‚ąă‚€ă‚łăƒłăźèżœćŠ ă‚„ć‰Šé™€ă‚’ă‚ąăƒ—ăƒȘă«èš±ćŻă—ăŸă™ă€‚"</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"ă‚čăƒ†ăƒŒă‚żă‚čăƒăƒŒăžăźèĄšç€ș"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"ć…šç”»éąèĄšç€ș"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"甂äș†ă™ă‚‹ă«ăŻă€äžŠă‹ă‚‰äž‹ă«ă‚čăƒŻă‚€ăƒ—ă—ăŸă™ă€‚"</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ç”»éąă‚’ć›žè»ąă•ă›ăŠèŠ‹ă‚„ă™ăă—ăŸă—ă‚‡ă†"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"戆ć‰Čç”»éąă‚’ç”‚äș†ă—ăŠèŠ‹ă‚„ă™ăă—ăŸă—ă‚‡ă†"</string>
     <string name="done_label" msgid="7283767013231718521">"漌äș†"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"ć††ćœąă‚čăƒ©ă‚€ăƒ€ăƒŒïŒˆæ™‚ïŒ‰"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"ć††ćœąă‚čăƒ©ă‚€ăƒ€ăƒŒïŒˆćˆ†ïŒ‰"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 7608199..8343198 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -315,7 +315,7 @@
     <string name="permgrouplab_microphone" msgid="2480597427667420076">"მიკროჀონი"</string>
     <string name="permgroupdesc_microphone" msgid="1047786732792487722">"აუდიოს áƒ©áƒáƒŹáƒ”áƒ áƒ"</string>
     <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"áƒ€áƒ˜áƒ–áƒ˜áƒ™áƒŁáƒ áƒ˜ áƒáƒ„áƒąáƒ˜áƒ•áƒáƒ‘áƒ"</string>
-    <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"áƒ€áƒ˜áƒ–áƒ˜áƒ™áƒŁáƒ áƒ˜ áƒáƒ„áƒąáƒ˜áƒ•áƒáƒ‘áƒáƒ–áƒ” წვდომა"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"áƒ€áƒ˜áƒ–áƒ˜áƒ™áƒŁáƒ  áƒáƒ„áƒąáƒ˜áƒ•áƒáƒ‘áƒáƒ–áƒ” წვდომა"</string>
     <string name="permgrouplab_camera" msgid="9090413408963547706">"კამერა"</string>
     <string name="permgroupdesc_camera" msgid="7585150538459320326">"áƒ€áƒáƒąáƒáƒ”áƒ‘áƒ˜áƒĄáƒ და ვიდეოების გადაჩება"</string>
     <string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"აჼლომაჼლო მოწყობილობები"</string>
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"áƒšáƒ”áƒŁáƒ«áƒšáƒ˜áƒ აჩბეჭდოს მოწყობილობის თითის ანაბეჭდის სენსორზე განჼორáƒȘიელებული ჟესჱები."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"ეკრანის ანაბეჭდის გადაჩება"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"áƒšáƒ”áƒŁáƒ«áƒšáƒ˜áƒ ეკრანის ანაბეჭდის გადაჩება."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"ქჹაჹუქიქ ზოლის გათიჹვა ან áƒȘვლილება"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"აპს ლეეძლება სჱაჱუსების ზოლის გათიჹვა და სისჱემის ჼაჱულების დამაჱება/წაჹლა."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"ქჹაჹუქიქ ზოლის ჩანაáƒȘვლება"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"სრულ ეკრანზე ნაჼვა"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"გამოსვლისათვის, გაასრიალეთ ზემოდან Ⴤვემოთ."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"გასაგებია"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ჹეაჱრიალეთ უკეთესი ჼედისთვის"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"უკეთესი ჼედვისთვის გამოდით გაყოჀილი ეკრანიდან"</string>
     <string name="done_label" msgid="7283767013231718521">"დასრულდა"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"საათების წრიული სლაიდერი"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"წუთების წრიული სლაიდერი"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 4dba2d1..c863cd8 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"ÒšÒ±Ń€Ń‹Đ»Ò“Ń‹ĐœŃ‹ÒŁ ŃĐ°ŃƒŃĐ°Ò› Ń–Đ·Ń– ŃĐ”ĐœŃĐŸŃ€Ń‹ĐœĐŽĐ° ĐŸŃ€Ń‹ĐœĐŽĐ°Đ»Ò“Đ°Đœ Ò›ĐžĐŒŃ‹Đ»ĐŽĐ°Ń€ĐŽŃ‹ ŃĐ°Ò›Ń‚Đ°ĐčЮы."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"ĐĄĐșŃ€ĐžĐœŃˆĐŸŃ‚ жасау"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"ДОсплДĐčĐŽŃ–ÒŁ сĐșŃ€ĐžĐœŃˆĐŸŃ‚Ń‹Đœ жасаĐč аласыз."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"ĐșÒŻĐčŃ–Đœ ĐșÓ©Ń€ŃĐ”Ń‚Ńƒ Ń‚Đ°Ò›Ń‚Đ°ŃŃ‹Đœ Ó©ŃˆŃ–Ń€Ńƒ ĐœĐ”ĐŒĐ”ŃĐ” Ó©Đ·ĐłĐ”Ń€Ń‚Ńƒ"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"ÒšĐŸĐ»ĐŽĐ°ĐœĐ±Đ°Ò“Đ° ĐșÒŻĐč Đ¶ĐŸĐ»Đ°Ò“Ń‹Đœ Ó©ŃˆŃ–Ń€ŃƒĐłĐ” ĐœĐ”ĐŒĐ”ŃĐ” Đ¶ÒŻĐčДліĐș Đ±Đ”Đ»ĐłŃ–ŃˆĐ”Đ»Đ”Ń€ĐŽŃ– Ò›ĐŸŃŃƒÒ“Đ° Đ¶Ó™ĐœĐ” Đ¶ĐŸŃŽÒ“Đ° Ń€Ò±Ò›ŃĐ°Ń‚ бДрДЎі."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"ĐșÒŻĐč Đ¶ĐŸĐ»Đ°Ò“Ń‹ Đ±ĐŸĐ»Ńƒ"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"ĐąĐŸĐ»Ń‹Ò› эĐșŃ€Đ°ĐœĐŽĐ° ĐșÓ©Ń€Ńƒ"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"ĐšŃ‹Ò“Ńƒ ÒŻŃˆŃ–Đœ Đ¶ĐŸÒ“Đ°Ń€Ń‹ĐŽĐ°Đœ Ń‚Ó©ĐŒĐ”Đœ Ò›Đ°Ń€Đ°Đč ŃŃ‹Ń€Ò“Ń‹Ń‚Ń‹ÒŁŃ‹Đ·."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"ĐąÒŻŃŃ–ĐœŃ–Đșті"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Đ–Đ°Ò›ŃŃ‹Ń€Đ°Ò› ĐșÓ©Ń€Ńƒ ÒŻŃˆŃ–Đœ Đ±Ò±Ń€Ń‹ÒŁŃ‹Đ·."</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Đ–Đ°Ò›ŃŃ‹Ń€Đ°Ò› ĐșÓ©Ń€Ńƒ ÒŻŃˆŃ–Đœ эĐșŃ€Đ°ĐœĐŽŃ‹ бөлу Ń€Đ”Đ¶ĐžĐŒŃ–ĐœĐ”Đœ ŃˆŃ‹Ò“Ń‹ÒŁŃ‹Đ·."</string>
     <string name="done_label" msgid="7283767013231718521">"ДаĐčŃ‹Đœ"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"ĐĄĐ°Ò“Đ°Ń‚Ń‚Đ°Ń€ аĐčĐœĐ°Đ»Ń‹ĐŒŃ‹ĐœŃ‹ÒŁ Ò›ĐŸĐ·Ò“Đ°Đ»Ń‚Ò›Ń‹ŃˆŃ‹"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"ĐœĐžĐœŃƒŃ‚ аĐčĐœĐ°Đ»Ń‹ĐŒŃ‹Đœ Ò›ĐŸĐ·Ò“Đ°Đ»Ń‚Ò›Ń‹Ńˆ"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 430493c..35b59be 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"ážąáž¶áž…áž…áž¶áž”áŸ‹áž™áž€â€‹áž…áž›áž“áž¶â€‹ážŠáŸ‚áž›áž’áŸ’ážœážŸáž“áŸ…áž›ážŸâ€‹áž“áŸ…áž›ážŸâ€‹áž§áž”áž€ážšážŽáŸâ€‹áž…áž¶áž”áŸ‹â€‹ážŸáŸ’áž“áž¶áž˜â€‹áž˜áŸ’ážšáž¶áž˜ážŠáŸƒâ€‹ážšáž”ážŸáŸ‹áž§áž”áž€ážšážŽáŸáž”áž¶áž“áŸ”"</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"ថត​ឱេក្រង់"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"ážąáž¶áž…â€‹ážážâ€‹ážąáŸáž€áŸ’ážšáž„áŸ‹â€‹áž“áŸƒâ€‹áž•áŸ’áž‘áž¶áŸ†áž„â€‹ážąáŸáž€áŸ’ážšáž„áŸ‹â€‹áž”áž¶áž“áŸ”"</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"បិទ ážŹâ€‹áž€áŸ‚â€‹ážšáž”áž¶ážšâ€‹ážŸáŸ’ážáž¶áž“áž—áž¶áž–"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"ážČ្យ​កម្មវិធឞ​បិទ​របារ​ស្ថានភាព ឬ​បន្ថែម និង​លុប​រឌប​តំណាង​ប្រព័ន្ធ។"</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"ធ្វសជារបារស្ថានភាព"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"áž€áŸ†áž–áž»áž„áž˜ážŸáž›áž–áŸáž‰ážąáŸáž€áŸ’ážšáž„áŸ‹"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"ដសម្បឞចាកចេញ ážŸážŒáž˜ážąážŒážŸáž–ážžáž›ážŸáž…áž»áŸ‡áž€áŸ’ážšáŸ„áž˜áŸ”"</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"យល់ហសយ"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"បង្វិលដសម្បឞមសលបានកាន់តែច្បាស់"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"áž…áŸáž‰áž–ážžáž˜áž»ážáž„áž¶ážšâ€‹áž”áŸ†áž”áŸ‚áž€ážąáŸáž€áŸ’ážšáž„áŸ‹ážŠážŸáž˜áŸ’áž”ážžáž˜ážŸáž›áž”áž¶áž“áž€áž¶áž“áŸ‹ážáŸ‚áž…áŸ’áž”áž¶ážŸáŸ‹"</string>
     <string name="done_label" msgid="7283767013231718521">"រវចរាល់"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"គ្រាប់​រំកិល​រង្វង់​ម៉ោង"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"គ្រាប់​រំកិល​រង្វង់​នាទឞ"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index d923937..d0decdf 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"àČžàČŸàȧàČšàČŠ àČ«àČżàȂàȗàČ°àł‌àČȘàłàȰàČżàȂàČŸàł àČžàł†àČšàłàČžàČ°àł‌àČšàČČàłàČČàČż àČšàČĄàł†àČžàČżàČŠ àČ—àł†àČ¶àłàȚàČ°àł‌àȗàČłàČšàłàČšàł àČ•àłàČŻàČŸàČȘàłàȚàČ°àł àČźàČŸàČĄàČż."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"àČžàłàČ•àłàČ°àł€àČšàł‌àȶàČŸàČŸàł àČ€àł†àČ—àł†àČŠàłàČ•àłŠàČłàłàČłàČż"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"àČȘàłàȰàČŠàČ°àłàȶàČšàČŠ àČžàłàČ•àłàČ°àł€àČšàł‌àȶàČŸàČŸàł àȅàČšàłàČšàł àČ€àł†àČ—àł†àČŠàłàČ•àłŠàČłàłàČłàČŹàČČàłàČČàČŠàł."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"àČžàłàČ„àČżàČ€àČż àČȘàČŸàłàȟàČżàČŻàČšàłàČšàł àČšàČżàČ·àłàČ•àłàȰàČżàČŻàČ—àłŠàČłàČżàČžàČż àȇàČČàłàČČàČ”àł‡ àČźàČŸàČ°àłàČȘàČĄàČżàČžàČż"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"àČžàłàČ„àČżàČ€àČż àČȘàČŸàłàȟàČżàČŻàČšàłàČšàł àČšàČżàČ·àłàČ•àłàȰàČżàČŻàČ—àłŠàČłàČżàČžàČČàł àȅàČ„àČ”àČŸ àČžàł‡àȰàČżàČžàČČàł àČźàČ€àłàČ€àł àČžàČżàČžàłàȟàȂ àȐàȕàČŸàČšàł‌àȗàČłàČšàłàČšàł àČ€àł†àČ—àł†àČŠàłàČčàČŸàȕàČČàł àȅàČȘàłàČČàČżàČ•àł‡àȶàČšàł‌àČ—àł† àȅàČ”àȕàČŸàȶ àČšàł€àČĄàłàČ€àłàČ€àČŠàł†."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"àČžàłàČ„àČżàČ€àČż àČȘàČŸàłàȟàČżàČŻàČŸàȗàČżàȰàČČàł"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"àČȘàł‚àČ°àłàČŁ àČȘàȰàČŠàł†àČŻàČšàłàČšàł àČ”àł€àČ•àłàČ·àČżàČžàČČàČŸàČ—àłàČ€àłàČ€àČżàČŠàł†"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"àČšàČżàČ°àłàȗàČźàČżàČžàČČàł, àČźàł‡àČČàČżàČšàČżàȂàČŠ àČ•àł†àČłàČ•àłàČ•àł† àČžàłàČ”àłˆàČȘàł àČźàČŸàČĄàČż."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"àČ€àČżàČłàČżàČŻàČżàČ€àł"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"àȅàČ€àłàČŻàłàČ€àłàČ€àČź àČ”àł€àČ•àłàČ·àČŁàł†àȗàČŸàȗàČż àČ€àČżàČ°àłàȗàČżàČžàČż"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"àȅàČ€àłàČŻàłàČ€àłàČ€àČź àČ”àł€àČ•àłàČ·àČŁàł†àȗàČŸàȗàČż àČžàłàČȘàłàČČàČżàČŸàł‌ àČžàłàČ•àłàČ°àł€àČšàł‌àČšàČżàȂàČŠ àČšàČżàČ°àłàȗàČźàČżàČžàČż"</string>
     <string name="done_label" msgid="7283767013231718521">"àČźàłàȗàČżàČŠàČżàČŠàł†"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"àȗàȂàČŸàł†àȗàČł àČ”àłƒàČ€àłàČ€àČŸàȕàČŸàȰ àČžàłàČČàłˆàČĄàČ°àł"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"àČšàČżàČźàČżàČ·àȗàČł àČ”àłƒàČ€àłàČ€àČŸàȕàČŸàȰ àČžàłàČČàłˆàČĄàČ°àł"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 4df0322..04dc86a 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"êž°êž° ì§€ëŹž 섌서에서 동작을 ìșĄìČ˜í•©ë‹ˆë‹€."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"ìŠ€íŹëŠ°ìƒ· ìŽŹì˜"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"디슀플레읎 ìŠ€íŹëŠ°ìƒ·ì„ ìŽŹì˜í•  수 있슔니닀."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"상태 표시쀄 ì‚Źìš© 쀑지 또는 수정"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"앱읎 상태 표시쀄을 ì‚Źìš©ì€‘ì§€í•˜ê±°ë‚˜ 시슀템 아읎윘을 추가 및 제거할 수 ìžˆë„ëĄ 허용합니닀."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"상태 표시쀄에 위ìč˜"</string>
@@ -596,7 +598,7 @@
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"너묮 밝음"</string>
     <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"전원 누늄읎 감지되었슔니닀."</string>
     <string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"ìĄ°ì • 시도"</string>
-    <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"ì§€ëŹžì„ ë“±ëĄí•  때마닀 손가띜을 ìĄ°êžˆì”© 읎동하섞요"</string>
+    <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"ì§€ëŹžìŽ 읞식될 때마닀 손가띜을 ìĄ°êžˆì”© 읎동하섞요"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
     <string name="fingerprint_error_not_match" msgid="4599441812893438961">"ì§€ëŹžìŽ 읞식되지 않았슔니닀."</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"전ìČŽ 화멎 ëȘšë“œ"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"ìą…ëŁŒí•˜ë €ë©Ž 위에서 ì•„ëž˜ëĄœ 슀와읎프합니닀."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"확읞"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ìčŽë©”띌 ëŻžëŠŹëłŽêž° 화멎읎 잘 ëłŽìŽë„ëĄ 회전하섞요."</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"ìčŽë©”띌 ëŻžëŠŹëłŽêž° 화멎읎 잘 ëłŽìŽë„ëĄ 화멎 분할을 ìą…ëŁŒí•˜ì„žìš”."</string>
     <string name="done_label" msgid="7283767013231718521">"ì™„ëŁŒ"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"시간 원형 ìŠŹëŒìŽë”"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"분 원형 ìŠŹëŒìŽë”"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 521c998..bdb8fe1 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"ĐąÒŻĐ·ĐŒÓ©ĐșŃ‚Ó©ĐłÒŻ ĐŒĐ°ĐœĐ¶Đ° ĐžĐ·ĐžĐœĐžĐœ ŃĐ”ĐœŃĐŸŃ€ŃƒĐœĐŽĐ° Đ¶Đ°ŃĐ°Đ»ĐłĐ°Đœ Đ¶Đ°ÒŁŃĐŸĐŸĐ»ĐŸŃ€ĐŽŃƒ жазЎырып алат."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"ĐĄĐșŃ€ĐžĐœŃˆĐŸŃ‚ тартып алуу"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"ДОсплДĐčĐŽĐžĐœ сĐșŃ€ĐžĐœŃˆĐŸŃ‚ŃƒĐœ тартып Đ°Đ»ŃĐ°ÒŁŃ‹Đ· Đ±ĐŸĐ»ĐŸŃ‚."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"абал тОлĐșĐ”ŃĐžĐœ Ó©Ń‡ÒŻŃ€ÒŻÒŻ жД Ó©Đ·ĐłÓ©Ń€Ń‚ÒŻÒŻ"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"ĐšĐŸĐ»ĐŽĐŸĐœĐŒĐŸĐłĐŸ абал тОлĐșĐ”ŃĐžĐœ Ó©Ń‡ÒŻŃ€ÒŻÒŻ жД Ń‚ŃƒŃ‚ŃƒĐŒ ŃÒŻŃ€Ó©Ń‚Ń‡Ó©Đ»Ó©Ń€ÒŻĐœ ĐșĐŸŃˆŃƒŃƒ жД алып салуу ĐŒÒŻĐŒĐșÒŻĐœŃ‡ÒŻĐ»ÒŻĐłÒŻĐœ бДрДт."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"абал тОлĐșĐ”ŃĐžĐœĐžĐœ ĐŒĐžĐ»ĐŽĐ”Ń‚ĐžĐœ атĐșаруу"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"ĐąĐŸĐ»ŃƒĐș эĐșŃ€Đ°Đœ Ń€Đ”Đ¶ĐžĐŒĐž"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Чыгуу ÒŻŃ‡ÒŻĐœ эĐșŃ€Đ°ĐœĐŽŃ‹ ылЎыĐč ŃÒŻŃ€ÒŻĐż ĐșĐŸŃŽÒŁŃƒĐ·."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"ĐąÒŻŃˆÒŻĐœĐŽÒŻĐŒ"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ЖаĐșшырааĐș ĐșÓ©Ń€ÒŻÒŻ ÒŻŃ‡ÒŻĐœ Đ±ŃƒŃ€ŃƒÒŁŃƒĐ·"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"ЖаĐșшырааĐș ĐșÓ©Ń€ÒŻÒŻ ÒŻŃ‡ÒŻĐœ эĐșŃ€Đ°ĐœĐŽŃ‹ Đ±Ó©Đ»ÒŻÒŻ Ń€Đ”Đ¶ĐžĐŒĐžĐœĐ”Đœ Ń‡Ń‹ĐłŃ‹ÒŁŃ‹Đ·"</string>
     <string name="done_label" msgid="7283767013231718521">"Даяр"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Саат жДбДсО"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"ĐœÒŻĐœÓ©Ń‚ жДбДсО"</string>
@@ -1986,7 +1990,7 @@
     <string name="app_category_social" msgid="2278269325488344054">"ĐĄĐŸŃ†ĐžĐ°Đ»ĐŽŃ‹Đș Đ¶Đ°ĐœĐ° ĐșĐŸĐŒĐŒŃƒĐœĐžĐșацоя"</string>
     <string name="app_category_news" msgid="1172762719574964544">"Đ–Đ°ÒŁŃ‹Đ»Ń‹Đșтар Đ¶Đ°ĐœĐ° Đ¶ŃƒŃ€ĐœĐ°Đ»ĐŽĐ°Ń€"</string>
     <string name="app_category_maps" msgid="6395725487922533156">"Карталар Đ¶Đ°ĐœĐ° Ń‡Đ°Đ±Ń‹Ń‚Ń‚ĐŸĐŸ"</string>
-    <string name="app_category_productivity" msgid="1844422703029557883">"ÓšĐœĐŽÒŻŃ€ÒŻŃˆ ĐșĐ°Ń‚Đ”ĐłĐŸŃ€ĐžŃŃŃ‹"</string>
+    <string name="app_category_productivity" msgid="1844422703029557883">"МаĐčĐœĐ°ĐżŃ‚ŃƒŃƒĐ»ŃƒĐș"</string>
     <string name="app_category_accessibility" msgid="6643521607848547683">"АтаĐčŃ‹Đœ ĐŒÒŻĐŒĐșÒŻĐœŃ‡ÒŻĐ»ÒŻĐșтөр"</string>
     <string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"ĐąÒŻĐ·ĐŒÓ©ĐșŃ‚ÒŻĐœ саĐșтагычы"</string>
     <string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB арĐșŃ‹Đ»ŃƒŃƒ ĐŒÒŻŃ‡ÒŻĐ»ÒŻŃˆŃ‚ÒŻĐșŃ‚Ó©Ń€ĐŽÒŻ Đ°ĐœŃ‹ĐșŃ‚ĐŸĐŸ"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index a172576..b7a4c05 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"àșȘàșČàșĄàșČàș”àșšàș±àș™àș—àș¶àșàș—່àșČàș—àșČàș‡àș—àș”່ເàșàș”àș”àș‚àș¶à»‰àș™à»ƒàș™àș­àșžàș›àș°àșàș­àș™à»€àșŠàș±àș™à»€àșŠàș”àș„àșČàșàș™àșŽà»‰àș§àșĄàș·à»„àș”້."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"àș–່àșČàșàșźàșčàșšà»œà»‰àșČàșˆà»"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"àșȘàșČàșĄàșČàș”àș–່àșČàșàșźàșčàșšà»œà»‰àșČàșˆà»à»„àș”້."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"àș›àșŽàș”àșàșČàș™àș™àș™àșłà»ƒàșŠà»‰ àș«àșŒàș· ແàșà»‰à»„àș‚ແàș–àșšàșȘàș°àș–àșČàș™àș°"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"àș­àș°àș™àșžàșàșČàș”ໃàș«à»‰à»àș­àș±àșšàșŻàș›àșŽàș”àșàșČàș™à»€àșźàș±àș”àș§àșœàșàș‚àș­àș‡à»àș–àșšàșȘàș°àș–àșČàș™àș° àș«àșŒàș·à»€àșžàș”່àșĄ ແàș„àș°àș„àș¶àșšà»„àș­àș„àș­àș™àș„àș°àșšàș»àșšàș­àș­àșà»„àș”້."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"ເàș›àș±àș™â€‹à»àș–àșšâ€‹àșȘàș°â€‹àș–àșČ​àș™àș°"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"àșàșČàș™â€‹à»€àșšàșŽà»ˆàș‡â€‹à»€àș•àș±àșĄâ€‹à»œà»‰àșČ​àșˆà»"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"àș«àșČàșàș•້àș­àș‡àșàșČàș™àș­àș­àș, ໃàș«à»‰àșźàșčàș”àșˆàșČàșàș—àșČàș‡à»€àș—àșŽàș‡àș„àș»àș‡àșĄàșČàș—àșČàș‡àș„àșžà»ˆàșĄ."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"ໄàș”້​ແàș„້àș§"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ໝàșžàș™à»€àșžàș·à»ˆàș­àșĄàșžàșĄàșĄàș­àș‡àș—àș”່àș”àș”àș‚àș¶à»‰àș™"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"àș­àș­àșàșˆàșČàșà»àșšà»ˆàș‡à»œà»‰àșČàșˆà»à»€àșžàș·à»ˆàș­àșĄàșžàșĄàșĄàș­àș‡àș—àș”່àș”àș”àș‚àș¶à»‰àș™"</string>
     <string name="done_label" msgid="7283767013231718521">"ແàș„້àș§à»†"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"ໂàș•ໝàșžàș™àș›àș±àșšàșŠàș»à»ˆàș§à»‚àșĄàș‡"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"ໂàș•ໝàșžàș™àș›àș±àșšàș™àșČàș—àș”"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 6da22ed..cdfa1ac 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -344,6 +344,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Gali uĆŸfiksuoti gestus, atliktus naudojant ÄŻrenginio piršto antspaudo jutiklÄŻ."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Ekrano kopijos kƫrimas"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Galima sukurti vaizdo ekrano kopiją."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"išjungti ar keisti bĆ«senos juostą"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"LeidĆŸiama programai neleisti bĆ«senos juostos arba pridėti ir pašalinti sistemos piktogramas."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"bƫti bƫsenos juosta"</string>
@@ -1842,6 +1844,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"PerĆŸiĆ«rima viso ekrano reĆŸimu"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Jei norite išeiti, perbraukite ĆŸemyn iš viršaus."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Supratau"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Pasukite, kad geriau matytumėte vaizdą"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Išeikite iš išskaidyto ekrano reĆŸimo, kad geriau matytumėte vaizdą"</string>
     <string name="done_label" msgid="7283767013231718521">"Atlikta"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Apskritas valandĆł šliauĆŸiklis"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Apskritas minučiĆł šliauĆŸiklis"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index aeb89a2..66826c9 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -243,7 +243,7 @@
     <string name="global_actions" product="tv" msgid="3871763739487450369">"Android TV opcijas"</string>
     <string name="global_actions" product="default" msgid="6410072189971495460">"TālruƆa opcijas"</string>
     <string name="global_action_lock" msgid="6949357274257655383">"Ekrāna bloķētājs"</string>
-    <string name="global_action_power_off" msgid="4404936470711393203">"Izslēgt strāvas padevi"</string>
+    <string name="global_action_power_off" msgid="4404936470711393203">"Izslēgt"</string>
     <string name="global_action_power_options" msgid="1185286119330160073">"Barošana"</string>
     <string name="global_action_restart" msgid="4678451019561687074">"Restartēt"</string>
     <string name="global_action_emergency" msgid="1387617624177105088">"Ārkārtas situācija"</string>
@@ -343,6 +343,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Var uztvert ĆŸestus ierÄ«ces pirksta nospieduma sensorā."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"EkrānuzƆēmuma izveide"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Var izveidot displeja ekrānuzƆēmumu."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"atspējot vai pārveidot statusa joslu"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Ä»auj lietotnei atspējot statusa joslu vai pievienot un noƆemt sistēmas ikonas."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"Bƫt par statusa joslu"</string>
@@ -1841,6 +1843,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"SkatÄ«šanās pilnekrāna reĆŸÄ«mā"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Lai izietu, no augšdaÄŒas velciet lejup."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Labi"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Lai uzlabotu skatu, pagrieziet ekrānu."</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Lai uzlabotu skatu, izejiet no ekrāna sadalÄ«šanas reĆŸÄ«ma."</string>
     <string name="done_label" msgid="7283767013231718521">"Gatavs"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Stundu apČveida slīdnis"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"MinĆ«šu apÄŒveida slÄ«dnis"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 44b8e46..dd5e44e 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"ĐœĐŸĐ¶Đ” Ўа ŃĐœĐžĐŒĐž ĐŽĐČОжДња ŃˆŃ‚ĐŸ сД ĐœĐ°ĐżŃ€Đ°ĐČĐ”ĐœĐž ĐœĐ° ŃĐ”ĐœĐ·ĐŸŃ€ĐŸŃ‚ за ĐŸŃ‚ĐżĐ”Ń‡Đ°Ń‚ĐŸŃ†Đž ĐœĐ° ŃƒŃ€Đ”ĐŽĐŸŃ‚."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"ЗачуĐČуĐČањД слОĐșа ĐŸĐŽ Đ”ĐșŃ€Đ°ĐœĐŸŃ‚"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"ĐœĐŸĐ¶Đ” Ўа Đ·Đ°Ń‡ŃƒĐČа слОĐșа ĐŸĐŽ Đ”ĐșŃ€Đ°ĐœĐŸŃ‚."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"ĐŸĐœĐ”ĐČĐŸĐ·ĐŒĐŸĐ¶Đž ОлО ĐžĐ·ĐŒĐ”ĐœĐž ŃŃ‚Đ°Ń‚ŃƒŃĐœĐ° Đ»Đ”ĐœŃ‚Đ°"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Đ”ĐŸĐ·ĐČĐŸĐ»ŃƒĐČа аплОĐșацојата Ўа ја ĐŸĐœĐ”ĐČĐŸĐ·ĐŒĐŸĐ¶Đž ŃŃ‚Đ°Ń‚ŃƒŃĐœĐ°Ń‚Đ° Đ»Đ”ĐœŃ‚Đ° ОлО Ўа ĐŽĐŸĐŽĐ°ĐČа ОлО ĐŸŃ‚ŃŃ‚Ń€Đ°ĐœŃƒĐČа ŃĐžŃŃ‚Đ”ĐŒŃĐșĐž ĐžĐșĐŸĐœĐž."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"Ўа ŃŃ‚Đ°ĐœĐ” ŃŃ‚Đ°Ń‚ŃƒŃĐœĐ° Đ»Đ”ĐœŃ‚Đ°"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"ĐĄĐ” проĐșажуĐČа ĐœĐ° цДл Đ”ĐșŃ€Đ°Đœ"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"За Ўа ОзлДзДтД, ĐżĐŸĐČлДчДтД ĐŸĐŽĐŸĐ·ĐłĐŸŃ€Đ° ĐœĐ°ĐŽĐŸĐ»Ńƒ."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"СфатоĐČ"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Đ ĐŸŃ‚ĐžŃ€Đ°Ń˜Ń‚Đ” за ĐżĐŸĐŽĐŸĐ±Đ°Ń€ проĐșаз"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"За ĐżĐŸĐŽĐŸĐ±Đ°Ń€ проĐșаз, ОзлДзДтД ĐŸĐŽ ĐżĐŸĐŽĐ”Đ»Đ”ĐœĐžĐŸŃ‚ Đ”ĐșŃ€Đ°Đœ"</string>
     <string name="done_label" msgid="7283767013231718521">"Đ“ĐŸŃ‚ĐŸĐČĐŸ"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"ПроĐșаз ĐœĐ° Ń‡Đ°ŃĐŸĐČĐž ĐČĐŸ ĐșŃ€ŃƒĐ¶ĐœĐŸ ĐŽĐČОжДњД"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"ПроĐșаз ĐœĐ° ĐŒĐžĐœŃƒŃ‚Đž ĐČĐŸ ĐșŃ€ŃƒĐ¶ĐœĐŸ ĐŽĐČОжДњД"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 1b7daec..4ed058d 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"àŽ‰àŽȘàŽ•àŽ°àŽŁàŽ€à”àŽ€àŽżàŽšà”àŽ±à”† àŽ«àŽżàŽ‚àŽ—à”ŒàŽȘà”àŽ°àŽżàŽšà”àŽ±à” àŽžà”†à”»àŽžàŽ±àŽżà”œ àŽšà”†àŽŻà”‌àŽ€ àŽœà”†àŽžà”‌àŽ±à”àŽ±àŽ±à”àŽ•à”Ÿ àŽ•à”àŽŻàŽŸàŽȘà”‌àŽšà”Œ àŽšà”†àŽŻà”àŽŻàŽŸàŽšàŽŸàŽ•à”àŽ‚."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"àŽžà”àŽ•à”àŽ°à”€àŽšà”‍àŽ·à”‹àŽŸà”àŽŸà” àŽŽàŽŸà”àŽ•à”àŽ•à”àŽ•"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"àŽĄàŽżàŽžà”‌àŽȘà”àŽČà”‡àŽŻà”àŽŸà”† àŽžà”‌àŽ•à”àŽ°à”€à”»àŽ·à”‹àŽŸà”àŽŸà” àŽŽàŽŸà”àŽ•à”àŽ•àŽŸà”» àŽ•àŽŽàŽżàŽŻà”àŽ‚."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"àŽžà”àŽ±à”àŽ±àŽŸàŽ±à”àŽ±àŽžà” àŽŹàŽŸà”Œ àŽȘà”àŽ°àŽ”à”ŒàŽ€à”àŽ€àŽšàŽ°àŽčàŽżàŽ€àŽźàŽŸàŽ•à”àŽ•à”àŽ• àŽ…àŽČà”àŽČà”†àŽ™à”àŽ•àŽżà”œ àŽȘàŽ°àŽżàŽ·à”‌àŽ•à”àŽ•àŽ°àŽżàŽ•à”àŽ•à”àŽ•"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"àŽšàŽżàŽČ àŽŹàŽŸà”Œ àŽȘà”àŽ°àŽ”à”ŒàŽ€à”àŽ€àŽ°àŽčàŽżàŽ€àŽźàŽŸàŽ•à”àŽ•à”àŽšà”àŽšàŽ€àŽżàŽšà” àŽ…àŽČà”àŽČà”†àŽ™à”àŽ•àŽżà”œ àŽžàŽżàŽžà”‌àŽ±à”àŽ±àŽ‚ àŽàŽ•à”àŽ•àŽŁà”àŽ•à”Ÿ àŽšà”‡à”ŒàŽ•à”àŽ•à”àŽšà”àŽšàŽ€àŽżàŽšà”àŽ‚ àŽšà”€àŽ•à”àŽ•àŽ‚àŽšà”†àŽŻà”àŽŻà”àŽšà”àŽšàŽ€àŽżàŽšà”àŽ‚ àŽ…àŽȘà”àŽČàŽżàŽ•à”àŽ•à”‡àŽ·àŽšà”† àŽ…àŽšà”àŽ”àŽŠàŽżàŽ•à”àŽ•à”àŽšà”àŽšà”."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"àŽžà”àŽ±à”àŽ±àŽŸàŽ±à”àŽ±àŽžà” àŽŹàŽŸà”Œ àŽ†àŽŻàŽżàŽ°àŽżàŽ•à”àŽ•à”àŽ•"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"àŽȘà”‚à”ŒàŽŁà”àŽŁ àŽžà”‌àŽ•à”àŽ°à”€àŽšàŽżà”œ àŽ•àŽŸàŽŁà”àŽšà”àŽšà”"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"àŽ…àŽ”àŽžàŽŸàŽšàŽżàŽȘà”àŽȘàŽżàŽ•à”àŽ•àŽŸà”», àŽźà”àŽ•àŽłàŽżà”œ àŽšàŽżàŽšà”àŽšà” àŽ€àŽŸàŽŽà”‹àŽŸà”àŽŸà” àŽžà”àŽ”à”ˆàŽȘà”àŽȘà” àŽšà”†àŽŻà”àŽŻà”àŽ•."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"àŽźàŽšàŽžà”àŽžàŽżàŽČàŽŸàŽŻàŽż"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"àŽźàŽżàŽ•àŽšà”àŽš àŽ•àŽŸàŽŽà”‌àŽšàŽŻà”‌àŽ•à”àŽ•àŽŸàŽŻàŽż àŽ±à”ŠàŽŸà”àŽŸà”‡àŽ±à”àŽ±à” àŽšà”†àŽŻà”àŽŻà”àŽ•"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"àŽźàŽżàŽ•àŽšà”àŽš àŽ•àŽŸàŽŽà”‌àŽšàŽŻà”‌àŽ•à”àŽ•àŽŸàŽŻàŽż àŽžà”‌àŽ•à”àŽ°à”€à”» àŽ”àŽżàŽ­àŽœàŽš àŽźà”‹àŽĄàŽżà”œ àŽšàŽżàŽšà”àŽšà” àŽȘà”àŽ±àŽ€à”àŽ€à”àŽ•àŽŸàŽ•à”àŽ•à”àŽ•"</string>
     <string name="done_label" msgid="7283767013231718521">"àŽȘà”‚à”ŒàŽ€à”àŽ€àŽżàŽŻàŽŸàŽ•à”àŽ•àŽż"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"àŽšàŽŸàŽ•à”àŽ°àŽżàŽ•àŽźàŽŸàŽŻàŽż àŽźàŽŁàŽżàŽ•à”àŽ•à”‚àŽ±à”àŽ•à”Ÿ àŽŠà”ƒàŽ¶à”àŽŻàŽźàŽŸàŽ•à”àŽšà”àŽš àŽžà”àŽČà”ˆàŽĄà”Œ"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"àŽšàŽŸàŽ•à”àŽ°àŽżàŽ•àŽźàŽŸàŽŻàŽż àŽźàŽżàŽšàŽżàŽ±à”àŽ±à”àŽ•à”Ÿ àŽŠà”ƒàŽ¶à”àŽŻàŽźàŽŸàŽ•à”àŽšà”àŽš àŽžà”àŽČà”ˆàŽĄà”Œ"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index dd6b40d..5b8209b 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -303,7 +303,7 @@
     <string name="permgrouplab_location" msgid="1858277002233964394">"БаĐčŃ€ŃˆĐžĐ»"</string>
     <string name="permgroupdesc_location" msgid="1995955142118450685">"ŃĐœŃ Ń‚Ó©Ń…Ó©Ó©Ń€Ó©ĐŒĐ¶ĐžĐčĐœ баĐčŃ€ŃˆĐžĐ»ĐŽ Ń…Đ°ĐœĐŽĐ°Đ»Ń‚ хоĐčх"</string>
     <string name="permgrouplab_calendar" msgid="6426860926123033230">"ĐšĐ°Đ»Đ”ĐœĐŽĐ°Ń€ŃŒ"</string>
-    <string name="permgroupdesc_calendar" msgid="6762751063361489379">"ĐšĐ°Đ»Đ”ĐœĐŽĐ°Ń€ŃŒ руу Ń…Đ°ĐœĐŽĐ°Ń…"</string>
+    <string name="permgroupdesc_calendar" msgid="6762751063361489379">"ĐšĐ°Đ»Đ”ĐœĐŽĐ°Ń€ŃŒĐŽ Ń…Đ°ĐœĐŽĐ°Ń…"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"ĐœĐ”ŃŃĐ”Đ¶"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS ĐŒĐ”ŃŃĐ”Đ¶ĐžĐčĐł ĐžĐ»ĐłŃŃŃ…, харах"</string>
     <string name="permgrouplab_storage" msgid="17339216290379241">"ЀаĐčлууЎ"</string>
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"ĐąÓ©Ń…Ó©Ó©Ń€Ó©ĐŒĐ¶ĐžĐčĐœ Ń…ŃƒŃ€ŃƒŃƒĐœŃ‹ хээ ĐŒŃĐŽŃ€ŃĐłŃ‡ĐžĐŽ Đ·Đ°ĐœĐłĐ°ŃĐ°Đœ Đ·Đ°ĐœĐłĐ°Đ°Đł Ń‚Đ°ĐœĐžĐœĐ°."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Đ”ŃĐ»ĐłŃŃ†ĐžĐčĐœ Đ·ŃƒŃ€ĐłĐžĐčĐł Юарах"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Đ”ŃĐ»ĐłŃŃ†ĐžĐčĐœ Đ·ŃƒŃ€ĐłĐžĐčĐł Юарах Đ±ĐŸĐ»ĐŸĐŒĐ¶Ń‚ĐŸĐč."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"статус ŃĐ°ĐŒĐ±Đ°Ń€Ń‹Đł оЮэĐČŃ…ĐłÒŻĐč Đ±ĐŸĐ»ĐłĐŸŃ… Đ±ĐŸĐ»ĐŸĐœ өөрчлөх"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Апп ĐœŃŒ статус ŃĐ°ĐŒĐ±Đ°Ń€Ń‹Đł оЮэĐČŃ…ĐłÒŻĐč Đ±ĐŸĐ»ĐłĐŸŃ… эсĐČŃĐ» ŃĐžŃŃ‚Đ”ĐŒ ĐŽÒŻŃ€ŃĐžĐčĐł ĐœŃĐŒŃŃ…, хасах Đ±ĐŸĐ»ĐŸĐŒĐ¶Ń‚ĐŸĐč."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"ŃŃ‚Đ°Ń‚ŃƒŃŃ‹Đœ хэсэг Đ±ĐŸĐ»ĐŸŃ…"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Đ‘ÒŻŃ‚ŃĐœ ĐŽŃĐ»ĐłŃŃ†ŃŃŃ€ ÒŻĐ·ŃĐ¶ баĐčĐœĐ°"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Гарахаар Đ±ĐŸĐ» Юээрээс ĐœŃŒ ĐŽĐŸĐŸŃˆ ĐœŃŒ Ń‡ĐžŃ€ĐœŃ ÒŻÒŻ."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"ОĐčĐ»ĐłĐŸĐ»ĐŸĐŸ"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ЄарагЎах баĐčЎлыг саĐčĐ¶Ń€ŃƒŃƒĐ»Đ°Ń… Đ±ĐŸĐ» ŃŃ€ĐłÒŻÒŻĐ»ĐœŃ ÒŻÒŻ"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"ЄарагЎах баĐčЎлыг саĐčĐ¶Ń€ŃƒŃƒĐ»Đ°Ń… Đ±ĐŸĐ» ĐŽŃĐ»ĐłŃŃ†ĐžĐčĐł хуĐČаах ĐłĐŸŃ€ĐžĐŒĐŸĐŸŃ ĐłĐ°Ń€ĐœĐ° уу"</string>
     <string name="done_label" msgid="7283767013231718521">"Đ”ŃƒŃƒŃŃĐ°Đœ"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Њаг ĐłÒŻĐčĐ»ĐłŃĐłŃ‡"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"ĐœĐžĐœŃƒŃ‚ ĐłÒŻĐčĐ»ĐłŃĐłŃ‡"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 6be015a..ee6e2ee 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"à€Ąà€żà€”à„‍à€čà€Ÿà€‡à€žà€šà„‍à€Żà€Ÿ à€«à€żà€‚à€—à€°à€Șà„à€°à€żà€‚à€Ÿ à€žà„‡à€‚à€šà„à€žà€°à€”à€°à„€à€Č à€œà„‡à€¶à„à€šà€° à€•à„…à€Șà„‍à€šà€° à€•à€°à„‚ à€¶à€•à€€à„‡."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"à€žà„à€•à„à€°à„€à€šà€¶à„‰à€Ÿ à€˜à„à€Żà€Ÿ"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"à€Ąà€żà€žà„à€Șà„à€Čà„‡à€šà€Ÿ à€žà„à€•à„à€°à„€à€šà€¶à„‰à€Ÿ à€˜à„‡à€Š à€¶à€•à€€à„‹."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"à€žà„à€Ÿà„‡à€Ÿà€ž à€Źà€Ÿà€° à€…à€•à„à€·à€ź à€•à€°à€Ÿ à€•à€żà€‚à€”à€Ÿ à€žà„à€§à€Ÿà€°à€żà€€ à€•à€°à€Ÿ"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"à€žà„à€Ÿà„‡à€Ÿà€ž à€Źà€Ÿà€° à€…à€•à„à€·à€ź à€•à€°à€Łà„à€Żà€Ÿà€žà€Ÿà€ à„€ à€•à€żà€‚à€”à€Ÿ à€žà€żà€žà„à€Ÿà€ź à€šà€żà€šà„à€čà„‡ à€œà„‹à€Ąà€Łà„à€Żà€Ÿà€žà€Ÿà€ à„€ à€†à€Łà€ż à€•à€Ÿà€ąà€Łà„à€Żà€Ÿà€žà€Ÿà€ à„€ à€…‍à„…à€Ș à€Čà€Ÿ à€…à€šà„à€źà€€à„€ à€Šà„‡à€€à„‡."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"à€žà„à€Ÿà„‡à€Ÿà€ž à€Źà€Ÿà€° à€čà„‹à€Š à€Šà„à€Żà€Ÿ"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"à€Șà„‚à€°à„à€Ł à€žà„à€•à„à€°à„€à€šà€”à€° à€Șà€Ÿà€čà€€ à€†à€čà€Ÿà€€"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"à€Źà€Ÿà€čà„‡à€° à€Șà€Ąà€Łà„à€Żà€Ÿà€žà€Ÿà€ à„€, à€”à€°à„‚à€š à€–à€Ÿà€Čà„€ à€žà„à€”à€Ÿà€‡à€Ș à€•à€°à€Ÿ."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"à€žà€źà€œà€Čà„‡"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"à€…à€§à€żà€• à€šà€Ÿà€‚à€—à€Čà„à€Żà€Ÿ à€Šà„ƒà€¶à„à€Żà€Ÿà€žà€Ÿà€ à„€ à€«à€żà€°à€”à€Ÿ"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"à€…à€§à€żà€• à€šà€Ÿà€‚à€—à€Čà„à€Żà€Ÿ à€Šà„ƒà€¶à„à€Żà€Ÿà€žà€Ÿà€ à„€ à€žà„à€Șà„à€Čà€żà€Ÿ à€žà„à€•à„à€°à„€à€šà€źà€§à„‚à€š à€Źà€Ÿà€čà„‡à€° à€Șà€Ąà€Ÿ"</string>
     <string name="done_label" msgid="7283767013231718521">"à€Șà„‚à€°à„à€Ł à€à€Ÿà€Čà„‡"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"à€€à€Ÿà€ž à€Șà€°à€żà€Șà€€à„à€°à€• à€žà„à€Čà€Ÿà€Żà€Ąà€°"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"à€źà€żà€šà€żà€Ÿà„‡ à€Șà€°à€żà€Șà€€à„à€°à€• à€žà„à€Čà€Ÿà€Żà€Ąà€°"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 8ac7373..8f54762 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Boleh menangkap gerak isyarat yang dilakukan pada penderia cap jari peranti."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Ambil tangkapan skrin"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Boleh mengambil tangkapan skrin paparan."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"lumpuhkan atau ubah suai bar status"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Membenarkan apl melumpuhkan bar status atau menambah dan mengalih keluar ikon sistem."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"jadi bar status"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Melihat skrin penuh"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Untuk keluar, leret dari atas ke bawah."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Faham"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Putar untuk mendapatkan paparan yang lebih baik"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Keluar daripada skrin pisah untuk mendapatkan paparan yang lebih baik"</string>
     <string name="done_label" msgid="7283767013231718521">"Selesai"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Penggelangsar bulatan jam"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Penggelangsar bulatan minit"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 8603b28..939df80 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"စကá€șပစá€čစညá€șှ၏ လကá€șá€—á€œá€±á€Ąá€Źá€›á€Żá€¶á€á€¶á€€á€­á€›á€­á€šá€Źá€á€œá€„á€ș လုပá€șဆေဏငá€șထဏသသည့á€ș လကá€șဟနá€șမျဏသကို မဟတá€șသာှထာှနိုငá€șသညá€ș။"</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"ဖနá€șá€žá€Źá€žá€•á€Œá€„á€șဓာတá€șပုံ ရိုကá€șရနá€ș"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"ဖနá€șá€žá€Źá€žá€•á€Œá€„á€șá€•á€Œá€žá€™á€Ÿá€Żá€€á€­á€Ż ဓာတá€șပုံရိုကá€șနိုငá€șပါသညá€ș။"</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"á€Ąá€á€Œá€±á€Ąá€”á€±á€•á€Œá€˜á€Źá€žá€Ąá€Źá€ž အလုပá€șမလုပá€șခိုငá€șှရနá€șá€žá€­á€Żá€·á€™á€Ÿá€Żá€á€ș မလမá€șသမံရနá€ș"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"အကá€șပá€șအာှ á€Ąá€á€Œá€±á€Ąá€”á€±á€•á€Œ ဘာှကို ပိတá€șခလင့á€ș á€žá€­á€Żá€·á€™á€Ÿá€á€ș စနစá€ș အိုငá€șကလနá€șမျဏသကို ထည့á€șခဌငá€șှ ဖယá€șá€›á€Ÿá€Źá€žá€á€Œá€„á€șှ á€•á€Œá€Żá€œá€Żá€•á€șခလင့á€ș á€•á€Œá€Żá€žá€Šá€ș။"</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"á€Ąá€á€Œá€±á€Ąá€”á€±á€•á€Œ á€˜á€Źá€žá€–á€Œá€…á€șပါစေ"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"မျကá€șá€”á€Ÿá€Źá€•á€Œá€„á€șá€Ąá€•á€Œá€Šá€·á€ș ကဌည့á€șနေသညá€ș"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"ထလကá€șရနá€ș ဥပေါá€șမဟ ဥေဏကá€șသို့ ဆလá€Čချပါ။"</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"á€›á€•á€«á€•á€Œá€ź"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ပိုကေဏငá€șá€žá€žá€±á€Źá€™á€Œá€„á€șကလငá€șá€žá€Ąá€á€œá€€á€ș လဟည့á€șပါ"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"ပိုကေဏငá€șá€žá€žá€±á€Źá€™á€Œá€„á€șကလငá€șá€žá€Ąá€á€œá€€á€ș မျကá€șá€”á€Ÿá€Źá€•á€Œá€„á€ș ခလá€Č၍ပဌသခဌငá€șသမဟ ထလကá€șပါ"</string>
     <string name="done_label" msgid="7283767013231718521">"á€•á€Œá€źá€žá€•á€«á€•á€Œá€ź"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"á€”á€Źá€›á€źá€›á€œá€±á€žá€á€»á€€á€șစရာ"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"မိနစá€șလဟည့á€șသေဏ á€›á€œá€±á€·á€œá€»á€Źá€žá€á€”á€ș"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index fe9f915..f0f4439 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Kan fange inn bevegelser som utføres på enhetens fingeravtrykkssensor."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Ta skjermdump"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Kan ikke ta en skjermdump av skjermen."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"deaktivere eller endre statusfeltet"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Lar appen deaktivere statusfeltet eller legge til og fjerne systemikoner."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"vise appen i statusfeltet"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Visning i fullskjerm"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Sveip ned fra toppen for å avslutte."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Skjønner"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Roter for å få en bedre visning"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Avslutt delt skjerm for å få en bedre visning"</string>
     <string name="done_label" msgid="7283767013231718521">"Ferdig"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Sirkulær glidebryter for timer"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Sirkulær glidebryter for minutter"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index edeb116..94551e5 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"à€Żà€žà€Čà„‡ à€Żà€šà„à€€à„à€°à€•‍à„‹ à€«à€żà€‚à€—à€°à€Șà„à€°à€żà€šà„à€Ÿà€žà€źà„à€Źà€šà„à€§à„€ à€žà„‡à€šà„à€žà€°à€źà€Ÿ à€—à€°à€żà€à€•à€Ÿ à€‡à€žà€Ÿà€°à€Ÿà€čà€°à„‚à€Čà€Ÿà€ˆ à€–à€żà€šà„‍à€š à€žà€•à„à€›à„€"</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"à€žà„à€•à„à€°à€żà€šà€žà€Ÿ à€Čà€żà€šà„à€čà„‹à€žà„"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"à€Ąà€żà€žà„à€Șà„à€Čà„‡à€•à„‹ à€žà„à€•à„à€°à€żà€šà€žà€Ÿ à€Čà€żà€š à€žà€•à€żà€šà„à€›à„€"</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"à€žà„à€„à€żà€€à€ż à€Șà€Ÿà„à€Ÿà€żà€Čà€Ÿà€ˆ à€…à€•à„à€·à€ź à€”à€Ÿ à€žà€‚à€¶à„‹à€§à€żà€€ à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"à€žà„à€„à€żà€€à€ż à€Șà€Ÿà„à€Ÿà€ż à€…à€žà€•à„à€·à€ź à€Șà€Ÿà€°à„à€š à€”à€Ÿ à€Șà„à€°à€Łà€Ÿà€Čà„€ à€†à€‡à€•à€šà€čà€°à„‚ à€„à€Șà„à€š à€° à€čà€Ÿà€Ÿà€‰à€š à€à€Șà€Čà€Ÿà€ˆ à€…à€šà„à€źà€€à€ż à€Šà€żà€šà„à€›à„€"</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"à€žà„à€Ÿà€Ÿà€Ÿà€ž à€Źà€Ÿà€° à€čà„à€š à€Šà€żà€šà„à€čà„‹à€žà„"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"à€Șà„‚à€°à€Ÿ à€Șà€°à„à€Šà€Ÿ à€čà„‡à€°à„à€Šà„ˆ"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"à€Źà€Ÿà€čà€żà€° à€šà€żà€žà„à€•à€š, à€źà€Ÿà€„à€żà€Źà€Ÿà€Ÿ à€€à€Č à€žà„à€”à€Ÿà€‡à€Ș à€—à€°à„à€šà„à€čà„‹à€žà„à„€"</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"à€Źà„à€à„‡à€"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"à€…à€ à€°à€Ÿà€źà„à€°à„‹ à€Šà„ƒà€¶à„à€Ż à€čà„‡à€°à„à€š à€šà€Ÿà€čà€šà„à€čà„à€šà„à€› à€­à€šà„‡ à€°à„‹à€Ÿà„‡à€Ÿ à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"à€…à€ à€°à€Ÿà€źà„à€°à„‹ à€Šà„ƒà€¶à„à€Ż à€čà„‡à€°à„à€š à€šà€Ÿà€čà€šà„à€čà„à€šà„à€› à€­à€šà„‡ \"à€žà„à€Șà„à€Čà€żà€Ÿ à€žà„à€•à„à€°à€żà€š\" à€Źà€Ÿà€Ÿ à€Źà€Ÿà€čà€żà€°à€żà€šà„à€čà„‹à€žà„"</string>
     <string name="done_label" msgid="7283767013231718521">"à€­à€Żà„‹"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"à€˜à€šà„à€Ÿà€Ÿ à€—à„‹à€Čà€Ÿà€•à€Ÿà€° à€žà„à€Čà€Ÿà€‡à€Ąà€°"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"à€źà€żà€šà„‡à€Ÿ à€—à„‹à€Čà€Ÿà€•à€Ÿà€° à€žà„à€Čà€Ÿà€‡à€Ąà€°"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 6028b91..0f7d35f 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Kan gebaren registreren die op de vingerafdruksensor van het apparaat worden getekend."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Screenshot maken"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Kan een screenshot van het scherm maken."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"statusbalk uitzetten of wijzigen"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Hiermee kan de app de statusbalk uitzetten of systeemiconen toevoegen en verwijderen."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"de statusbalk zijn"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Volledig scherm wordt getoond"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Swipe omlaag vanaf de bovenkant van het scherm om af te sluiten."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Ik snap het"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Draai voor een betere weergave"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Sluit het gesplitste scherm voor een betere weergave"</string>
     <string name="done_label" msgid="7283767013231718521">"Klaar"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Ronde schuifregelaar voor uren"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Ronde schuifregelaar voor minuten"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 83cb868..26821da 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"àŹĄàŹżàŹ­àŹŸàŹ‡àŹžà­‌àŹ° àŹŸàŹżàŹȘàŹšàŹżàŹčà­àŹš àŹžà­‡àŹšàŹžàŹ°à­ àŹ‰àŹȘàŹ°à­‡ àŹœà­‡àŹ¶à­àŹšàŹ°à­‍ àŹ•à­à­ŸàŹŸàŹȘàŹšàŹ°à­‍ àŹ•àŹŸàŹ°à­àŹŻà­à­Ÿ àŹ•àŹ°àŹŸàŹŻàŹŸàŹ‡àŹȘàŹŸàŹ°àŹżàŹŹà„€"</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"àŹžà­àŹ•à­àŹ°àŹżàŹšàŹžàŹŸà­ àŹšàŹżàŹ…àŹšà­àŹ€à­"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"àŹĄàŹżàŹžàŹȘ୍àŹČà­‡àŹ° àŹàŹ• àŹžà­àŹ•à­àŹ°àŹżàŹšàŹžàŹŸà­ àŹšàŹżàŹ†àŹŻàŹŸàŹ‡àŹȘàŹŸàŹ°à­‡à„€"</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"àŹ·à­àŹŸàŹŸàŹŸàŹžà­‌ àŹŹàŹŸàŹ°à­‌àŹ•à­ àŹ…àŹ•à­àŹ·àŹź àŹ•àŹżàŹźà­à­±àŹŸ àŹžàŹ‚àŹ¶à­‹àŹ§àŹš àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"àŹ†àŹȘ୍‍àŹ•à­, àŹžà­àŹ„àŹżàŹ€àŹż àŹŹàŹŸàŹ°à­‍ àŹ…àŹ•à­àŹ·àŹź àŹ•àŹ°àŹżàŹŹàŹŸàŹ•à­ àŹ•àŹżàŹźà­àŹŹàŹŸ àŹžàŹżàŹ·à­àŹŸàŹźà­‍ àŹ†àŹ‡àŹ•àŹšà­‍ àŹŻà­‹àŹĄàŹŒàŹżàŹŹàŹŸ àŹ•àŹżàŹźà­àŹŹàŹŸ àŹŹàŹŸàŹčàŹŸàŹ° àŹ•àŹ°àŹżàŹŹàŹŸàŹ•à­ àŹŠà­‡àŹ‡àŹ„àŹŸàŹà„€"</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"àŹ·à­àŹŸàŹŸàŹŸàŹžà­‍ àŹŹàŹŸàŹ°à­‍ àŹ°àŹčàŹżàŹŹàŹŸàŹ•à­ àŹŠàŹżàŹ…àŹšà­àŹ€à­"</string>
@@ -1077,13 +1079,13 @@
     <string name="menu_space_shortcut_label" msgid="5949311515646872071">"àŹžà­àŹȘà­‡àŹžà­‍"</string>
     <string name="menu_enter_shortcut_label" msgid="6709499510082897320">"àŹàŹŁà­àŹŸàŹ°à­"</string>
     <string name="menu_delete_shortcut_label" msgid="4365787714477739080">"àŹĄàŹżàŹČàŹżàŹŸà­‌ àŹ•àŹ°àŹšà­àŹ€à­"</string>
-    <string name="search_go" msgid="2141477624421347086">"àŹžàŹšà­àŹ§àŹŸàŹš àŹ•àŹ°àŹšà­àŹ€à­"</string>
-    <string name="search_hint" msgid="455364685740251925">"àŹžàŹšà­àŹ§àŹŸàŹš…"</string>
-    <string name="searchview_description_search" msgid="1045552007537359343">"àŹžàŹšà­àŹ§àŹŸàŹš àŹ•àŹ°àŹšà­àŹ€à­"</string>
+    <string name="search_go" msgid="2141477624421347086">"àŹžàŹ°à­àŹšà­àŹš àŹ•àŹ°àŹšà­àŹ€à­"</string>
+    <string name="search_hint" msgid="455364685740251925">"àŹžàŹ°à­àŹšà­àŹš àŹ•àŹ°àŹšà­àŹ€à­…"</string>
+    <string name="searchview_description_search" msgid="1045552007537359343">"àŹžàŹ°à­àŹšà­àŹš àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="searchview_description_query" msgid="7430242366971716338">"àŹ•à­à­±à­‡àŹ°à­€ àŹžàŹ°à­àŹšà­àŹš àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="searchview_description_clear" msgid="1989371719192982900">"àŹ•à­à­±à­‡àŹ°à­€ àŹ–àŹŸàŹČàŹż àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="searchview_description_submit" msgid="6771060386117334686">"àŹ•à­à­±à­‡àŹ°à­€ àŹŠàŹŸàŹ–àŹČ àŹ•àŹ°àŹšà­àŹ€à­"</string>
-    <string name="searchview_description_voice" msgid="42360159504884679">"àŹ­àŹàŹžà­‍ àŹžàŹ°à­àŹšà­àŹš"</string>
+    <string name="searchview_description_voice" msgid="42360159504884679">"àŹ­àŹàŹž àŹžàŹ°à­àŹšà­àŹš"</string>
     <string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"’àŹžà­àŹȘàŹ°à­àŹ¶ àŹ•àŹ°àŹż àŹàŹ•à­àŹžàŹȘ୍àŹČà­‹àŹ°à­‍ àŹ•àŹ°àŹšà­àŹ€à­’ àŹžàŹ•à­àŹ·àŹź àŹ•àŹ°àŹżàŹŹà­‡?"</string>
     <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> ’àŹžà­àŹȘàŹ°à­àŹ¶ àŹ•àŹ°àŹż àŹàŹ•à­àŹžàŹȘ୍àŹČà­‹àŹ°à­ àŹ•àŹ°àŹšà­àŹ€à­’ àŹžàŹ•à­àŹ·àŹź àŹ•àŹ°àŹżàŹŹàŹŸàŹ•à­ àŹšàŹŸàŹčà­‡àŹà„€ ’àŹžà­àŹȘàŹ°à­àŹ¶ àŹ•àŹ°àŹż àŹàŹ•à­àŹžàŹȘ୍àŹČà­‹àŹ°à­ àŹ•àŹ°àŹšà­àŹ€à­’ àŹ…àŹšà­‌ àŹ„àŹżàŹŹàŹŸàŹŹà­‡àŹłà­‡, àŹ†àŹȘàŹŁàŹ™à­àŹ• àŹ†àŹ™à­àŹ—à­àŹ àŹż àŹ€àŹłà­‡ àŹ•’àŹŁ àŹ…àŹ›àŹż, àŹ€àŹŸàŹčàŹŸàŹ° àŹŹà­à­ŸàŹŸàŹ–à­à­ŸàŹŸ àŹŠà­‡àŹ–àŹżàŹȘàŹŸàŹ°àŹżàŹŹà­‡ àŹ•àŹżàŹźà­àŹŹàŹŸ àŹŸàŹŸàŹŹà­‍àŹČà­‡àŹŸà­‍ àŹžàŹč àŹ•àŹ„àŹŸàŹŹàŹŸàŹ°à­àŹ€à­àŹ€àŹŸ àŹ•àŹ°àŹżàŹŹàŹŸàŹ•à­ àŹœà­‡àŹ¶à­àŹšàŹ°à­‌ àŹ•àŹ°àŹżàŹȘàŹŸàŹ°àŹżàŹŹà­‡à„€"</string>
     <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> ’àŹžà­àŹȘàŹ°à­àŹ¶ àŹ•àŹ°àŹż àŹàŹ•à­àŹžàŹȘ୍àŹČà­‹àŹ°à­ àŹ•àŹ°àŹšà­àŹ€à­’ àŹžàŹ•à­àŹ·àŹź àŹ•àŹ°àŹżàŹŹàŹŸàŹ•à­ àŹšàŹŸàŹčà­‡àŹà„€ ’àŹžà­àŹȘàŹ°à­àŹ¶ àŹ•àŹ°àŹż àŹàŹ•à­àŹžàŹȘ୍àŹČà­‹àŹ°à­ àŹ•àŹ°àŹšà­àŹ€à­’ àŹ…àŹšà­‌ àŹ„àŹżàŹŹàŹŸàŹŹà­‡àŹłà­‡, àŹ†àŹȘàŹŁàŹ™à­àŹ• àŹ†àŹ™à­àŹ—à­àŹ àŹż àŹ€àŹłà­‡ àŹ•’àŹŁ àŹ…àŹ›àŹż, àŹ€àŹŸàŹčàŹŸàŹ° àŹŹà­à­ŸàŹŸàŹ–à­à­ŸàŹŸ àŹŠà­‡àŹ–àŹżàŹȘàŹŸàŹ°àŹżàŹŹà­‡ àŹ•àŹżàŹźà­àŹŹàŹŸ àŹ«à­‹àŹšà­‍ àŹžàŹč àŹ•àŹ„àŹŸàŹŹàŹŸàŹ°à­àŹ€à­àŹ€àŹŸ àŹ•àŹ°àŹżàŹŹàŹŸàŹ•à­ àŹœà­‡àŹ¶à­àŹšàŹ°à­‌ àŹ•àŹ°àŹżàŹȘàŹŸàŹ°àŹżàŹŹà­‡à„€"</string>
@@ -1463,7 +1465,7 @@
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"àŹœà­àŹźà­ àŹšàŹżà­ŸàŹšà­àŹ€à­àŹ°àŹŁ àŹȘàŹŸàŹ‡àŹ àŹŠà­àŹ‡àŹ„àŹ° àŹŸàŹŸàŹȘ୍‌ àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"à­±àŹżàŹœà­‡àŹŸà­‍ àŹŻà­‹àŹĄàŹŒàŹżàŹȘàŹŸàŹ°àŹżàŹŹ àŹšàŹŸàŹčàŹżàŹà„€"</string>
     <string name="ime_action_go" msgid="5536744546326495436">"àŹŻàŹŸàŹ†àŹšà­àŹ€à­"</string>
-    <string name="ime_action_search" msgid="4501435960587287668">"àŹžàŹšà­àŹ§àŹŸàŹš àŹ•àŹ°àŹšà­àŹ€à­"</string>
+    <string name="ime_action_search" msgid="4501435960587287668">"àŹžàŹ°à­àŹšà­àŹš àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="ime_action_send" msgid="8456843745664334138">"àŹȘàŹ àŹŸàŹšà­àŹ€à­"</string>
     <string name="ime_action_next" msgid="4169702997635728543">"àŹȘàŹ°àŹŹàŹ°à­àŹ€à­àŹ€à­€"</string>
     <string name="ime_action_done" msgid="6299921014822891569">"àŹčà­‹àŹ‡àŹ—àŹČàŹŸ"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"àŹȘà­‚àŹ°à­àŹŁà­àŹŁ àŹžà­àŹ•à­àŹ°à­€àŹšàŹ°à­‡ àŹŠà­‡àŹ–àŹŸàŹŻàŹŸàŹ‰àŹ›àŹż"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"àŹŹàŹŸàŹčàŹŸàŹ°àŹżàŹŹàŹŸ àŹȘàŹŸàŹ‡àŹ, àŹ‰àŹȘàŹ°à­ àŹ€àŹłàŹ•à­ àŹžà­à­±àŹŸàŹ‡àŹȘ୍‍ àŹ•àŹ°àŹšà­àŹ€à­à„€"</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"àŹŹà­àŹàŹżàŹ—àŹČàŹż"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"àŹàŹ• àŹ­àŹČ àŹ­à­à­Ÿà­ àŹȘàŹŸàŹ‡àŹ àŹ°à­‹àŹŸà­‡àŹŸ àŹ•àŹ°àŹšà­àŹ€à­"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"àŹàŹ• àŹ­àŹČ àŹ­à­à­Ÿà­ àŹȘàŹŸàŹ‡àŹ àŹžà­àŹȘ୍àŹČàŹżàŹŸ àŹžà­àŹ•à­àŹ°àŹżàŹšàŹ°à­ àŹŹàŹŸàŹčàŹŸàŹ°àŹż àŹŻàŹŸàŹ†àŹšà­àŹ€à­"</string>
     <string name="done_label" msgid="7283767013231718521">"àŹčà­‹àŹ‡àŹ—àŹČàŹŸ"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"àŹ˜àŹŁà­àŹŸàŹŸ àŹžàŹ°à­àŹ•à­àŹČàŹŸàŹ°à­‍ àŹžà­àŹČàŹŸàŹ‡àŹĄàŹ°à­‍"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"àŹźàŹżàŹšàŹżàŹŸà­àŹž àŹžàŹ°à­àŹ•à­àŹČàŹŸàŹ°à­‍ àŹžà­àŹČàŹŸàŹ‡àŹĄàŹ°à­‍"</string>
@@ -1931,7 +1935,7 @@
     <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"àŹȘà­àŹ°àŹžà­àŹ€àŹŸàŹŹàŹżàŹ€"</string>
     <string name="language_picker_section_all" msgid="1985809075777564284">"àŹžàŹźàŹžà­àŹ€ àŹ­àŹŸàŹ·àŹŸ"</string>
     <string name="region_picker_section_all" msgid="756441309928774155">"àŹžàŹźàŹžà­àŹ€ àŹ…àŹžà­àŹšàŹł"</string>
-    <string name="locale_search_menu" msgid="6258090710176422934">"àŹžàŹšà­àŹ§àŹŸàŹš àŹ•àŹ°àŹšà­àŹ€à­"</string>
+    <string name="locale_search_menu" msgid="6258090710176422934">"àŹžàŹ°à­àŹšà­àŹš àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="app_suspended_title" msgid="888873445010322650">"àŹ†àŹȘ୍‌ àŹ‰àŹȘàŹČàŹŹà­àŹ§ àŹšàŹŸàŹčàŹżàŹ"</string>
     <string name="app_suspended_default_message" msgid="6451215678552004172">"àŹŹàŹ°à­àŹ€à­àŹ€àŹźàŹŸàŹš <xliff:g id="APP_NAME_0">%1$s</xliff:g> àŹ‰àŹȘàŹČàŹŹà­àŹ§ àŹšàŹŸàŹčàŹżàŹà„€ àŹàŹčàŹŸ <xliff:g id="APP_NAME_1">%2$s</xliff:g> àŹŠà­àŹ”àŹŸàŹ°àŹŸ àŹȘàŹ°àŹżàŹšàŹŸàŹłàŹżàŹ€ àŹčà­‡àŹ‰àŹ›àŹżà„€"</string>
     <string name="app_suspended_more_details" msgid="211260942831587014">"àŹ…àŹ§àŹżàŹ• àŹœàŹŸàŹŁàŹšà­àŹ€à­"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index d78c6fc..c8c382b 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"àšĄà©€àš”àšŸàšˆàšžàšŸàš‚ àšŠà©‡ àš«àšżà©°àš—àš°àšȘà©àš°àšżà©°àšŸ àšžà©ˆàš‚àšžàš° \'àš€à©‡ àš•à©€àš€à©‡ àš—àš àšžà©°àš•à©‡àš€àšŸàš‚ àššà©‚à©° àš•à©ˆàšȘàššàš° àš•àš° àšžàš•àšŠà©‡ àščàššà„€"</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"àšžàš•à©àš°à©€àššàšžàšŒàšŸàšŸ àšČàš“"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"àšĄàšżàšžàšȘàšČੇ àšŠàšŸ àšžàš•à©àš°à©€àššàšžàšŒàšŸàšŸ àšČੈ àšžàš•àšŠà©€ àščà©ˆà„€"</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"àšžàš„àšżàš€à©€ àšȘà©±àšŸà©€ àšŹà©°àšŠ àš•àš°à©‹ àšœàšŸàš‚ àšžà©°àšžàšŒà©‹àš§àšżàš€ àš•àš°à©‹"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"àšàšȘ àššà©‚à©° àšžàš„àšżàš€à©€ àšȘà©±àšŸà©€ àššà©‚à©° àššàšŸàšČੂ àš•àš°àšš àšœàšŸàš‚ àšžàšżàšžàšŸàšź àšȘà©àš°àš€à©€àš•àšŸàš‚ àššà©‚à©° àšœà©‹à©œàšš àš…àš€à©‡ àščàšŸàšŸàš‰àšŁ àšŠà©€ àš†àš—àšżàš† àšŠàšżà©°àšŠàšŸ àščà©ˆà„€"</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"àšžàš„àšżàš€à©€ àšȘà©±àšŸà©€ àšŹàšŁàšš àšŠàšżàš“"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"àšȘà©‚àš°à©€ àšžàš•à©àš°à©€àšš \'àš€à©‡ àšŠà©‡àš–à©‹"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"àšŹàšŸàščàš° àšœàšŸàšŁ àšČàšˆ, àš‰àšȘàš°à©‹àš‚ àščà©‡àš àšŸàš‚ àšžàš”àšŸàšˆàšȘ àš•àš°à©‹à„€"</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"àšžàšźàš àšČàšżàš†"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"àšŹàšżàščàš€àš° àšŠà©àš°àšżàšžàšŒ àš…àššà©àš­àš” àšČàšˆ àš˜à©àšźàšŸàš“"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"àšŹàšżàščàš€àš° àšŠà©àš°àšżàšžàšŒ àš…àššà©àš­àš” àšČàšˆ àšžàšȘàšČàšżàšŸ àšžàš•à©àš°à©€àšš àš€à©‹àš‚ àšŹàšŸàščàš° àš†àš“"</string>
     <string name="done_label" msgid="7283767013231718521">"àščੋ àš—àšżàš†"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"àš˜à©°àšŸà©‡ àšžàš°àš•à©àšČàš° àšžàšČàšŸàšˆàšĄàš°"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"àšźàšżà©°àšŸ àšžàš°àš•à©àšČàš° àšžàšČàšŸàšˆàšĄàš°"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index a2c06b7..8425a7a 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -344,6 +344,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"MoĆŒe przechwytywać gesty wykonywane na czytniku linii papilarnych w urządzeniu."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Robienie zrzutu ekranu"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"MoĆŒe robić zrzuty ekranu wyƛwietlacza."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"wyƂączanie lub zmienianie paska stanu"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Pozwala aplikacji na wyƂączanie paska stanu oraz dodawanie i usuwanie ikon systemowych."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"dziaƂanie jako pasek stanu"</string>
@@ -1842,6 +1844,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"WƂączony peƂny ekran"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Aby wyjƛć, przesuƄ palcem z góry na dóƂ."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Obróć, aby lepiej widzieć"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Zamknij podzielony ekran, aby lepiej widzieć"</string>
     <string name="done_label" msgid="7283767013231718521">"Gotowe"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"KoƂowy suwak godzin"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"KoƂowy suwak minut"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 9768208..4fe36de 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -53,7 +53,7 @@
     <string name="enablePin" msgid="2543771964137091212">"Falha. Ative o bloqueio do chip/R-UIM."</string>
     <plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
       <item quantity="one">Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>. Caso o código correto não seja digitado, o chip será bloqueado.</item>
-      <item quantity="many">You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts before SIM is locked.</item>
+      <item quantity="many">Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>. Caso o código correto não seja digitado, o chip será bloqueado.</item>
       <item quantity="other">Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>. Caso o código correto não seja digitado, o chip será bloqueado.</item>
     </plurals>
     <string name="imei" msgid="2157082351232630390">"IMEI"</string>
@@ -343,6 +343,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Pode captar gestos realizados no sensor de impressão digital do dispositivo."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Fazer uma captura de tela"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Pode fazer uma captura de tela."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"desativar ou modificar a barra de status"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Permite que o app desative a barra de status ou adicione e remova ícones do sistema."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"ser a barra de status"</string>
@@ -1841,6 +1843,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Visualização em tela cheia"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Para sair, deslize de cima para baixo."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Entendi"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Gire a tela para ter uma visualização melhor"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Saia da tela dividida para ter uma visualização melhor"</string>
     <string name="done_label" msgid="7283767013231718521">"Concluído"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Controle deslizante circular das horas"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Controle deslizante circular dos minutos"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 6d7f391..b6fbbea 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -52,7 +52,7 @@
     <string name="needPuk2" msgid="7032612093451537186">"Introduza o PUK2 para desbloquear o cartão SIM."</string>
     <string name="enablePin" msgid="2543771964137091212">"Ação sem êxito. Ative o bloqueio do SIM/RUIM."</string>
     <plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
-      <item quantity="many">You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts before SIM is locked.</item>
+      <item quantity="many">Tem mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas antes de o cartão SIM ficar bloqueado.</item>
       <item quantity="other">Tem mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas antes de o cartão SIM ficar bloqueado.</item>
       <item quantity="one">Tem mais <xliff:g id="NUMBER_0">%d</xliff:g> tentativa antes de o cartão SIM ficar bloqueado.</item>
     </plurals>
@@ -343,6 +343,7 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Pode capturar gestos realizados no sensor de impressões digitais do dispositivo."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Fazer captura de ecrã"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"É possível tirar uma captura de ecrã."</string>
+    <string name="dream_preview_title" msgid="5570751491996100804">"Pré-visualização, <xliff:g id="DREAM_NAME">%1$s</xliff:g>"</string>
     <string name="permlab_statusBar" msgid="8798267849526214017">"desativar ou modificar barra de estado"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Permite à app desativar a barra de estado ou adicionar e remover ícones do sistema."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"ser apresentada na barra de estado"</string>
@@ -1235,7 +1236,7 @@
     <string name="unsupported_display_size_show" msgid="980129850974919375">"Mostrar sempre"</string>
     <string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> foi concebida para uma versão incompatível do SO Android e pode ter um comportamento inesperado. Pode estar disponível uma versão atualizada da app."</string>
     <string name="unsupported_compile_sdk_show" msgid="1601210057960312248">"Mostrar sempre"</string>
-    <string name="unsupported_compile_sdk_check_update" msgid="1103639989147664456">"Verificar atualizações"</string>
+    <string name="unsupported_compile_sdk_check_update" msgid="1103639989147664456">"Rever atualizações"</string>
     <string name="smv_application" msgid="3775183542777792638">"A app <xliff:g id="APPLICATION">%1$s</xliff:g> (processo <xliff:g id="PROCESS">%2$s</xliff:g>) violou a política StrictMode auto-imposta."</string>
     <string name="smv_process" msgid="1398801497130695446">"O processo <xliff:g id="PROCESS">%1$s</xliff:g> violou a política StrictMode auto-imposta."</string>
     <string name="android_upgrading_title" product="default" msgid="7279077384220829683">"O telemóvel está a atualizar…"</string>
@@ -1841,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Visualização de ecrã inteiro"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Para sair, deslize rapidamente para baixo a partir da parte superior."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rode para uma melhor visualização"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Saia do ecrã dividido para uma melhor visualização"</string>
     <string name="done_label" msgid="7283767013231718521">"Concluído"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Controlo de deslize circular das horas"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Controlo de deslize circular dos minutos"</string>
@@ -1961,7 +1964,7 @@
     <string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Não é possível aceder a esta app no seu dispositivo <xliff:g id="DEVICE">%1$s</xliff:g>. Em alternativa, experimente no tablet."</string>
     <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Não é possível aceder a esta app no seu dispositivo <xliff:g id="DEVICE">%1$s</xliff:g>. Em alternativa, experimente no telemóvel."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Esta app foi concebida para uma versão mais antiga do Android e pode não funcionar corretamente. Experimente verificar se existem atualizações ou contacte o programador."</string>
-    <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Verificar atualizações"</string>
+    <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Rever atualizações"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Tem mensagens novas"</string>
     <string name="new_sms_notification_content" msgid="3197949934153460639">"Abra a app de SMS para ver"</string>
     <string name="profile_encrypted_title" msgid="9001208667521266472">"Algumas funcionalidades limitadas"</string>
@@ -2291,7 +2294,7 @@
     <string name="notification_title_long_running_fgs" msgid="8170284286477131587">"Uma app ainda está ativa"</string>
     <string name="notification_content_abusive_bg_apps" msgid="5296898075922695259">"A app <xliff:g id="APP">%1$s</xliff:g> está a ser executada em segundo plano Toque para gerir a utilização da bateria."</string>
     <string name="notification_content_long_running_fgs" msgid="8258193410039977101">"A app <xliff:g id="APP">%1$s</xliff:g> pode afetar a autonomia da bateria. Toque para rever as apps ativas."</string>
-    <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Verificar apps ativas"</string>
+    <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Rever apps ativas"</string>
     <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Não é possível aceder à câmara do telemóvel a partir do dispositivo <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
     <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Não é possível aceder à câmara do tablet a partir do dispositivo <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
     <string name="vdm_secure_window" msgid="161700398158812314">"Não é possível aceder a isto durante o streaming. Em alternativa, experimente no telemóvel."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 9768208..4fe36de 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -53,7 +53,7 @@
     <string name="enablePin" msgid="2543771964137091212">"Falha. Ative o bloqueio do chip/R-UIM."</string>
     <plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
       <item quantity="one">Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>. Caso o código correto não seja digitado, o chip será bloqueado.</item>
-      <item quantity="many">You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts before SIM is locked.</item>
+      <item quantity="many">Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>. Caso o código correto não seja digitado, o chip será bloqueado.</item>
       <item quantity="other">Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>. Caso o código correto não seja digitado, o chip será bloqueado.</item>
     </plurals>
     <string name="imei" msgid="2157082351232630390">"IMEI"</string>
@@ -343,6 +343,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Pode captar gestos realizados no sensor de impressão digital do dispositivo."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Fazer uma captura de tela"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Pode fazer uma captura de tela."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"desativar ou modificar a barra de status"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Permite que o app desative a barra de status ou adicione e remova ícones do sistema."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"ser a barra de status"</string>
@@ -1841,6 +1843,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Visualização em tela cheia"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Para sair, deslize de cima para baixo."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Entendi"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Gire a tela para ter uma visualização melhor"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Saia da tela dividida para ter uma visualização melhor"</string>
     <string name="done_label" msgid="7283767013231718521">"Concluído"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Controle deslizante circular das horas"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Controle deslizante circular dos minutos"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 83fba3f..a3be673 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -343,6 +343,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Poate reda gesturile făcute pe senzorul de amprentă al dispozitivului."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Fă o captură de ecran"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Poate face o captură de ecran."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"dezactivare sau modificare bare de stare"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Permite aplicației să dezactiveze bara de stare sau să adauge și să elimine pictograme de sistem."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"să fie bara de stare"</string>
@@ -1841,6 +1843,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Vizualizare pe ecran complet"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Pentru a ieși, glisează de sus în jos."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Am înțeles"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rotește pentru o previzualizare mai bună"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Ieși din ecranul împărțit pentru o previzualizare mai bună"</string>
     <string name="done_label" msgid="7283767013231718521">"Terminat"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Selector circular pentru ore"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Selector circular pentru minute"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 713e67d..d5067ae 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -344,6 +344,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Đ˜ŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČать сĐșĐ°ĐœĐ”Ń€ ĐŸŃ‚ĐżĐ”Ń‡Đ°Ń‚ĐșĐŸĐČ ĐżĐ°Đ»ŃŒŃ†Đ”ĐČ ĐŽĐ»Ń ĐŽĐŸĐżĐŸĐ»ĐœĐžŃ‚Đ”Đ»ŃŒĐœŃ‹Ń… Đ¶Đ”ŃŃ‚ĐŸĐČ."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"ĐĄĐŸĐ·ĐŽĐ°ĐČать сĐșŃ€ĐžĐœŃˆĐŸŃ‚Ń‹"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"ĐĄĐŸĐ·ĐŽĐ°ĐČать ŃĐœĐžĐŒĐșĐž эĐșŃ€Đ°ĐœĐ°."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"ОтĐșĐ»ŃŽŃ‡Đ”ĐœĐžĐ”/ĐžĐ·ĐŒĐ”ĐœĐ”ĐœĐžĐ” ŃŃ‚Ń€ĐŸĐșĐž ŃĐŸŃŃ‚ĐŸŃĐœĐžŃ"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"ĐŸŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” ŃĐŒĐŸĐ¶Đ”Ń‚ ĐŸŃ‚ĐșĐ»ŃŽŃ‡Đ°Ń‚ŃŒ ŃŃ‚Ń€ĐŸĐșу ŃĐŸŃŃ‚ĐŸŃĐœĐžŃ, а таĐșжД ĐŽĐŸĐ±Đ°ĐČĐ»ŃŃ‚ŃŒ Đž ŃƒĐŽĐ°Đ»ŃŃ‚ŃŒ ŃĐžŃŃ‚Đ”ĐŒĐœŃ‹Đ” Đ·ĐœĐ°Ń‡ĐșĐž."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"Đ—Đ°ĐŒĐ”ĐœĐ° ŃŃ‚Ń€ĐŸĐșĐž ŃĐŸŃŃ‚ĐŸŃĐœĐžŃ"</string>
@@ -598,7 +600,7 @@
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"ХлОшĐșĐŸĐŒ сĐČĐ”Ń‚Đ»ĐŸ."</string>
     <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Вы ĐœĐ°Đ¶Đ°Đ»Đž ĐșĐœĐŸĐżĐșу ĐżĐžŃ‚Đ°ĐœĐžŃ."</string>
     <string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"ĐŸĐŸĐżŃ€ĐŸĐ±ŃƒĐčтД ĐžĐ·ĐŒĐ”ĐœĐžŃ‚ŃŒ ĐżĐŸĐ»ĐŸĐ¶Đ”ĐœĐžĐ” ĐżĐ°Đ»ŃŒŃ†Đ°."</string>
-    <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"КажЎыĐč раз ĐœĐ”ĐŒĐœĐŸĐłĐŸ ĐŒĐ”ĐœŃĐčтД ĐżĐŸĐ»ĐŸĐ¶Đ”ĐœĐžĐ” ĐżĐ°Đ»ŃŒŃ†Đ°."</string>
+    <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"КажЎыĐč раз ĐœĐ”ĐŒĐœĐŸĐłĐŸ ĐŒĐ”ĐœŃĐčтД ĐżĐŸĐ»ĐŸĐ¶Đ”ĐœĐžĐ” ĐżĐ°Đ»ŃŒŃ†Đ°"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
     <string name="fingerprint_error_not_match" msgid="4599441812893438961">"ĐžŃ‚ĐżĐ”Ń‡Đ°Ń‚ĐŸĐș ĐœĐ” Ń€Đ°ŃĐżĐŸĐ·ĐœĐ°Đœ."</string>
@@ -1842,6 +1844,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"ĐŸĐŸĐ»ĐœĐŸŃĐșŃ€Đ°ĐœĐœŃ‹Đč Ń€Đ”Đ¶ĐžĐŒ"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Đ§Ń‚ĐŸĐ±Ń‹ ĐČыĐčто, ĐżŃ€ĐŸĐČДЎОтД ĐżĐŸ эĐșŃ€Đ°ĐœŃƒ сĐČĐ”Ń€Ń…Ńƒ ĐČĐœĐžĐ·."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"ОК"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ĐŸĐŸĐČĐ”Ń€ĐœĐžŃ‚Đ”, Ń‡Ń‚ĐŸĐ±Ń‹ Đ»ŃƒŃ‡ŃˆĐ” ĐČĐžĐŽĐ”Ń‚ŃŒ."</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"ВыĐčЎОтД Оз Ń€Đ”Đ¶ĐžĐŒĐ° Ń€Đ°Đ·ĐŽĐ”Đ»Đ”ĐœĐžŃ эĐșŃ€Đ°ĐœĐ°, Ń‡Ń‚ĐŸĐ±Ń‹ Đ»ŃƒŃ‡ŃˆĐ” ĐČĐžĐŽĐ”Ń‚ŃŒ."</string>
     <string name="done_label" msgid="7283767013231718521">"Đ“ĐŸŃ‚ĐŸĐČĐŸ"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Đ’Ń‹Đ±ĐŸŃ€ Ń‡Đ°ŃĐŸĐČ ĐœĐ° цОфДрблатД"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Đ’Ń‹Đ±ĐŸŃ€ ĐŒĐžĐœŃƒŃ‚ ĐœĐ° цОфДрблатД"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 8605c67..66b0cd6 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"උඎාංගà¶șෙහි ඇඟිගි සගකුණු à·ƒà¶‚à·€à·šà¶Żà¶šà¶ș à¶žà¶­ à·ƒà·’à¶Żà·” කරන ඉංගිත ග්‍රහණà¶ș කළ හැකිà¶ș."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"තිර රුව ගන්න"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"à·ƒà¶‚à¶Żà¶»à·Šà·à¶šà¶șේ තිර රුවක් ගැනීඞට හැකිà¶ș."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"තත්ව තීරුව à¶…à¶¶à¶œ කරන්න හෝ වෙනස් කරන්න"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"තත්ව තීරුව අක්‍රිà¶ș කිරීඞට හෝ à¶Žà¶Żà·Šà¶°à¶­à·’ නිරූඎක එකතු හෝ ඉවත් කිරීඞට à¶șà·™à¶Żà·”à¶žà¶§ අවසර à¶Żà·š."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"තත්ත්ව තීරුව බවට ඎත්වීඞ"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"ඞුළු තිරà¶ș බගඞින්"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"ඉවත් වීඞට, ඉහළ සිට à¶Žà·„à·…à¶§ ස්වà¶șà·’à¶Žà·Š කරන්න"</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"වැටහුණි"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"වඩා à·„à·œà¶ł à¶Żà·ƒà·”à¶±à¶šà·Š à·ƒà¶łà·„à· කරකවන්න"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"වඩා à·„à·œà¶ł à¶Żà¶»à·Šà·à¶±à¶șක් à·ƒà¶łà·„à· à¶¶à·™à¶Żà·”à¶žà·Š තිරà¶șෙන් ඎිටවන්න"</string>
     <string name="done_label" msgid="7283767013231718521">"අවසන්"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"ඎැà¶ș කවාකාර සර්ඎනà¶ș"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"ඞිනිත්තු කවාකාර සර්ඎනà¶ș"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 63ff006..816feef 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -344,6 +344,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"DokáĆŸe zaznamenaĆ„ gestá na senzore odtlačkov prstov zariadenia."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"VytvoriĆ„ snímku obrazovky"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Je moĆŸné vytvoriĆ„ snímku obrazovky."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"zakázanie alebo zmeny stavového riadka"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"UmoĆŸĆˆuje aplikácii vypnúĆ„ stavový riadok alebo pridaĆ„ a odstrániĆ„ systémové ikony."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"vydávanie sa za stavový riadok"</string>
@@ -1842,6 +1844,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Zobrazenie na celú obrazovku"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Ukončíte potiahnutím zhora nadol."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Dobre"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Otočte zariadenie pre lepšie zobrazenie"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Ukončite rozdelenú obrazovku pre lepšie zobrazenie"</string>
     <string name="done_label" msgid="7283767013231718521">"Hotovo"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Kruhový posúvač hodín"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Kruhový posúvač minút"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index e0026a6..61f2b51 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -344,6 +344,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Prepoznava poteze, narejene po tipalu prstnih odtisov naprave."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Ustvarjanje posnetka zaslona"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Lahko naredi posnetek zaslona."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"onemogočanje ali spreminjanje vrstice stanja"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Aplikacijam omogoča onemogočenje vrstice stanja ali dodajanje in odstranjevanje ikon sistema."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"postane vrstica stanja"</string>
@@ -1842,6 +1844,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Vklopljen je celozaslonski način"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Zaprete ga tako, da z vrha s prstom povlečete navzdol."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Razumem"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Zasukajte za boljši pregled."</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Zaprite razdeljeni zaslon za boljši pregled."</string>
     <string name="done_label" msgid="7283767013231718521">"Dokončano"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Okrogli drsnik za ure"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Okrogli drsnik za minute"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 47e208d..1708904 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Mund të regjistrojë gjestet e kryera në sensorin e gjurmës së gishtit të pajisjes."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Nxirr një pamje të ekranit"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Mund të nxjerrë një pamje e ekranit."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"çaktivizo ose modifiko shiritin e statusit"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Lejon aplikacionin të çaktivizojë shiritin e statusit dhe të heqë ikonat e sistemit."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"të bëhet shiriti i statusit"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Po shikon ekranin e plotë"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Për të dalë, rrëshqit nga lart poshtë."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"E kuptova"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rrotullo për një pamje më të mirë"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Dil nga ekrani i ndarë për një pamje më të mirë"</string>
     <string name="done_label" msgid="7283767013231718521">"U krye"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Rrëshqitësi rrethor i orëve"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Rrëshqitësi rrethor i minutave"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index f606c8f2c..fb36169 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -343,6 +343,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"ĐœĐŸĐ¶Đ” Ўа Ń€Đ”ĐłĐžŃŃ‚Ń€ŃƒŃ˜Đ” ĐżĐŸĐșрДтД ĐœĐ° ŃĐ”ĐœĐ·ĐŸŃ€Ńƒ за ĐŸŃ‚ĐžŃĐ°Đș прста ĐœĐ° ŃƒŃ€Đ”Ń’Đ°Ń˜Ńƒ."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"НапраĐČĐž ŃĐœĐžĐŒĐ°Đș Đ”ĐșŃ€Đ°ĐœĐ°"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"ĐœĐŸĐ¶Đ” Ўа ĐœĐ°ĐżŃ€Đ°ĐČĐž ŃĐœĐžĐŒĐ°Đș Đ”ĐșŃ€Đ°ĐœĐ°."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"ĐŸĐœĐ”ĐŒĐŸĐłŃƒŃ›Đ°ĐČањД ОлО ĐžĐ·ĐŒĐ”ĐœĐ° ŃŃ‚Đ°Ń‚ŃƒŃĐœĐ” траĐșĐ”"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Đ”ĐŸĐ·ĐČĐŸŃ™Đ°ĐČа аплОĐșацојо Ўа ĐŸĐœĐ”ĐŒĐŸĐłŃƒŃ›Đž ŃŃ‚Đ°Ń‚ŃƒŃĐœŃƒ траĐșу ОлО Ўа ĐŽĐŸĐŽĐ°Ń˜Đ” Đž уĐșлања ŃĐžŃŃ‚Đ”ĐŒŃĐșĐ” ĐžĐșĐŸĐœĐ”."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"Ń„ŃƒĐœĐșŃ†ĐžĐŸĐœĐžŃĐ°ŃšĐ” ĐșĐ°ĐŸ ŃŃ‚Đ°Ń‚ŃƒŃĐœĐ° траĐșа"</string>
@@ -1841,6 +1843,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"ПроĐșазујД сД Ń†Đ”ĐŸ Đ”ĐșŃ€Đ°Đœ"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Да бОстД ОзашлО, прДĐČŃƒŃ†ĐžŃ‚Đ” ĐœĐ°ĐŽĐŸĐ»Đ” ĐŸĐŽĐŸĐ·ĐłĐŸ."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"ВажО"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Đ ĐŸŃ‚ĐžŃ€Đ°Ń˜Ń‚Đ” раЮо Đ±ĐŸŃ™Đ”Đł проĐșаза"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Đ˜Đ·Đ°Ń’ĐžŃ‚Đ” Оз ĐżĐŸĐŽĐ”Ń™Đ”ĐœĐŸĐł Đ”ĐșŃ€Đ°ĐœĐ° раЮо Đ±ĐŸŃ™Đ”Đł проĐșаза"</string>
     <string name="done_label" msgid="7283767013231718521">"Đ“ĐŸŃ‚ĐŸĐČĐŸ"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"ĐšŃ€ŃƒĐ¶ĐœĐž ĐșлОзач за сатД"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"ĐšŃ€ŃƒĐ¶ĐœĐž ĐșлОзач за ĐŒĐžĐœŃƒŃ‚Đ”"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index d79049c..6c593df 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Kan registrera rörelser som utförs med hjälp av enhetens fingeravtryckssensor."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Ta skärmbild"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Kan ta en skärmbild av skärmen."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"inaktivera eller ändra statusfält"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Tillåter att appen inaktiverar statusfältet eller lägger till och tar bort systemikoner."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"visas i statusfältet"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Visar på fullskärm"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Svep nedåt från skärmens överkant för att avsluta."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rotera för att få en bättre vy"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Stäng delad skärm för att få en bättre vy"</string>
     <string name="done_label" msgid="7283767013231718521">"Klart"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Cirkelreglage för timmar"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Cirkelreglage för minuter"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 180293f..715c1c4 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Inaweza kurekodi ishara zinazotekelezwa kwenye kitambua alama ya kidole."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Piga picha ya skrini"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Inaweza kupiga picha ya skrini ya onyesho."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"zima au rekebisha mwambaa hali"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Inaruhusu programu kulemaza upau wa hali au kuongeza na kutoa aikoni za mfumo."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"kuwa sehemu ya arifa"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Unatazama skrini nzima"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Ili kuondoka, telezesha kidole kutoka juu hadi chini."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Nimeelewa"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Zungusha ili upate mwonekano bora"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Funga skrini iliyogawanywa ili upate mwonekano bora"</string>
     <string name="done_label" msgid="7283767013231718521">"Imekamilika"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Kitelezi cha mviringo wa saa"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Kitelezi cha mviringo wa dakika"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 92f0b0a..2c8c532 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"àźšàźŸàź€àź©àź€àŻàź€àźżàź©àŻ àź•àŻˆàź°àŻ‡àź•àŻˆ àźšàŻ†àź©àŻàźšàźŸàź°àŻàźźàŻ‡àźČàŻ àźšàŻ†àźŻàŻàźŻàźȘàŻàźȘàźŸàŻàźźàŻ àźšàŻˆàź•àŻˆàź•àźłàŻˆàź•àŻ àź•àŻ‡àźȘàŻàźŸàŻàźšàź°àŻ àźšàŻ†àźŻàŻàźŻ àźźàŻàźŸàźżàźŻàŻàźźàŻ."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"àźžàŻàź•àźżàź°àŻ€àź©àŻàź·àźŸàźŸàŻàźŸàŻˆ àźŽàźŸàŻàź•àŻàź•àŻàźźàŻ"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"àźŸàźżàźžàŻàźȘàŻàźłàŻ‡àź”àŻˆ àźžàŻàź•àźżàź°àŻ€àź©àŻàź·àźŸàźŸàŻ àźŽàźŸàŻàź•àŻàź• àźźàŻàźŸàźżàźŻàŻàźźàŻ."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"àźšàźżàźČàŻˆàźȘàŻ àźȘàźŸàŻàźŸàźżàźŻàŻˆ àźźàŻàźŸàź•àŻàź•àŻàź€àźČàŻ àź…àźČàŻàźČàź€àŻ àźźàźŸàź±àŻàź±àŻàź€àźČàŻ"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"àźšàźżàźČàŻˆàźȘàŻ àźȘàźŸàŻàźŸàźżàźŻàŻˆ àźźàŻàźŸàź•àŻàź• àź…àźČàŻàźČàź€àŻ àźźàŻàź±àŻˆàźźàŻˆàźŻàźżàźČàŻ àźàź•àźŸàź©àŻàź•àźłàŻˆàźšàŻ àźšàŻ‡àź°àŻàź•àŻàź• àźźàź±àŻàź±àŻàźźàŻ àź…àź•àź±àŻàź± àź†àźȘàŻàźžàŻˆ àź…àź©àŻàźźàź€àźżàź•àŻàź•àźżàź±àź€àŻ."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"àźšàźżàźČàŻˆàźȘàŻ àźȘàźŸàŻàźŸàźżàźŻàźżàźČàŻ àź‡àź°àŻàź•àŻàź•àŻàźźàŻ"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"àźźàŻàźŽàŻàź€àŻ àź€àźżàź°àŻˆàźŻàźżàźČàŻ àź•àźŸàźŸàŻàźŸàŻàź•àźżàź±àź€àŻ"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"àź”àŻ†àźłàźżàźŻàŻ‡àź±, àźźàŻ‡àźČàźżàź°àŻàźšàŻàź€àŻ àź•àŻ€àźŽàŻ‡ àźžàŻàź”àŻˆàźȘàŻ àźšàŻ†àźŻàŻàźŻàź”àŻàźźàŻ"</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"àźȘàŻàź°àźżàźšàŻàź€àź€àŻ"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"àźšàźżàź±àźšàŻàź€ àź•àźŸàźŸàŻàźšàźżàź•àŻàź•àŻ àźšàŻàźŽàź±àŻàź±àŻàź™àŻàź•àźłàŻ"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"àźšàźżàź±àźšàŻàź€ àź•àźŸàźŸàŻàźšàźżàź•àŻàź•àŻ àź€àźżàź°àŻˆàźȘàŻ àźȘàźżàź°àźżàźȘàŻàźȘàŻàźȘàŻ àźȘàźŻàź©àŻàźźàŻàź±àŻˆàźŻàźżàźČàŻ àź‡àź°àŻàźšàŻàź€àŻ àź”àŻ†àźłàźżàźŻàŻ‡àź±àŻàź™àŻàź•àźłàŻ"</string>
     <string name="done_label" msgid="7283767013231718521">"àźźàŻàźŸàźżàźšàŻàź€àź€àŻ"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"àźźàźŁàźżàźšàŻ‡àź° àź”àźŸàŻàźŸ àź”àźŸàźżàź” àźžàŻàźČàŻˆàźŸàź°àŻ"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"àźšàźżàźźàźżàźŸàź™àŻàź•àźłàŻàź•àŻàź•àźŸàź© àź”àźŸàŻàźŸàź”àźŸàźżàź” àźžàŻàźČàŻˆàźŸàź°àŻ"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 4e287c1..bc53802 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"à°Șà°°à°żà°•à°° ఔేà°Čà°żà°źà±à°Šà±à°° ఞెచ్ఞటర్‌à°Čో ఉà°Șà°Żà±‹à°—à°żà°‚à°šà°żà°š ఞంజ్ఞà°Čచు à°•à±à°Żà°Ÿà°Ș్చర్ à°šà±‡à°Żà°”à°šà±à°šà±."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"ఞ్క్రీచ్‌షటట్‌చు à°€à±€à°Żà°‚à°Ąà°ż"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"à°Ąà°żà°žà±‌à°Ș్à°Čే à°Żà±Šà°•à±à°• ఞ్క్రీచ్‌షటట్ ఀీఞుకోఔచ్చు."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"ఞ్టేటఞ్‌ à°Źà°Ÿà°°à±‌చు à°Ąà°żà°œà±‡à°Źà±à°Č్ à°šà±‡à°Żà°Ąà°‚ à°Čేఊట à°źà°Ÿà°°à±à°šà°Ąà°‚"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"ఞ్టేటఞ్‌ à°Źà°Ÿà°°à±‌చు à°Ąà°żà°œà±‡à°Źà±à°Č్ à°šà±‡à°Żà°Ąà°Ÿà°šà°żà°•à°ż à°Čేఊట à°žà°żà°žà±à°Ÿà°źà± à°šà°żà°č్చటà°Čచు à°œà±‹à°Ąà°żà°‚à°šà°Ąà°Ÿà°šà°żà°•à°ż à°źà°°à°żà°Żà± à°€à±€à°žà°żà°”à±‡à°Żà°Ąà°Ÿà°šà°żà°•à°ż à°Żà°Ÿà°Ș్‌చు à°…à°šà±à°źà°€à°żà°žà±à°€à±à°‚à°Šà°ż."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"ఞ్టేటఞ్‌ à°Șట్టీగట à°‰à°‚à°Ąà°Ÿà°‚"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"ఫుà°Č్-ఞ్క్రీచ్‌à°Čో à°”à±€à°•à±à°·à°żà°žà±à°€à±à°šà±à°šà°Ÿà°°à±"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"à°šà°żà°·à±à°•à±à°°à°źà°żà°‚à°šà°Ąà°Ÿà°šà°żà°•à°ż, à°Șై à°šà±à°‚à°Ąà°ż à°•à±à°°à°żà°‚à°Šà°żà°•à°ż ఞ్ఔైà°Ș్ à°šà±‡à°Żà°‚à°Ąà°ż."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"à°…à°°à±à°„à°źà±ˆà°‚à°Šà°ż"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"à°źà±†à°°à±à°—à±ˆà°š à°”à±€à°•à±à°·à°Ł కోఞం à°€à°żà°Ș్à°Șà°‚à°Ąà°ż"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"à°źà±†à°°à±à°—à±ˆà°š à°”à±€à°•à±à°·à°Ł కోఞం ఞ్à°Ș్à°Čà°żà°Ÿà± ఞ్క్రీచ్ à°šà±à°‚à°Ąà°ż à°šà°żà°·à±à°•à±à°°à°źà°żà°‚à°šà°‚à°Ąà°ż"</string>
     <string name="done_label" msgid="7283767013231718521">"à°Șà±‚à°°à±à°€à°Żà°żà°‚à°Šà°ż"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"గంటà°Č ఔృఀ్ఀటకటర ఞ్à°Čà°Żà°żà°Ąà°°à±"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"à°šà°żà°źà°żà°·à°Ÿà°Č ఔృఀ్ఀటకటర ఞ్à°Čà°Żà°żà°Ąà°°à±"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index a14b574..d12b209 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"àžȘàžČàžĄàžČàžŁàž–àžˆàž±àžšàž—àčˆàžČàž—àžČàž‡àžȘàž±àžĄàžœàž±àžȘàž—àž”àčˆàč€àžàžŽàž”àž‚àž¶àč‰àž™àžšàž™àč€àž‹àč‡àž™àč€àž‹àž­àžŁàčŒàž„àžČàžąàž™àžŽàč‰àž§àžĄàž·àž­àž‚àž­àž‡àž­àžžàž›àžàžŁàž“àčŒ"</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"àž–àčˆàžČàžąàž àžČàžžàž«àž™àč‰àžČàžˆàž­"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"àž–àčˆàžČàžąàž àžČàžžàž«àž™àč‰àžČàžˆàž­àč„àž”àč‰"</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"àž›àžŽàž”àžàžČàžŁàčƒàžŠàč‰àž‡àžČàž™àž«àžŁàž·àž­àčàžàč‰àč„àž‚àčàž–àžšàžȘàž–àžČàž™àž°"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"àž­àž™àžžàžàžČàž•àčƒàž«àč‰àčàž­àž›àžžàž„àžŽàč€àž„àžŠàž±àž™àž›àžŽàž”àčƒàžŠàč‰àž‡àžČàž™àčàž–àžšàžȘàž–àžČàž™àž°àž«àžŁàž·àž­àč€àžžàžŽàčˆàžĄàčàž„àž°àž™àžłàč„àž­àž„àž­àž™àžŁàž°àžšàžšàž­àž­àž"</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"àč€àž›àč‡àž™àčàž–àžšàžȘàž–àžČàž™àž°"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"àžàžłàž„àž±àž‡àž”àžčàčàžšàžšàč€àž•àč‡àžĄàž«àž™àč‰àžČàžˆàž­"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"àž«àžČàžàž•àč‰àž­àž‡àžàžČàžŁàž­àž­àž àčƒàž«àč‰àč€àž„àž·àčˆàž­àž™àž„àž‡àžˆàžČàžàž”àč‰àžČàž™àžšàž™"</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"àžŁàž±àžšàž—àžŁàžČàžš"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"àž«àžĄàžžàž™àč€àžžàž·àčˆàž­àžŁàž±àžšàžĄàžžàžĄàžĄàž­àž‡àž—àž”àčˆàž”àž”àžąàžŽàčˆàž‡àž‚àž¶àč‰àž™"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"àž­àž­àžàžˆàžČàžàč‚àž«àžĄàž”àčàžąàžàž«àž™àč‰àžČàžˆàž­àč€àžžàž·àčˆàž­àžŁàž±àžšàžĄàžžàžĄàžĄàž­àž‡àž—àž”àčˆàž”àž”àžąàžŽàčˆàž‡àž‚àž¶àč‰àž™"</string>
     <string name="done_label" msgid="7283767013231718521">"àč€àžȘàžŁàč‡àžˆàžȘàžŽàč‰àž™"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"àž•àž±àž§àč€àž„àž·àčˆàž­àž™àž«àžĄàžžàž™àžŁàž°àžšàžžàžŠàž±àčˆàž§àč‚àžĄàž‡"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"àž•àž±àž§àč€àž„àž·àčˆàž­àž™àž«àžĄàžžàž™àžŁàž°àžšàžžàž™àžČàž—àž”"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 349a39b..4c9e388 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Makukunan ang mga galaw na ginawa sa sensor para sa fingerprint ng device."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Kumuha ng screenshot"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Puwedeng kumuha ng screenshot ng display."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"i-disable o baguhin ang status bar"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Pinapayagan ang app na i-disable ang status bar o magdagdag at mag-alis ng mga icon ng system."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"maging status bar"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Panonood sa full screen"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Upang lumabas, mag-swipe mula sa itaas pababa."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Nakuha ko"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"I-rotate para sa mas magandang view"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Lumabas sa split screen para sa mas magandang view"</string>
     <string name="done_label" msgid="7283767013231718521">"Tapos na"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Pabilog na slider ng mga oras"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Pabilog na slider ng mga minuto"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 4e8c5ef..4e6c6c7 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Cihazın parmak izi sensörlerinde gerçekleßtirilen hareketleri yakalayabilir."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Ekran görüntüsü al"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Ekran görüntüsü alınabilir."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"durum çubuğunu devre dıßı bırak veya değißtir"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Uygulamaya, durum çubuğunu devre dıßı bırakma ve sistem simgelerini ekleyip kaldırma izni verir."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"durum çubuğunda olma"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Tam ekran olarak görüntüleme"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Çıkmak için yukarıdan aßağıya doğru hızlıca kaydırın."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Anladım"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Daha iyi bir görünüm elde etmek için ekranı döndürün"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Daha iyi bir görünüm elde etmek için bölünmüß ekrandan çıkın"</string>
     <string name="done_label" msgid="7283767013231718521">"Bitti"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Saat kaydırma çemberi"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Dakika kaydırma çemberi"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index b62e4a3..28828e7 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -344,6 +344,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"ĐœĐŸĐ¶Đ” фіĐșсуĐČато жДстО ĐœĐ° сĐșĐ°ĐœĐ”Ń€Ń– ĐČіЎбОтĐșіĐČ ĐżĐ°Đ»ŃŒŃ†Ń–ĐČ."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Đ ĐŸĐ±ĐžŃ‚Đž Đ·ĐœŃ–ĐŒĐșĐž Đ”ĐșŃ€Đ°ĐœĐ°"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"ĐœĐŸĐ¶Đ” Ń€ĐŸĐ±ĐžŃ‚Đž Đ·ĐœŃ–ĐŒĐșĐž Đ”ĐșŃ€Đ°ĐœĐ°."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"ĐČĐžĐŒĐžĐșато чо Đ·ĐŒŃ–Đœ. Ń€ŃĐŽĐŸĐș ŃŃ‚Đ°ĐœŃƒ"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Đ”ĐŸĐ·ĐČĐŸĐ»ŃŃ” ĐżŃ€ĐŸĐłŃ€Đ°ĐŒŃ– ĐČĐžĐŒĐžĐșато Ń€ŃĐŽĐŸĐș ŃŃ‚Đ°ĐœŃƒ чо ĐŽĐŸĐŽĐ°ĐČато та ĐČĐžĐŽĐ°Đ»ŃŃ‚Đž піĐșŃ‚ĐŸĐłŃ€Đ°ĐŒĐž ŃĐžŃŃ‚Đ”ĐŒĐž."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"ĐČŃ–ĐŽĐŸĐ±Ń€Đ°Đ¶Đ°Ń‚ĐžŃŃ яĐș Ń€ŃĐŽĐŸĐș ŃŃ‚Đ°ĐœŃƒ"</string>
@@ -1842,6 +1844,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"ĐŸĐ”Ń€Đ”ĐłĐ»ŃĐŽ ĐœĐ° ĐČĐ”ŃŃŒ Đ”ĐșŃ€Đ°Đœ"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Đ©ĐŸĐ± ĐČĐžĐčто, ĐżŃ€ĐŸĐČĐ”ĐŽŃ–Ń‚ŃŒ ĐżĐ°Đ»ŃŒŃ†Đ”ĐŒ Đ·ĐČĐ”Ń€Ń…Ńƒ ĐČĐœĐžĐ·."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ĐžĐ±Đ”Ń€ĐœŃ–Ń‚ŃŒ ĐŽĐ»Ń ĐșŃ€Đ°Ń‰ĐŸĐłĐŸ ĐŸĐłĐ»ŃĐŽŃƒ"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Đ”Đ»Ń ĐșŃ€Đ°Ń‰ĐŸĐłĐŸ ĐŸĐłĐ»ŃĐŽŃƒ ĐČĐžĐčЮіть Ń–Đ· Ń€Đ”Đ¶ĐžĐŒŃƒ Ń€ĐŸĐ·ĐŽŃ–Đ»Đ”ĐœĐœŃ Đ”ĐșŃ€Đ°ĐœĐ°"</string>
     <string name="done_label" msgid="7283767013231718521">"Đ“ĐŸŃ‚ĐŸĐČĐŸ"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"ВОбір ĐłĐŸĐŽĐžĐœ ĐœĐ° цОфДрблаті"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"ВОбір хĐČĐžĐ»ĐžĐœ ĐœĐ° цОфДрблаті"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index e3783c3..d94ccc1 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"ŰąÙ„Û کے ÙÙ†ÚŻŰ± ÙŸŰ±Ù†Ùč ŰłÛŒÙ†ŰłŰ± ÙŸŰ± کیے ÚŻŰŠÛ’ Ű§ŰŽŰ§Ű±ÙˆÚș کو Ú©ÛŒÙŸÚ†Ű± ک۱ ŰłÚ©ŰȘۧ ہے۔"</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Ű§ŰłÚ©Ű±ÛŒÙ† ێۧÙč لیÚș"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"ÚˆŰłÙŸÙ„Û’ کۧ Ű§ŰłÚ©Ű±ÛŒÙ† ێۧÙč Ù„ÛŒŰ§ ۏۧ ŰłÚ©ŰȘۧ ہے۔"</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"ۧ۳ÙčیÙčŰł ۚۧ۱ کو ŰșÛŒŰ± فŰčŰ§Ù„ ÛŒŰ§ ۧ۳ میÚș ŰȘŰ±Ù…ÛŒÙ… Ú©Ű±ÛŒÚș"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Ű§ÛŒÙŸ کو ۧ۳ÙčیÙčŰł ۚۧ۱ ŰșÛŒŰ± فŰčŰ§Ù„ Ú©Ű±Ù†Û’ ÛŒŰ§ ۳۳Ùčم ŰąŰŠÛŒÚ©Ù†ŰČ ŰŽŰ§Ù…Ù„ Ú©Ű±Ù†Û’ Ű§ÙˆŰ± ہÙčŰ§Ù†Û’ کی ۧۏۧŰČŰȘ ŰŻÛŒŰȘۧ ہے۔"</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"ŰšŰ·ÙˆŰ± ۧ۳ÙčیÙčŰł ۚۧ۱ Ú©Ű§Ù… لیÚș"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"ÙŸÙˆŰ±ÛŒ Ű§ŰłÚ©Ű±ÛŒÙ† میÚș ŰŻÛŒÚ©ÚŸ Ű±ÛÛ’ ہیÚș"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"۟ۧ۱ۏ ہونے Ú©ÛŒÙ„ŰŠÛ’ Ű§ÙˆÙŸŰ± ŰłÛ’ نیچے ŰłÙˆŰ§ŰŠÙŸ Ú©Ű±ÛŒÚș۔"</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"ŰłÙ…ŰŹÚŸ Űą ÚŻŰŠÛŒ"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ŰšÛŰȘ۱ Ù…Ù†ŰžŰ± کے لیے ÚŻÚŸÙ…Ű§ŰŠÛŒÚș"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"ŰšÛŰȘ۱ Ù…Ù†ŰžŰ± کے لیے Ű§ŰłÙŸÙ„Ùč Ű§ŰłÚ©Ű±ÛŒÙ† ŰłÛ’ ŰšŰ§ÛŰ± نکلیÚș"</string>
     <string name="done_label" msgid="7283767013231718521">"ہو ÚŻÛŒŰ§"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"ÚŻÚŸÙ†ÙčوÚș کۧ ŰłŰ±Ú©Ù„Ű± ŰłÙ„Ű§ŰŠÛŒÚˆŰ±"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"منÙčŰł ŰłŰ±Ú©Ù„Ű± ŰłÙ„Ű§ŰŠÛŒÚˆŰ±"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 4a23c25..8db468b 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Barmoq izi skanerida kiritilgan ishoralarni taniy oladi."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Skrinshot olish"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Ekrandan skrinshot olishi mumkin."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"holat panelini o‘zgartirish yoki o‘chirish"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Ilova holat panelini o‘chirib qo‘yishi hamda tizim ikonkalarini qo‘shishi yoki olib tashlashi mumkin."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"holat qatorida ko‘rinishi"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Butun ekranli rejim"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Chiqish uchun tepadan pastga torting."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Yaxshiroq koʻrish uchun kamerani buring"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Yaxshiroq koʻrish uchun ajratilgan ekran rejimidan chiqing"</string>
     <string name="done_label" msgid="7283767013231718521">"Tayyor"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Doiradan soatni tanlang"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Doiradan daqiqani tanlang"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index e27495f..30cab1a 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Có thể ghi láșĄi các cá»­ chỉ Ä‘Æ°á»Łc thá»±c hiện trên cáșŁm biáșżn vân tay cá»§a thiáșżt bị."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"ChỄp áșŁnh màn hình"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Có thể chỄp áșŁnh màn hình."</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"vô hiệu hóa hoáș·c sá»­a đổi thanh tráșĄng thái"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Cho phép ứng dỄng vô hiệu hóa thanh tráșĄng thái hoáș·c thêm và xóa biểu tÆ°á»Łng hệ thống."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"trở thành thanh tráșĄng thái"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Xem toàn màn hình"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Để thoát, hãy vuốt từ trên cùng xuống dưới."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Xoay để xem dễ hÆĄn"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Thoát cháșż độ chia đôi màn hình để xem dễ hÆĄn"</string>
     <string name="done_label" msgid="7283767013231718521">"Xong"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Thanh trÆ°á»Łt giờ hình tròn"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Thanh trÆ°á»Łt phút hình tròn"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 1c419fc..3a8ab3c 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"ćŻä»„æ•æ‰ćœšèźŸć€‡æŒ‡çșčäŒ æ„Ÿć™šäžŠæ‰§èĄŒçš„æ‰‹ćŠżă€‚"</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"æˆȘć–ć±ć蕿ˆȘć›Ÿ"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"揯æˆȘć–æ˜Ÿç€șç”»éąçš„ć±ć蕿ˆȘć›Ÿă€‚"</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"ćœç”šæˆ–äżźæ”č状态栏"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"ć…èźžćș”ç”šćœç”šçŠ¶æ€æ æˆ–è€…ćąžćˆ çł»ç»Ÿć›Ÿæ ‡ă€‚"</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"甚䜜状态栏"</string>
@@ -596,7 +598,7 @@
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"慉çșżć€Șäșź"</string>
     <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"æŁ€æ”‹ćˆ°æŒ‰äž‹“ç””æș”æŒ‰é’źçš„æ“äœœ"</string>
     <string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"èŻ·ć°èŻ•è°ƒæ•ŽæŒ‡çșč"</string>
-    <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"èŻ·ćœšæŻæŹĄæ”Ÿæ‰‹æŒ‡æ—¶ç•„ćŸźæ›Žæ”迉‹æŒ‡çš„äœçœź"</string>
+    <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"æŻæŹĄæ”Ÿæ‰‹æŒ‡æ—¶ïŒŒèŻ·ç•„ćŸźć˜æąæ‰‹æŒ‡çš„äœçœź"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
     <string name="fingerprint_error_not_match" msgid="4599441812893438961">"æœȘèƒœèŻ†ćˆ«æŒ‡çșč"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"ç›źć‰ć€„äșŽć…šć±æšĄćŒ"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"芁退ć‡șïŒŒèŻ·ä»ŽéĄ¶éƒšć‘äž‹æ»‘ćŠšă€‚"</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"矄道äș†"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"æ—‹èœŹćŻæ”čć–„éą„è§ˆæ•ˆæžœ"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"退ć‡șćˆ†ć±ćŻæ”čć–„éą„è§ˆæ•ˆæžœ"</string>
     <string name="done_label" msgid="7283767013231718521">"ćźŒæˆ"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"ć°æ—¶èœŹç›˜"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"ćˆ†é’ŸèœŹç›˜"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index cc4d15e..0e65d66 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"ćŻä»„æ“·ć–ćœšèŁçœźæŒ‡çŽ‹æ„Ÿæ‡‰ć™šäžŠćŸ·èĄŒçš„æ‰‹ć‹ąă€‚"</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"æ“·ć–èžąć蕿“·ćœ–"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"ćŻä»„æ“·ć–èžąć蕿ˆȘ朖。"</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"ćœç”šæˆ–äżźæ”čç‹€æ…‹ćˆ—"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"ć…èš±æ‡‰ç”šçš‹ćŒćœç”šç‹€æ…‹ćˆ—ïŒŒäžŠćŻæ–°ćąžæˆ–ç§»é™€çł»ç”±ćœ–ç€ș。"</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"成ç‚șç‹€æ…‹ćˆ—"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"é–‹ć•Ÿć…šèžąćč•"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"ç”±é ‚éƒšć‘äž‹æ»‘ć‹•ćłćŻé€€ć‡ș。"</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"矄道äș†"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"旋蜉仄æ”čć–„é èŠœæ•ˆæžœ"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"退ć‡ș戆ć‰Čèžąćč•仄æ”čć–„é èŠœæ•ˆæžœ"</string>
     <string name="done_label" msgid="7283767013231718521">"ćźŒæˆ"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"ć°æ™‚ç’°ćœąæ»‘æĄż"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"ćˆ†é˜ç’°ćœąæ»‘æĄż"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index b4b6c3b..bd37579 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"ćŻä»„æ“·ć–äœżç”šè€…ć°èŁçœźçš„æŒ‡çŽ‹æ„Ÿæ‡‰ć™šćŸ·èĄŒçš„æ‰‹ć‹ąă€‚"</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"æ“·ć–èžąćč•ç•«éą"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"ćŻä»„æ“·ć–èžąćč•ç•«éąă€‚"</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"ćœç”šæˆ–èźŠæ›Žç‹€æ…‹ćˆ—"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"ć…èš±æ‡‰ç”šçš‹ćŒćœç”šç‹€æ…‹ćˆ—ïŒŒäžŠćŻæ–°ćąžæˆ–ç§»é™€çł»ç”±ćœ–ç€ș。"</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"ä»„ç‹€æ…‹ćˆ—éĄŻç€ș"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"ä»„ć…šèžąć蕿ȘąèŠ–"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"ćŠ‚èŠé€€ć‡șïŒŒè«‹ćŸžç•«éąé ‚ç«Żć‘äž‹æ»‘ć‹•ă€‚"</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"矄道äș†"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"æ—‹èœ‰èžąćč•ä»„ç€èŠœćźŒæ•Žçš„æȘąèŠ–ç•«éą"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"ç”æŸćˆ†ć‰Čç•«éąä»„ć…šèžąćč•瀏芜"</string>
     <string name="done_label" msgid="7283767013231718521">"ćźŒæˆ"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"ć°æ™‚æ•žç’°ç‹€æ»‘æĄż"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"ćˆ†é˜æ•žç’°ç‹€æ»‘æĄż"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index d40fd0a..abc673b 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -342,6 +342,8 @@
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Ingathatha ukuthinta okwenziwe kunzwa yezigxivizo zeminwe zedivayisi."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Thatha isithombe-skrini"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Ingathatha isithombe-skrini sesiboniso"</string>
+    <!-- no translation found for dream_preview_title (5570751491996100804) -->
+    <skip />
     <string name="permlab_statusBar" msgid="8798267849526214017">"khubaza noma guqula ibha yomumo"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Ivumela uhlelo lokusebenza ukuthi yenze umudwa ochaza ngesimo ukuthi ungasebenzi noma ukufaka noma ukukhipha izithonjana zohlelo."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"yiba yibha yesimo"</string>
@@ -1840,6 +1842,8 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Ukubuka isikrini esigcwele"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Ukuze uphume, swayiphela phansi kusuka phezulu."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Ngiyitholile"</string>
+    <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Zungezisa ukuze uthole ukubuka okungcono"</string>
+    <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Phuma ekuhlukaniseni isikrini ukuze ubuke kangcono"</string>
     <string name="done_label" msgid="7283767013231718521">"Kwenziwe"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Amahora weslayidi esiyindingilizi"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Amaminithi weslayidi esiyindingilizi"</string>
diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml
index 4b27bf2..fe296c7 100644
--- a/core/res/res/values/bools.xml
+++ b/core/res/res/values/bools.xml
@@ -18,7 +18,6 @@
     <bool name="kg_enable_camera_default_widget">true</bool>
     <bool name="kg_center_small_widgets_vertically">false</bool>
     <bool name="kg_top_align_page_shrink_on_bouncer_visible">true</bool>
-    <bool name="kg_wake_on_acquire_start">false</bool>
     <bool name="action_bar_embed_tabs">true</bool>
     <bool name="split_action_bar_is_narrow">true</bool>
     <bool name="preferences_prefer_dual_pane">false</bool>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index d5875f5..b83d3b4e 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -150,6 +150,8 @@
     <color name="notification_default_color">#757575</color> <!-- Gray 600 -->
 
     <color name="notification_action_button_text_color">@color/notification_default_color</color>
+    <item  name="notification_action_disabled_content_alpha" format="float" type="dimen">0.38</item>
+    <item  name="notification_action_disabled_container_alpha" format="float" type="dimen">0.12</item>
 
     <color name="notification_progress_background_color">@color/notification_secondary_text_color_current</color>
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index a6be07d..dafa0ad 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -652,6 +652,16 @@
          The default is false. -->
     <bool name="config_lidControlsSleep">false</bool>
 
+    <!-- The device states (supplied by DeviceStateManager) that should be treated as open by the
+         device fold controller. Default is empty. -->
+    <integer-array name="config_openDeviceStates">
+        <!-- Example:
+        <item>0</item>
+        <item>1</item>
+        <item>2</item>
+        -->
+    </integer-array>
+
     <!-- The device states (supplied by DeviceStateManager) that should be treated as folded by the
          display fold controller. Default is empty. -->
     <integer-array name="config_foldedDeviceStates">
@@ -672,6 +682,16 @@
         -->
     </integer-array>
 
+    <!-- The device states (supplied by DeviceStateManager) that should be treated as a rear display
+     state. Default is empty. -->
+    <integer-array name="config_rearDisplayDeviceStates">
+        <!-- Example:
+        <item>0</item>
+        <item>1</item>
+        <item>2</item>
+        -->
+    </integer-array>
+
     <!-- Indicates whether the window manager reacts to half-fold device states by overriding
      rotation. -->
     <bool name="config_windowManagerHalfFoldAutoRotateOverride">false</bool>
@@ -964,6 +984,15 @@
     <!-- Boolean indicating whether light mode is allowed when DWB is turned on. -->
     <bool name="config_displayWhiteBalanceLightModeAllowed">true</bool>
 
+    <!-- Duration, in milliseconds, of the display white balance animated transitions. -->
+    <integer name="config_displayWhiteBalanceTransitionTime">3000</integer>
+
+    <!-- Device states where the sensor based rotation values should be reversed around the Z axis
+         for the default display.
+         TODO(b/265312193): Remove this workaround when this bug is fixed.-->
+    <integer-array name="config_deviceStatesToReverseDefaultDisplayRotationAroundZAxis">
+    </integer-array>
+
     <!-- Indicate available ColorDisplayManager.COLOR_MODE_xxx. -->
     <integer-array name="config_availableColorModes">
         <!-- Example:
@@ -6054,4 +6083,13 @@
     <!-- Whether the lock screen is allowed to run its own live wallpaper,
          different from the home screen wallpaper. -->
     <bool name="config_independentLockscreenLiveWallpaper">false</bool>
+
+    <!-- Whether the vendor power press code need to be mapped. -->
+    <bool name="config_powerPressMapping">false</bool>
+
+    <!-- Power press vendor code. -->
+    <integer name="config_powerPressCode">-1</integer>
+
+    <!-- Whether to show weather on the lock screen by default. -->
+    <bool name="config_lockscreenWeatherEnabledByDefault">false</bool>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 9410e06..2091c05 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -975,6 +975,11 @@
     <!-- Description for the capability of an accessibility service to take screenshot. [CHAR LIMIT=NONE] -->
     <string name="capability_desc_canTakeScreenshot">Can take a screenshot of the display.</string>
 
+    <!-- Dream -->
+
+    <!-- The title to use when a dream is opened in preview mode. [CHAR LIMIT=NONE] -->
+    <string name="dream_preview_title">Preview, <xliff:g id="dream_name" example="Clock">%1$s</xliff:g></string>
+
     <!--  Permissions -->
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -5154,6 +5159,14 @@
     <!-- Cling help message confirmation button when hiding the navigation bar entering immersive mode [CHAR LIMIT=30] -->
     <string name="immersive_cling_positive">Got it</string>
 
+    <!-- Text on a toast shown after the system rotates the screen for camera app
+         compatibility. [CHAR LIMIT=NONE] -->
+    <string name="display_rotation_camera_compat_toast_after_rotation">Rotate for a better view</string>
+
+    <!-- Text on a toast shown when a camera view is started within the app that may not be able
+         to display the camera preview correctly while in split screen. [CHAR LIMIT=NONE] -->
+    <string name="display_rotation_camera_compat_toast_in_split_screen">Exit split screen for a better view</string>
+
     <!-- Label for button to confirm chosen date or time [CHAR LIMIT=30] -->
     <string name="done_label">Done</string>
     <!--
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index be9f6d2..591ba5f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2562,6 +2562,8 @@
   <java-symbol type="string" name="zen_mode_default_weekends_name" />
   <java-symbol type="string" name="zen_mode_default_events_name" />
   <java-symbol type="string" name="zen_mode_default_every_night_name" />
+  <java-symbol type="string" name="display_rotation_camera_compat_toast_after_rotation" />
+  <java-symbol type="string" name="display_rotation_camera_compat_toast_in_split_screen" />
   <java-symbol type="array" name="config_system_condition_providers" />
   <java-symbol type="string" name="muted_by" />
   <java-symbol type="string" name="zen_mode_alarm" />
@@ -2653,6 +2655,8 @@
   <java-symbol type="integer" name="config_sideFpsToastTimeout"/>
   <java-symbol type="integer" name="config_sidefpsSkipWaitForPowerAcquireMessage"/>
   <java-symbol type="integer" name="config_sidefpsSkipWaitForPowerVendorAcquireMessage"/>
+  <java-symbol type="integer" name="config_powerPressCode"/>
+  <java-symbol type="bool" name="config_powerPressMapping"/>
 
   <!-- Clickable toast used during sidefps enrollment -->
   <java-symbol type="layout" name="side_fps_toast" />
@@ -2841,7 +2845,6 @@
   <java-symbol type="dimen" name="fast_scroller_minimum_touch_target" />
   <java-symbol type="array" name="config_cdma_international_roaming_indicators" />
   <java-symbol type="string" name="kg_text_message_separator" />
-  <java-symbol type="bool" name="kg_wake_on_acquire_start" />
 
   <java-symbol type="bool" name="config_use_sim_language_file" />
   <java-symbol type="bool" name="config_LTE_eri_for_network_name" />
@@ -3303,7 +3306,10 @@
   <java-symbol type="id" name="notification_action_list_margin_target" />
   <java-symbol type="dimen" name="notification_actions_padding_start"/>
   <java-symbol type="dimen" name="notification_actions_collapsed_priority_width"/>
+  <!--prefer to use disabled content and surface alpha values for disabled actions-->
   <java-symbol type="dimen" name="notification_action_disabled_alpha" />
+  <java-symbol type="dimen" name="notification_action_disabled_content_alpha" />
+  <java-symbol type="dimen" name="notification_action_disabled_container_alpha" />
   <java-symbol type="id" name="tag_margin_end_when_icon_visible" />
   <java-symbol type="id" name="tag_margin_end_when_icon_gone" />
   <java-symbol type="id" name="tag_uses_right_icon_drawable" />
@@ -3355,6 +3361,7 @@
   <java-symbol type="string" name="unsupported_display_size_message" />
 
   <java-symbol type="layout" name="notification_material_action_emphasized" />
+  <java-symbol type="layout" name="notification_material_action_emphasized_tombstone" />
 
   <!-- Package name for the device provisioning package -->
   <java-symbol type="string" name="config_deviceProvisioningPackage" />
@@ -3436,6 +3443,12 @@
   <java-symbol type="array" name="config_displayWhiteBalanceDisplayPrimaries" />
   <java-symbol type="array" name="config_displayWhiteBalanceDisplayNominalWhite" />
   <java-symbol type="bool" name="config_displayWhiteBalanceLightModeAllowed" />
+  <java-symbol type="integer" name="config_displayWhiteBalanceTransitionTime" />
+
+  <!-- Device states where the sensor based rotation values should be reversed around the Z axis
+       for the default display.
+       TODO(b/265312193): Remove this workaround when this bug is fixed.-->
+  <java-symbol type="array" name="config_deviceStatesToReverseDefaultDisplayRotationAroundZAxis" />
 
   <!-- Default first user restrictions -->
   <java-symbol type="array" name="config_defaultFirstUserRestrictions" />
@@ -4015,8 +4028,10 @@
   <java-symbol type="integer" name="config_maxScanTasksForHomeVisibility" />
 
   <!-- For Foldables -->
+  <java-symbol type="array" name="config_openDeviceStates" />
   <java-symbol type="array" name="config_foldedDeviceStates" />
   <java-symbol type="array" name="config_halfFoldedDeviceStates" />
+  <java-symbol type="array" name="config_rearDisplayDeviceStates" />
   <java-symbol type="bool" name="config_windowManagerHalfFoldAutoRotateOverride" />
   <java-symbol type="array" name="config_deviceStatesOnWhichToWakeUp" />
   <java-symbol type="array" name="config_deviceStatesOnWhichToSleep" />
@@ -4272,6 +4287,8 @@
   <java-symbol type="string" name="capability_desc_canTakeScreenshot" />
   <java-symbol type="string" name="capability_title_canTakeScreenshot" />
 
+  <java-symbol type="string" name="dream_preview_title" />
+
   <java-symbol type="string" name="config_servicesExtensionPackage" />
 
   <!-- For app process exit info tracking -->
@@ -4883,4 +4900,7 @@
   <java-symbol type="id" name="language_picker_header" />
 
   <java-symbol type="dimen" name="status_bar_height_default" />
+
+  <!-- Whether to show weather on the lockscreen by default. -->
+  <java-symbol type="bool" name="config_lockscreenWeatherEnabledByDefault" />
 </resources>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 3e4b1cc..e96c642 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1414,6 +1414,7 @@
         <activity android:name="com.android.internal.app.ChooserWrapperActivity"/>
         <activity android:name="com.android.internal.app.ResolverWrapperActivity"/>
         <activity android:name="com.android.internal.app.IntentForwarderActivityTest$IntentForwarderWrapperActivity"/>
+        <activity android:name="com.android.internal.accessibility.AccessibilityShortcutChooserActivityTest$TestAccessibilityShortcutChooserActivity"/>
 
         <receiver android:name="android.app.activity.AbortReceiver"
             android:exported="true">
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutChooserActivityTest.java
new file mode 100644
index 0000000..973b904
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutChooserActivityTest.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.accessibility;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.doubleClick;
+import static androidx.test.espresso.action.ViewActions.scrollTo;
+import static androidx.test.espresso.action.ViewActions.swipeUp;
+import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.RootMatchers.isDialog;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withClassName;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.endsWith;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.when;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.Handler;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IAccessibilityManager;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.R;
+import com.android.internal.accessibility.dialog.AccessibilityShortcutChooserActivity;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.Collections;
+
+/**
+ * Tests for {@link AccessibilityShortcutChooserActivity}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityShortcutChooserActivityTest {
+    private static final String ONE_HANDED_MODE = "One-Handed mode";
+    private static final String TEST_LABEL = "TEST_LABEL";
+    private static final ComponentName TEST_COMPONENT_NAME = new ComponentName("package", "class");
+
+    @Rule
+    public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+    @Mock
+    private AccessibilityServiceInfo mAccessibilityServiceInfo;
+    @Mock
+    private ResolveInfo mResolveInfo;
+    @Mock
+    private ServiceInfo mServiceInfo;
+    @Mock
+    private ApplicationInfo mApplicationInfo;
+    @Mock
+    private IAccessibilityManager mAccessibilityManagerService;
+
+    @Test
+    public void doubleClickTestServiceAndClickDenyButton_permissionDialogDoesNotExist()
+            throws Exception {
+        configureTestService();
+        final ActivityScenario<TestAccessibilityShortcutChooserActivity> scenario =
+                ActivityScenario.launch(TestAccessibilityShortcutChooserActivity.class);
+        scenario.moveToState(Lifecycle.State.CREATED);
+        scenario.moveToState(Lifecycle.State.STARTED);
+        scenario.moveToState(Lifecycle.State.RESUMED);
+
+        onView(withText(R.string.accessibility_select_shortcut_menu_title)).inRoot(
+                isDialog()).check(matches(isDisplayed()));
+        onView(withText(R.string.edit_accessibility_shortcut_menu_button)).perform(click());
+        onView(withText(TEST_LABEL)).perform(scrollTo(), doubleClick());
+        onView(withId(R.id.accessibility_permission_enable_deny_button)).perform(scrollTo(),
+                click());
+
+        onView(withId(R.id.accessibility_permissionDialog_title)).inRoot(isDialog()).check(
+                doesNotExist());
+        scenario.moveToState(Lifecycle.State.DESTROYED);
+    }
+
+    @Test
+    public void popEditShortcutMenuList_oneHandedModeEnabled_shouldBeInListView() {
+        TestUtils.setOneHandedModeEnabled(this, /* enabled= */ true);
+        final ActivityScenario<TestAccessibilityShortcutChooserActivity> scenario =
+                ActivityScenario.launch(TestAccessibilityShortcutChooserActivity.class);
+        scenario.moveToState(Lifecycle.State.CREATED);
+        scenario.moveToState(Lifecycle.State.STARTED);
+        scenario.moveToState(Lifecycle.State.RESUMED);
+
+        onView(withText(R.string.accessibility_select_shortcut_menu_title)).inRoot(
+                isDialog()).check(matches(isDisplayed()));
+        onView(withText(R.string.edit_accessibility_shortcut_menu_button)).perform(click());
+        onView(allOf(withClassName(endsWith("ListView")), isDisplayed())).perform(swipeUp());
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        onView(withText(ONE_HANDED_MODE)).inRoot(isDialog()).check(matches(isDisplayed()));
+        scenario.moveToState(Lifecycle.State.DESTROYED);
+    }
+
+    @Test
+    public void popEditShortcutMenuList_oneHandedModeDisabled_shouldNotBeInListView() {
+        TestUtils.setOneHandedModeEnabled(this, /* enabled= */ false);
+        final ActivityScenario<TestAccessibilityShortcutChooserActivity> scenario =
+                ActivityScenario.launch(TestAccessibilityShortcutChooserActivity.class);
+        scenario.moveToState(Lifecycle.State.CREATED);
+        scenario.moveToState(Lifecycle.State.STARTED);
+        scenario.moveToState(Lifecycle.State.RESUMED);
+
+        onView(withText(R.string.accessibility_select_shortcut_menu_title)).inRoot(
+                isDialog()).check(matches(isDisplayed()));
+        onView(withText(R.string.edit_accessibility_shortcut_menu_button)).perform(click());
+        onView(allOf(withClassName(endsWith("ListView")), isDisplayed())).perform(swipeUp());
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        onView(withText(ONE_HANDED_MODE)).inRoot(isDialog()).check(doesNotExist());
+        scenario.moveToState(Lifecycle.State.DESTROYED);
+    }
+
+    private void configureTestService() throws Exception {
+        when(mAccessibilityServiceInfo.getResolveInfo()).thenReturn(mResolveInfo);
+        mResolveInfo.serviceInfo = mServiceInfo;
+        mServiceInfo.applicationInfo = mApplicationInfo;
+        when(mResolveInfo.loadLabel(any(PackageManager.class))).thenReturn(TEST_LABEL);
+        when(mAccessibilityServiceInfo.getComponentName()).thenReturn(TEST_COMPONENT_NAME);
+        when(mAccessibilityManagerService.getInstalledAccessibilityServiceList(
+                anyInt())).thenReturn(Collections.singletonList(mAccessibilityServiceInfo));
+
+        TestAccessibilityShortcutChooserActivity.setupForTesting(mAccessibilityManagerService);
+    }
+
+    /**
+     * Used for testing.
+     */
+    public static class TestAccessibilityShortcutChooserActivity extends
+            AccessibilityShortcutChooserActivity {
+        private static IAccessibilityManager sAccessibilityManagerService;
+
+        public static void setupForTesting(IAccessibilityManager accessibilityManagerService) {
+            sAccessibilityManagerService = accessibilityManagerService;
+        }
+
+        @Override
+        public Object getSystemService(String name) {
+            if (Context.ACCESSIBILITY_SERVICE.equals(name)
+                    && sAccessibilityManagerService != null) {
+                return new AccessibilityManager(this, new Handler(getMainLooper()),
+                        sAccessibilityManagerService, /* userId= */ 0, /* serviceConnect= */ true);
+            }
+
+            return super.getSystemService(name);
+        }
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index 6baf305..c92ae2c 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -21,6 +21,11 @@
 import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
 import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
 
+import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME;
+import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME;
+import static com.android.internal.accessibility.AccessibilityShortcutController.ONE_HANDED_COMPONENT_NAME;
+import static com.android.internal.accessibility.AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
@@ -203,6 +208,17 @@
         when(mAlertDialog.getWindow()).thenReturn(window);
 
         when(mTextToSpeech.getVoice()).thenReturn(mVoice);
+
+        // Clears the sFrameworkShortcutFeaturesMap field which was not properly initialized
+        // during testing.
+        try {
+            Field field = AccessibilityShortcutController.class.getDeclaredField(
+                    "sFrameworkShortcutFeaturesMap");
+            field.setAccessible(true);
+            field.set(window, null);
+        } catch (Exception e) {
+            throw new RuntimeException("Unable to set sFrameworkShortcutFeaturesMap", e);
+        }
     }
 
     @AfterClass
@@ -428,11 +444,10 @@
     }
 
     @Test
-    public void getFrameworkFeatureMap_shouldBeNonNullAndUnmodifiable() {
-        Map<ComponentName, AccessibilityShortcutController.ToggleableFrameworkFeatureInfo>
+    public void getFrameworkFeatureMap_shouldBeUnmodifiable() {
+        final Map<ComponentName, AccessibilityShortcutController.ToggleableFrameworkFeatureInfo>
                 frameworkFeatureMap =
                 AccessibilityShortcutController.getFrameworkShortcutFeaturesMap();
-        assertTrue("Framework features not supported", frameworkFeatureMap.size() > 0);
 
         try {
             frameworkFeatureMap.clear();
@@ -443,6 +458,39 @@
     }
 
     @Test
+    public void getFrameworkFeatureMap_containsExpectedDefaultKeys() {
+        final Map<ComponentName, AccessibilityShortcutController.ToggleableFrameworkFeatureInfo>
+                frameworkFeatureMap =
+                AccessibilityShortcutController.getFrameworkShortcutFeaturesMap();
+
+        assertTrue(frameworkFeatureMap.containsKey(COLOR_INVERSION_COMPONENT_NAME));
+        assertTrue(frameworkFeatureMap.containsKey(DALTONIZER_COMPONENT_NAME));
+        assertTrue(frameworkFeatureMap.containsKey(REDUCE_BRIGHT_COLORS_COMPONENT_NAME));
+    }
+
+    @Test
+    public void getFrameworkFeatureMap_oneHandedModeEnabled_containsExpectedKey() {
+        TestUtils.setOneHandedModeEnabled(this, /* enabled= */ true);
+
+        final Map<ComponentName, AccessibilityShortcutController.ToggleableFrameworkFeatureInfo>
+                frameworkFeatureMap =
+                AccessibilityShortcutController.getFrameworkShortcutFeaturesMap();
+
+        assertTrue(frameworkFeatureMap.containsKey(ONE_HANDED_COMPONENT_NAME));
+    }
+
+    @Test
+    public void getFrameworkFeatureMap_oneHandedModeDisabled_containsExpectedKey() {
+        TestUtils.setOneHandedModeEnabled(this, /* enabled= */ false);
+
+        final Map<ComponentName, AccessibilityShortcutController.ToggleableFrameworkFeatureInfo>
+                frameworkFeatureMap =
+                AccessibilityShortcutController.getFrameworkShortcutFeaturesMap();
+
+        assertFalse(frameworkFeatureMap.containsKey(ONE_HANDED_COMPONENT_NAME));
+    }
+
+    @Test
     public void testOnAccessibilityShortcut_forServiceWithNoSummary_doesNotCrash()
             throws Exception {
         configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/TestUtils.java b/core/tests/coretests/src/com/android/internal/accessibility/TestUtils.java
new file mode 100644
index 0000000..ff014ad
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/accessibility/TestUtils.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.accessibility;
+
+import com.android.internal.os.RoSystemProperties;
+
+import java.lang.reflect.Field;
+
+/**
+ * Test utility methods.
+ */
+public class TestUtils {
+
+    /**
+     * Sets the {@code enabled} of the given OneHandedMode flags to simulate device behavior.
+     */
+    public static void setOneHandedModeEnabled(Object obj, boolean enabled) {
+        try {
+            final Field field = RoSystemProperties.class.getDeclaredField(
+                    "SUPPORT_ONE_HANDED_MODE");
+            field.setAccessible(true);
+            field.setBoolean(obj, enabled);
+        } catch (ReflectiveOperationException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/config/sysui/OWNERS b/core/tests/coretests/src/com/android/internal/config/sysui/OWNERS
new file mode 100644
index 0000000..2e96c97
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/config/sysui/OWNERS
@@ -0,0 +1 @@
+include /packages/SystemUI/OWNERS
diff --git a/core/tests/coretests/src/com/android/internal/config/sysui/SystemUiSystemPropertiesFlagsTest.java b/core/tests/coretests/src/com/android/internal/config/sysui/SystemUiSystemPropertiesFlagsTest.java
new file mode 100644
index 0000000..6b9d39c
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/config/sysui/SystemUiSystemPropertiesFlagsTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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.internal.config.sysui;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.Flag;
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.FlagResolver;
+
+import junit.framework.TestCase;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@SmallTest
+public class SystemUiSystemPropertiesFlagsTest extends TestCase {
+
+    public class TestableDebugResolver extends SystemUiSystemPropertiesFlags.DebugResolver {
+        final Map<String, Boolean> mTestData = new HashMap<>();
+
+        @Override
+        public boolean getBoolean(String key, boolean defaultValue) {
+            Boolean testValue = mTestData.get(key);
+            return testValue == null ? defaultValue : testValue;
+        }
+
+        public void set(Flag flag, Boolean value) {
+            mTestData.put(flag.mSysPropKey, value);
+        }
+    }
+
+    private FlagResolver mProdResolver;
+    private TestableDebugResolver mDebugResolver;
+
+    private Flag mReleasedFlag;
+    private Flag mTeamfoodFlag;
+    private Flag mDevFlag;
+
+    public void setUp() {
+        mProdResolver = new SystemUiSystemPropertiesFlags.ProdResolver();
+        mDebugResolver = new TestableDebugResolver();
+        mReleasedFlag = SystemUiSystemPropertiesFlags.releasedFlag("mReleasedFlag");
+        mTeamfoodFlag = SystemUiSystemPropertiesFlags.teamfoodFlag("mTeamfoodFlag");
+        mDevFlag = SystemUiSystemPropertiesFlags.devFlag("mDevFlag");
+    }
+
+    public void tearDown() {
+        SystemUiSystemPropertiesFlags.TEST_RESOLVER = null;
+    }
+
+    public void testProdResolverReturnsDefault() {
+        assertThat(mProdResolver.isEnabled(mReleasedFlag)).isTrue();
+        assertThat(mProdResolver.isEnabled(mTeamfoodFlag)).isFalse();
+        assertThat(mProdResolver.isEnabled(mDevFlag)).isFalse();
+    }
+
+    public void testDebugResolverAndReleasedFlag() {
+        assertThat(mDebugResolver.isEnabled(mReleasedFlag)).isTrue();
+
+        mDebugResolver.set(mReleasedFlag, false);
+        assertThat(mDebugResolver.isEnabled(mReleasedFlag)).isFalse();
+
+        mDebugResolver.set(mReleasedFlag, true);
+        assertThat(mDebugResolver.isEnabled(mReleasedFlag)).isTrue();
+    }
+
+    private void assertTeamfoodFlag(Boolean flagValue, Boolean teamfood, boolean expected) {
+        mDebugResolver.set(mTeamfoodFlag, flagValue);
+        mDebugResolver.set(SystemUiSystemPropertiesFlags.TEAMFOOD, teamfood);
+        assertThat(mDebugResolver.isEnabled(mTeamfoodFlag)).isEqualTo(expected);
+    }
+
+    public void testDebugResolverAndTeamfoodFlag() {
+        assertTeamfoodFlag(null, null, false);
+        assertTeamfoodFlag(true, null, true);
+        assertTeamfoodFlag(false, null, false);
+        assertTeamfoodFlag(null, true, true);
+        assertTeamfoodFlag(true, true, true);
+        assertTeamfoodFlag(false, true, false);
+        assertTeamfoodFlag(null, false, false);
+        assertTeamfoodFlag(true, false, true);
+        assertTeamfoodFlag(false, false, false);
+    }
+
+    public void testDebugResolverAndDevFlag() {
+        assertThat(mDebugResolver.isEnabled(mDevFlag)).isFalse();
+
+        mDebugResolver.set(mDevFlag, true);
+        assertThat(mDebugResolver.isEnabled(mDevFlag)).isTrue();
+
+        mDebugResolver.set(mDevFlag, false);
+        assertThat(mDebugResolver.isEnabled(mDevFlag)).isFalse();
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
index 52feac5..4c9b2b7 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
@@ -360,6 +360,7 @@
         // map of ActivityManager process states and how long to simulate run time in each state
         Map<Integer, Integer> stateRuntimeMap = new HashMap<Integer, Integer>();
         stateRuntimeMap.put(ActivityManager.PROCESS_STATE_TOP, 1111);
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_BOUND_TOP, 7382);
         stateRuntimeMap.put(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE, 1234);
         stateRuntimeMap.put(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 2468);
         stateRuntimeMap.put(ActivityManager.PROCESS_STATE_TOP_SLEEPING, 7531);
@@ -396,7 +397,8 @@
 
         actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE,
                 elapsedTimeUs, STATS_SINCE_CHARGED);
-        expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE)
+                + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
         assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs);
 
         actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_TOP_SLEEPING,
@@ -406,8 +408,7 @@
 
         actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_FOREGROUND,
                 elapsedTimeUs, STATS_SINCE_CHARGED);
-        expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)
-                + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+        expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
         assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs);
 
         actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_BACKGROUND,
@@ -415,7 +416,8 @@
         expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND)
                 + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_BACKUP)
                 + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_SERVICE)
-                + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_RECEIVER);
+                + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_RECEIVER)
+                + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_BOUND_TOP);
         assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs);
 
         actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_CACHED,
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
index 354b937..2742861 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
@@ -78,9 +78,9 @@
                 batteryUsageStats.getUidBatteryConsumers();
         final UidBatteryConsumer uidBatteryConsumer = uidBatteryConsumers.get(0);
         assertThat(uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND))
-                .isEqualTo(60 * MINUTE_IN_MS);
+                .isEqualTo(20 * MINUTE_IN_MS);
         assertThat(uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND))
-                .isEqualTo(10 * MINUTE_IN_MS);
+                .isEqualTo(40 * MINUTE_IN_MS);
         assertThat(uidBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AUDIO))
                 .isWithin(PRECISION).of(2.0);
         assertThat(
@@ -121,22 +121,44 @@
     private BatteryStatsImpl prepareBatteryStats() {
         BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
 
-        batteryStats.noteActivityResumedLocked(APP_UID,
-                10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS);
-        batteryStats.noteUidProcessStateLocked(APP_UID, ActivityManager.PROCESS_STATE_TOP,
-                10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS);
-        batteryStats.noteActivityPausedLocked(APP_UID,
-                30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS);
-        batteryStats.noteUidProcessStateLocked(APP_UID, ActivityManager.PROCESS_STATE_SERVICE,
-                30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS);
-        batteryStats.noteUidProcessStateLocked(APP_UID,
-                ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE,
-                40 * MINUTE_IN_MS, 40 * MINUTE_IN_MS);
-        batteryStats.noteUidProcessStateLocked(APP_UID,
-                ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE,
-                50 * MINUTE_IN_MS, 50 * MINUTE_IN_MS);
-        batteryStats.noteUidProcessStateLocked(APP_UID, ActivityManager.PROCESS_STATE_CACHED_EMPTY,
-                80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS);
+        mStatsRule.setTime(10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS);
+        synchronized (batteryStats) {
+            batteryStats.noteActivityResumedLocked(APP_UID);
+        }
+
+        mStatsRule.setTime(10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS);
+        synchronized (batteryStats) {
+            batteryStats.noteUidProcessStateLocked(APP_UID, ActivityManager.PROCESS_STATE_TOP);
+        }
+        mStatsRule.setTime(30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS);
+        synchronized (batteryStats) {
+            batteryStats.noteActivityPausedLocked(APP_UID);
+        }
+        mStatsRule.setTime(30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS);
+        synchronized (batteryStats) {
+            batteryStats.noteUidProcessStateLocked(APP_UID,
+                    ActivityManager.PROCESS_STATE_SERVICE);
+        }
+        mStatsRule.setTime(40 * MINUTE_IN_MS, 40 * MINUTE_IN_MS);
+        synchronized (batteryStats) {
+            batteryStats.noteUidProcessStateLocked(APP_UID,
+                    ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        }
+        mStatsRule.setTime(50 * MINUTE_IN_MS, 50 * MINUTE_IN_MS);
+        synchronized (batteryStats) {
+            batteryStats.noteUidProcessStateLocked(APP_UID,
+                    ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+        }
+        mStatsRule.setTime(60 * MINUTE_IN_MS, 60 * MINUTE_IN_MS);
+        synchronized (batteryStats) {
+            batteryStats.noteUidProcessStateLocked(APP_UID,
+                    ActivityManager.PROCESS_STATE_BOUND_TOP);
+        }
+        mStatsRule.setTime(70 * MINUTE_IN_MS, 70 * MINUTE_IN_MS);
+        synchronized (batteryStats) {
+            batteryStats.noteUidProcessStateLocked(APP_UID,
+                    ActivityManager.PROCESS_STATE_CACHED_EMPTY);
+        }
 
         batteryStats.noteFlashlightOnLocked(APP_UID, 1000, 1000);
         batteryStats.noteFlashlightOffLocked(APP_UID, 5000, 5000);
diff --git a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotRequestTest.java b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotRequestTest.java
index 30540a5..89acbc7 100644
--- a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotRequestTest.java
+++ b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotRequestTest.java
@@ -131,6 +131,12 @@
         assertEquals(Insets.NONE, out.getInsets());
     }
 
+    @Test
+    public void testInvalidType() {
+        assertThrows(IllegalArgumentException.class,
+                () -> new ScreenshotRequest.Builder(5, 2).build());
+    }
+
     private Bitmap makeHardwareBitmap(int width, int height) {
         HardwareBuffer buffer = HardwareBuffer.create(
                 width, height, HardwareBuffer.RGBA_8888, 1, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index e0e13f5..6dcee6d 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -49,6 +49,7 @@
         <permission name="android.permission.READ_FRAME_BUFFER"/>
         <permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
         <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+        <permission name="android.permission.READ_PRECISE_PHONE_STATE"/>
         <permission name="android.permission.REAL_GET_TASKS"/>
         <permission name="android.permission.REQUEST_NETWORK_SCORES"/>
         <permission name="android.permission.RECEIVE_MEDIA_RESOURCE_USAGE"/>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 58a2073..caa118a 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -306,6 +306,7 @@
         <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
         <!-- Permission required for UiModeManager CTS test -->
         <permission name="android.permission.READ_PROJECTION_STATE"/>
+        <permission name="android.permission.READ_WALLPAPER_INTERNAL"/>
         <permission name="android.permission.READ_WIFI_CREDENTIAL"/>
         <permission name="android.permission.REAL_GET_TASKS"/>
         <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index f47d9c6..1cf819a 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -3331,6 +3331,12 @@
       "group": "WM_DEBUG_STATES",
       "at": "com\/android\/server\/wm\/TaskFragment.java"
     },
+    "1015746067": {
+      "message": "Display id=%d is ignoring orientation request for %d, return %d following a per-app override for %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_ORIENTATION",
+      "at": "com\/android\/server\/wm\/DisplayContent.java"
+    },
     "1022095595": {
       "message": "TaskFragment info changed name=%s",
       "level": "VERBOSE",
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java
index fa173072..d39d4b4 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java
@@ -412,11 +412,11 @@
 
     private static ParsedRequiresPermission parseRequiresPermissionRecursively(
             MethodInvocationTree tree, VisitorState state) {
-        if (ENFORCE_VIA_CONTEXT.matches(tree, state)) {
+        if (ENFORCE_VIA_CONTEXT.matches(tree, state) && tree.getArguments().size() > 0) {
             final ParsedRequiresPermission res = new ParsedRequiresPermission();
             res.allOf.add(String.valueOf(ASTHelpers.constValue(tree.getArguments().get(0))));
             return res;
-        } else if (ENFORCE_VIA_CHECKER.matches(tree, state)) {
+        } else if (ENFORCE_VIA_CHECKER.matches(tree, state) && tree.getArguments().size() > 1) {
             final ParsedRequiresPermission res = new ParsedRequiresPermission();
             res.allOf.add(String.valueOf(ASTHelpers.constValue(tree.getArguments().get(1))));
             return res;
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/RequiresPermissionCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/RequiresPermissionCheckerTest.java
index 388988e..38831b1 100644
--- a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/RequiresPermissionCheckerTest.java
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/RequiresPermissionCheckerTest.java
@@ -415,4 +415,27 @@
                         "}")
                 .doTest();
     }
+
+    @Test
+    public void testInvalidFunctions() {
+        compilationHelper
+                .addSourceFile("/android/annotation/RequiresPermission.java")
+                .addSourceFile("/android/annotation/SuppressLint.java")
+                .addSourceFile("/android/content/Context.java")
+                .addSourceLines("Example.java",
+                        "import android.annotation.RequiresPermission;",
+                        "import android.annotation.SuppressLint;",
+                        "import android.content.Context;",
+                        "class Foo extends Context {",
+                        "  private static final String RED = \"red\";",
+                        "  public void checkPermission() {",
+                        "  }",
+                        "  @RequiresPermission(RED)",
+                        "  // BUG: Diagnostic contains:",
+                        "  public void exampleScoped(Context context) {",
+                        "    checkPermission();",
+                        "  }",
+                        "}")
+                .doTest();
+    }
 }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
index 3adae70..ff5f256 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
@@ -25,12 +25,12 @@
 import android.util.ArraySet;
 
 import androidx.annotation.NonNull;
+import androidx.window.extensions.core.util.function.Consumer;
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 
 import java.util.concurrent.Executor;
-import java.util.function.Consumer;
 
 /**
  * Reference implementation of androidx.window.extensions.area OEM interface for use with
@@ -252,4 +252,37 @@
             }
         }
     }
+
+    @Override
+    public void addRearDisplayPresentationStatusListener(
+            @NonNull Consumer<ExtensionWindowAreaStatus> consumer) {
+        throw new UnsupportedOperationException(
+                "addRearDisplayPresentationStatusListener is not supported in API_VERSION=2");
+    }
+
+    @Override
+    public void removeRearDisplayPresentationStatusListener(
+            @NonNull Consumer<ExtensionWindowAreaStatus> consumer) {
+        throw new UnsupportedOperationException(
+                "removeRearDisplayPresentationStatusListener is not supported in API_VERSION=2");
+    }
+
+    @Override
+    public void startRearDisplayPresentationSession(@NonNull Activity activity,
+            @NonNull Consumer<@WindowAreaSessionState Integer> consumer) {
+        throw new UnsupportedOperationException(
+                "startRearDisplayPresentationSession is not supported in API_VERSION=2");
+    }
+
+    @Override
+    public void endRearDisplayPresentationSession() {
+        throw new UnsupportedOperationException(
+                "endRearDisplayPresentationSession is not supported in API_VERSION=2");
+    }
+
+    @Override
+    public ExtensionWindowAreaPresentation getRearDisplayPresentation() {
+        throw new UnsupportedOperationException(
+                "getRearDisplayPresentation is not supported in API_VERSION=2");
+    }
 }
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 8b3a471..569eb80 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -2156,4 +2156,30 @@
         return configuration != null
                 && configuration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_PINNED;
     }
+
+    @Override
+    public ActivityOptions setLaunchingActivityStack(@NonNull ActivityOptions options,
+            @NonNull IBinder token) {
+        throw new UnsupportedOperationException(
+                "setLaunchingActivityStack is not supported in API_VERSION=2");
+    }
+
+    @Override
+    public void finishActivityStacks(@NonNull Set<IBinder> activityStackTokens) {
+        throw new UnsupportedOperationException(
+                "finishActivityStacks is not supported in API_VERSION=2");
+    }
+
+    @Override
+    public void invalidateTopVisibleSplitAttributes() {
+        throw new UnsupportedOperationException(
+                "invalidateTopVisibleSplitAttributes is not supported in API_VERSION=2");
+    }
+
+    @Override
+    public void updateSplitAttributes(@NonNull IBinder splitInfoToken,
+            @NonNull SplitAttributes splitAttributes) {
+        throw new UnsupportedOperationException(
+                "updateSplitAttributes is not supported in API_VERSION=2");
+    }
 }
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index cddbf469..c3b6916 100644
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index f615ad6..0f45219 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -47,6 +47,7 @@
         "src/com/android/wm/shell/sysui/ShellSharedConstants.java",
         "src/com/android/wm/shell/common/TransactionPool.java",
         "src/com/android/wm/shell/animation/Interpolators.java",
+        "src/com/android/wm/shell/pip/PipContentOverlay.java",
         "src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java",
     ],
     path: "src",
diff --git a/libs/WindowManager/Shell/res/drawable/caption_close_button.xml b/libs/WindowManager/Shell/res/drawable/caption_close_button.xml
new file mode 100644
index 0000000..e258564
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/caption_close_button.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="32.0dp"
+        android:height="32.0dp"
+        android:viewportWidth="32.0"
+        android:viewportHeight="32.0"
+>
+    <group android:scaleX="0.5"
+           android:scaleY="0.5"
+           android:translateY="4.0">
+        <path
+            android:fillColor="#FFFF0000"
+            android:pathData="M12.45,38.35 L9.65,35.55 21.2,24 9.65,12.45 12.45,9.65 24,21.2 35.55,9.65 38.35,12.45 26.8,24 38.35,35.55 35.55,38.35 24,26.8Z"/>
+    </group>
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/caption_collapse_menu_button.xml b/libs/WindowManager/Shell/res/drawable/caption_collapse_menu_button.xml
new file mode 100644
index 0000000..166552d
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/caption_collapse_menu_button.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+>
+    <group android:scaleX="1.25"
+           android:scaleY="1.75"
+           android:translateY="6.0">
+        <path
+            android:fillColor="@android:color/black"
+            android:pathData="M10.3937 6.93935L11.3337 5.99935L6.00033 0.666016L0.666992 5.99935L1.60699 6.93935L6.00033 2.55268"/>
+    </group>
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/caption_screenshot_button.xml b/libs/WindowManager/Shell/res/drawable/caption_screenshot_button.xml
new file mode 100644
index 0000000..7c86888
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/caption_screenshot_button.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="32.0dp"
+        android:height="32.0dp"
+        android:viewportWidth="32.0"
+        android:viewportHeight="32.0"
+>
+    <group android:scaleX="0.5"
+           android:scaleY="0.5"
+           android:translateY="4.0">
+        <path
+            android:fillColor="@android:color/black"
+            android:pathData="M10,38V28.35H13V35H19.65V38ZM10,19.65V10H19.65V13H13V19.65ZM28.35,38V35H35V28.35H38V38ZM35,19.65V13H28.35V10H38V19.65Z"/>
+    </group>
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/caption_select_button.xml b/libs/WindowManager/Shell/res/drawable/caption_select_button.xml
new file mode 100644
index 0000000..8c60c84
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/caption_select_button.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="32.0dp"
+        android:height="32.0dp"
+        android:viewportWidth="32.0"
+        android:viewportHeight="32.0"
+>
+    <group
+           android:translateX="4.0"
+           android:translateY="6.0">
+        <path
+            android:fillColor="@android:color/black"
+            android:pathData="M13.7021 12.5833L16.5676 15.5L15.426 16.7333L12.526 13.8333L10.4426 15.9167V10.5H15.9176L13.7021 12.5833ZM13.8343 3.83333H15.501V5.5H13.8343V3.83333ZM15.501 2.16667H13.8343V0.566667C14.751 0.566667 15.501 1.33333 15.501 2.16667ZM10.501 0.5H12.1676V2.16667H10.501V0.5ZM13.8343 7.16667H15.501V8.83333H13.8343V7.16667ZM5.50098 15.5H3.83431V13.8333H5.50098V15.5ZM2.16764 5.5H0.500977V3.83333H2.16764V5.5ZM2.16764 0.566667V2.16667H0.500977C0.500977 1.33333 1.33431 0.566667 2.16764 0.566667ZM2.16764 12.1667H0.500977V10.5H2.16764V12.1667ZM5.50098 2.16667H3.83431V0.5H5.50098V2.16667ZM8.83431 2.16667H7.16764V0.5H8.83431V2.16667ZM8.83431 15.5H7.16764V13.8333H8.83431V15.5ZM2.16764 8.83333H0.500977V7.16667H2.16764V8.83333ZM2.16764 15.5667C1.25098 15.5667 0.500977 14.6667 0.500977 13.8333H2.16764V15.5667Z"/>
+    </group>
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml b/libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml
index c9f2623..27e0b18 100644
--- a/libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml
+++ b/libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml
@@ -17,9 +17,10 @@
         android:width="24dp"
         android:height="24dp"
         android:viewportWidth="24"
-        android:viewportHeight="24">
+        android:viewportHeight="24"
+        android:tint="@color/decor_button_dark_color">
     <group android:translateY="8.0">
         <path
-            android:fillColor="@android:color/black" android:pathData="M3,5V3H21V5Z"/>
+            android:fillColor="@android:color/white" android:pathData="M3,5V3H21V5Z"/>
     </group>
 </vector>
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_menu_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_menu_background.xml
index 416287d..9167382 100644
--- a/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_menu_background.xml
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_menu_background.xml
@@ -18,4 +18,5 @@
        xmlns:android="http://schemas.android.com/apk/res/android">
     <solid android:color="@android:color/white" />
     <corners android:radius="20dp" />
+    <stroke android:width="1dp" android:color="#b3b3b3"/>
 </shape>
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_title.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_title.xml
index 416287d..ef30060 100644
--- a/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_title.xml
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_title.xml
@@ -15,7 +15,8 @@
   ~ limitations under the License.
   -->
 <shape android:shape="rectangle"
+       android:tintMode="multiply"
+       android:tint="@color/decor_title_color"
        xmlns:android="http://schemas.android.com/apk/res/android">
     <solid android:color="@android:color/white" />
-    <corners android:radius="20dp" />
 </shape>
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
index 2994593..b3f8e801 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
+++ b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
@@ -25,12 +25,10 @@
         android:fillAlpha="0.8"
         android:pathData="M0,24 a24,24 0 1,0 48,0 a24,24 0 1,0 -48,0"/>
     <group
-        android:scaleX="0.8"
-        android:scaleY="0.8"
-        android:translateX="10"
-        android:translateY="10">
+        android:translateX="12"
+        android:translateY="12">
         <path
-            android:pathData="M0,36V24.5H3V30.85L10.4,23.45L12.55,25.6L5.15,33H11.5V36H0ZM24.5,36V33H30.85L23.5,25.65L25.65,23.5L33,30.85V24.5H36V36H24.5ZM10.35,12.5L3,5.15V11.5H0V0H11.5V3H5.15L12.5,10.35L10.35,12.5ZM25.65,12.5L23.5,10.35L30.85,3H24.5V0H36V11.5H33V5.15L25.65,12.5Z"
-            android:fillColor="@color/compat_controls_text"/>
+            android:fillColor="@color/compat_controls_text"
+            android:pathData="M3,21V15H5V17.6L8.1,14.5L9.5,15.9L6.4,19H9V21ZM15,21V19H17.6L14.5,15.9L15.9,14.5L19,17.6V15H21V21ZM8.1,9.5 L5,6.4V9H3V3H9V5H6.4L9.5,8.1ZM15.9,9.5 L14.5,8.1 17.6,5H15V3H21V9H19V6.4Z"/>
     </group>
 </vector>
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_decor_handle_menu.xml
index 8b4792a..f6e3f2e 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_decor_handle_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_decor_handle_menu.xml
@@ -1,49 +1,129 @@
 <?xml version="1.0" encoding="utf-8"?>
-    <!--
-      ~ Copyright (C) 2022 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.
-      -->
+<!--
+  ~ Copyright (C) 2022 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.
+  -->
 <com.android.wm.shell.windowdecor.WindowDecorLinearLayout
-xmlns:android="http://schemas.android.com/apk/res/android"
-android:id="@+id/handle_menu"
-android:layout_width="wrap_content"
-android:layout_height="wrap_content"
-android:gravity="center_horizontal"
-android:background="@drawable/desktop_mode_decor_menu_background">
-    <Button
-        style="@style/CaptionButtonStyle"
-        android:id="@+id/fullscreen_button"
-        android:contentDescription="@string/fullscreen_text"
-        android:background="@drawable/caption_fullscreen_button"/>
-    <Button
-        style="@style/CaptionButtonStyle"
-        android:id="@+id/split_screen_button"
-        android:contentDescription="@string/split_screen_text"
-        android:background="@drawable/caption_split_screen_button"/>
-    <Button
-        style="@style/CaptionButtonStyle"
-        android:id="@+id/floating_button"
-        android:contentDescription="@string/float_button_text"
-        android:background="@drawable/caption_floating_button"/>
-    <Button
-        style="@style/CaptionButtonStyle"
-        android:id="@+id/desktop_button"
-        android:contentDescription="@string/desktop_text"
-        android:background="@drawable/caption_desktop_button"/>
-    <Button
-        style="@style/CaptionButtonStyle"
-        android:id="@+id/more_button"
-        android:contentDescription="@string/more_button_text"
-        android:background="@drawable/caption_more_button"/>
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/handle_menu"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:background="@drawable/desktop_mode_decor_menu_background"
+    android:elevation="@dimen/caption_menu_elevation"
+    android:divider="?android:attr/dividerHorizontal"
+    android:showDividers="middle"
+    android:dividerPadding="18dip">
+    <RelativeLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content">
+        <ImageView
+            android:id="@+id/application_icon"
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            android:layout_margin="12dp"
+            android:contentDescription="@string/app_icon_text"
+            android:layout_alignParentStart="true"
+            android:layout_centerVertical="true"/>
+        <TextView
+            android:id="@+id/application_name"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_toEndOf="@+id/application_icon"
+            android:layout_toStartOf="@+id/collapse_menu_button"
+            android:textColor="#FF000000"
+            android:layout_centerVertical="true"/>
+        <Button
+            android:id="@+id/collapse_menu_button"
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            android:layout_marginEnd="10dp"
+            android:contentDescription="@string/collapse_menu_text"
+            android:layout_alignParentEnd="true"
+            android:background="@drawable/caption_collapse_menu_button"
+            android:layout_centerVertical="true"/>
+    </RelativeLayout>
+    <LinearLayout
+        android:id="@+id/windowing_mode_buttons"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center_horizontal">
+        <Space
+            android:layout_width="0dp"
+            android:layout_height="1dp"
+            android:layout_weight="0.5" />
+        <Button
+            style="@style/CaptionWindowingButtonStyle"
+            android:id="@+id/fullscreen_button"
+            android:contentDescription="@string/fullscreen_text"
+            android:background="@drawable/caption_fullscreen_button"/>
+        <Space
+            android:layout_width="0dp"
+            android:layout_height="1dp"
+            android:layout_weight="1" />
+        <Button
+            style="@style/CaptionWindowingButtonStyle"
+            android:id="@+id/split_screen_button"
+            android:contentDescription="@string/split_screen_text"
+            android:background="@drawable/caption_split_screen_button"/>
+        <Space
+            android:layout_width="0dp"
+            android:layout_height="1dp"
+            android:layout_weight="1" />
+        <Button
+            style="@style/CaptionWindowingButtonStyle"
+            android:id="@+id/floating_button"
+            android:contentDescription="@string/float_button_text"
+            android:background="@drawable/caption_floating_button"/>
+        <Space
+            android:layout_width="0dp"
+            android:layout_height="1dp"
+            android:layout_weight="1" />
+        <Button
+            style="@style/CaptionWindowingButtonStyle"
+            android:id="@+id/desktop_button"
+            android:contentDescription="@string/desktop_text"
+            android:background="@drawable/caption_desktop_button"/>
+        <Space
+            android:layout_width="0dp"
+            android:layout_height="1dp"
+            android:layout_weight="0.5" />
+
+    </LinearLayout>
+    <LinearLayout
+        android:id="@+id/menu_buttons_misc"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+        <Button
+            style="@style/CaptionMenuButtonStyle"
+            android:id="@+id/screenshot_button"
+            android:contentDescription="@string/screenshot_text"
+            android:text="@string/screenshot_text"
+            android:drawableStart="@drawable/caption_screenshot_button"/>
+        <Button
+            style="@style/CaptionMenuButtonStyle"
+            android:id="@+id/select_button"
+            android:contentDescription="@string/select_text"
+            android:text="@string/select_text"
+            android:drawableStart="@drawable/caption_select_button"/>
+        <Button
+            style="@style/CaptionMenuButtonStyle"
+            android:id="@+id/close_button"
+            android:contentDescription="@string/close_text"
+            android:text="@string/close_text"
+            android:drawableStart="@drawable/caption_close_button"
+            android:textColor="#FFFF0000"/>
+    </LinearLayout>
 </com.android.wm.shell.windowdecor.WindowDecorLinearLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor.xml
index 2a4cc02..29cf151 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor.xml
@@ -17,21 +17,20 @@
 <com.android.wm.shell.windowdecor.WindowDecorLinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/desktop_mode_caption"
-    android:layout_width="wrap_content"
+    android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:gravity="center_horizontal"
     android:background="@drawable/desktop_mode_decor_title">
     <Button
         style="@style/CaptionButtonStyle"
         android:id="@+id/back_button"
         android:contentDescription="@string/back_button_text"
-        android:background="@drawable/decor_back_button_dark"
-    />
+        android:background="@drawable/decor_back_button_dark"/>
     <Button
         android:id="@+id/caption_handle"
         android:layout_width="128dp"
         android:layout_height="32dp"
         android:layout_margin="5dp"
-        android:padding="4dp"
         android:contentDescription="@string/handle_text"
         android:background="@drawable/decor_handle_dark"/>
     <Button
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index 3d50d22..22d9219 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Bo 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Bo 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Volskerm onder"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Verdeel links"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Verdeel regs"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Verdeel bo"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Verdeel onder"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Gebruik eenhandmodus"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Swiep van die onderkant van die skerm af op of tik enige plek bo die program om uit te gaan"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Begin eenhandmodus"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dubbeltik buite ’n program om dit te herposisioneer"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Het dit"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Vou uit vir meer inligting."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Herbegin vir ’n beter aansig?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Jy kan die app herbegin sodat dit beter op jou skerm lyk, maar jy sal dalk jou vordering of enige ongestoorde veranderinge verloor"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Kanselleer"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Herbegin"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Moenie weer wys nie"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Maksimeer"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Maak klein"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Maak toe"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Terug"</string>
     <string name="handle_text" msgid="1766582106752184456">"Handvatsel"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Volskerm"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Rekenaarmodus"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Verdeelde skerm"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Meer"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Sweef"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index 70304aa..17ea053 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ኹላይ 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"ኹላይ 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"á‹šá‰łá‰œ ሙሉ ማያ ገጜ"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"ወደ ግራ ኹፋፍል"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"ወደ ቀኝ ኹፋፍል"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"ወደ ላይ ኹፋፍል"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"ወደ á‰łá‰œ ኹፋፍል"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"á‰ŁáˆˆáŠ áŠ•á‹” ኄጅ ሁነታን በመጠቀም ላይ"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"áˆˆáˆ˜á‹áŒŁá‰” ኚማያው ግርጌ ወደ ላይ ይጄሚጉ ወይም ኹመተግበáˆȘያው በላይ ማንኛውም ቩታ ላይ መታ ያዔርጉ"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"á‰ŁáˆˆáŠ áŠ•á‹” ኄጅ ሁነታ ጀምር"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ቩታውን ለመቀዹር ኹመተግበáˆȘያው ውáŒȘ ሁለቮ መታ ያዔርጉ"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"ገባኝ"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ለተጹማáˆȘ መሹጃ ይዘርጉ፱"</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"ለተሻለ ዕይታ ኄንደገና ይጀመር?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"በማያ ገጜዎ ላይ ዚተሻለ ሆኖ ኄንá‹Čታይ መተግበáˆȘያውን ኄንደገና ማሔጀመር ይቜላሉ ነገር ግን ዚደሚሱበቔን ዚሂደቔ ደሹጃ ወይም ማናቾውንም á‹«áˆá‰°á‰€áˆ˜áŒĄ ለውጊቜ áˆŠá‹«áŒĄ ይቜላሉ"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"ይቅር"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"ኄንደገና ያሔጀምሩ"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"ዳግም አታሳይ"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"አሔፋ"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"áŠ áˆłáŠ•áˆ”"</string>
     <string name="close_button_text" msgid="2913281996024033299">"ዝጋ"</string>
     <string name="back_button_text" msgid="1469718707134137085">"ተመለሔ"</string>
     <string name="handle_text" msgid="1766582106752184456">"áˆ˜á‹«á‹Ł"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"ሙሉ ማያ"</string>
     <string name="desktop_text" msgid="1077633567027630454">"ዚዎሔክቶፕ ሁነታ"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"ዹተኹፈለ ማያ ገጜ"</string>
     <string name="more_button_text" msgid="3655388105592893530">"ተጹማáˆȘ"</string>
     <string name="float_button_text" msgid="9221657008391364581">"ተንሳፋፊ"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index 0f74aab..2e4c8ef 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -47,6 +47,14 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"۶ۚ۷ Ű­ŰŹÙ… Ű§Ù„Ù†Ű§ÙŰ°Ű© Ű§Ù„ŰčÙ„ÙˆÙŠŰ© ليكون ل٠%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"۶ۚ۷ Ű­ŰŹÙ… Ű§Ù„Ù†Ű§ÙŰ°Ű© Ű§Ù„ŰčÙ„ÙˆÙŠŰ© ليكون ÙŁÙ %"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Űč۱۶ Ű§Ù„Ù†Ű§ÙŰ°Ű© Ű§Ù„ŰłÙÙ„ÙŠŰ© ŰšÙ…Ù„ŰĄ Ű§Ù„ŰŽŰ§ŰŽŰ©"</string>
+    <!-- no translation found for accessibility_split_left (1713683765575562458) -->
+    <skip />
+    <!-- no translation found for accessibility_split_right (8441001008181296837) -->
+    <skip />
+    <!-- no translation found for accessibility_split_top (2789329702027147146) -->
+    <skip />
+    <!-- no translation found for accessibility_split_bottom (8694551025220868191) -->
+    <skip />
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"ۧ۳ŰȘŰźŰŻŰ§Ù… ÙˆŰ¶Űč \"Ű§Ù„ŰȘŰ”ÙŰ­ ŰšÙŠŰŻ ÙˆŰ§Ű­ŰŻŰ©\""</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Ù„Ù„ŰźŰ±ÙˆŰŹŰŒ Ù…Ű±ÙÙ‘Ű± ŰłŰ±ÙŠŰčÙ‹Ű§ من ŰŁŰłÙÙ„ Ű§Ù„ŰŽŰ§ŰŽŰ© Ű„Ù„Ù‰ ŰŁŰčÙ„Ű§Ù‡Ű§ ŰŁÙˆ Ű§Ù†Ù‚Ű± في ŰŁÙŠ Ù…ÙƒŰ§Ù† فوق Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚."</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"ۚۯۥ ÙˆŰ¶Űč \"Ű§Ù„ŰȘŰ”ÙŰ­ ŰšÙŠŰŻ ÙˆŰ§Ű­ŰŻŰ©\""</string>
@@ -82,14 +90,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Ű§Ù†Ù‚Ű± Ù…Ű±Ù‘ŰȘين ۟ۧ۱ۏ ŰȘŰ·ŰšÙŠÙ‚ لŰȘŰșÙŠÙŠŰ± Ù…ÙˆŰ¶Űčه."</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Ű­ŰłÙ†Ù‹Ű§"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Ű§Ù„ŰȘÙˆŰłÙŠŰč Ù„Ù„Ű­Ű”ÙˆÙ„ Űčلى مŰČÙŠŰŻ من Ű§Ù„Ù…ŰčÙ„ÙˆÙ…Ű§ŰȘ"</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"هل ŰȘŰ±ÙŠŰŻ Ű„Űčۧۯ۩ ŰȘŰŽŰșيل Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚ لŰčŰ±Ű¶Ù‡ ŰšŰŽÙƒÙ„ ŰŁÙŰ¶Ù„ŰŸ"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"يمكنك Ű„Űčۧۯ۩ ŰȘŰŽŰșيل Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚ Ű­ŰȘى ÙŠŰžÙ‡Ű± ŰšŰŽÙƒÙ„ ŰŁÙŰ¶Ù„ Űčلى ێۧێŰȘÙƒŰŒ ولكن Ù‚ŰŻ ŰȘÙÙ‚ŰŻ ŰȘÙ‚ŰŻÙ…Ùƒ ŰŁÙˆ ŰŁÙŠ ŰȘŰșÙŠÙŠŰ±Ű§ŰȘ ŰșÙŠŰ± Ù…Ű­ÙÙˆŰžŰ©."</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Ű„Ù„Űșۧۥ"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Ű„Űčۧۯ۩ Ű§Ù„ŰȘŰŽŰșيل"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"ŰčŰŻÙ… Űč۱۶ Ù…Ű±ŰšÙ‘Űč Ű­ÙˆŰ§Ű± Ű§Ù„ŰȘŰŁÙƒÙŠŰŻ Ù…ŰŹŰŻŰŻÙ‹Ű§"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"ŰȘÙƒŰšÙŠŰ±"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"ŰȘŰ”ŰșÙŠŰ±"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Ű„ŰșÙ„Ű§Ù‚"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Ű±ŰŹÙˆŰč"</string>
     <string name="handle_text" msgid="1766582106752184456">"Ù…Ù‚ŰšŰ¶"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Ù…Ù„ŰĄ Ű§Ù„ŰŽŰ§ŰŽŰ©"</string>
     <string name="desktop_text" msgid="1077633567027630454">"ÙˆŰ¶Űč ۳۷ۭ Ű§Ù„Ù…ÙƒŰȘŰš"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"ŰȘÙ‚ŰłÙŠÙ… Ű§Ù„ŰŽŰ§ŰŽŰ©"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Ű§Ù„Ù…ŰČÙŠŰŻ"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Ù†Ű§ÙŰ°Ű© ŰčŰ§ŰŠÙ…Ű©"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index a0213f4..c885197 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"àŠ¶à§€àŠ°à§àŠ· àŠžà§àŠ•à§à§°à§€àŠšàŠ–àŠš à§«à§Š% àŠ•à§°àŠ•"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"àŠ¶à§€àŠ°à§àŠ· àŠžà§àŠ•à§à§°à§€àŠšàŠ–àŠš à§©à§Š% àŠ•à§°àŠ•"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"àŠ€àŠČà§° àŠžà§àŠ•à§à§°à§€àŠšàŠ–àŠš àŠžàŠźà§àŠȘà§‚à§°à§àŠŁ àŠžà§àŠ•à§à§°à§€àŠš àŠ•à§°àŠ•"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"àŠŹàŠŸàŠ“àŠàŠ«àŠŸàŠČে àŠŹàŠżàŠ­àŠŸàŠœàŠš àŠ•à§°àŠ•"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"àŠžà§‹àŠàŠ«àŠŸàŠČে àŠŹàŠżàŠ­àŠŸàŠœàŠš àŠ•à§°àŠ•"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"àŠàŠ•à§‡àŠŹàŠŸà§°à§‡ àŠ“àŠȘà§°à§°àŠ«àŠŸàŠČে àŠŹàŠżàŠ­àŠŸàŠœàŠš àŠ•à§°àŠ•"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"àŠàŠ•à§‡àŠŹàŠŸà§°à§‡ àŠ€àŠČà§°àŠ«àŠŸàŠČে àŠŹàŠżàŠ­àŠŸàŠœàŠš àŠ•à§°àŠ•"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"àŠàŠ–àŠš àŠčàŠŸàŠ€à§‡à§°à§‡ àŠŹà§àŠŻà§±àŠčàŠŸà§° àŠ•à§°àŠŸ àŠź’àŠĄ àŠŹà§àŠŻà§±àŠčàŠŸà§° àŠ•à§°àŠŸ"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"àŠŹàŠŸàŠčàŠżà§° àŠč’àŠŹàŠČৈ àŠžà§àŠ•à§à§°à§€àŠšàŠ–àŠšà§° àŠàŠ•à§‡àŠŹàŠŸà§°à§‡ àŠ€àŠČà§° àŠȘà§°àŠŸ àŠ“àŠȘà§°àŠČৈ àŠ›à§‹à§±àŠŸàŠ‡àŠȘ àŠ•à§°àŠ• àŠ…àŠ„àŠŹàŠŸ àŠàŠȘ্‌àŠŸà§‹à§° àŠ“àŠȘà§°àŠ€ àŠŻàŠżàŠ•à§‹àŠšà§‹ àŠ àŠŸàŠ‡àŠ€ àŠŸàŠżàŠȘàŠ•"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"àŠàŠ–àŠš àŠčàŠŸàŠ€à§‡à§°à§‡ àŠŹà§àŠŻà§±àŠčàŠŸà§° àŠ•à§°àŠŸ àŠź\'àŠĄàŠŸà§‹ àŠ†à§°àŠźà§àŠ­ àŠ•à§°àŠ•"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"àŠàŠȘ্‌àŠŸà§‹à§° àŠžà§àŠ„àŠŸàŠš àŠžàŠČàŠšàŠż àŠ•à§°àŠżàŠŹàŠČৈ àŠ‡àŠŻàŠŒàŠŸà§° àŠŹàŠŸàŠčàŠżà§°àŠ€ àŠŠà§àŠŹàŠŸà§° àŠŸàŠżàŠȘàŠ•"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"àŠŹà§àŠœàŠż àŠȘàŠŸàŠČà§‹àŠ"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"àŠ…àŠ§àŠżàŠ• àŠ€àŠ„à§àŠŻà§° àŠŹàŠŸàŠŹà§‡ àŠŹàŠżàŠžà§àŠ€àŠŸà§° àŠ•à§°àŠ•à„€"</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"àŠ‰àŠšà§àŠšàŠ€àŠ­àŠŸà§±à§‡ àŠŠà§‡àŠ–àŠŸ àŠȘàŠŸàŠŹàŠČৈ à§°àŠżàŠ·à§àŠŸàŠŸà§°à§àŠŸ àŠ•à§°àŠżàŠŹàŠšà§‡?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"àŠ†àŠȘà§‹àŠšàŠŸà§° àŠžà§àŠ•à§à§°à§€àŠšàŠ€ àŠ‰àŠšà§àŠšàŠ€àŠ­àŠŸà§±à§‡ àŠŠà§‡àŠ–àŠŸ àŠȘàŠŸàŠŹàŠČৈ àŠ†àŠȘà§àŠšàŠż àŠàŠȘ্‌àŠŸà§‹ à§°àŠżàŠ·à§àŠŸàŠŸà§°à§àŠŸ àŠ•à§°àŠżàŠŹ àŠȘàŠŸà§°à§‡, àŠ•àŠżàŠšà§àŠ€à§ àŠ†àŠȘà§àŠšàŠż àŠ†àŠȘà§‹àŠšàŠŸà§° àŠ…àŠ—à§à§°àŠ—àŠ€àŠż àŠ…àŠ„àŠŹàŠŸ àŠ›à§‡àŠ­ àŠšàŠ•à§°àŠŸ àŠŻàŠżàŠ•à§‹àŠšà§‹ àŠžàŠŸàŠČàŠžàŠČàŠšàŠż àŠčà§‡à§°à§à§±àŠŸàŠŹ àŠȘàŠŸà§°à§‡"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"àŠŹàŠŸàŠ€àŠżàŠČ àŠ•à§°àŠ•"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"à§°àŠżàŠ·à§àŠŸàŠŸà§°à§àŠŸ àŠ•à§°àŠ•"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"àŠȘà§àŠšà§°àŠŸàŠ‡ àŠšà§‡àŠŠà§‡àŠ–à§à§±àŠŸàŠŹ"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"àŠžà§°à§àŠŹàŠŸàŠ§àŠżàŠ• àŠźàŠŸàŠ€à§à§°àŠŸàŠČৈ àŠŹàŠąàŠŒàŠŸàŠ“àŠ•"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"àŠźàŠżàŠšàŠżàŠźàŠŸàŠ‡àŠœ àŠ•à§°àŠ•"</string>
     <string name="close_button_text" msgid="2913281996024033299">"àŠŹàŠšà§àŠ§ àŠ•à§°àŠ•"</string>
     <string name="back_button_text" msgid="1469718707134137085">"àŠ‰àŠ­àŠ€àŠż àŠŻàŠŸàŠ“àŠ•"</string>
     <string name="handle_text" msgid="1766582106752184456">"àŠčà§‡àŠŁà§àŠĄà§‡àŠČ"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"àŠžàŠźà§àŠȘà§‚à§°à§àŠŁ àŠžà§àŠ•à§à§°à§€àŠš"</string>
     <string name="desktop_text" msgid="1077633567027630454">"àŠĄà§‡àŠžà§àŠ•àŠŸàŠȘ àŠź’àŠĄ"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"àŠŹàŠżàŠ­àŠŸàŠœàŠżàŠ€ àŠžà§àŠ•à§à§°à§€àŠš"</string>
     <string name="more_button_text" msgid="3655388105592893530">"àŠ…àŠ§àŠżàŠ•"</string>
     <string name="float_button_text" msgid="9221657008391364581">"àŠ“àŠȘàŠ™àŠŸ"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index f842bfe..4427fa9 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Yuxarı 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Yuxarı 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Aßağı tam ekran"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Sola ayırın"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Sağa ayırın"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Yuxarı ayırın"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Aßağı ayırın"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Birəlli rejim istifadəsi"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Çıxmaq üçün ekranın aßağısından yuxarıya doğru sürüßdürün və ya tətbiqin yuxarısında istənilən yerə toxunun"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Birəlli rejim baßlasın"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Tətbiqin yerini dəyißmək üçün kənarına iki dəfə toxunun"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Anladım"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Ətraflı məlumat üçün genißləndirin."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Daha yaxßı görünüß üçün yenidən baßladılsın?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Tətbiqi yenidən baßlada bilərsiniz ki, ekranınızda daha yaxßı görünsün, lakin irəliləyißi və ya yadda saxlanmamıß dəyißiklikləri itirə bilərsiniz"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Ləğv edin"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Yenidən baßladın"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Yenidən göstərməyin"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Böyüdün"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Kiçildin"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Bağlayın"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Geriyə"</string>
     <string name="handle_text" msgid="1766582106752184456">"Hər kəsə açıq istifadəçi adı"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Tam Ekran"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Masaüstü Rejimi"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Bölünmüß Ekran"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Ardı"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Üzən pəncərə"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
index 540ae7c..a67ba39 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Gornji ekran 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Gornji ekran 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"ReĆŸim celog ekrana za donji ekran"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Podelite levo"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Podelite desno"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Podelite u vrhu"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Podelite u dnu"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"KorišÄ‡enje reĆŸima jednom rukom"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Da biste izašli, prevucite nagore od dna ekrana ili dodirnite bilo gde iznad aplikacije"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Pokrenite reĆŸim jednom rukom"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dvaput dodirnite izvan aplikacije da biste promenili njenu poziciju"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"VaĆŸi"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Proširite za još informacija."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Ćœelite li da restartujete radi boljeg prikaza?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"MoĆŸete da restartujete aplikaciju da bi izgledala bolje na ekranu, s tim što moĆŸete da izgubite ono što ste uradili ili nesačuvane promene, ako ih ima"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"OtkaĆŸi"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Restartuj"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ne prikazuj ponovo"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Uvećajte"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Umanjite"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Zatvorite"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Nazad"</string>
     <string name="handle_text" msgid="1766582106752184456">"Identifikator"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Preko celog ekrana"</string>
     <string name="desktop_text" msgid="1077633567027630454">"ReĆŸim za računare"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Podeljeni ekran"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Još"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Plutajuće"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index bea7538..5adb2ec 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Đ’Đ”Ń€Ń…ĐœŃ– эĐșŃ€Đ°Đœ – 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Đ’Đ”Ń€Ń…ĐœŃ– эĐșŃ€Đ°Đœ – 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"ĐŃ–Đ¶ĐœŃ– эĐșŃ€Đ°Đœ – ĐżĐŸŃžĐœĐ°ŃĐșŃ€Đ°ĐœĐœŃ‹ Ń€ŃĐ¶Ń‹ĐŒ"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"ĐŸĐ°ĐŽĐ·ŃĐ»Ń–Ń†ŃŒ злДĐČа"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"ĐŸĐ°ĐŽĐ·ŃĐ»Ń–Ń†ŃŒ спраĐČа"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"ĐŸĐ°ĐŽĐ·ŃĐ»Ń–Ń†ŃŒ уĐČДрсД"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"ĐŸĐ°ĐŽĐ·ŃĐ»Ń–Ń†ŃŒ ŃƒĐœŃ–Đ·Đ”"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"ВыĐșĐ°Ń€Ń‹ŃŃ‚ĐŸŃžĐČаДцца Ń€ŃĐ¶Ń‹ĐŒ ĐșіраĐČĐ°ĐœĐœŃ Đ°ĐŽĐœĐŸĐč руĐșĐŸĐč"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Каб ĐČыĐčсці, праĐČŃĐŽĐ·Ń–Ń†Đ” па эĐșŃ€Đ°ĐœĐ” ĐżĐ°Đ»ŃŒŃ†Đ°ĐŒ Đ·ĐœŃ–Đ·Ńƒ ўĐČДрх Đ°Đ±ĐŸ ĐœĐ°Ń†Ń–ŃĐœŃ–Ń†Đ” ў Đ»ŃŽĐ±Ń‹ĐŒ ĐŒĐ”ŃŃ†Ń‹ ĐœĐ°ĐŽ ĐżŃ€Đ°ĐłŃ€Đ°ĐŒĐ°Đč"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Запусціць Ń€ŃĐ¶Ń‹ĐŒ ĐșіраĐČĐ°ĐœĐœŃ Đ°ĐŽĐœĐŸĐč руĐșĐŸĐč"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ДĐČĐŸĐčчы ĐœĐ°Ń†Ń–ŃĐœŃ–Ń†Đ” эĐșŃ€Đ°Đœ па-за ĐżŃ€Đ°ĐłŃ€Đ°ĐŒĐ°Đč, Đșаб ĐżĐ”Ń€Đ°ĐŒŃŃŃ†Ń–Ń†ŃŒ ŃĐ”"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Đ—Ń€Đ°Đ·ŃƒĐŒĐ”Đ»Đ°"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Đ Đ°Đ·ĐłĐ°Ń€ĐœŃƒŃ†ŃŒ ĐŽĐ»Ń ЮаЮатĐșĐŸĐČаĐč Ń–ĐœŃ„Đ°Ń€ĐŒĐ°Ń†Ń‹Ń–"</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"ĐŸĐ”Ń€Đ°Đ·Đ°ĐżŃƒŃŃ†Ń–Ń†ŃŒ?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Вы ĐŒĐŸĐ¶Đ°Ń†Đ” ĐżĐ”Ń€Đ°Đ·Đ°ĐżŃƒŃŃ†Ń–Ń†ŃŒ ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃƒ, Đșаб ŃĐœĐ° ĐČŃ‹ĐłĐ»ŃĐŽĐ°Đ»Đ° лДпш ĐœĐ° ĐČĐ°ŃˆŃ‹ĐŒ эĐșŃ€Đ°ĐœĐ”, алД пры ĐłŃŃ‚Ń‹ĐŒ ĐŒĐŸĐłŃƒŃ†ŃŒ Đ·ĐœŃ–ĐșĐœŃƒŃ†ŃŒ ĐŽĐ°ĐœŃ‹Ń пра ĐČаш прагрэс Đ°Đ±ĐŸ ĐœĐ”Đ·Đ°Ń…Đ°ĐČĐ°ĐœŃ‹Ń Đ·ĐŒŃĐœĐ”ĐœĐœŃ–"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"ĐĄĐșасаĐČаць"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"ĐŸĐ”Ń€Đ°Đ·Đ°ĐżŃƒŃŃ†Ń–Ń†ŃŒ"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Đ‘ĐŸĐ»ŃŒŃˆ ĐœĐ” паĐșазĐČаць"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Đ Đ°Đ·ĐłĐ°Ń€ĐœŃƒŃ†ŃŒ"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Đ—ĐłĐ°Ń€ĐœŃƒŃ†ŃŒ"</string>
     <string name="close_button_text" msgid="2913281996024033299">"ЗаĐșрыць"</string>
     <string name="back_button_text" msgid="1469718707134137085">"ĐĐ°Đ·Đ°ĐŽ"</string>
     <string name="handle_text" msgid="1766582106752184456">"МарĐșДр"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"На ўĐČĐ”ŃŃŒ эĐșŃ€Đ°Đœ"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Đ ŃĐ¶Ń‹ĐŒ ĐżŃ€Đ°Ń†ĐŸŃžĐœĐ°ĐłĐ° стала"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"ĐŸĐ°ĐŽĐ·ŃĐ»Ń–Ń†ŃŒ эĐșŃ€Đ°Đœ"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Яшчэ"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Đ—Ń€Đ°Đ±Ń–Ń†ŃŒ Ń€ŃƒŃ…ĐŸĐŒŃ‹ĐŒ аĐșĐœĐŸĐŒ"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index 59915e6..8c82d1a 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Đ“ĐŸŃ€Đ”Đœ Đ”ĐșŃ€Đ°Đœ: 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Đ“ĐŸŃ€Đ”Đœ Đ”ĐșŃ€Đ°Đœ: 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Đ”ĐŸĐ»Đ”Đœ Đ”ĐșŃ€Đ°Đœ: ĐŸĐŸĐșазĐČĐ°ĐœĐ” ĐœĐ° Ń†ŃĐ» Đ”ĐșŃ€Đ°Đœ"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Đ Đ°Đ·ĐŽĐ”Đ»ŃĐœĐ” ĐČ Đ»ŃĐČата част"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Đ Đ°Đ·ĐŽĐ”Đ»ŃĐœĐ” ĐČ ĐŽŃŃĐœĐ°Ń‚Đ° част"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Đ Đ°Đ·ĐŽĐ”Đ»ŃĐœĐ” ĐČ ĐłĐŸŃ€ĐœĐ°Ń‚Đ° част"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Đ Đ°Đ·ĐŽĐ”Đ»ŃĐœĐ” ĐČ ĐŽĐŸĐ»ĐœĐ°Ń‚Đ° част"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Đ˜Đ·ĐżĐŸĐ»Đ·ĐČĐ°ĐœĐ” ĐœĐ° Ń€Đ”Đ¶ĐžĐŒĐ° за Ń€Đ°Đ±ĐŸŃ‚Đ° с Đ”ĐŽĐœĐ° ръĐșа"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"За ĐžĐ·Ń…ĐŸĐŽ прДĐșараĐčтД пръст ĐœĐ°ĐłĐŸŃ€Đ” ĐŸŃ‚ ĐŽĐŸĐ»ĐœĐ°Ń‚Đ° част ĐœĐ° Đ”ĐșŃ€Đ°ĐœĐ° ОлО ĐŽĐŸĐșĐŸŃĐœĐ”Ń‚Đ” ĐżŃ€ĐŸĐžĐ·ĐČĐŸĐ»ĐœĐŸ ĐŒŃŃŃ‚ĐŸ ĐœĐ°ĐŽ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ”Ń‚ĐŸ"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"ĐĄŃ‚Đ°Ń€Ń‚ĐžŃ€Đ°ĐœĐ” ĐœĐ° Ń€Đ”Đ¶ĐžĐŒĐ° за Ń€Đ°Đ±ĐŸŃ‚Đ° с Đ”ĐŽĐœĐ° ръĐșа"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Đ”ĐŸĐșĐŸŃĐœĐ”Ń‚Đ” ĐŽĐČа пъто ОзĐČŃŠĐœ ĐŽĐ°ĐŽĐ”ĐœĐŸ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ”, за Ўа ĐżŃ€ĐŸĐŒĐ”ĐœĐžŃ‚Đ” ĐżĐŸĐ·ĐžŃ†ĐžŃŃ‚Đ° ĐŒŃƒ"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Разбрах"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"РазгъĐČĐ°ĐœĐ” за ĐŸŃ‰Đ” ĐžĐœŃ„ĐŸŃ€ĐŒĐ°Ń†ĐžŃ."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Да сД рДстартОра лО с цДл ĐżĐŸĐŽĐŸĐ±Ń€ŃĐČĐ°ĐœĐ” ĐœĐ° ОзглДЎа?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"ĐœĐŸĐ¶Đ”Ń‚Đ” Ўа рДстартОратД ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ”Ń‚ĐŸ, за Ўа ОзглДжЎа ĐżĐŸ-ĐŽĐŸĐ±Ń€Đ” ĐœĐ° Đ”ĐșŃ€Đ°ĐœĐ°. Đ’ŃŠĐ·ĐŒĐŸĐ¶ĐœĐŸ Đ” ĐŸĐ±Đ°Ń‡Đ” Ўа Đ·Đ°ĐłŃƒĐ±ĐžŃ‚Đ” ĐœĐ°ĐżŃ€Đ”ĐŽŃŠĐșа сО ОлО ĐœĐ”Đ·Đ°ĐżĐ°Đ·Đ”ĐœĐžŃ‚Đ” ĐżŃ€ĐŸĐŒĐ”ĐœĐž"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"ОтĐșаз"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Đ Đ”ŃŃ‚Đ°Ń€Ń‚ĐžŃ€Đ°ĐœĐ”"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Да ĐœĐ” сД ĐżĐŸĐșазĐČа ĐŸŃ‚ĐœĐŸĐČĐŸ"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"ĐŁĐČДлОчаĐČĐ°ĐœĐ”"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"ĐĐ°ĐŒĐ°Đ»ŃĐČĐ°ĐœĐ”"</string>
     <string name="close_button_text" msgid="2913281996024033299">"ЗатĐČĐ°Ń€ŃĐœĐ”"</string>
     <string name="back_button_text" msgid="1469718707134137085">"ĐĐ°Đ·Đ°ĐŽ"</string>
     <string name="handle_text" msgid="1766582106752184456">"ĐœĐ°ĐœĐžĐżŃƒĐ»Đ°Ń‚ĐŸŃ€"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"ĐŠŃĐ» Đ”ĐșŃ€Đ°Đœ"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Đ Đ”Đ¶ĐžĐŒ за ĐœĐ°ŃŃ‚ĐŸĐ»ĐœĐž ĐșĐŸĐŒĐżŃŽŃ‚Ń€Đž"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Đ Đ°Đ·ĐŽĐ”Đ»ŃĐœĐ” ĐœĐ° Đ”ĐșŃ€Đ°ĐœĐ°"</string>
     <string name="more_button_text" msgid="3655388105592893530">"ОщД"</string>
     <string name="float_button_text" msgid="9221657008391364581">"ĐŸĐ»Đ°ĐČĐ°Ń‰ĐŸ"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index 63c9684..11a4175 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -47,6 +47,14 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"àŠ¶à§€àŠ°à§àŠ· à§«à§Š%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"àŠ¶à§€àŠ°à§àŠ· à§©à§Š%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"àŠšà§€àŠšà§‡àŠ° àŠ…àŠ‚àŠ¶ àŠšàŠżàŠŻàŠŒà§‡ àŠȘà§‚àŠ°à§àŠŁ àŠžà§àŠ•à§àŠ°àŠżàŠš"</string>
+    <!-- no translation found for accessibility_split_left (1713683765575562458) -->
+    <skip />
+    <!-- no translation found for accessibility_split_right (8441001008181296837) -->
+    <skip />
+    <!-- no translation found for accessibility_split_top (2789329702027147146) -->
+    <skip />
+    <!-- no translation found for accessibility_split_bottom (8694551025220868191) -->
+    <skip />
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"\'àŠàŠ• àŠčàŠŸàŠ€à§‡ àŠŹà§àŠŻàŠŹàŠčàŠŸàŠ° àŠ•àŠ°àŠŸàŠ° àŠźà§‹àŠĄ\'-àŠàŠ° àŠŹà§àŠŻàŠŹàŠčàŠŸàŠ°"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"àŠŹà§‡àŠ°àŠżàŠŻàŠŒà§‡ àŠ†àŠžàŠŸàŠ° àŠœàŠšà§àŠŻ, àŠžà§àŠ•à§àŠ°àŠżàŠšà§‡àŠ° àŠšàŠżàŠš àŠ„à§‡àŠ•à§‡ àŠ‰àŠȘàŠ°à§‡àŠ° àŠŠàŠżàŠ•à§‡ àŠžà§‹àŠŻàŠŒàŠŸàŠ‡àŠȘ àŠ•àŠ°à§àŠš àŠ…àŠ„àŠŹàŠŸ àŠ…à§àŠŻàŠŸàŠȘ àŠ†àŠ‡àŠ•àŠšà§‡àŠ° àŠ‰àŠȘàŠ°à§‡ àŠŻà§‡àŠ•à§‹àŠšàŠ“ àŠœàŠŸàŠŻàŠŒàŠ—àŠŸàŠŻàŠŒ àŠŸà§àŠŻàŠŸàŠȘ àŠ•àŠ°à§àŠš"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"\'àŠàŠ• àŠčàŠŸàŠ€à§‡ àŠŹà§àŠŻàŠŹàŠčàŠŸàŠ° àŠ•àŠ°àŠŸàŠ° àŠźà§‹àŠĄ\' àŠ¶à§àŠ°à§ àŠ•àŠ°à§àŠš"</string>
@@ -82,14 +90,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"àŠ•à§‹àŠšàŠ“ àŠ…à§àŠŻàŠŸàŠȘà§‡àŠ° àŠžà§àŠ„àŠŸàŠš àŠȘàŠ°àŠżàŠŹàŠ°à§àŠ€àŠš àŠ•àŠ°àŠ€à§‡ àŠ€àŠŸàŠ° àŠŹàŠŸàŠ‡àŠ°à§‡ àŠĄàŠŹàŠČ àŠŸà§àŠŻàŠŸàŠȘ àŠ•àŠ°à§àŠš"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"àŠŹà§àŠà§‡àŠ›àŠż"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"àŠ†àŠ°àŠ“ àŠ€àŠ„à§àŠŻà§‡àŠ° àŠœàŠšà§àŠŻ àŠŹàŠĄàŠŒ àŠ•àŠ°à§àŠšà„€"</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"àŠ†àŠ°àŠ“ àŠ­àŠŸàŠČàŠ­àŠŸàŠŹà§‡ àŠŠà§‡àŠ–àŠŸàŠ° àŠœàŠšà§àŠŻ àŠ°àŠżàŠžà§àŠŸàŠŸàŠ°à§àŠŸ àŠ•àŠ°àŠŹà§‡àŠš?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"àŠžà§àŠ•à§àŠ°àŠżàŠšà§‡ àŠ†àŠ°àŠ“ àŠ­àŠŸàŠČàŠ­àŠŸàŠŹà§‡ àŠŠà§‡àŠ–àŠŸàŠ° àŠœàŠšà§àŠŻ àŠ†àŠȘàŠšàŠż àŠ…à§àŠŻàŠŸàŠȘ àŠ°àŠżàŠžà§àŠŸàŠŸàŠ°à§àŠŸ àŠ•àŠ°àŠ€à§‡ àŠȘàŠŸàŠ°àŠŹà§‡àŠš, àŠàŠ° àŠ«àŠČে àŠšàŠČàŠ€à§‡ àŠ„àŠŸàŠ•àŠŸ àŠ•à§‹àŠšàŠ“ àŠȘà§àŠ°àŠ•à§àŠ°àŠżàŠŻàŠŒàŠŸ àŠŹàŠŸ àŠžà§‡àŠ­ àŠšàŠŸ àŠ•àŠ°àŠŸ àŠȘàŠ°àŠżàŠŹàŠ°à§àŠ€àŠš àŠčàŠŸàŠ°àŠżàŠŻàŠŒà§‡ àŠŻà§‡àŠ€à§‡ àŠȘàŠŸàŠ°à§‡"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"àŠŹàŠŸàŠ€àŠżàŠČ àŠ•àŠ°à§àŠš"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"àŠ°àŠżàŠžà§àŠŸàŠŸàŠ°à§àŠŸ àŠ•àŠ°à§àŠš"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"àŠ†àŠ° àŠŠà§‡àŠ–àŠ€à§‡ àŠšàŠŸàŠ‡ àŠšàŠŸ"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"àŠŹàŠĄàŠŒ àŠ•àŠ°à§àŠš"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"àŠ›à§‹àŠŸ àŠ•àŠ°à§àŠš"</string>
     <string name="close_button_text" msgid="2913281996024033299">"àŠŹàŠšà§àŠ§ àŠ•àŠ°à§àŠš"</string>
     <string name="back_button_text" msgid="1469718707134137085">"àŠ«àŠżàŠ°à§‡ àŠŻàŠŸàŠš"</string>
     <string name="handle_text" msgid="1766582106752184456">"àŠčàŠŸàŠ€àŠČ"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"àŠ«à§àŠČàŠžà§àŠ•à§àŠ°àŠżàŠš"</string>
     <string name="desktop_text" msgid="1077633567027630454">"àŠĄà§‡àŠžà§àŠ•àŠŸàŠȘ àŠźà§‹àŠĄ"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"àŠžà§àŠȘ্àŠČàŠżàŠŸ àŠžà§àŠ•à§àŠ°àŠżàŠš"</string>
     <string name="more_button_text" msgid="3655388105592893530">"àŠ†àŠ°àŠ“"</string>
     <string name="float_button_text" msgid="9221657008391364581">"àŠ«à§àŠČà§‹àŠŸ"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index b725efe..c65ce08 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Gore 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Gore 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Donji ekran kao cijeli ekran"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Podjela ulijevo"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Podjela udesno"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Podjela nagore"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Podjela nadolje"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Korištenje načina rada jednom rukom"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Da izađete, prevucite s dna ekrana prema gore ili dodirnite bilo gdje iznad aplikacije"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Započinjanje načina rada jednom rukom"</string>
@@ -82,14 +86,24 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dvaput dodirnite izvan aplikacije da promijenite njen poloĆŸaj"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Razumijem"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Proširite za više informacija."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Ponovo pokrenuti za bolji prikaz?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"MoĆŸete ponovo pokrenuti aplikaciju da bolje izgleda na ekranu, ali moĆŸete izgubiti napredak ili izmjene koje nisu sačuvane"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"OtkaĆŸi"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Ponovo pokreni"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ne prikazuj ponovo"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Maksimiziranje"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Minimiziranje"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Zatvaranje"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Nazad"</string>
     <string name="handle_text" msgid="1766582106752184456">"Identifikator"</string>
+    <string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikacije"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Cijeli ekran"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Način rada radne površine"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Podijeljeni ekran"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Više"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Lebdeći"</string>
+    <string name="select_text" msgid="5139083974039906583">"Odaberite"</string>
+    <string name="screenshot_text" msgid="1477704010087786671">"Snimka zaslona"</string>
+    <string name="close_text" msgid="4986518933445178928">"Zatvorite"</string>
+    <string name="collapse_menu_text" msgid="7515008122450342029">"Zatvorite izbornik"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index 4383916..f50b8f2 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -22,7 +22,7 @@
     <string name="pip_phone_settings" msgid="5468987116750491918">"Configuració"</string>
     <string name="pip_phone_enter_split" msgid="7042877263880641911">"Entra al mode de pantalla dividida"</string>
     <string name="pip_menu_title" msgid="5393619322111827096">"Menú"</string>
-    <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> està en pantalla en pantalla"</string>
+    <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> està en mode d\'imatge sobre imatge"</string>
     <string name="pip_notification_message" msgid="8854051911700302620">"Si no vols que <xliff:g id="NAME">%s</xliff:g> utilitzi aquesta funció, toca per obrir la configuració i desactiva-la."</string>
     <string name="pip_play" msgid="3496151081459417097">"Reprodueix"</string>
     <string name="pip_pause" msgid="690688849510295232">"Posa en pausa"</string>
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Pantalla superior al 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Pantalla superior al 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Pantalla inferior completa"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Divideix a l\'esquerra"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Divideix a la dreta"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Divideix a la part superior"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Divideix a la part inferior"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"S\'està utilitzant el mode d\'una mà"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Per sortir, llisca cap amunt des de la part inferior de la pantalla o toca qualsevol lloc a sobre de l\'aplicació"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Inicia el mode d\'una mà"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Fes doble toc fora d\'una aplicació per canviar-ne la posició"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Entesos"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Desplega per obtenir més informació."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Vols reiniciar per a una millor visualització?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Pots reiniciar l\'aplicació perquè es vegi millor en pantalla, però és possible que perdis el teu progrés o qualsevol canvi que no hagis desat"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Cancel·la"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Reinicia"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"No ho tornis a mostrar"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Maximitza"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Minimitza"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Tanca"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Enrere"</string>
     <string name="handle_text" msgid="1766582106752184456">"Ansa"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Pantalla completa"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Mode d\'escriptori"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Pantalla dividida"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Més"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Flotant"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings_tv.xml b/libs/WindowManager/Shell/res/values-ca/strings_tv.xml
index 94ba0db..daa8c1d 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings_tv.xml
@@ -17,7 +17,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_channel_tv_pip" msgid="2576686079160402435">"Pantalla en pantalla"</string>
+    <string name="notification_channel_tv_pip" msgid="2576686079160402435">"Imatge sobre imatge"</string>
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programa sense títol)"</string>
     <string name="pip_close" msgid="2955969519031223530">"Tanca"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
@@ -25,7 +25,7 @@
     <string name="pip_expand" msgid="1051966011679297308">"Desplega"</string>
     <string name="pip_collapse" msgid="3903295106641385962">"Replega"</string>
     <string name="pip_edu_text" msgid="3672999496647508701">" Prem dos cops "<annotation icon="home_icon">" INICI "</annotation>" per accedir als controls"</string>
-    <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menú de pantalla en pantalla."</string>
+    <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menú d\'imatge sobre imatge."</string>
     <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mou cap a l\'esquerra"</string>
     <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Mou cap a la dreta"</string>
     <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Mou cap amunt"</string>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index e5cb26f..ac36edbb 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50 % nahoƙe"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"30 % nahoƙe"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Dolní část na celou obrazovku"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Rozdělit vlevo"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Rozdělit vpravo"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Rozdělit nahoƙe"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Rozdělit dole"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"PouĆŸívání reĆŸimu jedné ruky"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"ReĆŸim ukončíte, kdyĆŸ pƙejedete prstem z dolní části obrazovky nahoru nebo klepnete kamkoli nad aplikaci"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Spustit reĆŸim jedné ruky"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dvojitým klepnutím mimo aplikaci změníte její umístění"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Rozbalením zobrazíte další informace."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Restartovat pro lepší zobrazení?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Aplikaci mĆŻĆŸete restartovat, aby na obrazovce vypadala lépe, ale mĆŻĆŸete pƙijít o svĆŻj postup nebo o neuloĆŸené změny"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Zrušit"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Restartovat"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Tuto zprávu pƙíště nezobrazovat"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Maximalizovat"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Minimalizovat"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Zavƙít"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Zpět"</string>
     <string name="handle_text" msgid="1766582106752184456">"Úchyt"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Celá obrazovka"</string>
     <string name="desktop_text" msgid="1077633567027630454">"ReĆŸim počítače"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Rozdělená obrazovka"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Více"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Plovoucí"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index 46f7c69..8635cc5 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Øverste 50 %"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Øverste 30 %"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Vis nederste del i fuld skærm"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Vis i venstre side"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Vis i højre side"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Vis øverst"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Vis nederst"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Brug af enhåndstilstand"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Du kan afslutte ved at stryge opad fra bunden af skærmen eller trykke et vilkårligt sted over appen"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Start enhåndstilstand"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Tryk to gange uden for en app for at justere dens placering"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Udvid for at få flere oplysninger."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Vil du genstarte for at få en bedre visning?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Du kan genstarte appen, så den ser bedre ud på din skærm, men du mister muligvis dine fremskridt og de ændringer, der ikke gemt"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Annuller"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Genstart"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Vis ikke igen"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Maksimér"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Minimer"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Luk"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Tilbage"</string>
     <string name="handle_text" msgid="1766582106752184456">"Håndtag"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Fuld skærm"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Computertilstand"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Opdelt skærm"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Mere"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Svævende"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index 1269d36..059a4da 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50 % oben"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"30 % oben"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Vollbild unten"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Links teilen"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Rechts teilen"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Oben teilen"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Unten teilen"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Einhandmodus wird verwendet"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Wenn du die App schließen möchtest, wische vom unteren Rand des Displays nach oben oder tippe auf eine beliebige Stelle oberhalb der App"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Einhandmodus starten"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Außerhalb einer App doppeltippen, um die Position zu ändern"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Ok"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Für weitere Informationen maximieren."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Für bessere Darstellung neu starten?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Du kannst die App neu starten, damit sie an die Bildschirmabmessungen deines Geräts angepasst dargestellt wird – jedoch können dadurch dein Fortschritt oder nicht gespeicherte Änderungen verloren gehen."</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Abbrechen"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Neu starten"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Nicht mehr anzeigen"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Maximieren"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Minimieren"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Schließen"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Zurück"</string>
     <string name="handle_text" msgid="1766582106752184456">"Ziehpunkt"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Vollbild"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Desktopmodus"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Geteilter Bildschirm"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Mehr"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Frei schwebend"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index f8a69ef..5800cc8 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ΠÎŹνω 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"ΠÎŹνω 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"ΚÎŹτω πλÎźρης οθόνη"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Διαχωρισμός αριστερÎŹ"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Διαχωρισμός δεξιÎŹ"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Διαχωρισμός επÎŹνω"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Διαχωρισμός κÎŹτω"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"ΧρÎźση λειτουργÎŻας ενός χεριού"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Για έξοδο, σύρετε προς τα πÎŹνω από το κÎŹτω μέρος της οθόνης Îź πατÎźστε οπουδÎźποτε πÎŹνω από την εφαρμογÎź."</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Έναρξη λειτουργÎŻας ενός χεριού"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ΠατÎźστε δύο φορές έξω από μια εφαρμογÎź για να αλλÎŹξετε τη θέση της"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Το κατÎŹλαβα"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ΑνÎŹπτυξη για περισσότερες πληροφορÎŻες."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"ΕπανεκκÎŻνηση για καλύτερη προβολÎź;"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"ΜπορεÎŻτε να επανεκκινÎźσετε την εφαρμογÎź για να προβÎŹλλεται καλύτερα στην οθόνη σας, αλλÎŹ η πρόοδός σας και τυχόν μη αποθηκευμένες αλλαγές ενδέχεται να χαθούν."</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Ακύρωση"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"ΕπανεκκÎŻνηση"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Να μην εμφανιστεÎŻ ξανÎŹ"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"ΜεγιστοποÎŻηση"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"ΕλαχιστοποÎŻηση"</string>
     <string name="close_button_text" msgid="2913281996024033299">"ΚλεÎŻσιμο"</string>
     <string name="back_button_text" msgid="1469718707134137085">"ΠÎŻσω"</string>
     <string name="handle_text" msgid="1766582106752184456">"ΛαβÎź"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"ΠλÎźρης οθόνη"</string>
     <string name="desktop_text" msgid="1077633567027630454">"ΛειτουργÎŻα επιφÎŹνειας εργασÎŻας"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Διαχωρισμός οθόνης"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Περισσότερα"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Κινούμενο"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index 8e46c3e..4d18f5e 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Top 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Top 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Bottom full screen"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Split left"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Split right"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Split top"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Split bottom"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Using one-handed mode"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"To exit, swipe up from the bottom of the screen or tap anywhere above the app"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Start one-handed mode"</string>
@@ -82,14 +86,24 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Double-tap outside an app to reposition it"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Got it"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Expand for more information."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Restart for a better view?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"You can restart the app so that it looks better on your screen, but you may lose your progress or any unsaved changes"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Cancel"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Restart"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Don\'t show again"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Maximise"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Minimise"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Close"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Back"</string>
     <string name="handle_text" msgid="1766582106752184456">"Handle"</string>
+    <string name="app_icon_text" msgid="2823268023931811747">"App icon"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Full screen"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Desktop mode"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Split screen"</string>
     <string name="more_button_text" msgid="3655388105592893530">"More"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Float"</string>
+    <string name="select_text" msgid="5139083974039906583">"Select"</string>
+    <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
+    <string name="close_text" msgid="4986518933445178928">"Close"</string>
+    <string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
index 7cbbf64..40b9d95 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Top 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Top 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Bottom full screen"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Split left"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Split right"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Split top"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Split bottom"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Using one-handed mode"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"To exit, swipe up from the bottom of the screen or tap anywhere above the app"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Start one-handed mode"</string>
@@ -82,14 +86,24 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Double-tap outside an app to reposition it"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Got it"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Expand for more information."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Restart for a better view?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"You can restart the app so it looks better on your screen, but you may lose your progress or any unsaved changes"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Cancel"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Restart"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Don’t show again"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Maximize"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Minimize"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Close"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Back"</string>
     <string name="handle_text" msgid="1766582106752184456">"Handle"</string>
+    <string name="app_icon_text" msgid="2823268023931811747">"App Icon"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Fullscreen"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Desktop Mode"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Split Screen"</string>
     <string name="more_button_text" msgid="3655388105592893530">"More"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Float"</string>
+    <string name="select_text" msgid="5139083974039906583">"Select"</string>
+    <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
+    <string name="close_text" msgid="4986518933445178928">"Close"</string>
+    <string name="collapse_menu_text" msgid="7515008122450342029">"Close Menu"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index 8e46c3e..4d18f5e 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Top 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Top 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Bottom full screen"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Split left"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Split right"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Split top"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Split bottom"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Using one-handed mode"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"To exit, swipe up from the bottom of the screen or tap anywhere above the app"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Start one-handed mode"</string>
@@ -82,14 +86,24 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Double-tap outside an app to reposition it"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Got it"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Expand for more information."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Restart for a better view?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"You can restart the app so that it looks better on your screen, but you may lose your progress or any unsaved changes"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Cancel"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Restart"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Don\'t show again"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Maximise"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Minimise"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Close"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Back"</string>
     <string name="handle_text" msgid="1766582106752184456">"Handle"</string>
+    <string name="app_icon_text" msgid="2823268023931811747">"App icon"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Full screen"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Desktop mode"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Split screen"</string>
     <string name="more_button_text" msgid="3655388105592893530">"More"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Float"</string>
+    <string name="select_text" msgid="5139083974039906583">"Select"</string>
+    <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
+    <string name="close_text" msgid="4986518933445178928">"Close"</string>
+    <string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index 8e46c3e..4d18f5e 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Top 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Top 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Bottom full screen"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Split left"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Split right"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Split top"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Split bottom"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Using one-handed mode"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"To exit, swipe up from the bottom of the screen or tap anywhere above the app"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Start one-handed mode"</string>
@@ -82,14 +86,24 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Double-tap outside an app to reposition it"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Got it"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Expand for more information."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Restart for a better view?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"You can restart the app so that it looks better on your screen, but you may lose your progress or any unsaved changes"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Cancel"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Restart"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Don\'t show again"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Maximise"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Minimise"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Close"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Back"</string>
     <string name="handle_text" msgid="1766582106752184456">"Handle"</string>
+    <string name="app_icon_text" msgid="2823268023931811747">"App icon"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Full screen"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Desktop mode"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Split screen"</string>
     <string name="more_button_text" msgid="3655388105592893530">"More"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Float"</string>
+    <string name="select_text" msgid="5139083974039906583">"Select"</string>
+    <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
+    <string name="close_text" msgid="4986518933445178928">"Close"</string>
+    <string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
index b2720be..e0fbfbb 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‏‎‎‏‎‏‏‏‏‏‎‏‏‎‏‏‏‎‏‎‎‏‏‎‎‎‏‏‏‎‎‎‏‎‎‎‏‏‎‏‎‏‎‎‎‏‏‏‎‎‏‎‎Top 50%‎‏‎‎‏‎"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‎‏‎‏‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‏‏‎‏‎‏‏‏‏‏‏‎‏‎‏‏‏‎‏‎‎‏‎‎‎‏‎Top 30%‎‏‎‎‏‎"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‎‎‏‏‎‎‏‏‎‏‎‎‎‏‎‏‎‎‎‎‎‏‏‎‎‎‎‏‏‏‏‏‏‎‎‏‏‎‏‎‎‏‎‎‏‏‏‏‎‎‏‏‎‎‎Bottom full screen‎‏‎‎‏‎"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‏‏‏‎‎‏‎‎‎‎‎‏‏‏‎‏‎‎‏‎‎‏‏‏‎‎‎‏‎‏‎‎‎‎‏‏‏‏‏‎‎‎‎‏‏‎‏‎‎‏‏‎‏‏‎‏‎‎Split left‎‏‎‎‏‎"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‏‎‎‎‏‏‏‎‏‎‏‏‎‏‏‎‎‎‏‏‏‎‏‎‎‎‏‎‏‎‏‏‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‏‎‏‎Split right‎‏‎‎‏‎"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‏‎‏‏‎‏‎‏‏‎‏‏‎‎‎‎‏‎‎‏‎‏‎‏‏‏‏‎‎‎‏‎‏‏‎‎‎‏‏‎‎‎‏‏‎‏‏‏‏‎‎‎‏‎‏‎‎Split top‎‏‎‎‏‎"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‎‎‏‎‏‎‎‎‎‎‎‎‎‎‏‎‏‏‏‎‏‏‏‏‎‏‏‏‏‎‎‏‏‎‎‎‏‎‏‏‎‎‎‎‏‎‏‏‏‏‏‎Split bottom‎‏‎‎‏‎"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‏‏‏‎‎‏‎‎‎‎‎‏‎‎‎‎‏‎‎‎‎‎‏‎‎‎‎‎‏‎‏‎‏‎‎‏‎‎‎‎‎‏‎‏‏‏‎‎Using one-handed mode‎‏‎‎‏‎"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‎‏‏‎‎‎‏‎‏‏‎‏‎‏‎‎‏‎‏‎‎‎‏‏‎‎‎‏‏‏‏‎‎‏‎‎‏‏‎‏‏‎‏‏‏‎‎‎‏‏‏‎‏‎‏‏‎To exit, swipe up from the bottom of the screen or tap anywhere above the app‎‏‎‎‏‎"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‎‏‏‏‎‏‎‏‏‏‎‏‎‎‏‏‏‏‎‏‎‏‏‎‏‏‎‎‎‎‏‏‏‎‏‎‎‎‎‎‏‎‏‎‏‎‏‏‏‏‎‎‏‎‎Start one-handed mode‎‏‎‎‏‎"</string>
@@ -82,14 +86,24 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‏‎‎‏‏‎‏‏‏‎‏‏‎‎‎‎‏‏‏‎‏‎‏‏‎‎‏‎‏‎‎‎‎‎‎‎‎‎‏‎‎‏‏‎‏‎‏‎Double-tap outside an app to reposition it‎‏‎‎‏‎"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‏‏‏‏‏‎‎‏‏‏‎‏‎‎‏‎‎‏‎‎‏‏‎‏‏‎‎‎‏‏‎‏‎‎‎‎‏‏‎‎‏‎‎‎‎‏‏‎‏‎‎‏‎Got it‎‏‎‎‏‎"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‎‎‎‎‎‎‏‎‏‏‏‏‎‎‎‏‏‎‏‎‎‎‎‎‎‎‏‏‎‏‏‏‏‎‎‎‎‎‎‏‏‎‎‎‏‎‎‎‏‏‎‏‎‏‎‎Expand for more information.‎‏‎‎‏‎"</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‏‎‎‎‎‏‎‏‎‏‏‏‎‏‎‏‎‎‏‎‎‏‎‏‎‎‎‎‏‎‏‎‎‎‎‏‎Restart for a better view?‎‏‎‎‏‎"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‏‏‏‎‎‏‎‏‏‎‏‎‎‎‎‏‎‎‏‏‏‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‏‏‎‏‏‎‎‎‏‎‎‏‏‎‎‎‎You can restart the app so it looks better on your screen, but you may lose your progress or any unsaved changes‎‏‎‎‏‎"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‎‎‎‎‎‎‏‏‏‏‏‎‎‎‎‎‏‏‎‎‎‎‏‎‎‏‎‎‎‎‏‏‎‏‏‎‎‏‎‎‎‎‎‎‏‏‏‎‎‏‏‎‏‎Cancel‎‏‎‎‏‎"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‎‏‏‎‎‎‏‎‎‎‎‎‎‏‎‏‎‏‎‏‏‎‏‏‎‎‎‎‏‎‏‎‏‎‏‏‏‎‏‎Restart‎‏‎‎‏‎"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‏‎‎‏‏‎‎‎‎‏‏‏‎‏‎‎‎‏‏‎‎‎‏‏‏‎‎‏‎‏‎‏‏‏‏‎‏‏‏‎‏‎‎‏‎‎‏‎‎‎‎‎‏‎‎Don’t show again‎‏‎‎‏‎"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‏‏‏‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‎‎‏‎‏‎‎‎‎‎‏‎‎‎‏‎‎‏‎‎‎‎‎‎‎‎‎‎‎‎‎‏‎‏‏‎Maximize‎‏‎‎‏‎"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‏‏‎‎‎‏‎‎‏‏‏‎‎‎‏‏‏‏‏‏‏‎‏‏‏‎‏‏‏‏‏‏‎‏‎‏‏‎‏‏‏‏‏‎‏‏‎‏‏‏‎‏‏‎‎‏‎Minimize‎‏‎‎‏‎"</string>
     <string name="close_button_text" msgid="2913281996024033299">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‏‎‏‏‏‎‎‎‎‎‏‏‏‎‏‎‎‎‏‎‏‎‎‏‎‎‎‏‏‏‏‎‎‎‏‏‎‏‏‎‏‏‎‎‎‎‎‎‎‏‎‎‏‏‎Close‎‏‎‎‏‎"</string>
     <string name="back_button_text" msgid="1469718707134137085">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‎‏‏‎‎‏‎‏‎‏‏‏‏‏‎‏‎‏‏‎‎‎‎‎‏‎‎‏‎‎‏‎‎‏‏‏‏‎‏‎‎‎‎‎‎‎‏‎‏‏‏‏‏‏‎‏‎Back‎‏‎‎‏‎"</string>
     <string name="handle_text" msgid="1766582106752184456">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‎‎‎‎‏‎‎‎‎‏‎‏‎‎‏‎‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‏‏‎‎‏‏‏‎‎‎‎‏‎‎‎‏‎‎‎‎Handle‎‏‎‎‏‎"</string>
+    <string name="app_icon_text" msgid="2823268023931811747">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎‏‎‏‏‏‎‎‏‎‎‎‎‏‏‎‏‎‎‏‏‏‏‎‎‏‏‏‎‎‎‎‎‏‎‏‏‎‎‏‎‏‏‎‏‏‏‏‎‏‎‎‎‏‏‎App Icon‎‏‎‎‏‎"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‎‎‏‎‎‎‎‏‎‏‏‎‎‎‎‎‏‏‎‏‏‏‎‏‏‏‏‏‎‎‏‎‏‏‏‎‏‎‎‎‏‎‏‏‏‎‏‏‎‎‏‎‏‏‏‏‎Fullscreen‎‏‎‎‏‎"</string>
     <string name="desktop_text" msgid="1077633567027630454">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‏‏‎‏‎‎‏‎‎‎‎‏‏‎‎‎‎‎‎‏‎‏‎‎‎‎‏‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‎‏‏‏‎‏‏‎‎Desktop Mode‎‏‎‎‏‎"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‎‎‎‎‎‏‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‎‎‏‎‏‎‏‏‏‏‏‎‎‎‎‏‏‏‎‏‏‏‎‎‎‏‎‎‎‏‏‎‎Split Screen‎‏‎‎‏‎"</string>
     <string name="more_button_text" msgid="3655388105592893530">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‏‏‎‏‎‏‎‎‎‏‏‎‎‎‎‎‏‏‏‎‏‎‏‏‎‏‏‏‎‎‎‎‎‏‏‎‏‎‏‏‏‎‎‎‎‎‏‎‏‏‎‏‎‎More‎‏‎‎‏‎"</string>
     <string name="float_button_text" msgid="9221657008391364581">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‎‎‎‎‎‏‏‎‎‎‎‏‏‎‏‎‎‎‏‏‎‏‎‏‎‎‎‏‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎Float‎‏‎‎‏‎"</string>
+    <string name="select_text" msgid="5139083974039906583">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‎‏‎‎‎‏‏‎‏‏‎‎‎‏‏‎‏‎‎‏‎‏‎‏‏‏‎‏‏‏‏‎‎‎‏‏‎‏‎‎‏‎‏‎‎‏‎‎‎‏‎‏‏‏‎Select‎‏‎‎‏‎"</string>
+    <string name="screenshot_text" msgid="1477704010087786671">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‏‎‎‎‎‎‎‏‏‏‎‏‏‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‎‎‎‏‏‏‎‎‏‏‏‏‏‎‏‎‎‎‏‎‏‎‏‏‏‏‎Screenshot‎‏‎‎‏‎"</string>
+    <string name="close_text" msgid="4986518933445178928">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‎‏‏‎‎‏‏‏‎‏‎‏‏‎‎‏‎‎‎‏‏‎‎‏‏‎‏‏‏‎‏‏‏‎‎‎‎‏‎‎‏‏‏‎‏‏‎‎‎‏‏‎‎‎‎‎Close‎‏‎‎‏‎"</string>
+    <string name="collapse_menu_text" msgid="7515008122450342029">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‎‏‎‏‎‏‎‏‎‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‏‎‏‏‎‏‏‎‏‎‎‏‏‏‏‏‏‎‎‎‎‏‎‎‎‏‏‎‏‎Close Menu‎‏‎‎‏‎"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index 47445a7..d306537 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Superior: 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Superior: 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Pantalla inferior completa"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Dividir a la izquierda"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Dividir a la derecha"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Dividir en la parte superior"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Dividir en la parte inferior"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Cómo usar el modo de una mano"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Para salir, desliza el dedo hacia arriba desde la parte inferior de la pantalla o presiona cualquier parte arriba de la app"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar el modo de una mano"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Presiona dos veces fuera de una app para cambiar su ubicación"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendido"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Expande para obtener más información."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"¿Quieres reiniciar para que se vea mejor?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Puedes reiniciar la app para que se vea mejor en la pantalla, pero podrías perder tu progreso o cualquier cambio que no hayas guardado"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Cancelar"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Reiniciar"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"No volver a mostrar"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Maximizar"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Minimizar"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Cerrar"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Atrás"</string>
     <string name="handle_text" msgid="1766582106752184456">"Controlador"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Pantalla completa"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Modo de escritorio"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Pantalla dividida"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Más"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Flotante"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index 6c45231..ab1ced4 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Superior 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Superior 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Pantalla inferior completa"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Dividir en la parte izquierda"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Dividir en la parte derecha"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Dividir en la parte superior"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Dividir en la parte inferior"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Usar modo Una mano"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Para salir, desliza el dedo hacia arriba desde la parte inferior de la pantalla o toca cualquier zona que haya encima de la aplicación"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar modo Una mano"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Toca dos veces fuera de una aplicación para cambiarla de posición"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendido"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Mostrar más información"</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"¿Reiniciar para que se vea mejor?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Puedes reiniciar la aplicación para que se vea mejor en la pantalla, pero puedes perder tu progreso o cualquier cambio que no hayas guardado"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Cancelar"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Reiniciar"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"No volver a mostrar"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Maximizar"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Minimizar"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Cerrar"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Atrás"</string>
     <string name="handle_text" msgid="1766582106752184456">"Controlador"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Pantalla completa"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Modo Escritorio"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Pantalla dividida"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Más"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Flotante"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index a8dc08c..4c4d87e 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Ülemine: 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Ülemine: 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Alumine täisekraan"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Jaga vasakule"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Jaga paremale"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Jaga üles"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Jaga alla"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"ÜhekäereĆŸiimi kasutamine"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Väljumiseks pühkige ekraani alaosast üles või puudutage rakenduse kohal olevat ala"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"ÜhekäereĆŸiimi käivitamine"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Topeltpuudutage rakendusest väljaspool, et selle asendit muuta"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Selge"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Laiendage lisateabe saamiseks."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Kas taaskäivitada parema vaate saavutamiseks?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Saate rakenduse taaskäivitada, et see näeks ekraanikuval parem välja, kuid võite kaotada edenemise või salvestamata muudatused"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Tühista"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Taaskäivita"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ära kuva uuesti"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Maksimeeri"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Minimeeri"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Sule"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Tagasi"</string>
     <string name="handle_text" msgid="1766582106752184456">"Käepide"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Täisekraan"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Lauaarvuti reĆŸiim"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Jagatud ekraanikuva"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Rohkem"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Hõljuv"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index 9fbf0a0..5642a5f 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Ezarri goialdea % 50en"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Ezarri goialdea % 30en"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Ezarri behealdea pantaila osoan"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Zatitu ezkerraldean"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Zatitu eskuinaldean"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Zatitu goialdean"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Zatitu behealdean"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Esku bakarreko modua erabiltzea"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Irteteko, pasatu hatza pantailaren behealdetik gora edo sakatu aplikazioaren gainaldea"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Abiarazi esku bakarreko modua"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Aplikazioaren posizioa aldatzeko, sakatu birritan haren kanpoaldea"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Ados"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Informazio gehiago lortzeko, zabaldu hau."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Aplikazioa berrabiarazi nahi duzu itxura hobea izan dezan?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Aplikazioa berrabiarazi egin dezakezu itxura hobea izan dezan, baina agian garapena edo gorde gabeko aldaketak galduko dituzu"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Utzi"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Berrabiarazi"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ez erakutsi berriro"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Maximizatu"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Minimizatu"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Itxi"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Atzera"</string>
     <string name="handle_text" msgid="1766582106752184456">"Kontu-izena"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Pantaila osoa"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Ordenagailuetarako modua"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Pantaila zatitua"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Gehiago"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Leiho gainerakorra"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index e7cb5f4..a15d7a4 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ÙȘÛ”Û° ŰšŰ§Ù„Ű§"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"ÙȘÛłÛ° ŰšŰ§Ù„Ű§"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"ŰȘÙ…Ű§Ù…‌Ű”ÙŰ­Ù‡ ÙŸŰ§ÛŒÛŒÙ†"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"ŰȘÙ‚ŰłÛŒÙ… ۧŰČ Ú†ÙŸ"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"ŰȘÙ‚ŰłÛŒÙ… ۧŰČ Ű±Ű§ŰłŰȘ"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"ŰȘÙ‚ŰłÛŒÙ… ۧŰČ ŰšŰ§Ù„Ű§"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"ŰȘÙ‚ŰłÛŒÙ… ۧŰČ ÙŸŰ§ÛŒÛŒÙ†"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ Ű­Ű§Ù„ŰȘ یک‌ŰŻŰłŰȘی"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"ŰšŰ±Ű§ÛŒ ۟ۧ۱ۏ ŰŽŰŻÙ†ŰŒ ۧŰČ ÙŸŰ§ÛŒÛŒÙ† Ű”ÙŰ­Ù‡‌Ù†Ù…Ű§ÛŒŰŽ ŰȘÙ†ŰŻ ŰšÙ‡‌Ű·Ű±Ù ŰšŰ§Ù„Ű§ ŰšÚ©ŰŽÛŒŰŻ ÛŒŰ§ ۯ۱ Ù‡Ű± ŰŹŰ§ÛŒÛŒ ۧŰČ ŰšŰ§Ù„Ű§ÛŒ ŰšŰ±Ù†Ű§Ù…Ù‡ که می‌ŰźÙˆŰ§Ù‡ÛŒŰŻ Ű¶Ű±ŰšÙ‡ ŰšŰČÙ†ÛŒŰŻ"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"ŰąŰșۧŰČ «Ű­Ű§Ù„ŰȘ یک‌ŰŻŰłŰȘی»"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ŰšŰ±Ű§ÛŒ ŰŹŰ§ŰšÙ‡‌ۏۧ Ú©Ű±ŰŻÙ† ŰšŰ±Ù†Ű§Ù…Ù‡ŰŒ ŰšÛŒŰ±ÙˆÙ† ۧŰČ ŰąÙ† ŰŻÙˆŰ¶Ű±ŰšÙ‡ ŰšŰČÙ†ÛŒŰŻ"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"مŰȘÙˆŰŹÙ‡‌Ű§Ù…"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ŰšŰ±Ű§ÛŒ Ű§Ű·Ù„Ű§ŰčۧŰȘ ŰšÛŒŰŽŰȘŰ±ŰŒ ÚŻŰłŰȘŰ±ŰŻÙ‡ Ú©Ù†ÛŒŰŻ."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"ŰšŰ±Ű§ÛŒ Ù†Ù…Ű§ÛŒŰŽ ŰšÙ‡ŰȘ۱ ۚۧŰČŰ±Ű§Ù‡‌Ű§Ù†ŰŻŰ§ŰČی ŰŽÙˆŰŻŰŸ"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"می‌ŰȘÙˆŰ§Ù†ÛŒŰŻ ŰšŰ±Ù†Ű§Ù…Ù‡ ۱ۧ ۚۧŰČŰ±Ű§Ù‡‌Ű§Ù†ŰŻŰ§ŰČی Ú©Ù†ÛŒŰŻ ŰȘۧ ŰšÙ‡ŰȘ۱ Ű±ÙˆÛŒ Ű”ÙŰ­Ù‡‌Ù†Ù…Ű§ÛŒŰŽ Ù†ŰŽŰ§Ù† ŰŻŰ§ŰŻÙ‡ ŰŽÙˆŰŻŰŒ Ű§Ù…Ű§ ممکن ۧ۳ŰȘ ÙŸÛŒŰŽŰ±ÙŰȘ ÛŒŰ§ ŰȘŰșÛŒÛŒŰ±Ű§ŰȘ Ű°ŰźÛŒŰ±Ù‡‌Ù†ŰŽŰŻÙ‡ ۱ۧ ۧŰČŰŻŰłŰȘ ŰšŰŻÙ‡ÛŒŰŻ"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"لŰșو Ú©Ű±ŰŻÙ†"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"ۚۧŰČŰ±Ű§Ù‡‌Ű§Ù†ŰŻŰ§ŰČی"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"ŰŻÙˆŰšŰ§Ű±Ù‡ Ù†ŰŽŰ§Ù† ŰŻŰ§ŰŻÙ‡ Ù†ŰŽÙˆŰŻ"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"ŰšŰČ۱گ Ú©Ű±ŰŻÙ†"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"کوچک Ú©Ű±ŰŻÙ†"</string>
     <string name="close_button_text" msgid="2913281996024033299">"ۚ۳ŰȘن"</string>
     <string name="back_button_text" msgid="1469718707134137085">"ۚ۱گێŰȘن"</string>
     <string name="handle_text" msgid="1766582106752184456">"ŰŻŰłŰȘÚŻÛŒŰ±Ù‡"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"ŰȘÙ…Ű§Ù…‌Ű”ÙŰ­Ù‡"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Ű­Ű§Ù„ŰȘ Ű±Ű§ÛŒŰ§Ù†Ù‡"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Ű”ÙŰ­Ù‡Ù” ŰŻÙˆÙ†ÛŒÙ…Ù‡"</string>
     <string name="more_button_text" msgid="3655388105592893530">"ŰšÛŒŰŽŰȘ۱"</string>
     <string name="float_button_text" msgid="9221657008391364581">"ŰŽÙ†Ű§ÙˆŰ±"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index 86199f3..8c679b6 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Yläosa 50 %"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Yläosa 30 %"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Alaosa koko näytölle"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Vasemmalla"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Oikealla"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Ylhäällä"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Alhaalla"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Yhden käden moodin käyttö"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Poistu pyyhkäisemällä ylös näytön alareunasta tai napauttamalla sovelluksen yllä"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Käynnistä yhden käden moodi"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Kaksoisnapauta sovelluksen ulkopuolella, jos haluat siirtää sitä"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Katso lisätietoja laajentamalla."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Käynnistetäänkö sovellus uudelleen, niin saat paremman näkymän?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Voit käynnistää sovelluksen uudelleen, jotta se näyttää paremmalta näytöllä, mutta saatat menettää edistymisesi tai tallentamattomat muutokset"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Peru"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Käynnistä uudelleen"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Älä näytä uudelleen"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Suurenna"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Pienennä"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Sulje"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Takaisin"</string>
     <string name="handle_text" msgid="1766582106752184456">"Kahva"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Koko näyttö"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Työpöytätila"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Jaettu näyttö"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Lisää"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Kelluva ikkuna"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index 1f3ac9e..d43aea5 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50 % dans le haut"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"30 % dans le haut"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Plein écran dans le bas"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Diviser à gauche"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Diviser à droite"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Diviser dans la partie supérieure"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Diviser dans la partie inférieure"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Utiliser le mode Une main"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Pour quitter, balayez l\'écran du bas vers le haut, ou touchez n\'importe où sur l\'écran en haut de l\'application"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Démarrer le mode Une main"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Touchez deux fois à côté d\'une application pour la repositionner"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Développer pour en savoir plus."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Redémarrer pour un meilleur affichage?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Vous pouvez redémarrer l\'application pour qu\'elle s\'affiche mieux sur votre écran, mais il se peut que vous perdiez votre progression ou toute modification non enregistrée"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Annuler"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Redémarrer"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ne plus afficher"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Agrandir"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Réduire"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Fermer"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Retour"</string>
     <string name="handle_text" msgid="1766582106752184456">"Identifiant"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Plein écran"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Mode Bureau"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Écran partagé"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Plus"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Flottant"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index f1dbb35..15e89f8 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Écran du haut à 50 %"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Écran du haut à 30 %"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Écran du bas en plein écran"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Affichée à gauche"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Affichée à droite"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Affichée en haut"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Affichée en haut"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Utiliser le mode une main"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Pour quitter, balayez l\'écran de bas en haut ou appuyez n\'importe où au-dessus de l\'application"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Démarrer le mode une main"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Appuyez deux fois en dehors d\'une appli pour la repositionner"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Développez pour obtenir plus d\'informations"</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Redémarrer pour améliorer l\'affichage ?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Vous pouvez redémarrer l\'appli pour en améliorer son aspect sur votre écran, mais vous risquez de perdre votre progression ou les modifications non enregistrées"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Annuler"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Redémarrer"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ne plus afficher"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Agrandir"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Réduire"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Fermer"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Retour"</string>
     <string name="handle_text" msgid="1766582106752184456">"Poignée"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Plein écran"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Mode ordinateur"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Écran partagé"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Plus"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Flottante"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index 6e215a1..f83564d 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50 % arriba"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"30 % arriba"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Pantalla completa abaixo"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Dividir (esquerda)"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Dividir (dereita)"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Dividir (arriba)"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Dividir (abaixo)"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Como se usa o modo dunha soa man?"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Para saír, pasa o dedo cara arriba desde a parte inferior da pantalla ou toca calquera lugar da zona situada encima da aplicación"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar modo dunha soa man"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Toca dúas veces fóra da aplicación para cambiala de posición"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendido"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Despregar para obter máis información."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Queres reiniciar a aplicación para que se vexa mellor?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Podes reiniciar a aplicación para que se vexa mellor na pantalla, pero podes perder o progreso que levas feito ou calquera cambio que non gardases"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Cancelar"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Reiniciar"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Non mostrar outra vez"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Maximizar"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Minimizar"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Pechar"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Atrás"</string>
     <string name="handle_text" msgid="1766582106752184456">"Controlador"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Pantalla completa"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Modo de escritorio"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Pantalla dividida"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Máis"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Flotante"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index ad086bb..bd36205 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"àȘ¶à«€àȘ°à«àȘ· 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"àȘ¶à«€àȘ°à«àȘ· 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"àȘ€àȘłàȘżàȘŻàȘŸàȘšà«€ àȘȘૂàȘ°à«àȘŁ àȘžà«àȘ•્àȘ°à«€àȘš"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"àȘĄàȘŸàȘŹà«‡ àȘ”àȘżàȘ­àȘŸàȘœàȘżàȘ€ àȘ•àȘ°à«‹"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"àȘœàȘźàȘŁà«‡ àȘ”àȘżàȘ­àȘŸàȘœàȘżàȘ€ àȘ•àȘ°à«‹"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"àȘ‰àȘȘàȘ° àȘ”àȘżàȘ­àȘŸàȘœàȘżàȘ€ àȘ•àȘ°à«‹"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"àȘšà«€àȘšà«‡ àȘ”àȘżàȘ­àȘŸàȘœàȘżàȘ€ àȘ•àȘ°à«‹"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"àȘàȘ•-àȘčàȘŸàȘ„ે àȘ”àȘŸàȘȘàȘ°à«‹ àȘźà«‹àȘĄàȘšà«‹ àȘ‰àȘȘàȘŻà«‹àȘ— àȘ•àȘ°à«€ àȘ°àȘč્àȘŻàȘŸàȘ‚ àȘ›à«€àȘ"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"àȘŹàȘčàȘŸàȘ° àȘšà«€àȘ•àȘłàȘ”àȘŸ àȘźàȘŸàȘŸà«‡, àȘžà«àȘ•્àȘ°à«€àȘšàȘšà«€ àȘšà«€àȘšà«‡àȘšàȘŸ àȘ­àȘŸàȘ—àȘ„ી àȘ‰àȘȘàȘ°àȘšà«€ àȘ€àȘ°àȘ« àȘžà«àȘ”àȘŸàȘ‡àȘȘ àȘ•àȘ°à«‹ àȘ…àȘ„àȘ”àȘŸ àȘàȘȘàȘšàȘŸ àȘ†àȘ‡àȘ•àȘš àȘȘàȘ° àȘ—àȘźà«‡ àȘ€à«àȘŻàȘŸàȘ‚ àȘŸà«…àȘȘ àȘ•àȘ°à«‹"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"àȘàȘ•-àȘčàȘŸàȘ„ે àȘ”àȘŸàȘȘàȘ°à«‹ àȘźà«‹àȘĄ àȘ¶àȘ°à«‚ àȘ•àȘ°à«‹"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"àȘ•à«‹àȘˆ àȘàȘȘàȘšà«€ àȘœàȘ—્àȘŻàȘŸ àȘŹàȘŠàȘČàȘ”àȘŸ àȘźàȘŸàȘŸà«‡, àȘ€à«‡àȘšà«€ àȘŹàȘčàȘŸàȘ° àȘŹà«‡ àȘ”àȘŸàȘ° àȘŸà«…àȘȘ àȘ•àȘ°à«‹"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"àȘžàȘźàȘœàȘŸàȘˆ àȘ—àȘŻà«àȘ‚"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"àȘ”àȘ§à« àȘźàȘŸàȘčàȘżàȘ€à«€ àȘźàȘŸàȘŸà«‡ àȘźà«‹àȘŸà«àȘ‚ àȘ•àȘ°à«‹."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"àȘŹàȘčેàȘ€àȘ° àȘ”્àȘŻà«‚ àȘźàȘŸàȘŸà«‡ àȘ«àȘ°à«€àȘ„ી àȘ¶àȘ°à«‚ àȘ•àȘ°à«€àȘ?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"àȘ€àȘźà«‡ àȘàȘȘàȘšà«‡ àȘ«àȘ°à«€àȘ„ી àȘ¶àȘ°à«‚ àȘ•àȘ°à«€ àȘ¶àȘ•à«‹ àȘ›à«‹, àȘœà«‡àȘ„ી àȘ€à«‡ àȘ€àȘźàȘŸàȘ°à«€ àȘžà«àȘ•્àȘ°à«€àȘš àȘȘàȘ° àȘ”àȘ§à« àȘžàȘŸàȘ°à«€ àȘ°à«€àȘ€à«‡ àȘŠà«‡àȘ–àȘŸàȘŻ, àȘȘàȘ°àȘ‚àȘ€à« àȘ†àȘź àȘ•àȘ°àȘ”àȘŸàȘ„ી àȘ€àȘźà«‡ àȘ€àȘźàȘŸàȘ°à«€ àȘàȘȘ àȘȘàȘ° àȘ•àȘ°à«€ àȘčોàȘŻ àȘàȘ”ી àȘ•à«‹àȘˆ àȘȘ્àȘ°àȘ•્àȘ°àȘżàȘŻàȘŸàȘšà«€ àȘȘ્àȘ°àȘ—àȘ€àȘż àȘ…àȘ„àȘ”àȘŸ àȘžàȘŸàȘšàȘ”્àȘŻàȘŸ àȘš àȘčોàȘŻ àȘàȘ”ો àȘ•à«‹àȘˆàȘȘàȘŁ àȘ«à«‡àȘ°àȘ«àȘŸàȘ° àȘ—ુàȘźàȘŸàȘ”ી àȘ¶àȘ•à«‹ àȘ›à«‹"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"àȘ°àȘŠ àȘ•àȘ°à«‹"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"àȘ«àȘ°à«€ àȘ¶àȘ°à«‚ àȘ•àȘ°à«‹"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"àȘ«àȘ°à«€àȘ„ી àȘŹàȘ€àȘŸàȘ”àȘ¶à«‹ àȘšàȘčીàȘ‚"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"àȘźà«‹àȘŸà«àȘ‚ àȘ•àȘ°à«‹"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"àȘšàȘŸàȘšà«àȘ‚ àȘ•àȘ°à«‹"</string>
     <string name="close_button_text" msgid="2913281996024033299">"àȘŹàȘ‚àȘ§ àȘ•àȘ°à«‹"</string>
     <string name="back_button_text" msgid="1469718707134137085">"àȘȘàȘŸàȘ›àȘł"</string>
     <string name="handle_text" msgid="1766582106752184456">"àȘčૅàȘšà«àȘĄàȘČ"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"àȘȘૂàȘ°à«àȘŁàȘžà«àȘ•્àȘ°à«€àȘš"</string>
     <string name="desktop_text" msgid="1077633567027630454">"àȘĄà«‡àȘžà«àȘ•àȘŸà«‰àȘȘ àȘźà«‹àȘĄ"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"àȘžà«àȘ•્àȘ°à«€àȘšàȘšà«‡ àȘ”àȘżàȘ­àȘŸàȘœàȘżàȘ€ àȘ•àȘ°à«‹"</string>
     <string name="more_button_text" msgid="3655388105592893530">"àȘ”àȘ§à«"</string>
     <string name="float_button_text" msgid="9221657008391364581">"àȘ«à«àȘČોàȘŸàȘżàȘ‚àȘ— àȘ”àȘżàȘšà«àȘĄà«‹"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index bed39fb..e56ad17 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"à€Šà€Șà€° à€•à„€ à€žà„à€•à„à€°à„€à€š à€•à„‹ 50% à€Źà€šà€Ÿà€à€‚"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"à€Šà€Șà€° à€•à„€ à€žà„à€•à„à€°à„€à€š à€•à„‹ 30% à€Źà€šà€Ÿà€à€‚"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"à€šà„€à€šà„‡ à€•à„€ à€žà„à€•à„à€°à„€à€š à€•à„‹ à€«à€Œà„à€Č à€žà„à€•à„à€°à„€à€š à€Źà€šà€Ÿà€à€‚"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"à€žà„à€•à„à€°à„€à€š à€•à„‹ à€Źà€Ÿà€à€‚ à€čà€żà€žà„à€žà„‡ à€źà„‡à€‚ à€žà„à€Șà„à€Čà€żà€Ÿ à€•à€°à„‡à€‚"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"à€žà„à€•à„à€°à„€à€š à€•à„‹ à€Šà€Ÿà€à€‚ à€čà€żà€žà„à€žà„‡ à€źà„‡à€‚ à€žà„à€Șà„à€Čà€żà€Ÿ à€•à€°à„‡à€‚"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"à€žà„à€•à„à€°à„€à€š à€•à„‹ à€Šà€Șà€° à€•à„‡ à€čà€żà€žà„à€žà„‡ à€źà„‡à€‚ à€žà„à€Șà„à€Čà€żà€Ÿ à€•à€°à„‡à€‚"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"à€žà„à€•à„à€°à„€à€š à€•à„‹ à€žà€Źà€žà„‡ à€šà„€à€šà„‡ à€”à€Ÿà€Čà„‡ à€čà€żà€žà„à€žà„‡ à€źà„‡à€‚ à€žà„à€Șà„à€Čà€żà€Ÿ à€•à€°à„‡à€‚"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"à€”à€š-à€čà„ˆà€‚à€Ąà„‡à€Ą à€źà„‹à€Ą à€•à€Ÿ à€‡à€žà„à€€à„‡à€źà€Ÿà€Č à€•à€°à€šà€Ÿ"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"à€‡à€ž à€źà„‹à€Ą à€žà„‡ à€Źà€Ÿà€čà€° à€šà€żà€•à€Čà€šà„‡ à€•à„‡ à€Čà€żà€, à€žà„à€•à„à€°à„€à€š à€•à„‡ à€žà€Źà€žà„‡ à€šà€żà€šà€Čà„‡ à€čà€żà€žà„à€žà„‡ à€žà„‡ à€Šà€Șà€° à€•à„€ à€“à€° à€žà„à€”à€Ÿà€‡à€Ș à€•à€°à„‡à€‚ à€Żà€Ÿ à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€•à„‡ à€Źà€Ÿà€čà€° à€•à€čà„€à€‚ à€­à„€ à€Ÿà„ˆà€Ș à€•à€°à„‡à€‚"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"à€”à€š-à€čà„ˆà€‚à€Ąà„‡à€Ą à€źà„‹à€Ą à€šà€Ÿà€Čà„‚ à€•à€°à„‡à€‚"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"à€•à€żà€žà„€ à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€•à„€ à€œà€—à€č à€Źà€Šà€Čà€šà„‡ à€•à„‡ à€Čà€żà€, à€‰à€žà€•à„‡ à€Źà€Ÿà€čà€° à€Šà„‹ à€Źà€Ÿà€° à€Ÿà„ˆà€Ș à€•à€°à„‡à€‚"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"à€ à„€à€• à€čà„ˆ"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"à€œà€Œà„à€Żà€Ÿà€Šà€Ÿ à€œà€Ÿà€šà€•à€Ÿà€°à„€ à€•à„‡ à€Čà€żà€ à€Źà€Ąà€Œà€Ÿ à€•à€°à„‡à€‚."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"à€Źà„‡à€čà€€à€° à€”à„à€Żà„‚ à€Șà€Ÿà€šà„‡ à€•à„‡ à€Čà€żà€ à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€•à„‹ à€°à„€à€žà„à€Ÿà€Ÿà€°à„à€Ÿ à€•à€°à€šà€Ÿ à€čà„ˆ?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"à€žà„à€•à„à€°à„€à€š à€Șà€° à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€•à€Ÿ à€Źà„‡à€čà€€à€° à€”à„à€Żà„‚ à€Șà€Ÿà€šà„‡ à€•à„‡ à€Čà€żà€ à€‰à€žà„‡ à€°à„€à€žà„à€Ÿà€Ÿà€°à„à€Ÿ à€•à€°à„‡à€‚. à€čà€Ÿà€Čà€Ÿà€‚à€•à€ż, à€†à€Șà€šà„‡ à€œà„‹ à€Źà€Šà€Čà€Ÿà€” à€žà„‡à€” à€šà€čà„€à€‚ à€•à€żà€ à€čà„ˆà€‚ à€Żà€Ÿ à€…à€Ź à€€à€• à€œà„‹ à€•à€Ÿà€ź à€•à€żà€ à€čà„ˆà€‚ à€‰à€šà€•à€Ÿ à€Ąà„‡à€Ÿà€Ÿ, à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€°à„€à€žà„à€Ÿà€Ÿà€°à„à€Ÿ à€•à€°à€šà„‡ à€Șà€° à€źà€żà€Ÿ à€žà€•à€€à€Ÿ à€čà„ˆ"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"à€°à€Šà„à€Š à€•à€°à„‡à€‚"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"à€°à„€à€žà„à€Ÿà€Ÿà€°à„à€Ÿ à€•à€°à„‡à€‚"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"à€«à€żà€° à€žà„‡ à€š à€Šà€żà€–à€Ÿà€à€‚"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"à€Źà€Ąà€Œà€Ÿ à€•à€°à„‡à€‚"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"à€”à€żà€‚à€Ąà„‹ à€›à„‹à€Ÿà„€ à€•à€°à„‡à€‚"</string>
     <string name="close_button_text" msgid="2913281996024033299">"à€Źà€‚à€Š à€•à€°à„‡à€‚"</string>
     <string name="back_button_text" msgid="1469718707134137085">"à€”à€Ÿà€Șà€ž à€œà€Ÿà€à€‚"</string>
     <string name="handle_text" msgid="1766582106752184456">"à€čà„ˆà€‚à€Ąà€Č"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"à€«à€Œà„à€Čà€žà„à€•à„à€°à„€à€š"</string>
     <string name="desktop_text" msgid="1077633567027630454">"à€Ąà„‡à€žà„à€•à€Ÿà„‰à€Ș à€źà„‹à€Ą"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"à€žà„à€Șà„à€Čà€żà€Ÿ à€žà„à€•à„à€°à„€à€š à€źà„‹à€Ą"</string>
     <string name="more_button_text" msgid="3655388105592893530">"à€œà€Œà„à€Żà€Ÿà€Šà€Ÿ à€Šà„‡à€–à„‡à€‚"</string>
     <string name="float_button_text" msgid="9221657008391364581">"à€«à€Œà„à€Čà„‹à€Ÿ"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index 1446e70..cde33c7 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Gornji zaslon na 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Gornji zaslon na 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Donji zaslon u cijeli zaslon"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Podijeli lijevo"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Podijeli desno"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Podijeli gore"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Podijeli dolje"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Korištenje načina rada jednom rukom"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Za izlaz prijeđite prstom od dna zaslona prema gore ili dodirnite bio gdje iznad aplikacije"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Pokretanje načina rada jednom rukom"</string>
@@ -82,14 +86,24 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dvaput dodirnite izvan aplikacije da biste je premjestili"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Shvaćam"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Proširite da biste saznali više."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Ćœelite li ponovno pokrenuti za bolji pregled?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"MoĆŸete ponovno pokrenuti aplikaciju tako da bolje izgleda na zaslonu, no mogli biste izgubiti napredak ili sve nespremljene promjene"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Odustani"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Pokreni ponovno"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ne prikazuj ponovno"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Maksimiziraj"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Minimiziraj"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Zatvori"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Natrag"</string>
     <string name="handle_text" msgid="1766582106752184456">"Pokazivač"</string>
+    <string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikacije"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Puni zaslon"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Stolni način rada"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Razdvojeni zaslon"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Više"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Plutajući"</string>
+    <string name="select_text" msgid="5139083974039906583">"Odaberite"</string>
+    <string name="screenshot_text" msgid="1477704010087786671">"Snimka zaslona"</string>
+    <string name="close_text" msgid="4986518933445178928">"Zatvorite"</string>
+    <string name="collapse_menu_text" msgid="7515008122450342029">"Zatvorite izbornik"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index 221c329..28536e6 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"FelsƑ 50%-ra"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"FelsƑ 30%-ra"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Alsó teljes képernyƑre"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Osztás a képernyƑ bal oldalán"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Osztás a képernyƑ jobb oldalán"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Osztás a képernyƑ tetején"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Osztás alul"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Egykezes mód használata"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"A kilépéshez csúsztasson felfelé a képernyƑ aljáról, vagy koppintson az alkalmazás felett a képernyƑ bármelyik részére"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Egykezes mód indítása"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Koppintson duplán az alkalmazáson kívül az áthelyezéséhez"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Értem"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Kibontással további információkhoz juthat."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Újraindítja a jobb megjelenítés érdekében?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Újraindíthatja az alkalmazást a képernyƑn való jobb megjelenítés érdekében, de elveszítheti az elƑrehaladását és az esetleges nem mentett változásokat"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Mégse"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Újraindítás"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ne jelenjen meg többé"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Teljes méret"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Kis méret"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Bezárás"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Vissza"</string>
     <string name="handle_text" msgid="1766582106752184456">"Fogópont"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Teljes képernyƑ"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Asztali üzemmód"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Osztott képernyƑ"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Továbbiak"</string>
     <string name="float_button_text" msgid="9221657008391364581">"LebegƑ"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index 7be9941..bd35084 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ŐŽŐ„Ö€Ö‡Ő« Ő§ŐŻÖ€ŐĄŐ¶ŐšŐ 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"ŐŽŐ„Ö€Ö‡Ő« Ő§ŐŻÖ€ŐĄŐ¶ŐšŐ 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Ő†Ő„Ö€Ö„Ö‡Ő« Ő§ŐŻÖ€ŐĄŐ¶ŐšŐ ŐŹŐ«ŐĄŐ§ŐŻÖ€ŐĄŐ¶"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Ő€ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐš Ő±ŐĄŐ­ ŐŻŐžŐČծվւծ"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Ő€ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐš ŐĄŐ» ŐŻŐžŐČծվւծ"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Ő€ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐš ŐŸŐ„Ö€Ö‡ŐžÖ‚ŐŽ"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Ő€ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐš Ő¶Ő„Ö€Ö„Ö‡ŐžÖ‚ŐŽ"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Ô»Ő¶ŐčŐșŐ„Őœ Ö…ŐŁŐżŐŸŐ„ŐŹ ŐŽŐ„ŐŻ Ő±Ő„ŐŒÖ„Ő« ŐŒŐ„ŐȘիՎից"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"ÔŽŐžÖ‚Ö€Őœ գալվւ հածար ŐŽŐĄŐżŐš ŐœŐĄŐ°Ő„ÖÖ€Ő„Ö„ Ő§ŐŻÖ€ŐĄŐ¶Ő« նՄրքևից ŐŸŐ„Ö€Ö‡ ŐŻŐĄŐŽ Ő°ŐșŐ„Ö„ Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐ« ŐŸŐ„Ö€Ö‡ŐžÖ‚ŐŽ վրևէ ŐżŐ„ŐČ։"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"ÔłŐžÖ€ŐźŐĄÖ€ŐŻŐ„ŐŹ ŐŽŐ„ŐŻ Ő±Ő„ŐŒÖ„Ő« ŐŒŐ„ŐȘŐ«ŐŽŐš"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ÔżÖ€ŐŻŐ¶ŐĄŐŻŐ« Ő°ŐșŐ„Ö„ Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐ« ŐŻŐžŐČÖ„Ő«Ő¶Ő ŐĄŐ”Ő¶ ŐżŐ„ŐČŐĄÖƒŐžŐ­Ő„ŐŹŐžÖ‚ հածար"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Ô”ŐČŐĄŐŸ"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ÔŸŐĄŐŸŐĄŐŹŐ„Ö„Ő ŐĄŐŸŐ„ŐŹŐ«Ő¶ Ő«ŐŽŐĄŐ¶ŐĄŐŹŐžÖ‚ հածար։"</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"ŐŽŐ„Ö€ŐĄŐŁŐžÖ€ŐźŐĄÖ€ŐŻŐ„ŐžŐŹ Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐš"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Ԯվւք կարվŐČ Ő„Ö„ ŐŸŐ„Ö€ŐĄŐŁŐžÖ€ŐźŐĄÖ€ŐŻŐ„ŐŹ Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐš, վրŐșŐ„ŐœŐŠŐ« ŐĄŐ”Ő¶ ŐĄŐŸŐ„ŐŹŐ« ŐŹŐĄŐŸ ÖŐžÖ‚ÖŐĄŐ€Ö€ŐŸŐ« Ő±Ő„Ö€ Ő§ŐŻÖ€ŐĄŐ¶Ő«Ő¶, ŐœŐĄŐŻŐĄŐ”Ő¶ Ő±Ő„Ö€ ŐĄŐŒŐĄŐ»ŐšŐ¶Ő©ŐĄÖŐš և ŐčŐșŐĄŐ°ŐŸŐĄŐź ÖƒŐžÖƒŐžŐ­ŐžÖ‚Ő©Ő”ŐžÖ‚Ő¶Ő¶Ő„Ö€Őš ŐŻŐŻŐžÖ€Ő„Ő¶"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Ő‰Ő„ŐČŐĄÖ€ŐŻŐ„ŐŹ"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"ŐŽŐ„Ö€ŐĄŐŁŐžÖ€ŐźŐĄÖ€ŐŻŐ„ŐŹ"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ô±Ő”ŐŹÖ‡Őœ ց՞ւՔց ŐčŐżŐĄŐŹ"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"ÔŸŐĄŐŸŐĄŐŹŐ„ŐŹ"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"ÔŸŐĄŐŹŐ„ŐŹ"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Ő“ŐĄŐŻŐ„ŐŹ"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Ő€Ő„Őż"</string>
     <string name="handle_text" msgid="1766582106752184456">"Ő†Ő·Ő«Őč"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"ÔŒŐ«ŐĄŐ§ŐŻÖ€ŐĄŐ¶"</string>
     <string name="desktop_text" msgid="1077633567027630454">"ՀածակարգŐčŐ« ŐŒŐ„ŐȘŐ«ŐŽ"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"ŐÖ€ŐžŐ°ŐŸŐĄŐź Ő§ŐŻÖ€ŐĄŐ¶"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Ô±ŐŸŐ„ŐŹŐ«Ő¶"</string>
     <string name="float_button_text" msgid="9221657008391364581">"ÔŒŐžŐČեց՞ŐČ ŐșŐĄŐżŐžÖ‚Ő°ŐĄŐ¶"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index 4e760ef..6f859ed 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Atas 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Atas 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Layar penuh di bawah"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Pisahkan ke kiri"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Pisahkan ke kanan"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Pisahkan ke atas"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Pisahkan ke bawah"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Menggunakan mode satu tangan"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Untuk keluar, geser layar dari bawah ke atas atau ketuk di mana saja di atas aplikasi"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Mulai mode satu tangan"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Ketuk dua kali di luar aplikasi untuk mengubah posisinya"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Oke"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Luaskan untuk melihat informasi selengkapnya."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Mulai ulang untuk tampilan yang lebih baik?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Anda dapat memulai ulang aplikasi agar terlihat lebih baik di layar, tetapi Anda mungkin kehilangan progres atau perubahan yang belum disimpan"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Batal"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Mulai ulang"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Jangan tampilkan lagi"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Maksimalkan"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Minimalkan"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Tutup"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Kembali"</string>
     <string name="handle_text" msgid="1766582106752184456">"Tuas"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Layar Penuh"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Mode Desktop"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Layar Terpisah"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Lainnya"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Mengambang"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index 50d4ee7..b17abf5 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Efri 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Efri 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Neðri á öllum skjánum"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Skipta vinstra megin"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Skipta hægra megin"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Skipta efst"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Skipta neðst"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Notkun einhentrar stillingar"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Til að loka skaltu strjúka upp frá neðri hluta skjásins eða ýta hvar sem er fyrir ofan forritið"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Ræsa einhenta stillingu"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Ýttu tvisvar utan við forrit til að færa það"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Ég skil"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Stækka til að sjá frekari upplýsingar."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Viltu endurræsa til að fá betri sýn?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Þú getur endurræst forritið svo það falli betur að skjánum en þú gætir tapað framvindunni eða óvistuðum breytingum"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Hætta við"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Endurræsa"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ekki sýna þetta aftur"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Stækka"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Minnka"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Loka"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Til baka"</string>
     <string name="handle_text" msgid="1766582106752184456">"Handfang"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Allur skjárinn"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Skjáborðsstilling"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Skjáskipting"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Meira"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Reikult"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index d2595f7..28e274b 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Schermata superiore al 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Schermata superiore al 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Schermata inferiore a schermo intero"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Dividi a sinistra"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Dividi a destra"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Dividi in alto"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Dividi in basso"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Usare la modalità a una mano"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Per uscire, scorri verso l\'alto dalla parte inferiore dello schermo oppure tocca un punto qualsiasi sopra l\'app"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Avvia la modalità a una mano"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Tocca due volte fuori da un\'app per riposizionarla"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Espandi per avere ulteriori informazioni."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Vuoi riavviare per migliorare la visualizzazione?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Puoi riavviare l\'app affinché venga visualizzata meglio sullo schermo, ma potresti perdere i tuoi progressi o eventuali modifiche non salvate"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Annulla"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Riavvia"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Non mostrare più"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Ingrandisci"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Riduci a icona"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Chiudi"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Indietro"</string>
     <string name="handle_text" msgid="1766582106752184456">"Handle"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Schermo intero"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Modalità desktop"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Schermo diviso"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Altro"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Mobile"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index 883596e..497e0b0 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ŚąŚœŚ™Ś•ŚŸ 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"ŚœŚžŚąŚœŚ” 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"ŚžŚĄŚš ŚȘŚ—ŚȘŚ•ŚŸ ŚžŚœŚ"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Ś€Ś™ŚŠŚ•Śœ Ś©ŚžŚŚœŚ”"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Ś€Ś™ŚŠŚ•Śœ Ś™ŚžŚ™Ś Ś”"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Ś€Ś™ŚŠŚ•Śœ ŚœŚžŚąŚœŚ”"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Ś€Ś™ŚŠŚ•Śœ ŚœŚžŚ˜Ś”"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"ŚŚ™Śš ŚœŚ”Ś©ŚȘŚžŚ© Ś‘ŚȘڛڕڠڔ \'ŚžŚŠŚ‘ Ś©Ś™ŚžŚ•Ś© ڑڙړ ڐڗŚȘ\'"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"ڛړڙ ŚœŚŠŚŚȘ, ڙک ŚœŚ”Ś—ŚœŚ™Ś§ ŚœŚžŚąŚœŚ” ŚžŚȘŚ—ŚȘŚ™ŚȘ Ś”ŚžŚĄŚš ڐڕ ŚœŚ”Ś§Ś™Ś© Ś‘ŚžŚ§Ś•Ś Ś›ŚœŚ©Ś”Ś• Ś‘ŚžŚĄŚš ŚžŚąŚœ Ś”ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś”"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Ś”Ś€ŚąŚœŚ” کڜ ŚžŚŠŚ‘ Ś©Ś™ŚžŚ•Ś© ڑڙړ ڐڗŚȘ"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ŚŠŚšŚ™Śš ŚœŚ”Ś§Ś™Ś© ڔڧکڔ Ś›Ś€Ś•ŚœŚ” ŚžŚ—Ś•Ś„ ŚœŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś” ڛړڙ ŚœŚžŚ§Ś ڐڕŚȘŚ” ŚžŚ—Ś“Ś©"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"ڔڑڠŚȘŚ™"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ŚžŚšŚ—Ś™Ś‘Ś™Ś ڛړڙ ŚœŚ§Ś‘Śœ ŚžŚ™Ś“Śą Ś Ś•ŚĄŚŁ."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"ŚœŚ”Ś€ŚąŚ™Śœ ŚžŚ—Ś“Ś© ڜŚȘŚŠŚ•Ś’Ś” Ś˜Ś•Ś‘Ś” ڙڕŚȘŚš?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"ŚŚ€Ś©Śš ŚœŚ”Ś€ŚąŚ™Śœ ŚžŚ—Ś“Ś© ڐŚȘ Ś”ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś” ڛړڙ کڔڙڐ ŚȘŚ•ŚŠŚ’ Ś‘ŚŚ•Ś€ŚŸ Ś˜Ś•Ś‘ ڙڕŚȘŚš Ś‘ŚžŚĄŚš, ŚŚ‘Śœ ڙڙŚȘŚ›ŚŸ کڔڔŚȘŚ§Ś“ŚžŚ•ŚȘ کڜښ ڐڕ Ś›Śœ کڙڠڕڙ کڜڐ Ś Ś©ŚžŚš ڙڐڑړڕ"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Ś‘Ś™Ś˜Ś•Śœ"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Ś”Ś€ŚąŚœŚ” ŚžŚ—Ś“Ś©"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"ŚŚ™ŚŸ ŚŠŚ•ŚšŚš ŚœŚ”ŚŠŚ™Ś’ ڐŚȘ Ś–Ś” کڕڑ"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Ś”Ś’Ś“ŚœŚ”"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"ŚžŚ–ŚąŚ•Śš"</string>
     <string name="close_button_text" msgid="2913281996024033299">"ŚĄŚ’Ś™ŚšŚ”"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Ś—Ś–ŚšŚ”"</string>
     <string name="handle_text" msgid="1766582106752184456">"ڠڧڕړŚȘ ڐڗڙږڔ"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"ŚžŚĄŚš ŚžŚœŚ"</string>
     <string name="desktop_text" msgid="1077633567027630454">"ŚžŚžŚ©Ś§ Ś”ŚžŚ—Ś©Ś‘"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"ŚžŚĄŚš ŚžŚ€Ś•ŚŠŚœ"</string>
     <string name="more_button_text" msgid="3655388105592893530">"ŚąŚ•Ś“"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Ś‘ŚœŚ•Ś Ś™Ś"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index 6bb22a2..7f7cd94 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"侊 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"侊 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"äž‹éƒšć…šç”»éą"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"ć·Šă«ćˆ†ć‰Č"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"ćłă«ćˆ†ć‰Č"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"äžŠă«ćˆ†ć‰Č"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"äž‹ă«ćˆ†ć‰Č"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"ç‰‡æ‰‹ăƒąăƒŒăƒ‰ăźäœżç”š"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"甂äș†ă™ă‚‹ă«ăŻă€ç”»éąă‚’䞋から䞊にă‚čăƒŻă‚€ăƒ—ă™ă‚‹ă‹ă€ă‚ąăƒ—ăƒȘăźä»»æ„ăźć Žæ‰€ă‚’ă‚żăƒƒăƒ—ă—ăŸă™"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"ç‰‡æ‰‹ăƒąăƒŒăƒ‰ă‚’é–‹ć§‹ă—ăŸă™"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"äœçœźă‚’ć€‰ăˆă‚‹ă«ăŻă‚ąăƒ—ăƒȘăźć€–ćŽă‚’ăƒ€ăƒ–ăƒ«ă‚żăƒƒăƒ—ă—ăŠăă ă•ă„"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"é–‹ăăšè©łçŽ°ăŒèĄšç€șă•ă‚ŒăŸă™ă€‚"</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"ć†è”·ć‹•ă—ăŠç”»éąă‚’ă™ăŁăă‚Šă•ă›ăŸă™ă‹ïŒŸ"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"ケプăƒȘă‚’ć†è”·ć‹•ă—ăŠç”»éąă‚’ă™ăŁăă‚Šă•ă›ă‚‹ă“ăšăŻă§ăăŸă™ăŒă€é€Čæ—çŠ¶æłăŒć€±ă‚ă‚Œă€äżć­˜ă•ă‚ŒăŠă„ăȘă„ć€‰æ›ŽăŒæ¶ˆăˆă‚‹ćŻèƒœæ€§ăŒă‚ă‚ŠăŸă™"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"ă‚­ăƒŁăƒłă‚»ăƒ«"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"ć†è”·ć‹•"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"æŹĄć›žă‹ă‚‰èĄšç€șしăȘい"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"æœ€ć€§ćŒ–"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"æœ€ć°ćŒ–"</string>
     <string name="close_button_text" msgid="2913281996024033299">"閉じる"</string>
     <string name="back_button_text" msgid="1469718707134137085">"æˆ»ă‚‹"</string>
     <string name="handle_text" msgid="1766582106752184456">"ăƒăƒłăƒ‰ăƒ«"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"ć…šç”»éąèĄšç€ș"</string>
     <string name="desktop_text" msgid="1077633567027630454">"デă‚čクトップ ăƒąăƒŒăƒ‰"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"戆ć‰Čç”»éą"</string>
     <string name="more_button_text" msgid="3655388105592893530">"ăăźä»–"</string>
     <string name="float_button_text" msgid="9221657008391364581">"ăƒ•ăƒ­ăƒŒăƒ†ă‚Łăƒłă‚°"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index 6cf7d78..deec26d 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ზედა ეკრანი — 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"ზედა ეკრანი — 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Ⴤვედა ნაწილის სრულ ეკრანზე გაჹლა"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"გაყოჀა მარáƒȘჼნივ"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"გაყოჀა მარჯვნივ"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"გაყოჀა ზემოთ"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"გაყოჀა Ⴤვემოთ"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"áƒȘალი ჼელის რეჟიმის გამოყენება"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"გასასვლელად áƒ’áƒáƒ“áƒáƒ€áƒŁáƒ áƒȘლეთ ეკრანის Ⴤვედა კიდიდან ზემოთ ან ჹეეჼეთ ნებისმიერ ადგილას აპის ზემოთ"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"áƒȘალი ჼელის რეჟიმის დაწყება"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ორმაგად ჹეეჼეთ აპის გარჹემო სივრáƒȘეს, რათა იქ სჼვაგან გადაიჱანოთ"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"გასაგებია"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"დამაჱებითი ინჀორმაáƒȘიისთვის გააჀართოეთ."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"გსურთ გადაჱვირთვა უკეთესი ჼედისთვის?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"ლეგიძლიათ გადაჱვირთოთ აპი იმისათვის, რომ áƒ—áƒ„áƒ•áƒ”áƒœáƒĄ ეკრანზე უკეთესად áƒ’áƒáƒ›áƒáƒ©áƒœáƒ“áƒ”áƒĄ, თუმáƒȘა თჄვენ მიერ ჹესრულებული მოჄმედებები ლეიძლება დაიკარგოს ან áƒȘვლილებების ჹენაჼვა ვერ მოჼერჼდეს"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"áƒ’áƒáƒŁáƒ„áƒ›áƒ”áƒ‘áƒ"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"გადაჱვირთვა"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"ა჊არ áƒ’áƒáƒ›áƒáƒ©áƒœáƒ“áƒ”áƒĄ"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"áƒ›áƒáƒ„áƒĄáƒ˜áƒ›áƒáƒšáƒŁáƒ áƒáƒ“ გაჹლა"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"ჩაკეáƒȘვა"</string>
     <string name="close_button_text" msgid="2913281996024033299">"დაჼურვა"</string>
     <string name="back_button_text" msgid="1469718707134137085">"უკან"</string>
     <string name="handle_text" msgid="1766582106752184456">"áƒ˜áƒ“áƒ”áƒœáƒąáƒ˜áƒ€áƒ˜áƒ™áƒáƒąáƒáƒ áƒ˜"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"სრულ ეკრანზე"</string>
     <string name="desktop_text" msgid="1077633567027630454">"დესკჱოპის რეჟიმი"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"ეკრანის გაყოჀა"</string>
     <string name="more_button_text" msgid="3655388105592893530">"სჼვა"</string>
     <string name="float_button_text" msgid="9221657008391364581">"ჀარჀაჹი"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index 216619a..d429ba9 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50% Đ¶ĐŸÒ“Đ°Ń€Ò“Ń‹ Đ¶Đ°Ò›Ń‚Đ°"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"30% Đ¶ĐŸÒ“Đ°Ń€Ò“Ń‹ Đ¶Đ°Ò›Ń‚Đ°"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"ĐąÓ©ĐŒĐ”ĐœĐłŃ–ŃŃ–Đœ Ń‚ĐŸĐ»Ń‹Ò› эĐșŃ€Đ°ĐœÒ“Đ° ŃˆŃ‹Ò“Đ°Ń€Ńƒ"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"ĐĄĐŸĐ» Đ¶Đ°Ò›Ń‚Đ°Đœ ŃˆŃ‹Ò“Đ°Ń€Ńƒ"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"ĐžÒŁ Đ¶Đ°Ò›Ń‚Đ°Đœ ŃˆŃ‹Ò“Đ°Ń€Ńƒ"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Đ–ĐŸÒ“Đ°Ń€Ń‹ĐŽĐ°Đœ ŃˆŃ‹Ò“Đ°Ń€Ńƒ"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"ĐŃŃ‚Ń‹ĐœĐ°Đœ ŃˆŃ‹Ò“Đ°Ń€Ńƒ"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Бір Ò›ĐŸĐ»ĐŒĐ”Đœ Đ”ĐœĐłŃ–Đ·Ńƒ Ń€Đ”Đ¶ĐžĐŒŃ–Đœ паĐčĐŽĐ°Đ»Đ°ĐœŃƒ"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"ĐšŃ‹Ò“Ńƒ ÒŻŃˆŃ–Đœ эĐșŃ€Đ°ĐœĐœŃ‹ÒŁ Ń‚Ó©ĐŒĐ”ĐœĐłŃ– Đ¶Đ°Ò“Ń‹ĐœĐ°Đœ Đ¶ĐŸÒ“Đ°Ń€Ń‹ Ò›Đ°Ń€Đ°Đč ŃŃ‹Ń€Ò“Ń‹Ń‚Ń‹ÒŁŃ‹Đ· ĐœĐ”ĐŒĐ”ŃĐ” Ò›ĐŸĐ»ĐŽĐ°ĐœĐ±Đ°ĐœŃ‹ÒŁ ÒŻŃŃ‚Ń–ĐœĐ”Đœ ĐșДз ĐșĐ”Đ»ĐłĐ”Đœ Đ¶Đ”Ń€ĐŽĐ”Đœ Ń‚ÒŻŃ€Ń‚Ń–ÒŁŃ–Đ·."</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Бір Ò›ĐŸĐ»ĐŒĐ”Đœ Đ”ĐœĐłŃ–Đ·Ńƒ Ń€Đ”Đ¶ĐžĐŒŃ–Đœ ісĐșĐ” Ò›ĐŸŃŃƒ"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ÒšĐŸĐ»ĐŽĐ°ĐœĐ±Đ°ĐœŃ‹ÒŁ ĐŸŃ€ĐœŃ‹Đœ Ó©Đ·ĐłĐ”Ń€Ń‚Ńƒ ÒŻŃˆŃ–Đœ ĐŸĐŽĐ°Đœ тыс жДрЎі Đ”Đșі рДт Ń‚ÒŻŃ€Ń‚Ń–ÒŁŃ–Đ·."</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"ĐąÒŻŃŃ–ĐœŃ–Đșті"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ĐąĐŸĐ»Ń‹Ò“Ń‹Ń€Đ°Ò› Đ°Ò›ĐżĐ°Ń€Đ°Ń‚ алу ÒŻŃˆŃ–Đœ Ń‚Đ”Ń€Đ”Đ·Đ”ĐœŃ– жаĐčŃ‹ÒŁŃ‹Đ·."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"ĐšÓ©Ń€Ń–ĐœŃ–ŃŃ‚Ń– Đ¶Đ°Ò›ŃĐ°Ń€Ń‚Ńƒ ÒŻŃˆŃ–Đœ Ó©ŃˆŃ–Ń€Ń–Đż Ò›ĐŸŃŃƒ ĐșДрДĐș пД?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Đ­ĐșŃ€Đ°ĐœĐŽĐ° Đ¶Đ°Ò›ŃŃ‹Ń€Đ°Ò› ĐșÓ©Ń€Ń–ĐœŃƒŃ– ÒŻŃˆŃ–Đœ Ò›ĐŸĐ»ĐŽĐ°ĐœĐ±Đ°ĐœŃ‹ Ó©ŃˆŃ–Ń€Ń–Đż Ò›ĐŸŃŃƒŃ‹ÒŁŃ‹Đ·Ò“Đ° Đ±ĐŸĐ»Đ°ĐŽŃ‹, Đ±Ń–Ń€Đ°Ò› ĐŒÒ±ĐœĐŽĐ°ĐčЎа Đ°Ò“Ń‹ĐŒĐŽĐ°Ò“Ń‹ ĐżŃ€ĐŸĐłŃ€Đ”ŃŃ Ó©ŃˆŃ–Đż Ò›Đ°Đ»ŃƒŃ‹ ĐœĐ”ĐŒĐ”ŃĐ” ŃĐ°Ò›Ń‚Đ°Đ»ĐŒĐ°Ò“Đ°Đœ өзгДрістДрЎі Đ¶ĐŸÒ“Đ°Đ»Ń‚ŃƒŃ‹ÒŁŃ‹Đ· ĐŒÒŻĐŒĐșŃ–Đœ."</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Бас тарту"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"ӹшіріп Ò›ĐŸŃŃƒ"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"ÒšĐ°Đčта ĐșÓ©Ń€ŃĐ”Ń‚Ń–Đ»ĐŒĐ”ŃŃ–Đœ"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Жаю"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"ĐšŃ–ŃˆŃ–Ń€Đ”Đčту"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Đ–Đ°Đ±Ńƒ"</string>
     <string name="back_button_text" msgid="1469718707134137085">"ĐŃ€Ń‚Ò›Đ°"</string>
     <string name="handle_text" msgid="1766582106752184456">"Đ˜ĐŽĐ”ĐœŃ‚ĐžŃ„ĐžĐșĐ°Ń‚ĐŸŃ€"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"ĐąĐŸĐ»Ń‹Ò› эĐșŃ€Đ°Đœ"</string>
     <string name="desktop_text" msgid="1077633567027630454">"ĐšĐŸĐŒĐżŃŒŃŽŃ‚Đ”Ń€ Ń€Đ”Đ¶ĐžĐŒŃ–"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Đ­ĐșŃ€Đ°ĐœĐŽŃ‹ бөлу"</string>
     <string name="more_button_text" msgid="3655388105592893530">"ÒšĐŸŃŃ‹ĐŒŃˆĐ°"</string>
     <string name="float_button_text" msgid="9221657008391364581">"ÒšĐ°Đ»Ò›Ń‹ĐŒĐ°"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index 79aca62..bfcf40b 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ខាងលស 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"ខាងលស 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"ážąáŸáž€áŸ’ážšáž„áŸ‹áž–áŸáž‰ážáž¶áž„áž€áŸ’ážšáŸ„áž˜"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"បំបែក​ខាងឆ្វេង"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"បំបែក​ខាងស្ដាំ"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"បំបែក​ខាងលស"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"បំបែក​ខាងក្រោម"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"កំពុងប្រស​មុខងារប្រសដៃម្ខាង"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"ដសម្បឞចាកចេញ ážŸážŒáž˜ážąážŒážŸážĄážŸáž„áž›ážŸâ€‹áž–ážžáž•áŸ’áž“áŸ‚áž€ážáž¶áž„áž€áŸ’ážšáŸ„áž˜ážąáŸáž€áŸ’ážšáž„áŸ‹ ážŹáž…áž»áž…áž•áŸ’áž“áŸ‚áž€ážŽáž¶áž˜ážœáž™â€‹áž“áŸ…ážáž¶áž„áž›ážŸáž€áž˜áŸ’áž˜ážœáž·áž’ážž"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"ចាប់ផ្ដសម​មុខងារប្រសដៃម្ខាង"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ចុចពឞរដង​នៅ​ក្រៅ​កម្មវិធឞ ដសម្បឞ​ប្ដឌរ​ទឞតាំង​កម្មវិធឞ​នោះ"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"យល់ហសយ"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ពង្រឞកដសម្បឞទទវលបានព័ត៌មានបន្ថែម។"</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"áž…áž¶áž”áŸ‹áž•áŸ’ážŠážŸáž˜â€‹ážĄážŸáž„ážœáž·áž‰ ážŠážŸáž˜áŸ’áž”ážžáž‘áž‘ážœáž›áž”áž¶áž“áž‘áž·ážŠáŸ’áž‹áž—áž¶áž–áž€áž¶áž“áŸ‹ážáŸ‚ážŸáŸ’ážąáž¶ážážŹ?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"ážąáŸ’áž“áž€ážąáž¶áž…áž…áž¶áž”áŸ‹áž•áŸ’ážŠážŸáž˜â€‹áž€áž˜áŸ’áž˜ážœáž·áž’ážžážĄážŸáž„ážœáž·áž‰ ážŠážŸáž˜áŸ’áž”ážžáž±áŸ’áž™áž€áž˜áŸ’áž˜ážœáž·áž’ážžáž“áŸáŸ‡áž˜ážŸáž›áž‘áŸ…ážŸáŸ’ážąáž¶ážáž‡áž¶áž„áž˜áž»áž“áž“áŸ…áž›ážŸážąáŸáž€áŸ’ážšáž„áŸ‹ážšáž”ážŸáŸ‹ážąáŸ’áž“áž€ áž”áŸ‰áž»áž“áŸ’ážáŸ‚ážąáŸ’áž“áž€ážąáž¶áž…áž“ážčáž„áž”áž¶ážáŸ‹áž”áž„áŸ‹ážŠáŸ†ážŽážŸážšáž€áž¶ážšážšáž”ážŸáŸ‹ážąáŸ’áž“áž€ ážŹáž€áž¶ážšáž€áŸ‚áž”áŸ’ážšáŸ‚ážŽáž¶áž˜ážœáž™ážŠáŸ‚áž›ážąáŸ’áž“áž€áž˜áž·áž“áž”áž¶áž“ážšáž€áŸ’ážŸáž¶áž‘áž»áž€"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"បោះបង់"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"áž…áž¶áž”áŸ‹áž•áŸ’ážŠážŸáž˜â€‹ážĄážŸáž„â€‹ážœáž·áž‰"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"កុំ​បង្ហាញ​ម្ដង​ទៀត"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"ពង្រើក"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"បង្រវម"</string>
     <string name="close_button_text" msgid="2913281996024033299">"បិទ"</string>
     <string name="back_button_text" msgid="1469718707134137085">"ថយក្រោយ"</string>
     <string name="handle_text" msgid="1766582106752184456">"ážˆáŸ’áž˜áŸ„áŸ‡ážąáŸ’áž“áž€áž”áŸ’ážšážŸáž”áŸ’ážšáž¶ážŸáŸ‹"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"ឱេក្រង់​ពេញ"</string>
     <string name="desktop_text" msgid="1077633567027630454">"មុខងារកុំព្យឌទ័រ"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"áž˜áž»ážáž„áž¶ážšâ€‹áž”áŸ†áž”áŸ‚áž€â€‹ážąáŸáž€áŸ’ážšáž„áŸ‹"</string>
     <string name="more_button_text" msgid="3655388105592893530">"ច្រសនទៀត"</string>
     <string name="float_button_text" msgid="9221657008391364581">"ឱណ្ដែត"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index 9e9333e..9d2515c 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50% àČźàł‡àČČàČ•àłàČ•àł†"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"30% àČźàł‡àČČàČ•àłàČ•àł†"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"àČ•àł†àČłàȗàČżàČš àČȘàł‚àČ°àłàČŁ àČȘàȰàČŠàł†"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"àȎàČĄàČ•àłàČ•àł† àČ”àČżàČ­àȜàČżàČžàČż"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"àČŹàČČàČ•àłàČ•àł† àČ”àČżàČ­àȜàČżàČžàČż"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"àČźàł‡àČČàČ•àłàČ•àł† àČ”àČżàČ­àȜàČżàČžàČż"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"àČ•àł†àČłàČ•àłàČ•àł† àČ”àČżàČ­àȜàČżàČžàČż"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"àȒàȂàČŠàł àČ•àłˆ àČźàł‹àČĄàł àČŹàČłàČžàłàČ”àłàČŠàȰ àČŹàČ—àłàČ—àł†"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"àČšàČżàČ°àłàȗàČźàČżàČžàČČàł, àČžàłàČ•àłàČ°àł€àČšàł‌àČš àČ•àł†àČłàȗàČżàČšàČżàȂàČŠ àČźàł‡àČČàČ•àłàČ•àł† àČžàłàČ”àłˆàČȘàł àČźàČŸàČĄàČż àȅàČ„àČ”àČŸ àČ†àłàČŻàČȘàł‌àČš àČźàł‡àČČàł† àȎàČČàłàČČàČżàČŻàČŸàČŠàČ°àł‚ àČŸàłàČŻàČŸàČȘàł àČźàČŸàČĄàČż"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"àȒàȂàČŠàł àČ•àłˆ àČźàł‹àČĄàł àȅàČšàłàČšàł àČȘàłàȰàČŸàȰàȂàČ­àČżàČžàČż"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"àČ†àłàČŻàČȘàł àȒàȂàČŠàȰ àČžàłàČ„àČŸàČšàČ”àČšàłàČšàł àČŹàČŠàČČàČŸàČŻàČżàČžàČČàł àȅàČŠàȰ àČčàłŠàȰàČ—àł† àČĄàČŹàČČàł-àČŸàłàČŻàČŸàČȘàł àČźàČŸàČĄàČż"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"àČžàȰàČż"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"àȇàČšàłàČšàČ·àłàČŸàł àČźàČŸàČčàČżàČ€àČżàȗàČŸàȗàČż àČ”àČżàČžàłàČ€àłƒàČ€àČ—àłŠàČłàČżàČžàČż."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"àȉàČ€àłàČ€àČź àČ”àł€àČ•àłàČ·àČŁàł†àȗàČŸàȗàČż àČ†àłàČŻàČȘàł àȅàČšàłàČšàł àČźàČ°àłàČȘàłàȰàČŸàȰàȂàČ­àČżàČžàČŹàł‡àČ•àł†?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"àČšàł€àČ”àł àČ†àłàČŻàČȘàł àȅàČšàłàČšàł àČźàČ°àłàČȘàłàȰàČŸàȰàȂàČ­àČżàČžàČŹàČčàłàČŠàł, àȇàČŠàȰàČżàȂàČŠ àČšàČżàČźàłàČź àČžàłàČ•àłàČ°àł€àČšàł‌àČšàČČàłàČČàČż àČ†àłàČŻàČȘàł àȉàČ€àłàČ€àČźàČ”àČŸàȗàČż àȕàČŸàČŁàČżàČžàłàČ€àłàČ€àČŠàł†, àȆàČŠàČ°àł† àČšàČżàČźàłàČź àČȘàłàȰàȗàČ€àČżàČŻàČšàłàČšàł àȅàČ„àČ”àČŸ àČŻàČŸàČ”àłàČŠàł‡ àȉàČłàČżàČžàČŠ àČŹàČŠàČČàČŸàČ”àČŁàł†àȗàČłàČšàłàČšàł àČšàł€àČ”àł àȕàČłàł†àČŠàłàČ•àłŠàČłàłàČłàČŹàČčàłàČŠàł"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"àȰàČŠàłàČŠàłàČźàČŸàČĄàČż"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"àČźàČ°àłàČȘàłàȰàČŸàȰàȂàČ­àČżàČžàČż"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"àČźàČ€àłàČ€àłŠàČźàłàČźàł† àČ€àł‹àȰàČżàČžàČŹàł‡àČĄàČż"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"àČčàČżàČ—àłàȗàČżàČžàČż"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"àČ•àłàČ—àłàȗàČżàČžàČż"</string>
     <string name="close_button_text" msgid="2913281996024033299">"àČźàłàČšàłàȚàČżàȰàČż"</string>
     <string name="back_button_text" msgid="1469718707134137085">"àČčàČżàȂàČŠàČ•àłàČ•àł†"</string>
     <string name="handle_text" msgid="1766582106752184456">"àČčàłàČŻàČŸàȂàČĄàČČàł"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"àČ«àłàČČàł‌àČžàłàČ•àłàČ°àł€àČšàł"</string>
     <string name="desktop_text" msgid="1077633567027630454">"àČĄàł†àČžàłàČ•àł‌àȟàČŸàČȘàł àČźàł‹àČĄàł"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"àČžàłàČȘàłàČČàČżàČŸàł àČžàłàČ•àłàČ°àł€àČšàł"</string>
     <string name="more_button_text" msgid="3655388105592893530">"àȇàČšàłàČšàČ·àłàČŸàł"</string>
     <string name="float_button_text" msgid="9221657008391364581">"àČ«àłàČČàł‹àČŸàł"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index f9b495a..55007fc 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"위ìȘœ 화멎 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"위ìȘœ 화멎 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"아래ìȘœ 화멎 전ìȎ화멎"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"왌ìȘœìœŒëĄœ 분할"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"였넞ìȘœìœŒëĄœ 분할"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"위ìȘœìœŒëĄœ 분할"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"아래ìȘœìœŒëĄœ 분할"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"한 손 ì‚Źìš© ëȘšë“œ ì‚Źìš©í•˜êž°"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"화멎 하닚에서 ìœ„ëĄœ 슀와읎프하거나 앱 상닚을 íƒ­í•˜ì—Ź ìą…ëŁŒí•©ë‹ˆë‹€."</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"한 손 ì‚Źìš© ëȘšë“œ 시작"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"앱 위ìč˜ë„Œ ìĄ°ì •í•˜ë €ë©Ž 앱 왞부넌 두 ëȈ 탭합니닀."</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"확읞"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"추가 ì •ëłŽëŠ” íŽŒìłì„œ 확읞하섞요."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"화멎에 맞êȌ 볮도록 닀시 시작할êčŒìš”?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"앱을 닀시 시작하멎 화멎에 더 잘 맞êȌ ëłŒ 수는 있지만 진행 상황 또는 저임되지 않은 변êČœì‚Źí•­ì„ 잃을 수도 있슔니닀."</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"췚소"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"닀시 시작"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"닀시 표시 안핚"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"씜대화"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"씜소화"</string>
     <string name="close_button_text" msgid="2913281996024033299">"ë‹«êž°"</string>
     <string name="back_button_text" msgid="1469718707134137085">"ë’€ëĄœ"</string>
     <string name="handle_text" msgid="1766582106752184456">"핞듀"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"전ìČŽ 화멎"</string>
     <string name="desktop_text" msgid="1077633567027630454">"ë°ìŠ€íŹí†± ëȘšë“œ"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"화멎 분할"</string>
     <string name="more_button_text" msgid="3655388105592893530">"ë”ëłŽêž°"</string>
     <string name="float_button_text" msgid="9221657008391364581">"플로팅"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 0858cfc..9691dba 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ÒźŃŃ‚ÒŻĐœĐșÒŻ эĐșŃ€Đ°ĐœĐŽŃ‹ 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"ÒźŃŃ‚ÒŻĐœĐșÒŻ эĐșŃ€Đ°ĐœĐŽŃ‹ 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"ЫлЎыĐčĐșы эĐșŃ€Đ°ĐœĐŽŃ‹ Ń‚ĐŸĐ»ŃƒĐș эĐșŃ€Đ°Đœ Ń€Đ”Đ¶ĐžĐŒĐžĐœĐ” Ó©Ń‚ĐșÓ©Ń€ÒŻÒŻ"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"ĐĄĐŸĐ»ĐłĐŸ Đ±Ó©Đ»ÒŻÒŻ"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"ĐžÒŁĐłĐŸ Đ±Ó©Đ»ÒŻÒŻ"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"ÓšĐčĐŽÓ© Đ±Ó©Đ»ÒŻÒŻ"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"ЫлЎыĐč Đ±Ó©Đ»ÒŻÒŻ"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Бор ĐșĐŸĐ» Ń€Đ”Đ¶ĐžĐŒĐžĐœ ĐșĐŸĐ»ĐŽĐŸĐœŃƒŃƒ"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Чыгуу ÒŻŃ‡ÒŻĐœ эĐșŃ€Đ°ĐœĐŽŃ‹ ылЎыĐč Đ¶Đ°ĐłŃ‹ĐœĐ°Đœ Ó©ĐčĐŽÓ© ŃÒŻŃ€ÒŻÒŁÒŻĐ· жД ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸĐœŃƒĐœ Ó©ĐčĐŽÓ© Đ¶Đ°ĐłŃ‹Đœ Đ±Đ°ŃŃ‹ÒŁŃ‹Đ·"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Бор ĐșĐŸĐ» Ń€Đ”Đ¶ĐžĐŒĐžĐœ Đ±Đ°ŃˆŃ‚ĐŸĐŸ"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ĐšĐŸĐ»ĐŽĐŸĐœĐŒĐŸĐœŃƒ Đ¶Ń‹Đ»ĐŽŃ‹Ń€ŃƒŃƒ ÒŻŃ‡ÒŻĐœ сырт Đ¶Đ°ĐłŃ‹Đœ эĐșĐž Đ¶ĐŸĐ»Ńƒ Ń‚Đ°ĐżŃ‚Đ°ÒŁŃ‹Đ·"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"ĐąÒŻŃˆÒŻĐœĐŽÒŻĐŒ"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ĐąĐŸĐ»ŃƒĐș ĐŒĐ°Đ°Đ»Ń‹ĐŒĐ°Ń‚ алуу ÒŻŃ‡ÒŻĐœ жаĐčып ĐșÓ©Ń€ÒŻÒŁÒŻĐ·."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"ЖаĐșшырааĐș ĐșÓ©Ń€ÒŻÒŻ ÒŻŃ‡ÒŻĐœ Ó©Ń‡ÒŻŃ€ÒŻĐż ĐșÒŻĐčĐłÒŻĐ·Ó©ŃÒŻĐ·Đ±ÒŻ?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Đ­ĐșŃ€Đ°ĐœĐŽĐ° жаĐșшырааĐș ĐșÓ©Ń€ÒŻÒŻ ÒŻŃ‡ÒŻĐœ ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸĐœŃƒ Ó©Ń‡ÒŻŃ€ÒŻĐż ĐșÒŻĐčĐłÒŻĐ·Ó© аласыз, Đ±ĐžŃ€ĐŸĐș атĐșĐ°Ń€Ń‹Đ»ĐłĐ°Đœ Ош жД саĐșŃ‚Đ°Đ»Đ±Đ°ĐłĐ°Đœ Ó©Đ·ĐłÓ©Ń€ÒŻÒŻĐ»Ó©Ń€ Ó©Ń‡ÒŻŃ€ÒŻĐ»ÒŻŃˆÒŻ ĐŒÒŻĐŒĐșÒŻĐœ"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"ĐąĐŸĐșŃ‚ĐŸŃ‚ŃƒŃƒ"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"ÓšŃ‡ÒŻŃ€ÒŻĐż ĐșÒŻĐčĐłÒŻĐ·ÒŻÒŻ"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Đ­ĐșĐžĐœŃ‡Đž ĐșÓ©Ń€ÒŻĐœĐ±Ó©ŃÒŻĐœ"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Đ§ĐŸÒŁĐŸĐčтуу"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"КОчОрДĐčŃ‚ÒŻÒŻ"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Đ–Đ°Đ±ŃƒŃƒ"</string>
     <string name="back_button_text" msgid="1469718707134137085">"АртĐșа"</string>
     <string name="handle_text" msgid="1766582106752184456">"МарĐșДр"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"ĐąĐŸĐ»ŃƒĐș эĐșŃ€Đ°Đœ"</string>
     <string name="desktop_text" msgid="1077633567027630454">"ĐšĐŸĐŒĐżŃŒŃŽŃ‚Đ”Ń€ Ń€Đ”Đ¶ĐžĐŒĐž"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Đ­ĐșŃ€Đ°ĐœĐŽŃ‹ Đ±Ó©Đ»ÒŻÒŻ"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Дагы"</string>
     <string name="float_button_text" msgid="9221657008391364581">"КалĐșŃ‹ĐŒĐ°"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index 8e42aa3..678e085 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ເàș—àșŽàș‡àșȘàșžàș” 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"ເàș—àșŽàș‡àșȘàșžàș” 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"ເàș•àș±àșĄà»œà»‰àșČàșˆà»àș„àșžà»ˆàșĄàșȘàșžàș”"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"ແàșàșàșŠà»‰àșČàș"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"ແàșàșàș‚àș§àșČ"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"ແàșàșà»€àș—àșŽàș‡"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"ແàșàșàș„àșžà»ˆàșĄ"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"àșàșłàș„àș±àș‡à»ƒàșŠà»‰à»‚ໝàș”àșĄàș·àș”àșœàș§"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"ເàșžàș·à»ˆàș­àș­àș­àș, ໃàș«à»‰àș›àș±àș”àș‚àș¶à»‰àș™àșˆàșČàșàș„àșžà»ˆàșĄàșȘàșžàș”àș‚àș­àș‡à»œà»‰àșČàșˆà» àș«àșŒàș· ແàș•àș°àșšà»ˆàș­àș™à»ƒàș”àșà»à»„àș”້àșąàșč່ເໜàș·àș­à»àș­àș±àșš"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"ເàș„àș”່àșĄà»‚ໝàș”àșĄàș·àș”àșœàș§"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ແàș•àș°àșȘàș­àș‡à»€àș—àș·à»ˆàș­à»ƒàșȘ່àș™àș­àșà»àș­àș±àșšà»ƒàș”ໜàș¶à»ˆàș‡à»€àșžàș·à»ˆàș­àșˆàș±àș”àș•àșłà»à»œà»ˆàș‡àș‚àș­àș‡àșĄàș±àș™àș„àș·àș™à»ƒà»à»ˆ"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"ເàș‚àș»à»‰àșČໃàșˆà»àș„້àș§"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"àș‚àș°àș«àșàșČàșà»€àșžàș·à»ˆàș­à»€àșšàșŽà»ˆàș‡àș‚ໍ້àșĄàșčàș™à»€àșžàș”່àșĄà»€àș•àș”àșĄ."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"àșŁàș”àșȘàș°àș•àșČàș”ເàșžàș·à»ˆàș­à»ƒàș«à»‰àșĄàș”àșĄàșžàșĄàșĄàș­àș‡àș—àș”່àș”àș”àș‚àș¶à»‰àș™àșšà»?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"àș—່àșČàș™àșȘàșČàșĄàșČàș”àșŁàș”àșȘàș°àș•àșČàș”ແàș­àș±àșšà»„àș”້ເàșžàș·à»ˆàș­à»ƒàș«à»‰àșĄàș±àș™à»€àșšàșŽà»ˆàș‡àș”àș”àș‚àș¶à»‰àș™à»ƒàș™à»œà»‰àșČàșˆà»àș‚àș­àș‡àș—່àșČàș™ ແàș•່àș—່àșČàș™àș­àșČàș”àșˆàș°àșȘàșčàș™à»€àșȘàșàș„àș§àșČàșĄàș„àș·àșšà»œà»‰àșČ àș«àșŒàș· àșàșČàș™àș›à»ˆàșœàș™à»àș›àș‡àș—àș”່àșšà»à»ˆà»„àș”້àșšàș±àș™àș—àș¶àșà»„àș§à»‰"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"àșàș»àșà»€àș„àș”àș"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"àșŁàș”àșȘàș°àș•àșČàș”"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"àșšà»à»ˆàș•້àș­àș‡àșȘàș°à»àș”àș‡àș­àș”àș"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"àș‚àș°àș«àșàșČàșà»ƒàș«àșà»ˆàșȘàșžàș”"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"àș«àșà»à»‰àș„àș»àș‡"</string>
     <string name="close_button_text" msgid="2913281996024033299">"àș›àșŽàș”"</string>
     <string name="back_button_text" msgid="1469718707134137085">"àșàș±àșšàș„àș·àș™"</string>
     <string name="handle_text" msgid="1766582106752184456">"àșĄàș·àșšàș±àș‡àș„àș±àșš"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"ເàș•àș±àșĄàșˆà»"</string>
     <string name="desktop_text" msgid="1077633567027630454">"ໂໝàș”ເàș”àș±àșȘàș—àș±àș­àșš"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"ແàșšà»ˆàș‡à»œà»‰àșČàșˆà»"</string>
     <string name="more_button_text" msgid="3655388105592893530">"ເàșžàș”່àșĄà»€àș•àș”àșĄ"</string>
     <string name="float_button_text" msgid="9221657008391364581">"àș„àș­àș"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index dc99690..f8a2a0f 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Viršutinis ekranas 50 %"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Viršutinis ekranas 30 %"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Apatinis ekranas viso ekrano reĆŸimu"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Išskaidyti kairėn"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Išskaidyti dešinėn"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Išskaidyti viršuje"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Išskaidyti apačioje"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Vienos rankos reĆŸimo naudojimas"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Jei norite išeiti, perbraukite aukštyn nuo ekrano apačios arba palieskite bet kur virš programos"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Pradėti vienos rankos reĆŸimą"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dukart palieskite uĆŸ programos ribĆł, kad pakeistumėte jos poziciją"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Supratau"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Išskleiskite, jei reikia daugiau informacijos."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Paleisti iš naujo, kad bĆ«tĆł geresnis vaizdas?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Galite iš naujo paleisti programą, kad ji geriau atrodytĆł ekrane, bet galite prarasti eigą ir neišsaugotus pakeitimus"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Atšaukti"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Paleisti iš naujo"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Daugiau neberodyti"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Padidinti"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"SumaĆŸinti"</string>
     <string name="close_button_text" msgid="2913281996024033299">"UĆŸdaryti"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Atgal"</string>
     <string name="handle_text" msgid="1766582106752184456">"Rankenėlė"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Visas ekranas"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Stalinio kompiuterio reĆŸimas"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Išskaidyto ekrano reĆŸimas"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Daugiau"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Slankusis langas"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index bd2eef4..d14bb67 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"AugšdaÄŒa 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"AugšdaÄŒa 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"ApakšdaÄŒu pa visu ekrānu"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Sadalījums pa kreisi"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Sadalījums pa labi"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"SadalÄ«jums augšdaČā"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"SadalÄ«jums apakšdaČā"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Vienas rokas reĆŸÄ«ma izmantošana"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Lai izietu, velciet augšup no ekrāna apakšdaÄŒas vai pieskarieties jebkurā vietā virs lietotnes"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Pāriet vienas rokas reĆŸÄ«mā"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Lai pārvietotu lietotni, veiciet dubultskārienu ārpus lietotnes"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Labi"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Izvērsiet, lai iegĆ«tu plašÄku informāciju."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Vai restartēt, lai uzlabotu skatu?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Varat restartēt lietotni, lai tā labāk izskatÄ«tos ekrānā, taču, iespējams, zaudēsiet paveikto vai nesaglabātas izmaiƆas (ja tādas ir)."</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Atcelt"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Restartēt"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Vairs nerādīt"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Maksimizēt"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Minimizēt"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Aizvērt"</string>
     <string name="back_button_text" msgid="1469718707134137085">"AtpakaČ"</string>
     <string name="handle_text" msgid="1766582106752184456">"Turis"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Pilnekrāna reĆŸÄ«ms"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Darbvirsmas reĆŸÄ«ms"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Sadalīt ekrānu"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Vairāk"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Peldošs"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index d133654..45aeb7f 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -47,6 +47,14 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Đ“ĐŸŃ€ĐœĐžĐŸŃ‚ 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Đ“ĐŸŃ€ĐœĐžĐŸŃ‚ 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Đ”ĐŸĐ»ĐœĐžĐŸŃ‚ ĐœĐ° цДл Đ”ĐșŃ€Đ°Đœ"</string>
+    <!-- no translation found for accessibility_split_left (1713683765575562458) -->
+    <skip />
+    <!-- no translation found for accessibility_split_right (8441001008181296837) -->
+    <skip />
+    <!-- no translation found for accessibility_split_top (2789329702027147146) -->
+    <skip />
+    <!-- no translation found for accessibility_split_bottom (8694551025220868191) -->
+    <skip />
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"ĐšĐŸŃ€ĐžŃŃ‚Đ”ŃšĐ” ĐœĐ° Ń€Đ”Đ¶ĐžĐŒĐŸŃ‚ ŃĐŸ Đ”ĐŽĐœĐ° раĐșа"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"За Ўа ОзлДзДтД, ĐżĐŸĐČлДчДтД ĐœĐ°ĐłĐŸŃ€Đ” ĐŸĐŽ ĐŽĐœĐŸŃ‚ĐŸ ĐœĐ° Đ”ĐșŃ€Đ°ĐœĐŸŃ‚ ОлО ĐŽĐŸĐżŃ€Đ”Ń‚Đ” ĐșаЎД Đ±ĐžĐ»ĐŸ ĐœĐ°ĐŽ аплОĐșацојата"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Đ—Đ°ĐżĐŸŃ‡ĐœĐž ĐłĐŸ Ń€Đ”Đ¶ĐžĐŒĐŸŃ‚ ŃĐŸ Đ”ĐŽĐœĐ° раĐșа"</string>
@@ -82,14 +90,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Đ”ĐŸĐżŃ€Đ”Ń‚Đ” ĐŽĐČапато ĐœĐ°ĐŽĐČĐŸŃ€ ĐŸĐŽ ĐœĐ”ĐșĐŸŃ˜Đ° аплОĐșацоја за Ўа ја ĐżŃ€Đ”ĐŒĐ”ŃŃ‚ĐžŃ‚Đ”"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"СфатоĐČ"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ĐŸŃ€ĐŸŃˆĐžŃ€Đ”Ń‚Đ” за ĐżĐŸĐČĐ”ŃœĐ” ĐžĐœŃ„ĐŸŃ€ĐŒĐ°Ń†ĐžĐž."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Да сД рДстартОра за ĐżĐŸĐŽĐŸĐ±Đ°Ń€ проĐșаз?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"ĐœĐŸĐ¶Đ” Ўа ја рДстартОратД аплОĐșацојата за Ўа ОзглДЎа ĐżĐŸĐŽĐŸĐ±Ń€ĐŸ ĐœĐ° Đ”ĐșŃ€Đ°ĐœĐŸŃ‚, ĐœĐŸ ĐŒĐŸĐ¶Đ” Ўа ĐłĐŸ ĐžĐ·ĐłŃƒĐ±ĐžŃ‚Đ” ĐœĐ°ĐżŃ€Đ”ĐŽĐŸĐșĐŸŃ‚ ОлО ĐœĐ”Đ·Đ°Ń‡ŃƒĐČĐ°ĐœĐžŃ‚Đ” ĐżŃ€ĐŸĐŒĐ”ĐœĐž"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"ОтĐșажО"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Đ Đ”ŃŃ‚Đ°Ń€Ń‚ĐžŃ€Đ°Ń˜"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"ĐĐ” проĐșажуĐČај ĐżĐŸĐČŃ‚ĐŸŃ€ĐœĐŸ"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Đ—ĐłĐŸĐ»Đ”ĐŒĐž"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"ĐœĐžĐœĐžĐŒĐžĐ·ĐžŃ€Đ°Ń˜"</string>
     <string name="close_button_text" msgid="2913281996024033299">"ЗатĐČĐŸŃ€Đž"</string>
     <string name="back_button_text" msgid="1469718707134137085">"ĐĐ°Đ·Đ°ĐŽ"</string>
     <string name="handle_text" msgid="1766582106752184456">"ĐŸŃ€Đ”Đșар"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"ЊДл Đ”ĐșŃ€Đ°Đœ"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Đ Đ”Đ¶ĐžĐŒ за ĐșĐŸĐŒĐżŃ˜ŃƒŃ‚Đ”Ń€"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"ĐŸĐŸĐŽĐ”Đ»Đ”Đœ Đ”ĐșŃ€Đ°Đœ"</string>
     <string name="more_button_text" msgid="3655388105592893530">"ĐŸĐŸĐČĐ”ŃœĐ”"</string>
     <string name="float_button_text" msgid="9221657008391364581">"ЛДбЎДчĐșĐŸ"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index 16927bf..ca5f0f7 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"àŽźà”àŽ•àŽłàŽżà”œ 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"àŽźà”àŽ•àŽłàŽżà”œ 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"àŽ€àŽŸàŽŽà”† àŽȘà”‚à”ŒàŽŁà”àŽŁ àŽžà”àŽ•à”àŽ°à”€à”»"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"àŽ‡àŽŸàŽ€à” àŽ­àŽŸàŽ—àŽ€à”àŽ€à”‡àŽ•à”àŽ•à” àŽ”àŽżàŽ­àŽœàŽżàŽ•à”àŽ•à”àŽ•"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"àŽ”àŽČàŽ€à” àŽ­àŽŸàŽ—àŽ€à”àŽ€à”‡àŽ•à”àŽ•à” àŽ”àŽżàŽ­àŽœàŽżàŽ•à”àŽ•à”àŽ•"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"àŽźà”àŽ•àŽłàŽżàŽČà”‡àŽ•à”àŽ•à” àŽ”àŽżàŽ­àŽœàŽżàŽ•à”àŽ•à”àŽ•"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"àŽ€àŽŸàŽŽà”‡àŽ•à”àŽ•à” àŽ”àŽżàŽ­àŽœàŽżàŽ•à”àŽ•à”àŽ•"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"àŽ’àŽ±à”àŽ±àŽ•à”àŽ•à”ˆ àŽźà”‹àŽĄà” àŽŽàŽ™à”àŽ™àŽšà”† àŽ‰àŽȘàŽŻà”‹àŽ—àŽżàŽ•à”àŽ•àŽŸàŽ‚"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"àŽȘà”àŽ±àŽ€à”àŽ€à” àŽ•àŽŸàŽ•à”àŽ•àŽŸà”», àŽžà”àŽ•à”àŽ°à”€àŽšàŽżàŽšà”àŽ±à”† àŽšà”àŽ”àŽŸà”† àŽšàŽżàŽšà”àŽšà” àŽźà”àŽ•àŽłàŽżàŽČà”‡àŽ•à”àŽ•à” àŽžà”àŽ”à”ˆàŽȘà”àŽȘà” àŽšà”†àŽŻà”àŽŻà”àŽ• àŽ…àŽČà”àŽČà”†àŽ™à”àŽ•àŽżà”œ àŽ†àŽȘà”àŽȘàŽżàŽšà” àŽźà”àŽ•àŽłàŽżàŽČàŽŸàŽŻàŽż àŽŽàŽ”àŽżàŽŸà”†àŽŻà”†àŽ™à”àŽ•àŽżàŽČà”àŽ‚ àŽŸàŽŸàŽȘà”àŽȘà” àŽšà”†àŽŻà”àŽŻà”àŽ•"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"àŽ’àŽ±à”àŽ±àŽ•à”àŽ•à”ˆ àŽźà”‹àŽĄà” àŽ†àŽ°àŽ‚àŽ­àŽżàŽšà”àŽšà”"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"àŽ†àŽȘà”àŽȘàŽżàŽšà”àŽ±à”† àŽžà”àŽ„àŽŸàŽšàŽ‚ àŽźàŽŸàŽ±à”àŽ±àŽŸà”» àŽ…àŽ€àŽżàŽšà” àŽȘà”àŽ±àŽ€à”àŽ€à” àŽĄàŽŹàŽżà”Ÿ àŽŸàŽŸàŽȘà”àŽȘà” àŽšà”†àŽŻà”àŽŻà”àŽ•"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"àŽźàŽšàŽžà”àŽžàŽżàŽČàŽŸàŽŻàŽż"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"àŽ•à”‚àŽŸà”àŽ€à”œ àŽ”àŽżàŽ”àŽ°àŽ™à”àŽ™à”ŸàŽ•à”àŽ•à” àŽ”àŽżàŽ•àŽžàŽżàŽȘà”àŽȘàŽżàŽ•à”àŽ•à”àŽ•."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"àŽźà”†àŽšà”àŽšàŽȘà”àŽȘà”†àŽŸà”àŽŸ àŽ•àŽŸàŽŽà”‌àŽšàŽŻà”‌àŽ•à”àŽ•àŽŸàŽŻàŽż àŽ±à”€àŽžà”àŽ±à”àŽ±àŽŸà”ŒàŽŸà”àŽŸà” àŽšà”†àŽŻà”àŽŻàŽŁà”‹?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"àŽ†àŽȘà”àŽȘà” àŽ±à”€àŽžà”àŽ±à”àŽ±àŽŸà”ŒàŽŸà”àŽŸà” àŽšà”†àŽŻà”àŽŻà”àŽ•àŽŻàŽŸàŽŁà”†àŽ™à”àŽ•àŽżà”œ àŽ‡àŽ€à” àŽšàŽżàŽ™à”àŽ™àŽłà”àŽŸà”† àŽžà”‌àŽ•à”àŽ°à”€àŽšàŽżà”œ àŽźà”†àŽšà”àŽšàŽȘà”àŽȘà”†àŽŸà”àŽŸàŽ€àŽŸàŽŻàŽż àŽ•àŽŸàŽŁà”àŽ‚, àŽŽàŽšà”àŽšàŽŸà”œ àŽ‡àŽ€à”àŽ”àŽ°à”†àŽŻà”àŽłà”àŽł àŽȘà”àŽ°à”‹àŽ—àŽ€àŽżàŽŻà”àŽ‚ àŽžàŽ‚àŽ°àŽ•à”àŽ·àŽżàŽ•à”àŽ•àŽŸàŽ€à”àŽ€ àŽźàŽŸàŽ±à”àŽ±àŽ™à”àŽ™àŽłà”àŽ‚ àŽšàŽżàŽ™à”àŽ™à”ŸàŽ•à”àŽ•à” àŽšàŽ·à”‌àŽŸàŽźàŽŸàŽ•à”àŽ‚"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"àŽ±àŽŠà”àŽŠàŽŸàŽ•à”àŽ•à”àŽ•"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"àŽ±à”€àŽžà”àŽ±à”àŽ±àŽŸà”ŒàŽŸà”àŽŸà” àŽšà”†àŽŻà”àŽŻà”‚"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"àŽ”à”€àŽŁà”àŽŸà”àŽ‚ àŽ•àŽŸàŽŁàŽżàŽ•à”àŽ•àŽ°à”àŽ€à”"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"àŽ”àŽČà”àŽ€àŽŸàŽ•à”àŽ•à”àŽ•"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"àŽšà”†àŽ±à”àŽ€àŽŸàŽ•à”àŽ•à”àŽ•"</string>
     <string name="close_button_text" msgid="2913281996024033299">"àŽ…àŽŸàŽŻà”àŽ•à”àŽ•à”àŽ•"</string>
     <string name="back_button_text" msgid="1469718707134137085">"àŽźàŽŸàŽ™à”àŽ™à”àŽ•"</string>
     <string name="handle_text" msgid="1766582106752184456">"àŽčàŽŸà”»àŽĄàŽżà”œ"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"àŽȘà”‚à”ŒàŽŁà”àŽŁàŽžà”àŽ•à”àŽ°à”€à”»"</string>
     <string name="desktop_text" msgid="1077633567027630454">"àŽĄà”†àŽžà”‌àŽ•à”àŽŸà”‹àŽȘà”àŽȘà” àŽźà”‹àŽĄà”"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"àŽžà”‌àŽ•à”àŽ°à”€à”» àŽ”àŽżàŽ­àŽœàŽšàŽ‚"</string>
     <string name="more_button_text" msgid="3655388105592893530">"àŽ•à”‚àŽŸà”àŽ€à”œ"</string>
     <string name="float_button_text" msgid="9221657008391364581">"àŽ«à”àŽČà”‹àŽŸà”àŽŸà”"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index 264f9a0..9678c5c7 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ДээЮ 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"ДээЮ 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Đ”ĐŸĐŸĐŽ Đ±ÒŻŃ‚ŃĐœ ĐŽŃĐ»ĐłŃŃ†"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Đ—ÒŻÒŻĐœ талЎ хуĐČаах"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Đ‘Đ°Ń€ŃƒŃƒĐœ талЎ хуĐČаах"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"ДээЮ талЎ хуĐČаах"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Đ”ĐŸĐŸĐŽ талЎ хуĐČаах"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Нэг ĐłĐ°Ń€Ń‹Đœ ĐłĐŸŃ€ĐžĐŒŃ‹Đł ашОглаж баĐčĐœĐ°"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Đ“Đ°Ń€Đ°Ń…Ń‹Đœ Ń‚ŃƒĐ»ĐŽ ĐŽŃĐ»ĐłŃŃ†ĐžĐčĐœ ĐŽĐŸĐŸĐŽ хэсгээс Юээш шуЮарч эсĐČŃĐ» апп Юээр Ń…ÒŻŃŃŃĐœ газраа Ń‚ĐŸĐČŃˆĐžĐœĐŸ уу"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Нэг ĐłĐ°Ń€Ń‹Đœ ĐłĐŸŃ€ĐžĐŒŃ‹Đł ŃŃ…Đ»ÒŻÒŻĐ»ŃŃ…"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Аппыг ĐŽĐ°Ń…ĐžĐœ баĐčŃ€Đ»ŃƒŃƒĐ»Đ°Ń…Ń‹Đœ Ń‚ŃƒĐ»ĐŽ ĐłĐ°ĐŽĐœĐ° талЎ ĐœŃŒ Ń…ĐŸŃ‘Ń€ Ń‚ĐŸĐČŃˆĐžĐœĐŸ"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"ОĐčĐ»ĐłĐŸĐ»ĐŸĐŸ"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ĐŃĐŒŃĐ»Ń‚ ĐŒŃĐŽŃŃĐ»ŃĐ» аĐČах Đ±ĐŸĐ» ĐŽŃĐ»ĐłŃĐœŃ ÒŻÒŻ."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"ЄарагЎах баĐčЎлыг саĐčĐ¶Ń€ŃƒŃƒĐ»Đ°Ń…Ń‹Đœ Ń‚ŃƒĐ»ĐŽ ĐŽĐ°Ń…ĐžĐœ ŃŃ…Đ»ÒŻÒŻĐ»ŃŃ… ÒŻÒŻ?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"йа аппыг ĐŽĐ°Ń…ĐžĐœ ŃŃ…Đ»ÒŻÒŻĐ»ŃŃ… Đ±ĐŸĐ»ĐŸĐŒĐ¶Ń‚ĐŸĐč бөгөөЎ ĐžĐœĐłŃŃĐœŃŃŃ€ ŃĐœŃ ĐœŃŒ Ń‚Đ°ĐœŃ‹ ĐŽŃĐ»ĐłŃŃ†ŃĐŽ ĐžĐ»ÒŻÒŻ саĐčĐœ харагЮах хэЮоĐč ч та яĐČцаа эсĐČŃĐ» Ń…Đ°ĐŽĐłĐ°Đ»Đ°Đ°ĐłÒŻĐč алОĐČаа өөрчлөлтөө алЎаж ĐŒĐ°ĐłĐ°ĐŽĐłÒŻĐč"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"ĐŠŃƒŃ†Đ»Đ°Ń…"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Đ”Đ°Ń…ĐžĐœ ŃŃ…Đ»ÒŻÒŻĐ»ŃŃ…"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"ДахОж Đ±ÒŻÒŻ Ń…Đ°Ń€ŃƒŃƒĐ»"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"ĐąĐŸĐŒŃ€ŃƒŃƒĐ»Đ°Ń…"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Багасгах"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Єаах"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Буцах"</string>
     <string name="handle_text" msgid="1766582106752184456">"Đ‘Đ°Ń€ĐžŃƒĐ»"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Đ‘ÒŻŃ‚ŃĐœ ĐŽŃĐ»ĐłŃŃ†"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Đ”ŃĐ»ĐłŃŃ†ĐžĐčĐœ ĐłĐŸŃ€ĐžĐŒ"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Đ”ŃĐ»ĐłŃŃ†ĐžĐčĐł хуĐČаах"</string>
     <string name="more_button_text" msgid="3655388105592893530">"БусаЮ"</string>
     <string name="float_button_text" msgid="9221657008391364581">"ЄөĐČөгч"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index 7a475ed..ea728e8 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"à€¶à„€à€°à„à€· 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"à€¶à„€à€°à„à€· 10"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"à€€à€łà€Ÿà€¶à„€ à€«à„à€Č à€žà„à€•à„à€°à„€à€š"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"à€Ąà€Ÿà€”à„€à€•à€Ąà„‡ à€žà„à€Șà„à€Čà€żà€Ÿ à€•à€°à€Ÿ"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"à€‰à€œà€”à„€à€•à€Ąà„‡ à€žà„à€Șà„à€Čà€żà€Ÿ à€•à€°à€Ÿ"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"à€žà€°à„à€”à€Ÿà€€ à€”à€°à€€à„€ à€žà„à€Șà„à€Čà€żà€Ÿ à€•à€°à€Ÿ"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"à€–à€Ÿà€Čà€€à„€ à€žà„à€Șà„à€Čà€żà€Ÿ à€•à€°à€Ÿ"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"à€à€•à€čà€Ÿà€€à„€ à€źà„‹à€Ą à€”à€Ÿà€Șà€°à€Łà„‡"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"à€Źà€Ÿà€čà„‡à€° à€Șà€Ąà€Łà„à€Żà€Ÿà€žà€Ÿà€ à„€ à€žà„à€•à„à€°à„€à€šà€šà„à€Żà€Ÿ à€–à€Ÿà€Čà„‚à€š à€”à€°à€šà„à€Żà€Ÿ à€Šà€żà€¶à„‡à€šà„‡ à€žà„à€”à€Ÿà€‡à€Ș à€•à€°à€Ÿ à€•à€żà€‚à€”à€Ÿ à„Čà€Șà€”à€° à€•à„‹à€ à„‡à€čà„€ à€Ÿà„…à€Ș à€•à€°à€Ÿ"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"à€à€•à€čà€Ÿà€€à„€ à€źà„‹à€Ą à€žà„à€°à„‚ à€•à€°à€Ÿ"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"à„Čà€Șà€šà„€ à€žà„à€„à€żà€€à„€ à€Șà„à€šà„à€čà€Ÿ à€Źà€Šà€Čà€Łà„à€Żà€Ÿà€žà€Ÿà€ à„€, à€€à„à€Żà€Ÿà€šà„à€Żà€Ÿ à€Źà€Ÿà€čà„‡à€° à€Šà„‹à€šà€Šà€Ÿ à€Ÿà„…à€Ș à€•à€°à€Ÿ"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"à€žà€źà€œà€Čà„‡"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"à€…à€§à€żà€• à€źà€Ÿà€čà€żà€€à„€à€žà€Ÿà€ à„€ à€”à€żà€žà„à€€à€Ÿà€° à€•à€°à€Ÿ."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"à€†à€Łà€–à„€ à€šà€Ÿà€‚à€—à€Čà„à€Żà€Ÿ à€Șà„à€°à€•à€Ÿà€°à„‡ à€Šà€żà€žà€Ÿà€”à„‡ à€Żà€Ÿà€žà€Ÿà€ à„€ à€°à„€à€žà„à€Ÿà€Ÿà€°à„à€Ÿ à€•à€°à€Ÿà€Żà€šà„‡ à€†à€čà„‡ à€•à€Ÿ?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"à€€à„à€źà„à€čà„€ à€…‍à„…à€Ș à€°à„€à€žà„à€Ÿà€Ÿà€°à„à€Ÿ à€•à€°à„‚ à€¶à€•à€€à€Ÿ, à€œà„‡à€Łà„‡à€•à€°à„‚à€š à€€à„‡ à€€à„à€źà€šà„à€Żà€Ÿ à€žà„à€•à„à€°à„€à€šà€”à€° à€†à€Łà€–à„€ à€šà€Ÿà€‚à€—à€Čà„à€Żà€Ÿ à€Șà„à€°à€•à€Ÿà€°à„‡ à€Šà€żà€žà„‡à€Č, à€Șà€Ł à€€à„à€źà€šà„€ à€Șà„à€°à€—à€€à„€ à€•à€żà€‚à€”à€Ÿ à€•à„‹à€Łà€€à„‡à€čà„€ à€žà„‡à€”à„à€č à€š à€•à„‡à€Čà„‡à€Čà„‡ à€Źà€Šà€Č à€€à„à€źà„à€čà„€ à€—à€źà€”à€Ÿà€Č"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"à€°à€Šà„à€Š à€•à€°à€Ÿ"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"à€°à„€à€žà„à€Ÿà€Ÿà€°à„à€Ÿ à€•à€°à€Ÿ"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"à€Șà„à€šà„à€čà€Ÿ à€Šà€Ÿà€–à€”à„‚ à€šà€•à€Ÿ"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"à€źà„‹à€ à„‡ à€•à€°à€Ÿ"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"à€Čà€čà€Ÿà€š à€•à€°à€Ÿ"</string>
     <string name="close_button_text" msgid="2913281996024033299">"à€Źà€‚à€Š à€•à€°à€Ÿ"</string>
     <string name="back_button_text" msgid="1469718707134137085">"à€źà€Ÿà€—à„‡ à€œà€Ÿ"</string>
     <string name="handle_text" msgid="1766582106752184456">"à€čà€à€Ąà€Č"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"à€«à„à€Čà€žà„‍à€•à„à€°à„€à€š"</string>
     <string name="desktop_text" msgid="1077633567027630454">"à€Ąà„‡à€žà„à€•à€Ÿà„‰à€Ș à€źà„‹à€Ą"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"à€žà„à€Șà„à€Čà€żà€Ÿ à€žà„à€•à„à€°à„€à€š"</string>
     <string name="more_button_text" msgid="3655388105592893530">"à€†à€Łà€–à„€"</string>
     <string name="float_button_text" msgid="9221657008391364581">"à€«à„à€Čà„‹à€Ÿ"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index be1dc24..8a9b81c 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Atas 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Atas 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Skrin penuh bawah"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Pisah kiri"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Pisah kanan"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Pisah atas"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Pisah bawah"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Menggunakan mod sebelah tangan"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Untuk keluar, leret ke atas daripada bahagian bawah skrin atau ketik pada mana-mana di bahagian atas apl"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Mulakan mod sebelah tangan"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Ketik dua kali di luar apl untuk menempatkan semula apl itu"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Kembangkan untuk mendapatkan maklumat lanjut."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Mulakan semula untuk mendapatkan paparan yang lebih baik?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Anda boleh memulakan semula apl supaya apl kelihatan lebih baik pada skrin anda tetapi anda mungkin akan hilang kemajuan anda atau apa-apa perubahan yang belum disimpan"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Batal"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Mulakan semula"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Jangan tunjukkan lagi"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Maksimumkan"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Minimumkan"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Tutup"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Kembali"</string>
     <string name="handle_text" msgid="1766582106752184456">"Pemegang"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Skrin penuh"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Mod Desktop"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Skrin Pisah"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Lagi"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Terapung"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index 7b2b7c5..b37bfa7 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ဥပေါá€șဘကá€ș မျကá€șá€”á€Ÿá€Źá€•á€Œá€„á€ș ၅၀%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"ဥပေါá€șဘကá€ș မျကá€șá€”á€Ÿá€Źá€•á€Œá€„á€ș ၃၀%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"ဥေဏကá€șခဌေ မျကá€șá€”á€Ÿá€Źá€•á€Œá€„á€șá€Ąá€•á€Œá€Šá€·á€ș"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"ဘယá€șဘကá€șကို ခလá€Čရနá€ș"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"ညာဘကá€șကို ခလá€Čရနá€ș"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"ထိပá€șပိုငá€șှကို ခလá€Čရနá€ș"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"ဥေဏကá€șá€á€Œá€±á€€á€­á€Ż ခလá€Čရနá€ș"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"လကá€șတစá€șဖကá€șသုံသမုဒá€ș á€Ąá€žá€Żá€¶á€žá€•á€Œá€Żá€á€Œá€„á€șှ"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"ထလကá€șရနá€ș ဖနá€șá€žá€Źá€žá€•á€Œá€„á€șáá€Ąá€±á€Źá€€á€șခဌေမဟ ဥပေါá€șá€žá€­á€Żá€·á€•á€œá€á€șဆလá€Čပါ á€žá€­á€Żá€·á€™á€Ÿá€Żá€á€ș အကá€șပá€șဥပေါá€șဘကá€ș မညá€șသည့á€șá€”á€±á€›á€Źá€á€œá€„á€șမဆို တို့ပါ"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"လကá€șတစá€șဖကá€șသုံသမုဒá€șကို စတငá€șလိုကá€șသညá€ș"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"á€”á€±á€›á€Źá€•á€Œá€”á€șချရနá€ș အကá€șပá€șá€Ąá€•á€Œá€„á€șဘကá€șကို နဟစá€șချကá€șတို့ပါ"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"နာှလညá€șá€•á€Œá€ź"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"နေဏကá€șထပá€șဥချကá€șအလကá€șá€™á€»á€Źá€žá€Ąá€á€œá€€á€ș ချá€Č့နိုငá€șသညá€ș။"</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"ပိုကေဏငá€șá€žá€žá€±á€Źá€™á€Œá€„á€șကလငá€șá€žá€Ąá€á€œá€€á€ș ပဌနá€șစမလာှ။"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"အကá€șပá€șကို ပဌနá€șစပါက ၎ငá€șှသညá€ș စခရငá€șပေါá€șတလငá€ș á€•á€­á€Żá€€á€Œá€Šá€·á€șကေဏငá€șá€žá€žá€œá€Źá€žá€žá€±á€Źá€șလညá€șှ သငá€ș၏ လုပá€șငနá€șှစဉá€ș (သို့) မသိမá€șသရသေသသေဏ á€Ąá€•á€Œá€±á€Źá€„á€șှအလá€Čမျဏသကို á€†á€Żá€¶á€žá€›á€Ÿá€Żá€¶á€žá€”á€­á€Żá€„á€șသညá€ș"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"မလုပá€șတေဏ့"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"ပဌနá€șစရနá€ș"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"နေဏကá€șထပá€șမပဌပါနဟင့á€ș"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"ချá€Č့ရနá€ș"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"ချုံ့ရနá€ș"</string>
     <string name="close_button_text" msgid="2913281996024033299">"ပိတá€șရနá€ș"</string>
     <string name="back_button_text" msgid="1469718707134137085">"နေဏကá€șသို့"</string>
     <string name="handle_text" msgid="1766582106752184456">"သုံသသူဥမညá€ș"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"ဖနá€șá€žá€Źá€žá€•á€Œá€„á€șá€Ąá€•á€Œá€Šá€·á€ș"</string>
     <string name="desktop_text" msgid="1077633567027630454">"ဒကá€șစá€șတေဏ့မုဒá€ș"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"မျကá€șá€”á€Ÿá€Źá€•á€Œá€„á€ș ခလá€Č၍ပဌသရနá€ș"</string>
     <string name="more_button_text" msgid="3655388105592893530">"á€•á€­á€Żá€•á€Œá€•á€«"</string>
     <string name="float_button_text" msgid="9221657008391364581">"á€™á€»á€Ÿá€±á€Źá€›á€”á€ș"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index 3e18b49..7c96f0d 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Sett størrelsen på den øverste delen av skjermen til 50 %"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Sett størrelsen på den øverste delen av skjermen til 30 %"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Utvid den nederste delen av skjermen til hele skjermen"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Del opp til venstre"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Del opp til høyre"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Del opp øverst"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Del opp nederst"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Bruk av enhåndsmodus"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"For å avslutte, sveip opp fra bunnen av skjermen eller trykk hvor som helst over appen"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Start enhåndsmodus"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dobbelttrykk utenfor en app for å flytte den"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Greit"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Vis for å få mer informasjon."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Vil du starte på nytt for bedre visning?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Du kan starte appen på nytt, slik at den ser bedre ut på skjermen, men du kan miste fremdrift eller ulagrede endringer"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Avbryt"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Start på nytt"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ikke vis dette igjen"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Maksimer"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Minimer"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Lukk"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Tilbake"</string>
     <string name="handle_text" msgid="1766582106752184456">"Håndtak"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Fullskjerm"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Skrivebordmodus"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Delt skjerm"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Mer"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Svevende"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index 4b10f5a..8a89d4c 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"à€źà€Ÿà€„à€żà€Čà„à€Čà„‹ à€­à€Ÿà€— à„«à„Š%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"à€źà€Ÿà€„à€żà€Čà„à€Čà„‹ à€­à€Ÿà€— à„©à„Š%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"à€€à€Čà„à€Čà„‹ à€­à€Ÿà€— à€«à„à€Č à€žà„à€•à„à€°à€żà€š"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"à€Źà€Ÿà€Żà€Ÿà€à€€à€żà€° à€žà„à€Șà„à€Čà€żà€Ÿ à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"à€Šà€Ÿà€Żà€Ÿà€à€€à€żà€° à€žà„à€Șà„à€Čà€żà€Ÿ à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"à€žà€żà€°à€Ÿà€šà€€à€żà€° à€žà„à€Șà„à€Čà€żà€Ÿ à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"à€Șà„à€›à€Ÿà€°à€€à€żà€° à€žà„à€Șà„à€Čà€żà€Ÿ à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"à€à€• à€čà€Ÿà€€à„‡ à€źà„‹à€Ą à€Șà„à€°à€Żà„‹à€— à€—à€°à€żà€à€Šà„ˆ à€›"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"à€Źà€Ÿà€čà€żà€° à€šà€żà€žà„à€•à€š, à€žà„à€•à„à€°à€żà€šà€•à„‹ à€Șà„à€›à€Ÿà€°à€Źà€Ÿà€Ÿ à€źà€Ÿà€„à€żà€€à€żà€° à€žà„à€”à€Ÿà€‡à€Ș à€—à€°à„à€šà„à€čà„‹à€žà„ à€”à€Ÿ à€à€Șà€­à€šà„à€Šà€Ÿ à€źà€Ÿà€„à€ż à€œà„à€šà€žà„à€•à„ˆ à€ à€Ÿà€‰à€à€źà€Ÿ à€Ÿà„à€Żà€Ÿà€Ș à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"à€à€• à€čà€Ÿà€€à„‡ à€źà„‹à€Ą à€žà„à€°à„ à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"à€€à€Șà€Ÿà€ˆà€‚ à€œà„à€š à€à€Șà€•à„‹ à€žà„à€„à€żà€€à€ż à€źà€żà€Čà€Ÿà€‰à€š à€šà€Ÿà€čà€šà„à€čà„à€šà„à€› à€žà„‹à€čà„€ à€à€Șà€•à„‹ à€Źà€Ÿà€čà€żà€° à€Ąà€Źà€Č à€Ÿà„à€Żà€Ÿà€Ș à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"à€Źà„à€à„‡à€"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"à€„à€Ș à€œà€Ÿà€šà€•à€Ÿà€°à„€ à€Șà„à€°à€Ÿà€Șà„à€€ à€—à€°à„à€š à€šà€Ÿà€čà€šà„à€čà„à€šà„à€› à€­à€šà„‡ à€à€•à„à€žà„à€Șà€Ÿà€šà„à€Ą à€—à€°à„à€šà„à€čà„‹à€žà„à„€"</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"à€…à€ à€°à€Ÿà€źà„à€°à„‹à€žà€à€— à€Šà„‡à€–à€żà€šà„‡ à€Źà€šà€Ÿà€‰à€š à€à€Ș à€°à€żà€žà„à€Ÿà€Ÿà€°à„à€Ÿ à€—à€°à„à€šà„‡ à€čà„‹?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"à€Żà„‹ à€à€Ș à€€à€Șà€Ÿà€ˆà€‚à€•à„‹ à€žà„à€•à„à€°à€żà€šà€źà€Ÿ à€…à€ à€°à€Ÿà€źà„à€°à„‹à€žà€à€— à€Šà„‡à€–à€żà€Żà„‹à€žà„ à€­à€šà„à€šà€Ÿà€•à€Ÿ à€Čà€Ÿà€—à€ż à€€à€Șà€Ÿà€ˆà€‚ à€žà„‹ à€à€Ș à€°à€żà€žà„à€Ÿà€Ÿà€°à„à€Ÿ à€—à€°à„à€š à€žà€•à„à€šà„à€čà„à€šà„à€› à€€à€° à€€à€Șà€Ÿà€ˆà€‚à€Čà„‡ à€…à€čà€żà€Čà„‡à€žà€źà„à€ź à€—à€°à„‡à€•à€Ÿ à€•à„à€°à€żà€Żà€Ÿà€•à€Čà€Ÿà€Ș à€”à€Ÿ à€žà„‡à€­ à€—à€°à„à€š à€Źà€Ÿà€à€•à„€ à€Șà€°à€żà€”à€°à„à€€à€šà€čà€°à„‚ à€čà€Ÿà„à€š à€žà€•à„à€›à€šà„"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"à€°à€Šà„à€Š à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"à€°à€żà€žà„à€Ÿà€Ÿà€°à„à€Ÿ à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"à€«à„‡à€°à€ż à€šà€Šà„‡à€–à€Ÿà€‡à€Żà„‹à€žà„"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"à€ à„à€Čà„‹ à€Źà€šà€Ÿà€‰à€šà„à€čà„‹à€žà„"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"à€źà€żà€šà€żà€źà€Ÿà€‡à€œ à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
     <string name="close_button_text" msgid="2913281996024033299">"à€Źà€šà„à€Š à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
     <string name="back_button_text" msgid="1469718707134137085">"à€Șà€›à€Ÿà€Ąà€ż"</string>
     <string name="handle_text" msgid="1766582106752184456">"à€čà„à€Żà€Ÿà€šà„à€Ąà€Č"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"à€«à„à€Č à€žà„à€•à„à€°à€żà€š"</string>
     <string name="desktop_text" msgid="1077633567027630454">"à€Ąà„‡à€žà„à€•à€Ÿà€Ș à€źà„‹à€Ą"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"à€žà„à€Șà„à€Čà€żà€Ÿ à€žà„à€•à„à€°à€żà€š"</string>
     <string name="more_button_text" msgid="3655388105592893530">"à€„à€Ș"</string>
     <string name="float_button_text" msgid="9221657008391364581">"à€«à„à€Čà„‹à€Ÿ"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index b056483..1f0b6d2 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Bovenste scherm 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Bovenste scherm 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Onderste scherm op volledig scherm"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Gesplitst scherm links"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Gesplitst scherm rechts"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Gesplitst scherm boven"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Gesplitst scherm onder"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Bediening met 1 hand gebruiken"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Als je wilt afsluiten, swipe je omhoog vanaf de onderkant van het scherm of tik je ergens boven de app"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Bediening met 1 hand starten"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dubbeltik naast een app om deze opnieuw te positioneren"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Uitvouwen voor meer informatie."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Opnieuw opstarten voor een betere weergave?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Je kunt de app opnieuw opstarten zodat deze er beter uitziet op je scherm, maar je kunt je voortgang of niet-opgeslagen wijzigingen kwijtraken"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Annuleren"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Opnieuw opstarten"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Niet opnieuw tonen"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Maximaliseren"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Minimaliseren"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Sluiten"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Terug"</string>
     <string name="handle_text" msgid="1766582106752184456">"Gebruikersnaam"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Volledig scherm"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Desktopmodus"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Gesplitst scherm"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Meer"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Zwevend"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index 5fd81f4..edfe3f0 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"àŹ‰àŹȘàŹ° àŹ†àŹĄàŹŒàŹ•à­ 50% àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"àŹ‰àŹȘàŹ° àŹ†àŹĄàŹŒàŹ•à­ 30% àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"àŹ€àŹł àŹ…àŹ‚àŹ¶àŹ° àŹȘà­‚àŹ°à­àŹŁà­àŹŁ àŹžà­àŹ•à­àŹ°à­€àŹšà­‍"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"àŹŹàŹŸàŹźàŹȘàŹŸàŹ•à­ àŹžà­àŹȘ୍àŹČàŹżàŹŸ àŹ•àŹ°àŹšà­àŹ€à­"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"àŹĄàŹŸàŹčàŹŸàŹŁàŹȘàŹŸàŹ•à­ àŹžà­àŹȘ୍àŹČàŹżàŹŸ àŹ•àŹ°àŹšà­àŹ€à­"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"àŹ¶à­€àŹ°à­àŹ·àŹ•à­ àŹžà­àŹȘ୍àŹČàŹżàŹŸ àŹ•àŹ°àŹšà­àŹ€à­"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"àŹšàŹżàŹźà­àŹšàŹ•à­ àŹžà­àŹČàŹżàŹŸ àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"àŹàŹ•-àŹčàŹŸàŹ€ àŹźà­‹àŹĄà­ àŹŹà­à­ŸàŹŹàŹčàŹŸàŹ° àŹ•àŹ°àŹż"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"àŹŹàŹŸàŹčàŹŸàŹ°àŹż àŹŻàŹżàŹŹàŹŸ àŹȘàŹŸàŹ‡àŹ, àŹžà­àŹ•à­àŹ°àŹżàŹšàŹ° àŹ€àŹłà­ àŹ‰àŹȘàŹ°àŹ•à­ àŹžà­à­±àŹŸàŹ‡àŹȘ୍ àŹ•àŹ°àŹšà­àŹ€à­ àŹ•àŹżàŹźà­àŹŹàŹŸ àŹ†àŹȘàŹ°à­‡ àŹŻà­‡ àŹ•à­ŒàŹŁàŹžàŹż àŹžà­àŹ„àŹŸàŹšàŹ°à­‡ àŹŸàŹŸàŹȘ୍ àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"àŹàŹ•-àŹčàŹŸàŹ€ àŹźà­‹àŹĄà­ àŹ†àŹ°àŹźà­àŹ­ àŹ•àŹ°àŹšà­àŹ€à­"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"àŹàŹ• àŹ†àŹȘàŹ•à­ àŹ°àŹżàŹȘà­‹àŹœàŹżàŹžàŹš àŹ•àŹ°àŹżàŹŹàŹŸ àŹȘàŹŸàŹ‡àŹ àŹàŹčàŹŸàŹ° àŹŹàŹŸàŹčàŹŸàŹ°à­‡ àŹŠà­àŹ‡àŹ„àŹ°-àŹŸàŹŸàŹȘ àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"àŹŹà­àŹàŹżàŹ—àŹČàŹż"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"àŹ…àŹ§àŹżàŹ• àŹžà­‚àŹšàŹšàŹŸ àŹȘàŹŸàŹ‡àŹ àŹŹàŹżàŹžà­àŹ€àŹŸàŹ° àŹ•àŹ°àŹšà­àŹ€à­à„€"</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"àŹàŹ• àŹ­àŹČ àŹ­à­à­Ÿà­ àŹȘàŹŸàŹ‡àŹ àŹ°àŹżàŹ·à­àŹŸàŹŸàŹ°à­àŹŸ àŹ•àŹ°àŹżàŹŹà­‡?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"àŹ†àŹȘàŹŁ àŹ†àŹȘàŹ•à­ àŹ°àŹżàŹ·à­àŹŸàŹŸàŹ°à­àŹŸ àŹ•àŹ°àŹżàŹȘàŹŸàŹ°àŹżàŹŹà­‡ àŹŻàŹŸàŹčàŹŸ àŹ«àŹłàŹ°à­‡ àŹàŹčàŹŸ àŹ†àŹȘàŹŁàŹ™à­àŹ• àŹžà­àŹ•à­àŹ°àŹżàŹšàŹ°à­‡ àŹ†àŹčà­àŹ°àŹż àŹ­àŹČ àŹŠà­‡àŹ–àŹŸàŹŻàŹżàŹŹ, àŹ•àŹżàŹšà­àŹ€à­ àŹ†àŹȘàŹŁ àŹ†àŹȘàŹŁàŹ™à­àŹ• àŹȘà­àŹ°àŹ—àŹ€àŹż àŹ•àŹżàŹźà­àŹŹàŹŸ àŹžà­‡àŹ­ àŹčà­‹àŹ‡àŹšàŹ„àŹżàŹŹàŹŸ àŹŻà­‡ àŹ•à­ŒàŹŁàŹžàŹż àŹȘàŹ°àŹżàŹŹàŹ°à­àŹ€à­àŹ€àŹš àŹčàŹ°àŹŸàŹ‡àŹȘàŹŸàŹ°àŹšà­àŹ€àŹż"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"àŹŹàŹŸàŹ€àŹżàŹČ àŹ•àŹ°àŹšà­àŹ€à­"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"àŹ°àŹżàŹ·à­àŹŸàŹŸàŹ°à­àŹŸ àŹ•àŹ°àŹšà­àŹ€à­"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"àŹȘà­àŹŁàŹż àŹŠà­‡àŹ–àŹŸàŹšà­àŹ€à­ àŹšàŹŸàŹčàŹżàŹ"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"àŹŹàŹĄàŹŒ àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"àŹ›à­‹àŹŸ àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="close_button_text" msgid="2913281996024033299">"àŹŹàŹšà­àŹŠ àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="back_button_text" msgid="1469718707134137085">"àŹȘàŹ›àŹ•à­ àŹ«à­‡àŹ°àŹšà­àŹ€à­"</string>
     <string name="handle_text" msgid="1766582106752184456">"àŹčà­‡àŹŁà­àŹĄà­‡àŹČ"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"àŹȘà­‚àŹ°à­àŹŁà­àŹŁàŹžà­àŹ•à­àŹ°àŹżàŹš"</string>
     <string name="desktop_text" msgid="1077633567027630454">"àŹĄà­‡àŹžà­àŹ•àŹŸàŹȘ àŹźà­‹àŹĄ"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"àŹžà­àŹȘ୍àŹČàŹżàŹŸ àŹžà­àŹ•à­àŹ°àŹżàŹš"</string>
     <string name="more_button_text" msgid="3655388105592893530">"àŹ…àŹ§àŹżàŹ•"</string>
     <string name="float_button_text" msgid="9221657008391364581">"àŹ«à­àŹČà­‹àŹŸ"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index ada79d1..8c2ad22 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"àš‰à©±àšȘàš° 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"àš‰à©±àšȘàš° 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"àščà©‡àš àšŸàš‚ àšȘà©‚àš°à©€ àšžàš•à©àš°à©€àšš"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"àš–à©±àšŹà©‡ àšȘàšŸàšžà©‡ àš”à©°àšĄà©‹"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"àšžà©±àšœà©‡ àšȘàšŸàšžà©‡ àš”à©°àšĄà©‹"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"àšžàšżàš–àš° \'àš€à©‡ àš”à©°àšĄà©‹"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"àščà©‡àš àšŸàš‚ àš”à©°àšĄà©‹"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"àš‡à©±àš• àščà©±àš„ àšźà©‹àšĄ àš”àš°àš€àšŁàšŸ"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"àšŹàšŸàščàš° àšœàšŸàšŁ àšČàšˆ, àšžàš•à©àš°à©€àšš àšŠà©‡ àščà©‡àš àšŸàš‚ àš€à©‹àš‚ àš‰à©±àšȘàš° àš”à©±àšČ àšžàš”àšŸàšˆàšȘ àš•àš°à©‹ àšœàšŸàš‚ àšàšȘ \'àš€à©‡ àš•àšżàš€à©‡ àš”à©€ àšŸà©ˆàšȘ àš•àš°à©‹"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"àš‡à©±àš• àščà©±àš„ àšźà©‹àšĄ àšžàšŒà©àš°à©‚ àš•àš°à©‹"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"àš•àšżàšžà©‡ àšàšȘ àšŠà©€ àšœàš—à©àščàšŸ àšŹàšŠàšČàšŁ àšČàšˆ àš‰àšž àšŠà©‡ àšŹàšŸàščàš° àšĄàšŹàšČ àšŸà©ˆàšȘ àš•àš°à©‹"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"àšžàšźàš àšČàšżàš†"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"àščà©‹àš° àšœàšŸàšŁàš•àšŸàš°à©€ àšČàšˆ àš”àšżàšžàš€àšŸàš° àš•àš°à©‹à„€"</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"àš•à©€ àšŹàšżàščàš€àš° àšŠà©àš°àšżàšžàšŒ àšČàšˆ àšàšȘ àššà©‚à©° àšźà©à©œ-àšžàšŒà©àš°à©‚ àš•àš°àššàšŸ àščੈ?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"àš€à©àšžà©€àš‚ àšàšȘ àššà©‚à©° àšźà©à©œ-àšžàšŒà©àš°à©‚ àš•àš° àšžàš•àšŠà©‡ àščੋ àš€àšŸàš‚ àšœà©‹ àš‡àšč àš€à©àščàšŸàšĄà©€ àšžàš•à©àš°à©€àšš \'àš€à©‡ àšŹàšżàščàš€àš° àšŠàšżàšžà©‡, àšȘàš° àš€à©àšžà©€àš‚ àš†àšȘàšŁà©€ àšȘà©àš°àš—àš€à©€ àšœàšŸàš‚ àš•àšżàšžà©‡ àš…àšŁàš°à©±àš–àšżàš…àš€ àš€àšŹàšŠà©€àšČੀ àššà©‚à©° àš—à©àš† àšžàš•àšŠà©‡ àščੋ"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"àš°à©±àšŠ àš•àš°à©‹"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"àšźà©à©œ-àšžàšŒà©àš°à©‚ àš•àš°à©‹"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"àšŠà©àšŹàšŸàš°àšŸ àššàšŸ àšŠàšżàš–àšŸàš“"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"àš”à©±àšĄàšŸ àš•àš°à©‹"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"àš›à©‹àšŸàšŸ àš•àš°à©‹"</string>
     <string name="close_button_text" msgid="2913281996024033299">"àšŹà©°àšŠ àš•àš°à©‹"</string>
     <string name="back_button_text" msgid="1469718707134137085">"àšȘàšżà©±àš›à©‡"</string>
     <string name="handle_text" msgid="1766582106752184456">"àščà©ˆàš‚àšĄàšČ"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"àšȘà©‚àš°à©€-àšžàš•à©àš°à©€àšš"</string>
     <string name="desktop_text" msgid="1077633567027630454">"àšĄà©ˆàšžàš•àšŸàšŸàšȘ àšźà©‹àšĄ"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"àšžàšȘàšČàšżàšŸ àšžàš•à©àš°à©€àšš"</string>
     <string name="more_button_text" msgid="3655388105592893530">"àščà©‹àš°"</string>
     <string name="float_button_text" msgid="9221657008391364581">"àš«àšŒàšČà©‹àšŸ"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index a97fd5c..191f6de 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50% górnej częƛci ekranu"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"30% górnej częƛci ekranu"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Dolna częƛć ekranu na peƂnym ekranie"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Podziel po lewej"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Podziel po prawej"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Podziel u góry"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Podziel u doƂu"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Korzystanie z trybu jednej ręki"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Aby zamknąć, przesuƄ palcem z doƂu ekranu w górę lub kliknij dowolne miejsce nad aplikacją"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Uruchom tryb jednej ręki"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Kliknij dwukrotnie poza aplikacją, aby ją przenieƛć"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"RozwiƄ, aby wyƛwietlić więcej informacji."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Uruchomić ponownie dla lepszego wyglądu?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"MoĆŒesz ponownie uruchomić aplikację, aby lepiej wyglądaƂa na ekranie, ale istnieje ryzyko, ĆŒe utracisz postępy i niezapisane zmiany"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Anuluj"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Uruchom ponownie"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Nie pokazuj ponownie"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Maksymalizuj"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Minimalizuj"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Zamknij"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Wstecz"</string>
     <string name="handle_text" msgid="1766582106752184456">"Uchwyt"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"PeƂny ekran"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Tryb pulpitu"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Podzielony ekran"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Więcej"</string>
     <string name="float_button_text" msgid="9221657008391364581">"PƂywające"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index 8edcddf..82409f4 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Parte superior a 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Parte superior a 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Parte inferior em tela cheia"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Dividir para a esquerda"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Dividir para a direita"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Dividir para cima"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Dividir para baixo"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Como usar o modo para uma mão"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Para sair, deslize de baixo para cima na tela ou toque em qualquer lugar acima do app"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar o modo para uma mão"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Toque duas vezes fora de um app para reposicionar"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendi"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Abra para ver mais informações."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Reiniciar para melhorar a visualização?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Você pode reiniciar o app para melhorar a visualização dele, mas talvez perca seu progresso ou mudanças não salvas"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Cancelar"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Reiniciar"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Não mostrar novamente"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Maximizar"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Minimizar"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Fechar"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Voltar"</string>
     <string name="handle_text" msgid="1766582106752184456">"Alça"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Tela cheia"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Modo área de trabalho"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Tela dividida"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Mais"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Ponto flutuante"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index e0636d4..f33a0ec 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50% no ecrã superior"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"30% no ecrã superior"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Ecrã inferior inteiro"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Divisão à esquerda"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Divisão à direita"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Divisão na parte superior"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Divisão na parte inferior"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Utilize o modo para uma mão"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Para sair, deslize rapidamente para cima a partir da parte inferior do ecrã ou toque em qualquer ponto acima da app."</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar o modo para uma mão"</string>
@@ -82,14 +86,24 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Toque duas vezes fora de uma app para a reposicionar"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Expandir para obter mais informações"</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Reiniciar para uma melhor visualização?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Pode reiniciar a app para ficar com um melhor aspeto no seu ecrã, mas pode perder o seu progresso ou eventuais alterações não guardadas"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Cancelar"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Reiniciar"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Não mostrar de novo"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Maximizar"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Minimizar"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Fechar"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Anterior"</string>
     <string name="handle_text" msgid="1766582106752184456">"Indicador"</string>
+    <string name="app_icon_text" msgid="2823268023931811747">"Ícone da app"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Ecrã inteiro"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Modo de ambiente de trabalho"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Ecrã dividido"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Mais"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Flutuar"</string>
+    <string name="select_text" msgid="5139083974039906583">"Selecionar"</string>
+    <string name="screenshot_text" msgid="1477704010087786671">"Captura de ecrã"</string>
+    <string name="close_text" msgid="4986518933445178928">"Fechar"</string>
+    <string name="collapse_menu_text" msgid="7515008122450342029">"Fechar menu"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index 8edcddf..82409f4 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Parte superior a 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Parte superior a 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Parte inferior em tela cheia"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Dividir para a esquerda"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Dividir para a direita"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Dividir para cima"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Dividir para baixo"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Como usar o modo para uma mão"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Para sair, deslize de baixo para cima na tela ou toque em qualquer lugar acima do app"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar o modo para uma mão"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Toque duas vezes fora de um app para reposicionar"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendi"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Abra para ver mais informações."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Reiniciar para melhorar a visualização?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Você pode reiniciar o app para melhorar a visualização dele, mas talvez perca seu progresso ou mudanças não salvas"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Cancelar"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Reiniciar"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Não mostrar novamente"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Maximizar"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Minimizar"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Fechar"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Voltar"</string>
     <string name="handle_text" msgid="1766582106752184456">"Alça"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Tela cheia"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Modo área de trabalho"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Tela dividida"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Mais"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Ponto flutuante"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index 9227216..29297f4 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Partea de sus: 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Partea de sus: 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Partea de jos pe ecran complet"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Împarte în stânga"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Împarte în dreapta"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Împarte în sus"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Împarte în jos"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Folosirea modului cu o mână"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Pentru a ieși, glisează în sus din partea de jos a ecranului sau atinge oriunde deasupra ferestrei aplicației"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Activează modul cu o mână"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Atinge de două ori lângă o aplicație pentru a o repoziționa"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Extinde pentru mai multe informații"</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Repornești pentru o vizualizare mai bună?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Poți să repornești aplicația ca să arate mai bine pe ecran, dar este posibil să pierzi progresul sau modificările nesalvate"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Anulează"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Repornește"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Nu mai afișa"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Maximizează"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Minimizează"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Închide"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Înapoi"</string>
     <string name="handle_text" msgid="1766582106752184456">"Ghidaj"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Ecran complet"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Modul desktop"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Ecran împărțit"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Mai multe"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Flotantă"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index f5fa1a4..de0bcf4 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Đ’Đ”Ń€Ń…ĐœĐžĐč ĐœĐ° 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Đ’Đ”Ń€Ń…ĐœĐžĐč ĐœĐ° 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"ĐĐžĐ¶ĐœĐžĐč ĐČĐŸ ĐČĐ”ŃŃŒ эĐșŃ€Đ°Đœ"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"ĐŸŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” слДĐČа"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"ĐŸŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” спраĐČа"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"ĐŸŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” сĐČĐ”Ń€Ń…Ńƒ"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"ĐŸŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” ŃĐœĐžĐ·Ńƒ"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Đ˜ŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°ĐœĐžĐ” Ń€Đ”Đ¶ĐžĐŒĐ° упраĐČĐ»Đ”ĐœĐžŃ ĐŸĐŽĐœĐŸĐč руĐșĐŸĐč"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Đ§Ń‚ĐŸĐ±Ń‹ ĐČыĐčто, ĐżŃ€ĐŸĐČДЎОтД ĐżĐŸ эĐșŃ€Đ°ĐœŃƒ ŃĐœĐžĐ·Ńƒ ĐČĐČДрх ОлО ĐșĐŸŃĐœĐžŃ‚Đ”ŃŃŒ ĐŸĐ±Đ»Đ°ŃŃ‚Đž за ĐżŃ€Đ”ĐŽĐ”Đ»Đ°ĐŒĐž ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ."</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Запустоть Ń€Đ”Đ¶ĐžĐŒ упраĐČĐ»Đ”ĐœĐžŃ ĐŸĐŽĐœĐŸĐč руĐșĐŸĐč"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Đ§Ń‚ĐŸĐ±Ń‹ ĐżĐ”Ń€Đ”ĐŒĐ”ŃŃ‚ĐžŃ‚ŃŒ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ”, ĐŽĐČажЎы ĐœĐ°Đ¶ĐŒĐžŃ‚Đ” Ń€ŃĐŽĐŸĐŒ с ĐœĐžĐŒ."</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"ОК"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"РазĐČĐ”Ń€ĐœŃƒŃ‚ŃŒ, Ń‡Ń‚ĐŸĐ±Ń‹ ŃƒĐ·ĐœĐ°Ń‚ŃŒ Đ±ĐŸĐ»ŃŒŃˆĐ”."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"ĐŸĐ”Ń€Đ”Đ·Đ°ĐżŃƒŃŃ‚ĐžŃ‚ŃŒ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ”?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Вы ĐŒĐŸĐ¶Đ”Ń‚Đ” ĐżĐ”Ń€Đ”Đ·Đ°ĐżŃƒŃŃ‚ĐžŃ‚ŃŒ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ”, Ń‡Ń‚ĐŸĐ±Ń‹ ĐŸĐœĐŸ Đ»ŃƒŃ‡ŃˆĐ” ŃĐŒĐŸŃ‚Ń€Đ”Đ»ĐŸŃŃŒ ĐœĐ° эĐșŃ€Đ°ĐœĐ”. Про ŃŃ‚ĐŸĐŒ ĐČаш ĐżŃ€ĐŸĐłŃ€Đ”ŃŃ ОлО ĐœĐ”ŃĐŸŃ…Ń€Đ°ĐœĐ”ĐœĐœŃ‹Đ” ĐžĐ·ĐŒĐ”ĐœĐ”ĐœĐžŃ ĐŒĐŸĐłŃƒŃ‚ Đ±Ń‹Ń‚ŃŒ ŃƒŃ‚Đ”Ń€ŃĐœŃ‹."</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"ĐžŃ‚ĐŒĐ”ĐœĐ°"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"ĐŸĐ”Ń€Đ”Đ·Đ°ĐżŃƒŃŃ‚ĐžŃ‚ŃŒ"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Đ‘ĐŸĐ»ŃŒŃˆĐ” ĐœĐ” ĐżĐŸĐșазыĐČать"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"РазĐČĐ”Ń€ĐœŃƒŃ‚ŃŒ"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"ĐĄĐČĐ”Ń€ĐœŃƒŃ‚ŃŒ"</string>
     <string name="close_button_text" msgid="2913281996024033299">"ЗаĐșрыть"</string>
     <string name="back_button_text" msgid="1469718707134137085">"ĐĐ°Đ·Đ°ĐŽ"</string>
     <string name="handle_text" msgid="1766582106752184456">"МарĐșДр"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"ĐŸĐŸĐ»ĐœĐŸŃĐșŃ€Đ°ĐœĐœŃ‹Đč Ń€Đ”Đ¶ĐžĐŒ"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Đ Đ”Đ¶ĐžĐŒ ĐșĐŸĐŒĐżŃŒŃŽŃ‚Đ”Ń€Đ°"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Đ Đ°Đ·ĐŽĐ”Đ»ĐžŃ‚ŃŒ эĐșŃ€Đ°Đœ"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Ещё"</string>
     <string name="float_button_text" msgid="9221657008391364581">"ĐŸĐ»Đ°ĐČающДД ĐŸĐșĐœĐŸ"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index 53e52f2..849dbac 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ඉහළඞ 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"ඉහළඞ 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"à¶Žà·„à·… ඎූර්ණ තිරà¶ș"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"වඞ à¶¶à·™à¶Żà¶±à·Šà¶±"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"à¶Żà¶šà·”à¶« à¶¶à·™à¶Żà¶±à·Šà¶±"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"ඉහළ à¶¶à·™à¶Żà¶±à·Šà¶±"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"à¶Žà·„à·… à¶¶à·™à¶Żà¶±à·Šà¶±"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"තනි-අත් à¶Žà·Š‍රකාරà¶ș භාවිත කරඞින්"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"ඎිටවීඞට, තිරà¶șේ à¶Žà·„à·… සිට ඉහළට ස්වà¶șà·’à¶Žà·Š කරන්න හෝ à¶șà·™à¶Żà·”à¶žà¶§ ඉහළින් ඕනෑඞ තැනක තට්ටු කරන්න"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"තනි අත් à¶Žà·Š‍රකාරà¶ș ආරඞ්භ කරන්න"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"à¶șà·™à¶Żà·”à¶žà¶šà·Š නැවත à·ƒà·Šà¶źà·à¶±à¶œà¶­ කිරීඞට ඎිටතින් à¶Żà·™à·€à¶»à¶šà·Š තට්ටු කරන්න"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"තේරුණා"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"à·€à·à¶©à·’à¶Żà·”à¶» තොරතුරු à·ƒà¶łà·„à· à¶Żà·’à¶œ හරින්න"</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"වඩා à·„à·œà¶ł à¶Żà·ƒà·”à¶±à¶šà·Š à·ƒà¶łà·„à· à¶șළි අරà¶čන්න à¶Ż?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"ඔබට එà¶ș ඔබේ තිරà¶șෙහි වඩා à·„à·œà¶łà·’à¶±à·Š ඎෙනෙන à¶Žà¶»à·’à¶Żà·’ à¶șà·™à¶Żà·”à¶ž à¶șළි ඇරà¶čිà¶ș හැකි නඞුත්, ඔබට ඔබේ à¶Žà·Š‍රගතිà¶ș හෝ නොසුරකින à¶œà¶Ż වෙනස්කඞ් à¶…à·„à·’à¶žà·’ විà¶ș හැක"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"අවගංගු කරන්න"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"à¶șළි අරà¶čන්න"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"නැවත නොඎෙන්වන්න"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"à·€à·’à·„à·’à¶Żà¶±à·Šà¶±"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"කුඩා කරන්න"</string>
     <string name="close_button_text" msgid="2913281996024033299">"වසන්න"</string>
     <string name="back_button_text" msgid="1469718707134137085">"ආඎසු"</string>
     <string name="handle_text" msgid="1766582106752184456">"à·„à·à¶Źà¶œà¶ș"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"ඎූර්ණ තිරà¶ș"</string>
     <string name="desktop_text" msgid="1077633567027630454">"ඩෙස්ක්ටොඎ් à¶Žà·Š‍රකාරà¶ș"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"à¶¶à·™à¶Żà·”à¶žà·Š තිරà¶ș"</string>
     <string name="more_button_text" msgid="3655388105592893530">"තව"</string>
     <string name="float_button_text" msgid="9221657008391364581">"ඎාවෙන"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index f004fd4..e86ecde 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Horná – 50 %"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Horná – 30 %"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Dolná – na celú obrazovku"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"RozdeliĆ„ vÄŸavo"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"RozdeliƄ vpravo"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"RozdeliƄ hore"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"RozdeliƄ dole"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"PouĆŸívanie reĆŸimu jednej ruky"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Ukončíte potiahnutím z dolnej časti obrazovky nahor alebo klepnutím kdekoÄŸvek nad aplikáciu"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"SpustiĆ„ reĆŸim jednej ruky"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dvojitým klepnutím mimo aplikácie zmeníte jej pozíciu"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Dobre"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Po rozbalení sa dozviete viac."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Chcete ju reštartovaĆ„, aby mala lepší vzhÄŸad?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Aplikáciu môĆŸete reštartovaĆ„, aby mala na obrazovke lepší vzhÄŸad, ale môĆŸete prísĆ„ o postup a všetky neuloĆŸené zmeny."</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"ZrušiĆ„"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"ReštartovaĆ„"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"UĆŸ nezobrazovaĆ„"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"MaximalizovaƄ"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"MinimalizovaƄ"</string>
     <string name="close_button_text" msgid="2913281996024033299">"ZavrieƄ"</string>
     <string name="back_button_text" msgid="1469718707134137085">"SpäĆ„"</string>
     <string name="handle_text" msgid="1766582106752184456">"RukoväĆ„"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Celá obrazovka"</string>
     <string name="desktop_text" msgid="1077633567027630454">"ReĆŸim počítača"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Rozdelená obrazovka"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Viac"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Plávajúce"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index c4808059..954e729 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Zgornji 50 %"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Zgornji 30 %"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Spodnji v celozaslonski način"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Delitev levo"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Delitev desno"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Delitev zgoraj"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Delitev spodaj"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Uporaba enoročnega načina"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Za izhod povlecite z dna zaslona navzgor ali se dotaknite na poljubnem mestu nad aplikacijo"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Zagon enoročnega načina"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dvakrat se dotaknite zunaj aplikacije, če jo ĆŸelite prestaviti."</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"V redu"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Razširitev za več informacij"</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Ćœelite znova zagnati za boljši pregled?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Če znova zaĆŸenete aplikacijo, bo prikaz na zaslonu boljši, vendar lahko izgubite napredek ali neshranjene spremembe."</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Prekliči"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Znova zaĆŸeni"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ne prikaĆŸi več"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Maksimiraj"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Minimiraj"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Zapri"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Nazaj"</string>
     <string name="handle_text" msgid="1766582106752184456">"Ročica"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Celozaslonsko"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Namizni način"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Razdeljen zaslon"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Več"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Lebdeče"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index b59d4db..6209ff9 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Lart 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Lart 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Ekrani i plotë poshtë"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Ndaj majtas"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Ndaj djathtas"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Ndaj lart"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Ndaj në fund"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Po përdor modalitetin e përdorimit me një dorë"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Për të dalë, rrëshqit lart nga fundi i ekranit ose trokit diku mbi aplikacion"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Modaliteti i përdorimit me një dorë"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Trokit dy herë jashtë një aplikacioni për ta ripozicionuar"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"E kuptova"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Zgjeroje për më shumë informacion."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Rinis për një pamje më të mirë?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Mund të rinisësh aplikacionin në mënyrë që të duket më mirë në ekranin tënd, por mund të humbësh progresin ose çdo ndryshim të paruajtur"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Anulo"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Rinis"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Mos e shfaq përsëri"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Maksimizo"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Minimizo"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Mbyll"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Pas"</string>
     <string name="handle_text" msgid="1766582106752184456">"Emërtimi"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Ekrani i plotë"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Modaliteti i desktopit"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Ekrani i ndarë"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Më shumë"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Pluskuese"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index 78d74d74..f810d3d 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Đ“ĐŸŃ€ŃšĐž Đ”ĐșŃ€Đ°Đœ 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Đ“ĐŸŃ€ŃšĐž Đ”ĐșŃ€Đ°Đœ 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Đ Đ”Đ¶ĐžĐŒ Ń†Đ”Đ»ĐŸĐł Đ”ĐșŃ€Đ°ĐœĐ° за ĐŽĐŸŃšĐž Đ”ĐșŃ€Đ°Đœ"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"ĐŸĐŸĐŽĐ”Đ»ĐžŃ‚Đ” лДĐČĐŸ"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"ĐŸĐŸĐŽĐ”Đ»ĐžŃ‚Đ” ĐŽĐ”ŃĐœĐŸ"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"ĐŸĐŸĐŽĐ”Đ»ĐžŃ‚Đ” у ĐČрху"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"ĐŸĐŸĐŽĐ”Đ»ĐžŃ‚Đ” у ĐŽĐœŃƒ"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"ĐšĐŸŃ€ĐžŃˆŃ›Đ”ŃšĐ” Ń€Đ”Đ¶ĐžĐŒĐ° Ń˜Đ”ĐŽĐœĐŸĐŒ руĐșĐŸĐŒ"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Да бОстД ОзашлО, прДĐČŃƒŃ†ĐžŃ‚Đ” ĐœĐ°ĐłĐŸŃ€Đ” ĐŸĐŽ ĐŽĐœĐ° Đ”ĐșŃ€Đ°ĐœĐ° ОлО ĐŽĐŸĐŽĐžŃ€ĐœĐžŃ‚Đ” Đ±ĐžĐ»ĐŸ гЎД ĐžĐ·ĐœĐ°ĐŽ аплОĐșĐ°Ń†ĐžŃ˜Đ”"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"ĐŸĐŸĐșŃ€Đ”ĐœĐžŃ‚Đ” Ń€Đ”Đ¶ĐžĐŒ Ń˜Đ”ĐŽĐœĐŸĐŒ руĐșĐŸĐŒ"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ДĐČапут ĐŽĐŸĐŽĐžŃ€ĐœĐžŃ‚Đ” ОзĐČĐ°Đœ аплОĐșĐ°Ń†ĐžŃ˜Đ” Ўа бОстД ĐżŃ€ĐŸĐŒĐ”ĐœĐžĐ»Đž ŃšĐ”ĐœŃƒ ĐżĐŸĐ·ĐžŃ†ĐžŃ˜Ńƒ"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"ВажО"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ĐŸŃ€ĐŸŃˆĐžŃ€ĐžŃ‚Đ” за Ń˜ĐŸŃˆ ĐžĐœŃ„ĐŸŃ€ĐŒĐ°Ń†ĐžŃ˜Đ°."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"ЖДлОтД лО Ўа Ń€Đ”ŃŃ‚Đ°Ń€Ń‚ŃƒŃ˜Đ”Ń‚Đ” раЮо Đ±ĐŸŃ™Đ”Đł проĐșаза?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"ĐœĐŸĐ¶Đ”Ń‚Đ” Ўа Ń€Đ”ŃŃ‚Đ°Ń€Ń‚ŃƒŃ˜Đ”Ń‚Đ” аплОĐșацоју Ўа бО ОзглДЎала Đ±ĐŸŃ™Đ” ĐœĐ° Đ”ĐșŃ€Đ°ĐœŃƒ, с Ń‚ĐžĐŒ ŃˆŃ‚ĐŸ ĐŒĐŸĐ¶Đ”Ń‚Đ” Ўа ĐžĐ·ĐłŃƒĐ±ĐžŃ‚Đ” ĐŸĐœĐŸ ŃˆŃ‚ĐŸ стД ŃƒŃ€Đ°ĐŽĐžĐ»Đž ОлО ĐœĐ”ŃĐ°Ń‡ŃƒĐČĐ°ĐœĐ” ĐżŃ€ĐŸĐŒĐ”ĐœĐ”, аĐșĐŸ ох ĐžĐŒĐ°"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"ОтĐșажО"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Đ Đ”ŃŃ‚Đ°Ń€Ń‚ŃƒŃ˜"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"ĐĐ” проĐșазуј ĐżĐŸĐœĐŸĐČĐŸ"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"ĐŁĐČĐ”Ń›Đ°Ń˜Ń‚Đ”"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"ĐŁĐŒĐ°ŃšĐžŃ‚Đ”"</string>
     <string name="close_button_text" msgid="2913281996024033299">"ЗатĐČĐŸŃ€ĐžŃ‚Đ”"</string>
     <string name="back_button_text" msgid="1469718707134137085">"ĐĐ°Đ·Đ°ĐŽ"</string>
     <string name="handle_text" msgid="1766582106752184456">"Đ˜ĐŽĐ”ĐœŃ‚ĐžŃ„ĐžĐșĐ°Ń‚ĐŸŃ€"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"ĐŸŃ€Đ”ĐșĐŸ Ń†Đ”Đ»ĐŸĐł Đ”ĐșŃ€Đ°ĐœĐ°"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Đ Đ”Đ¶ĐžĐŒ за Ń€Đ°Ń‡ŃƒĐœĐ°Ń€Đ”"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"ĐŸĐŸĐŽĐ”Ń™Đ”ĐœĐž Đ”ĐșŃ€Đ°Đœ"</string>
     <string name="more_button_text" msgid="3655388105592893530">"ĐˆĐŸŃˆ"</string>
     <string name="float_button_text" msgid="9221657008391364581">"ĐŸĐ»ŃƒŃ‚Đ°Ń˜ŃƒŃ›Đ”"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index cda3040..0e44871 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Övre 50 %"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Övre 30 %"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Helskärm på nedre skärm"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Till vänster på delad skärm"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Till höger på delad skärm"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Upptill på delad skärm"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Nedtill på delad skärm"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Använda enhandsläge"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Avsluta genom att svepa uppåt från skärmens nederkant eller trycka ovanför appen"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Starta enhandsläge"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Tryck snabbt två gånger utanför en app för att flytta den"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Utöka för mer information."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Vill du starta om för en bättre vy?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Du kan starta om appen så den passar bättre på skärmen men du kan förlora dina framsteg och eventuella osparade ändringar."</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Avbryt"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Starta om"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Visa inte igen"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Utöka"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Minimera"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Stäng"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Tillbaka"</string>
     <string name="handle_text" msgid="1766582106752184456">"Handtag"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Helskärm"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Datorläge"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Delad skärm"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Mer"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Svävande"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index fee34eb..e167bca 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Juu 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Juu 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Skrini nzima ya chini"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Gawanya sehemu ya kushoto"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Gawanya sehemu ya kulia"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Gawanya sehemu ya juu"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Gawanya sehemu ya chini"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Kutumia hali ya kutumia kwa mkono mmoja"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Ili ufunge, telezesha kidole juu kutoka sehemu ya chini ya skrini au uguse mahali popote juu ya programu"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Anzisha hali ya kutumia kwa mkono mmoja"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Gusa mara mbili nje ya programu ili uihamishe"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Nimeelewa"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Panua ili upate maelezo zaidi."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Ungependa kuzima kisha uwashe ili upate mwonekano bora zaidi?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Unaweza kuzima kisha uwashe programu yako ili ifanye kazi vizuri zaidi kwenye skrini yako, lakini unaweza kupoteza maendeleo yako au mabadiliko yoyote ambayo hayajahifadhiwa"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Ghairi"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Zima kisha uwashe"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Usionyeshe tena"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Panua"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Punguza"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Funga"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Rudi nyuma"</string>
     <string name="handle_text" msgid="1766582106752184456">"Ncha"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Skrini nzima"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Hali ya Kompyuta ya mezani"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Gawa Skrini"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Zaidi"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Inayoelea"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index 49f128d..286d608 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"àźźàŻ‡àźČàŻ‡ 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"àźźàŻ‡àźČàŻ‡ 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"àź•àŻ€àźŽàŻàźȘàŻàźȘàŻàź±àźźàŻ àźźàŻàźŽàŻàź€àŻ àź€àźżàź°àŻˆ"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"àź‡àźŸàź€àŻàźȘàŻàź±àźźàźŸàź•àźȘàŻ àźȘàźżàź°àźżàź•àŻàź•àŻàźźàŻ"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"àź”àźČàź€àŻàźȘàŻàź±àźźàźŸàź•àźȘàŻ àźȘàźżàź°àźżàź•àŻàź•àŻàźźàŻ"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"àźźàŻ‡àź±àŻàźȘàŻàź±àźźàźŸàź•àźȘàŻ àźȘàźżàź°àźżàź•àŻàź•àŻàźźàŻ"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"àź•àŻ€àźŽàŻàźȘàŻàź±àźźàźŸàź•àźȘàŻ àźȘàźżàź°àźżàź•àŻàź•àŻàźźàŻ"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"àź’àź±àŻàź±àŻˆàź•àŻ àź•àŻˆàźȘàŻ àźȘàźŻàź©àŻàźźàŻàź±àŻˆàźŻàŻˆàźȘàŻ àźȘàźŻàź©àŻàźȘàźŸàŻàź€àŻàź€àŻàź€àźČàŻ"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"àź”àŻ†àźłàźżàźŻàŻ‡àź±, àź€àźżàź°àŻˆàźŻàźżàź©àŻ àź•àŻ€àźŽàźżàź°àŻàźšàŻàź€àŻ àźźàŻ‡àźČàŻàźšàŻ‹àź•àŻàź•àźż àźžàŻàź”àŻˆàźȘàŻ àźšàŻ†àźŻàŻàźŻàź”àŻàźźàŻ àź…àźČàŻàźČàź€àŻ àź†àźȘàŻàźžàŻàź•àŻàź•àŻ àźźàŻ‡àźČàŻ‡ àźàź€àŻ‡àź©àŻàźźàŻ àź“àź°àŻ àź‡àźŸàź€àŻàź€àźżàźČàŻ àź€àźŸàŻàźŸàź”àŻàźźàŻ"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"àź’àź±àŻàź±àŻˆàź•àŻ àź•àŻˆàźȘàŻ àźȘàźŻàź©àŻàźźàŻàź±àŻˆàźŻàŻˆàź€àŻ àź€àŻŠàźŸàź™àŻàź•àŻàźźàŻ"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"àź†àźȘàŻàźžàŻˆ àź‡àźŸàźźàŻ àźźàźŸàź±àŻàź± àź…àź€àź©àŻ àź”àŻ†àźłàźżàźŻàźżàźČàŻ àź‡àź°àŻàźźàŻàź±àŻˆ àź€àźŸàŻàźŸàźČàźŸàźźàŻ"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"àźšàź°àźż"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"àź•àŻ‚àźŸàŻàź€àźČàŻ àź€àź•àź”àźČàŻàź•àźłàŻàź•àŻàź•àŻ àź”àźżàź°àźżàź”àźŸàź•àŻàź•àźČàźŸàźźàŻ."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"àźšàź°àźżàźŻàźŸàź© àź”àźżàź€àź€àŻàź€àźżàźČàŻ àź•àźŸàźŸàŻàźŸàźȘàŻàźȘàźŸ àźźàŻ€àźŁàŻàźŸàŻàźźàŻ àź€àŻŠàźŸàź™àŻàź•àź”àźŸ?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"àź†àźȘàŻàźžàŻˆ àźźàŻ€àźŁàŻàźŸàŻàźźàŻ àź€àŻŠàźŸàź™àŻàź•àŻàź”àź€àź©àŻ àźźàŻ‚àźČàźźàŻ àź€àźżàź°àŻˆàźŻàźżàźČàŻ àź…àź€àŻ àźšàź°àźżàźŻàźŸàź© àź”àźżàź€àź€àŻàź€àźżàźČàŻ àź€àŻ‹àź±àŻàź±àźźàźłàźżàź•àŻàź•àŻàźźàŻ. àź†àź©àźŸàźČàŻ àźšàŻ†àźŻàźČàŻàźšàźżàźČàŻˆàźŻàŻˆàźŻàŻ‹ àźšàŻ‡àźźàźżàź•àŻàź•àźȘàŻàźȘàźŸàźŸàźźàźČàŻ àź‡àź°àŻàź•àŻàź•àŻàźźàŻ àźźàźŸàź±àŻàź±àź™àŻàź•àźłàŻˆàźŻàŻ‹ àźšàŻ€àź™àŻàź•àźłàŻ àź‡àźŽàź•àŻàź•àź•àŻàź•àŻ‚àźŸàŻàźźàŻ."</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"àź°àź€àŻàź€àŻàźšàŻ†àźŻàŻ"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"àźźàŻ€àźŁàŻàźŸàŻàźźàŻ àź€àŻŠàźŸàź™àŻàź•àŻ"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"àźźàŻ€àźŁàŻàźŸàŻàźźàŻ àź•àźŸàźŸàŻàźŸàźŸàź€àŻ‡"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"àźȘàŻ†àź°àźżàź€àźŸàź•àŻàź•àŻàźźàŻ"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"àźšàźżàź±àźżàź€àźŸàź•àŻàź•àŻàźźàŻ"</string>
     <string name="close_button_text" msgid="2913281996024033299">"àźźàŻ‚àźŸàŻàźźàŻ"</string>
     <string name="back_button_text" msgid="1469718707134137085">"àźȘàźżàź©àŻàźšàŻ†àźČàŻàźČàŻàźźàŻ"</string>
     <string name="handle_text" msgid="1766582106752184456">"àźčàŻ‡àźŁàŻàźŸàźżàźČàŻ"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"àźźàŻàźŽàŻàź€àŻàź€àźżàź°àŻˆ"</string>
     <string name="desktop_text" msgid="1077633567027630454">"àźŸàŻ†àźžàŻàź•àŻàźŸàźŸàźȘàŻ àźȘàźŻàź©àŻàźźàŻàź±àŻˆ"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"àź€àźżàź°àŻˆàźŻàŻˆàźȘàŻ àźȘàźżàź°àźżàź•àŻàź•àŻàźźàŻ"</string>
     <string name="more_button_text" msgid="3655388105592893530">"àź•àŻ‚àźŸàŻàź€àźČàŻ àź”àźżàź°àŻàźȘàŻàźȘàź€àŻàź€àŻ‡àź°àŻàź”àŻàź•àźłàŻ"</string>
     <string name="float_button_text" msgid="9221657008391364581">"àźźàźżàź€àź•àŻàź•àŻàźźàŻ àźšàźŸàźłàź°àźźàŻ"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index f0c8be5..ec3ca78 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ఎగుఔ 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"ఎగుఔ 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"à°Šà°żà°—à±à°” ఫుà°Č్-ఞ్క్రీచ్‌"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"à°Žà°Ąà°ź ఔైà°Șుచ్చ à°­à°Ÿà°—à°‚à°Čో à°”à°żà°­à°œà°żà°‚à°šà°‚à°Ąà°ż"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"à°•à±à°Ąà°ż ఔైà°Șుచ్చ à°­à°Ÿà°—à°‚à°Čో à°”à°żà°­à°œà°żà°‚à°šà°‚à°Ąà°ż"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"ఎగుఔ à°­à°Ÿà°—à°‚à°Čో à°”à°żà°­à°œà°żà°‚à°šà°‚à°Ąà°ż"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"à°Šà°żà°—à±à°” à°­à°Ÿà°—à°‚à°Čో à°”à°żà°­à°œà°żà°‚à°šà°‚à°Ąà°ż"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"ఔచ్-à°čà±à°Żà°Ÿà°‚à°Ąà±†à°Ąà± à°źà±‹à°Ąà±‌చు ఉà°Șà°Żà±‹à°—à°żà°‚à°šà°Ąà°‚"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"à°šà°żà°·à±à°•à±à°°à°źà°żà°‚à°šà°Ąà°Ÿà°šà°żà°•à°ż, ఞ్క్రీచ్ à°•à°żà°‚à°Šà°ż à°­à°Ÿà°—à°‚ à°šà±à°‚à°Ąà°ż à°Șà±ˆà°•à°ż ఞ్ఔైà°Ș్ à°šà±‡à°Żà°‚à°Ąà°ż à°Čేఊట à°Żà°Ÿà°Ș్ à°Șైచ à°Žà°•à±à°•à°Ąà±ˆà°šà°Ÿ à°Ÿà±à°Żà°Ÿà°Ș్ à°šà±‡à°Żà°‚à°Ąà°ż"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"ఔచ్-à°čà±à°Żà°Ÿà°‚à°Ąà±†à°Ąà± à°źà±‹à°Ąà±‌చు à°Șà±à°°à°Ÿà°°à°‚à°­à°żà°žà±à°€à±à°‚à°Šà°ż"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"à°Żà°Ÿà°Ș్ à°žà±à°„à°Ÿà°šà°Ÿà°šà±à°šà°ż à°źà°Ÿà°°à±à°šà°Ąà°Ÿà°šà°żà°•à°ż à°Šà°Ÿà°šà°ż ఔెà°Čుà°Șà°Č à°Ąà°Źà±à°Č్-à°Ÿà±à°Żà°Ÿà°Ș్ à°šà±‡à°Żà°‚à°Ąà°ż"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"à°…à°°à±à°„à°źà±ˆà°‚à°Šà°ż"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"à°źà°°à°żà°‚à°€ à°žà°źà°Ÿà°šà°Ÿà°°à°‚ కోఞం à°”à°żà°žà±à°€à°°à°żà°‚à°šà°‚à°Ąà°ż."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"à°źà±†à°°à±à°—à±ˆà°š à°”à±€à°•à±à°·à°Ł కోఞం రీఞ్టటర్ట్ à°šà±‡à°Żà°Ÿà°Čà°Ÿ?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"à°źà±€à°°à± à°Żà°Ÿà°Ș్‌à°šà°ż రీఞ్టటర్ట్ à°šà±‡à°Żà°”à°šà±à°šà±, ఀఊ్ఔటరట à°‡à°Šà°ż à°źà±€ ఞ్క్రీచ్‌à°Șై à°źà±†à°°à±à°—à±à°—à°Ÿ à°•à°šà°żà°Șà°żà°žà±à°€à±à°‚à°Šà°ż, à°•à°Ÿà°šà±€ à°źà±€à°°à± à°źà±€ à°Ș్రోగ్రెఞ్‌చు à°—à°Ÿà°šà±€ à°Čేఊట ఞేఔ్ à°šà±‡à°Żà°šà°ż ఏఔైచట à°źà°Ÿà°°à±à°Șుà°Čచు à°—à°Ÿà°šà±€ కోà°Č్à°Șోఔచ్చు"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"రఊ్ఊు à°šà±‡à°Żà°‚à°Ąà°ż"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"రీఞ్టటర్ట్ à°šà±‡à°Żà°‚à°Ąà°ż"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"à°źà°łà±à°Čీ చూà°Șఔఊ్ఊు"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"à°—à°°à°żà°·à±à°Ÿà±€à°•à°°à°żà°‚à°šà°‚à°Ąà°ż"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"à°•à±à°Šà°żà°‚à°šà°‚à°Ąà°ż"</string>
     <string name="close_button_text" msgid="2913281996024033299">"à°źà±‚à°žà°żà°”à±‡à°Żà°‚à°Ąà°ż"</string>
     <string name="back_button_text" msgid="1469718707134137085">"ఔెచుకకు"</string>
     <string name="handle_text" msgid="1766582106752184456">"à°čà±à°Żà°Ÿà°‚à°Ąà°żà°Č్"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"ఫుà°Č్-ఞ్క్రీచ్"</string>
     <string name="desktop_text" msgid="1077633567027630454">"à°Ąà±†à°žà±à°•à±‌à°Ÿà°Ÿà°Ș్ à°źà±‹à°Ąà±"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"ఞ్à°Ș్à°Čà°żà°Ÿà± ఞ్క్రీచ్"</string>
     <string name="more_button_text" msgid="3655388105592893530">"à°źà°°à°żà°šà±à°šà°ż"</string>
     <string name="float_button_text" msgid="9221657008391364581">"ఫ్à°Čోట్"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index 2437e03..1a9fa18 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"àž”àč‰àžČàž™àžšàž™ 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"àž”àč‰àžČàž™àžšàž™ 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"àč€àž•àč‡àžĄàž«àž™àč‰àžČàžˆàž­àž”àč‰àžČàž™àž„àčˆàžČàž‡"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"àčàžąàžàč„àž›àž—àžČàž‡àž‹àč‰àžČàžą"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"àčàžąàžàč„àž›àž—àžČàž‡àž‚àž§àžČ"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"àčàžąàžàč„àž›àž”àč‰àžČàž™àžšàž™"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"àčàžąàžàč„àž›àž”àč‰àžČàž™àž„àčˆàžČàž‡"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"àžàžČàžŁàčƒàžŠàč‰àč‚àž«àžĄàž”àžĄàž·àž­àč€àž”àž”àžąàž§"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"àž«àžČàžàž•àč‰àž­àž‡àžàžČàžŁàž­àž­àž àčƒàž«àč‰àč€àž„àž·àčˆàž­àž™àž‚àž¶àč‰àž™àžˆàžČàžàž”àč‰àžČàž™àž„àčˆàžČàž‡àž‚àž­àž‡àž«àž™àč‰àžČàžˆàž­àž«àžŁàž·àž­àčàž•àž°àž—àž”àčˆàčƒàž”àžàč‡àč„àž”àč‰àč€àž«àž™àž·àž­àčàž­àž›"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"àč€àžŁàžŽàčˆàžĄàč‚àž«àžĄàž”àžĄàž·àž­àč€àž”àž”àžąàž§"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"àčàž•àž°àžȘàž­àž‡àž„àžŁàž±àč‰àž‡àž”àč‰àžČàž™àž™àž­àžàčàž­àž›àč€àžžàž·àčˆàž­àč€àž›àž„àž”àčˆàžąàž™àž•àžłàčàž«àž™àčˆàž‡"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"àžŁàž±àžšàž—àžŁàžČàžš"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"àž‚àžąàžČàžąàč€àžžàž·àčˆàž­àž”àžčàž‚àč‰àž­àžĄàžčàž„àč€àžžàžŽàčˆàžĄàč€àž•àžŽàžĄ"</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"àžŁàž”àžȘàž•àžČàžŁàčŒàž—àč€àžžàž·àčˆàž­àžŁàž±àžšàžĄàžžàžĄàžĄàž­àž‡àž—àž”àčˆàž”àž”àžąàžŽàčˆàž‡àž‚àž¶àč‰àž™àčƒàžŠàčˆàč„àž«àžĄ"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"àž„àžžàž“àžŁàž”àžȘàž•àžČàžŁàčŒàž—àčàž­àž›àč€àžžàž·àčˆàž­àžŁàž±àžšàžĄàžžàžĄàžĄàž­àž‡àž—àž”àčˆàž”àž”àžąàžŽàčˆàž‡àž‚àž¶àč‰àž™àžšàž™àž«àž™àč‰àžČàžˆàž­àč„àž”àč‰ àčàž•àčˆàž„àž§àžČàžĄàž„àž·àžšàž«àž™àč‰àžČàčàž„àž°àžàžČàžŁàč€àž›àž„àž”àčˆàžąàž™àčàž›àž„àž‡àčƒàž”àč† àž—àž”àčˆàč„àžĄàčˆàč„àž”àč‰àžšàž±àž™àž—àž¶àžàž­àžČàžˆàž«àžČàžąàč„àž›"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"àžąàžàč€àž„àžŽàž"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"àžŁàž”àžȘàž•àžČàžŁàčŒàž—"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"àč„àžĄàčˆàž•àč‰àž­àž‡àčàžȘàž”àž‡àž‚àč‰àž­àž„àž§àžČàžĄàž™àž”àč‰àž­àž”àž"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"àž‚àžąàžČàžąàčƒàž«àžàčˆàžȘàžžàž”"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"àžąàčˆàž­"</string>
     <string name="close_button_text" msgid="2913281996024033299">"àž›àžŽàž”"</string>
     <string name="back_button_text" msgid="1469718707134137085">"àžàž„àž±àžš"</string>
     <string name="handle_text" msgid="1766582106752184456">"àčàžźàž™àč€àž”àžŽàž„"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"àč€àž•àč‡àžĄàž«àž™àč‰àžČàžˆàž­"</string>
     <string name="desktop_text" msgid="1077633567027630454">"àč‚àž«àžĄàž”àč€àž”àžȘàžàčŒàž—àč‡àž­àž›"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"àčàžąàžàž«àž™àč‰àžČàžˆàž­"</string>
     <string name="more_button_text" msgid="3655388105592893530">"àč€àžžàžŽàčˆàžĄàč€àž•àžŽàžĄ"</string>
     <string name="float_button_text" msgid="9221657008391364581">"àž„àčˆàž­àž‡àž„àž­àžą"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index 86ef757..27c4363 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Gawing 50% ang nasa itaas"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Gawing 30% ang nasa itaas"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"I-full screen ang nasa ibaba"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Hatiin sa kaliwa"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Hatiin sa kanan"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Hatiin sa itaas"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Hatiin sa ilalim"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Paggamit ng one-hand mode"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Para lumabas, mag-swipe pataas mula sa ibaba ng screen o mag-tap kahit saan sa itaas ng app"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Simulan ang one-hand mode"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Mag-double tap sa labas ng app para baguhin ang posisyon nito"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"I-expand para sa higit pang impormasyon."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"I-restart para sa mas magandang hitsura?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Puwede mong i-restart ang app para maging mas maganda ang itsura nito sa iyong screen, pero posibleng mawala ang pag-usad mo o anumang hindi na-save na pagbabago"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Kanselahin"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"I-restart"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Huwag nang ipakita ulit"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"I-maximize"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"I-minimize"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Isara"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Bumalik"</string>
     <string name="handle_text" msgid="1766582106752184456">"Handle"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Fullscreen"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Desktop Mode"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Split Screen"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Higit pa"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Float"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index c4060cc..a9ad92d 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Üstte %50"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Üstte %30"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Altta tam ekran"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Sol tarafta böl"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Sağ tarafta böl"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Üst tarafta böl"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Alt tarafta böl"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Tek el modunu kullanma"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Çıkmak için ekranın alt kısmından yukarı kaydırın veya uygulamanın üzerinde herhangi bir yere dokunun"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Tek el modunu baßlat"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Yeniden konumlandırmak için uygulamanın dıßına iki kez dokunun"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Anladım"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Daha fazla bilgi için genißletin."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Daha iyi bir görünüm için yeniden baßlatılsın mı?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Ekranınızda daha iyi görünmesi için uygulamayı yeniden baßlatabilirsiniz, ancak ilerlemenizi ve kaydedilmemiß değißikliklerinizi kaybedebilirsiniz"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"İptal"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Yeniden baßlat"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Bir daha gösterme"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Ekranı Kapla"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Küçült"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Kapat"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Geri"</string>
     <string name="handle_text" msgid="1766582106752184456">"Herkese açık kullanıcı adı"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Tam Ekran"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Masaüstü Modu"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Bölünmüß Ekran"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Daha Fazla"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Havada Süzülen"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index 166041d..a9b05d5 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Đ’Đ”Ń€Ń…ĐœŃ” ĐČіĐșĐœĐŸ ĐœĐ° 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Đ’Đ”Ń€Ń…ĐœŃ” ĐČіĐșĐœĐŸ ĐœĐ° 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"ĐĐžĐ¶ĐœŃ” ĐČіĐșĐœĐŸ ĐœĐ° ĐČĐ”ŃŃŒ Đ”ĐșŃ€Đ°Đœ"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Đ ĐŸĐ·ĐŽŃ–Đ»ĐžŃ‚Đž зліĐČа"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Đ ĐŸĐ·ĐŽŃ–Đ»ĐžŃ‚Đž спраĐČа"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Đ ĐŸĐ·ĐŽŃ–Đ»ĐžŃ‚Đž ĐČĐłĐŸŃ€Ń–"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Đ ĐŸĐ·ĐŽŃ–Đ»ĐžŃ‚Đž ĐČĐœĐžĐ·Ńƒ"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"ĐŻĐș ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČатося Ń€Đ”Đ¶ĐžĐŒĐŸĐŒ ĐșĐ”Ń€ŃƒĐČĐ°ĐœĐœŃ ĐŸĐŽĐœŃ–Ń”ŃŽ руĐșĐŸŃŽ"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Đ©ĐŸĐ± ĐČĐžĐčто, ĐżŃ€ĐŸĐČĐ”ĐŽŃ–Ń‚ŃŒ ĐżĐ°Đ»ŃŒŃ†Đ”ĐŒ ĐżĐŸ Đ”ĐșŃ€Đ°ĐœŃƒ Đ·ĐœĐžĐ·Ńƒ ĐČĐłĐŸŃ€Ńƒ Đ°Đ±ĐŸ Ń‚ĐŸŃ€ĐșĐœŃ–Ń‚ŃŒŃŃ Đ”ĐșŃ€Đ°ĐœĐ° ĐœĐ°ĐŽ ĐŽĐŸĐŽĐ°Ń‚ĐșĐŸĐŒ"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"ĐŁĐČŃ–ĐŒĐșĐœŃƒŃ‚Đž Ń€Đ”Đ¶ĐžĐŒ ĐșĐ”Ń€ŃƒĐČĐ°ĐœĐœŃ ĐŸĐŽĐœŃ–Ń”ŃŽ руĐșĐŸŃŽ"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Đ©ĐŸĐ± ĐżĐ”Ń€Đ”ĐŒŃ–ŃŃ‚ĐžŃ‚Đž ĐŽĐŸĐŽĐ°Ń‚ĐŸĐș, ĐŽĐČічі Ń‚ĐŸŃ€ĐșĐœŃ–Ń‚ŃŒŃŃ ĐŸĐ±Đ»Đ°ŃŃ‚Ń– ĐżĐŸĐ·Đ° ĐœĐžĐŒ"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"ОK"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Đ ĐŸĐ·ĐłĐŸŃ€ĐœŃ–Ń‚ŃŒ, Ń‰ĐŸĐ± ĐŽŃ–Đ·ĐœĐ°Ń‚ĐžŃŃ Đ±Ń–Đ»ŃŒŃˆĐ”."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"ĐŸĐ”Ń€Đ”Đ·Đ°ĐżŃƒŃŃ‚ĐžŃ‚Đž ĐŽĐ»Ń Đ·Ń€ŃƒŃ‡ĐœŃ–ŃˆĐŸĐłĐŸ ĐżĐ”Ń€Đ”ĐłĐ»ŃĐŽŃƒ?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Во ĐŒĐŸĐ¶Đ”Ń‚Đ” ĐżĐ”Ń€Đ”Đ·Đ°ĐżŃƒŃŃ‚ĐžŃ‚Đž ĐŽĐŸĐŽĐ°Ń‚ĐŸĐș, Ń‰ĐŸĐ± ĐżĐŸĐșращото ĐčĐŸĐłĐŸ ĐČĐžĐłĐ»ŃĐŽ ĐœĐ° Đ”ĐșŃ€Đ°ĐœŃ–, алД ĐČаші ĐŽĐŸŃŃĐłĐœĐ”ĐœĐœŃ Đ°Đ±ĐŸ ĐœĐ”Đ·Đ±Đ”Ń€Đ”Đ¶Đ”ĐœŃ– Đ·ĐŒŃ–ĐœĐž ĐŒĐŸĐ¶Đ” Đ±ŃƒŃ‚Đž ĐČŃ‚Ń€Đ°Ń‡Đ”ĐœĐŸ"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"ĐĄĐșасуĐČато"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"ĐŸĐ”Ń€Đ”Đ·Đ°ĐżŃƒŃŃ‚ĐžŃ‚Đž"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Đ‘Ń–Đ»ŃŒŃˆĐ” ĐœĐ” ĐżĐŸĐșазуĐČато"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Đ—Đ±Ń–Đ»ŃŒŃˆĐžŃ‚Đž"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Đ—ĐłĐŸŃ€ĐœŃƒŃ‚Đž"</string>
     <string name="close_button_text" msgid="2913281996024033299">"ЗаĐșрото"</string>
     <string name="back_button_text" msgid="1469718707134137085">"ĐĐ°Đ·Đ°ĐŽ"</string>
     <string name="handle_text" msgid="1766582106752184456">"МарĐșДр"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"На ĐČĐ”ŃŃŒ Đ”ĐșŃ€Đ°Đœ"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Đ Đ”Đ¶ĐžĐŒ ĐșĐŸĐŒĐż’ютДра"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Đ ĐŸĐ·ĐŽŃ–Đ»ĐžŃ‚Đž Đ”ĐșŃ€Đ°Đœ"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Đ‘Ń–Đ»ŃŒŃˆĐ”"</string>
     <string name="float_button_text" msgid="9221657008391364581">"ĐŸĐ»Đ°ĐČаючД ĐČіĐșĐœĐŸ"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index ca6a937..1c31a3d 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Ű§ÙˆÙŸŰ± %50"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Ű§ÙˆÙŸŰ± %30"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"نچلی فل Ű§ŰłÚ©Ű±ÛŒÙ†"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"ŰŻŰ§ŰŠÛŒÚș Ű·Ű±Ù ŰȘÙ‚ŰłÛŒÙ… Ú©Ű±ÛŒÚș"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"ŰšŰ§ŰŠÛŒÚș Ű·Ű±Ù ŰȘÙ‚ŰłÛŒÙ… Ú©Ű±ÛŒÚș"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Ű§ÙˆÙŸŰ± کی Ű·Ű±Ù ŰȘÙ‚ŰłÛŒÙ… Ú©Ű±ÛŒÚș"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"نیچے کی Ű·Ű±Ù ŰȘÙ‚ŰłÛŒÙ… Ú©Ű±ÛŒÚș"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Ű§ÛŒÚ© ÛŰ§ŰȘÚŸ کی ÙˆŰ¶Űč کۧ ۧ۳ŰȘŰčÙ…Ű§Ù„ Ú©Ű±Ù†Ű§"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"ŰšŰ§ÛŰ± نکلنے Ú©ÛŒÙ„ŰŠÛ’ŰŒ Ű§ŰłÚ©Ű±ÛŒÙ† کے نیچے ŰłÛ’ Ű§ÙˆÙŸŰ± کی Ű·Ű±Ù ŰłÙˆŰ§ŰŠÙŸ Ú©Ű±ÛŒÚș ÛŒŰ§ Ű§ÛŒÙŸ کے Ű§ÙˆÙŸŰ± کہیÚș ŰšÚŸÛŒ ŰȘÚŸÙŸŰȘÚŸÙŸŰ§ŰŠÛŒÚș"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Ű§ÛŒÚ© ÛŰ§ŰȘÚŸ کی ÙˆŰ¶Űč ŰŽŰ±ÙˆŰč Ú©Ű±ÛŒÚș"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Ú©ŰłÛŒ Ű§ÛŒÙŸ کی ٟوŰČÛŒŰŽÙ† ŰȘŰšŰŻÛŒÙ„ Ú©Ű±Ù†Û’ کے لیے ۧ۳ Ű§ÛŒÙŸ کے ŰšŰ§ÛŰ± ŰŻÙˆ ۚۧ۱ ŰȘÚŸÙŸŰȘÚŸÙŸŰ§ŰŠÛŒÚș"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"ŰłÙ…ŰŹÚŸ Űą ÚŻŰŠÛŒ"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"مŰČÛŒŰŻ مŰčÙ„ÙˆÙ…Ű§ŰȘ کے لیے ÙŸÚŸÛŒÙ„Ű§ŰŠÛŒÚș۔"</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"ŰšÛŰȘ۱ Ù…Ù†ŰžŰ± کے لیے Ű±ÛŒ ŰłÙčۧ۱Ùč Ú©Ű±ÛŒÚș۟"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"ŰąÙŸ Ű§ÛŒÙŸ کو Ű±ÛŒ ŰłÙčۧ۱Ùč ک۱ ŰłÚ©ŰȘے ہیÚș ŰȘŰ§Ú©Û یہ ŰąÙŸ کی Ű§ŰłÚ©Ű±ÛŒÙ† ÙŸŰ± ŰšÛŰȘ۱ Ù†ŰžŰ± ŰąŰŠÛ’ŰŒ ŰȘŰ§ÛÙ… ŰąÙŸ Ű§ÙŸÙ†ÛŒ ÙŸÛŒŰŽŰ±ÙŰȘ ŰłÛ’ ÛŒŰ§ Ú©ŰłÛŒ ŰșÛŒŰ± Ù…Ű­ÙÙˆŰž ŰŽŰŻÛ ŰȘŰšŰŻÛŒÙ„ÛŒÙˆÚș ŰłÛ’ Ù…Ű­Ű±ÙˆÙ… ہو ŰłÚ©ŰȘے ہیÚș"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Ù…Ù†ŰłÙˆŰź Ú©Ű±ÛŒÚș"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Ű±ÛŒ ۧ۳Ùčۧ۱Ùč Ú©Ű±ÛŒÚș"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"ŰŻÙˆŰšŰ§Ű±Û نہ ŰŻÚ©ÚŸŰ§ŰŠÛŒÚș"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"۹ڑۧ Ú©Ű±ÛŒÚș"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Ú†ÚŸÙˆÙčۧ Ú©Ű±ÛŒÚș"</string>
     <string name="close_button_text" msgid="2913281996024033299">"ŰšÙ†ŰŻ Ú©Ű±ÛŒÚș"</string>
     <string name="back_button_text" msgid="1469718707134137085">"ٟیچڟے"</string>
     <string name="handle_text" msgid="1766582106752184456">"ہینڈل"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"مکمل Ű§ŰłÚ©Ű±ÛŒÙ†"</string>
     <string name="desktop_text" msgid="1077633567027630454">"ÚˆÛŒŰłÚ© ÙčŰ§ÙŸ موڈ"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Ű§ŰłÙŸÙ„Ùč Ű§ŰłÚ©Ű±ÛŒÙ†"</string>
     <string name="more_button_text" msgid="3655388105592893530">"مŰČÛŒŰŻ"</string>
     <string name="float_button_text" msgid="9221657008391364581">"فلوÙč"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index 8f173d5..3ce1672 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Tepada 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Tepada 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Pastda to‘liq ekran"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Chapga ajratish"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Oʻngga ajratish"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Yuqoriga ajratish"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Pastga ajratish"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Ixcham rejimdan foydalanish"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Chiqish uchun ekran pastidan tepaga suring yoki ilovaning tepasidagi istalgan joyga bosing."</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Ixcham rejimni ishga tushirish"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Qayta joylash uchun ilova tashqarisiga ikki marta bosing"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Batafsil axborot olish uchun kengaytiring."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Yaxshi koʻrinishi uchun qayta ishga tushirilsinmi?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Ilovani ekranda yaxshiroq koʻrinishi uchun qayta ishga tushirishingiz mumkin. Bunda jarayonlar yoki saqlanmagan oʻzgarishlar yoʻqolishi mumkin."</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Bekor qilish"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Qaytadan"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Boshqa chiqmasin"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Yoyish"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Kichraytirish"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Yopish"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Orqaga"</string>
     <string name="handle_text" msgid="1766582106752184456">"Identifikator"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Butun ekran"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Desktop rejimi"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Ekranni ikkiga ajratish"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Yana"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Pufakli"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index 1d5b9d6..1bf967a 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Trên 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Trên 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Toàn màn hình phía dưới"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Chia đôi màn hình về bên trái"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Chia đôi màn hình về bên pháșŁi"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Chia đôi màn hình lên trên cùng"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Chia đôi màn hình xuống dưới cùng"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Cách dùng cháșż độ một tay"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Để thoát, hãy vuốt lên từ cuối màn hình hoáș·c nháș„n vào vị trí báș„t kỳ phía trên ứng dỄng"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"BáșŻt đáș§u cháșż độ một tay"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Nháș„n đúp bên ngoài ứng dỄng để đáș·t láșĄi vị trí"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Mở rộng để xem thêm thông tin."</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Khởi động láșĄi để ứng dỄng trông vừa váș·n hÆĄn?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"BáșĄn có thể khởi động láșĄi ứng dỄng để ứng dỄng nhìn đáșčp hÆĄn trên màn hình. Tuy nhiên, náșżu làm váș­y, báșĄn có thể máș„t tiáșżn trình hoáș·c mọi thay đổi chưa lưu"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Huá»·"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Khởi động láșĄi"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Không hiện láșĄi"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Phóng to"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Thu nhỏ"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Đóng"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Quay láșĄi"</string>
     <string name="handle_text" msgid="1766582106752184456">"Xá»­ lý"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Toàn màn hình"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Cháșż độ máy tính"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Chia đôi màn hình"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Tuỳ chọn khác"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Nổi"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index 87f2973..3a8dd24 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"饶郚 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"饶郚 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"ćș•éƒšć…šć±"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"ć·Šćˆ†ć±"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"ćłćˆ†ć±"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"äžŠćˆ†ć±"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"äž‹ćˆ†ć±"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"äœżç”šć•æ‰‹æšĄćŒ"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"橂需退ć‡șïŒŒèŻ·ä»Žć±ćč•ćș•éƒšć‘äžŠæ»‘ćŠšïŒŒæˆ–ç‚č按ćș”甚䞊æ–čçš„ä»»æ„äœçœź"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"ćŻćŠšć•æ‰‹æšĄćŒ"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ćœšæŸäžȘćș”ç”šć€–èżžç»­ç‚čæŒ‰äž€æŹĄïŒŒćłćŻè°ƒæ•Žćźƒçš„äœçœź"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"矄道äș†"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ć±•ćŒ€ćłćŻäș†è§ŁèŻŠæƒ…ă€‚"</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"é‡ćŻä»„æ”čèż›ć€–è§‚ïŒŸ"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"æ‚šćŻä»„é‡ćŻćș”ç”šïŒŒäœżć…¶ćœšć±ćč•䞊的星ç€șæ•ˆæžœæ›Žć„œïŒŒäœ†æ‚šćŻèƒœäŒšäžąć€±èż›ćșŠæˆ–任䜕æœȘäżć­˜çš„æ›Žæ”č"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"ć–æ¶ˆ"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"重搯"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"äžć†æ˜Ÿç€ș"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"æœ€ć€§ćŒ–"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"æœ€ć°ćŒ–"</string>
     <string name="close_button_text" msgid="2913281996024033299">"慳闭"</string>
     <string name="back_button_text" msgid="1469718707134137085">"èż”ć›ž"</string>
     <string name="handle_text" msgid="1766582106752184456">"怄理"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"ć…šć±"</string>
     <string name="desktop_text" msgid="1077633567027630454">"æĄŒéąæšĄćŒ"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"ćˆ†ć±"</string>
     <string name="more_button_text" msgid="3655388105592893530">"æ›Žć€š"</string>
     <string name="float_button_text" msgid="9221657008391364581">"æ‚Źæ”ź"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index f9b22d22..112bd41 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"頂郹 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"頂郹 30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"ćș•éƒšć…šèžąćč•"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"戆ć‰Čć·ŠćŽć€ćŸŸ"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"戆ć‰Č揳恮捀柟"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"戆ć‰Č侊æ–č捀柟"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"戆ć‰Č例æ–č捀柟"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"äœżç”šć–źæ‰‹æšĄćŒ"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"ćŠ‚èŠé€€ć‡șïŒŒè«‹ćŸžèžąćč•ćș•éƒšć‘äžŠæ»‘ć‹•ïŒŒæˆ–èŒ•æŒ‰æ‡‰ç”šçš‹ćŒäžŠæ–čçš„ä»»äœ•äœçœź"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"é–‹ć§‹ć–źæ‰‹æšĄćŒ"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ćœšæ‡‰ç”šçš‹ćŒć€–èŒ•æŒ‰ć…©äž‹ćłćŻèȘżæ•Žäœçœź"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"矄道äș†"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ć±•é–‹ćłćŻæŸ„çœ‹è©łæƒ…ă€‚"</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"èŠé‡æ–°ć•Ÿć‹•æ”č斄æȘąèŠ–ç•«éąć—ŽïŒŸ"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"æ‚šćŻé‡æ–°ć•Ÿć‹•æ‡‰ç”šçš‹ćŒïŒŒèź“çł»ç”±æ›Žæ–°æȘąèŠ–ç•«éąïŒ›äœ†çł»ç”±ćŻèƒœäžæœƒć„Č歘盼才é€ČćșŠćŠæ‚šäœœć‡șçš„ä»»äœ•èźŠæ›Ž"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"ć–æ¶ˆ"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"é‡æ–°ć•Ÿć‹•"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"äžèŠć†éĄŻç€ș"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"æœ€ć€§ćŒ–"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"æœ€ć°ćŒ–"</string>
     <string name="close_button_text" msgid="2913281996024033299">"關閉"</string>
     <string name="back_button_text" msgid="1469718707134137085">"èż”ćŽ»"</string>
     <string name="handle_text" msgid="1766582106752184456">"控點"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"ć…šèžąćč•"</string>
     <string name="desktop_text" msgid="1077633567027630454">"æĄŒéąæšĄćŒ"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"戆ć‰Čèžąćč•"</string>
     <string name="more_button_text" msgid="3655388105592893530">"æ›Žć€š"</string>
     <string name="float_button_text" msgid="9221657008391364581">"æ”źć‹•"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index 1438e52..edb0c7e 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"仄 50% çš„èžąćč•ç©ș間顯ç€șé ‚ç«Żç•«éą"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"仄 30% çš„èžąćč•ç©ș間顯ç€șé ‚ç«Żç•«éą"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"ä»„ć…šèžąćč•饯ç€șćș•éƒšç•«éą"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"戆ć‰Čć·ŠćŽć€ćŸŸ"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"戆ć‰Č揳恮捀柟"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"戆ć‰Č侊æ–č捀柟"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"戆ć‰Č例æ–č捀柟"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"äœżç”šć–źæ‰‹æšĄćŒ"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"ćŠ‚èŠé€€ć‡șïŒŒè«‹ćŸžèžąćč•ćș•éƒšć‘äžŠæ»‘ć‹•ïŒŒæˆ–èŒ•è§žæ‡‰ç”šçš‹ćŒäžŠæ–čçš„ä»»äœ•äœçœź"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"ć•Ÿć‹•ć–źæ‰‹æšĄćŒ"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ćœšæ‡‰ç”šçš‹ćŒć€–èŒ•è§žć…©äž‹ćłćŻèȘżæ•Žäœçœź"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"我矄道äș†"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ć±•é–‹ćłćŻæŸ„çœ‹è©łçŽ°èł‡èšŠă€‚"</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"èŠé‡æ–°ć•Ÿć‹•æ”č斄æȘąèŠ–ç•«éąć—ŽïŒŸ"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"äœ ćŻä»„é‡æ–°ć•Ÿć‹•æ‡‰ç”šçš‹ćŒïŒŒèź“çł»ç”±æ›Žæ–°æȘąèŠ–ç•«éąă€‚äžéŽïŒŒçł»ç”±ćŻèƒœäžæœƒć„Č歘盼才é€ČćșŠćŠäœ æ‰€ćšçš„任䜕èꊿ›Ž"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"ć–æ¶ˆ"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"é‡æ–°ć•Ÿć‹•"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"äžèŠć†éĄŻç€ș"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"æœ€ć€§ćŒ–"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"æœ€ć°ćŒ–"</string>
     <string name="close_button_text" msgid="2913281996024033299">"關閉"</string>
     <string name="back_button_text" msgid="1469718707134137085">"èż”ć›ž"</string>
     <string name="handle_text" msgid="1766582106752184456">"控點"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"ć…šèžąćč•"</string>
     <string name="desktop_text" msgid="1077633567027630454">"é›»è…ŠæšĄćŒ"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"戆ć‰Čç•«éą"</string>
     <string name="more_button_text" msgid="3655388105592893530">"æ›Žć€š"</string>
     <string name="float_button_text" msgid="9221657008391364581">"æ”źć‹•"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index e9238dc..749b223 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -47,6 +47,10 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Okuphezulu okungu-50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Okuphezulu okungu-30%"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Ngaphansi kwesikrini esigcwele"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Hlukanisa ngakwesobunxele"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Hlukanisa ngakwesokudla"</string>
+    <string name="accessibility_split_top" msgid="2789329702027147146">"Hlukanisa phezulu"</string>
+    <string name="accessibility_split_bottom" msgid="8694551025220868191">"Hlukanisa phansi"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Ukusebenzisa imodi yesandla esisodwa"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Ukuze uphume, swayipha ngaphezulu kusuka ngezansi kwesikrini noma thepha noma kuphi ngenhla kohlelo lokusebenza"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Qalisa imodi yesandla esisodwa"</string>
@@ -82,14 +86,29 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Thepha kabili ngaphandle kwe-app ukuze uyimise kabusha"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Ngiyezwa"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Nweba ukuze uthole ulwazi olwengeziwe"</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"Qala kabusha ukuze uthole ukubuka okungcono?"</string>
+    <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"Ungakwazi ukuqala kabusha i-app ukuze ibukeke kangcono esikrinini sakho, kodwa ungase ulahlekelwe ukuqhubeka kwakho nanoma yiziphi izinguquko ezingalondoloziwe"</string>
+    <string name="letterbox_restart_cancel" msgid="1342209132692537805">"Khansela"</string>
+    <string name="letterbox_restart_restart" msgid="8529976234412442973">"Qala kabusha"</string>
+    <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ungabonisi futhi"</string>
     <string name="maximize_button_text" msgid="1650859196290301963">"Khulisa"</string>
     <string name="minimize_button_text" msgid="271592547935841753">"Nciphisa"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Vala"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Emuva"</string>
     <string name="handle_text" msgid="1766582106752184456">"Isibambo"</string>
+    <!-- no translation found for app_icon_text (2823268023931811747) -->
+    <skip />
     <string name="fullscreen_text" msgid="1162316685217676079">"Isikrini esigcwele"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Imodi Yedeskithophu"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Hlukanisa isikrini"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Okwengeziwe"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Iflowuthi"</string>
+    <!-- no translation found for select_text (5139083974039906583) -->
+    <skip />
+    <!-- no translation found for screenshot_text (1477704010087786671) -->
+    <skip />
+    <!-- no translation found for close_text (4986518933445178928) -->
+    <skip />
+    <!-- no translation found for collapse_menu_text (7515008122450342029) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 774f6c6..76eb094 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -105,6 +105,10 @@
         1.777778
     </item>
 
+    <!-- The aspect ratio that by which optimizations to large screen sizes are made.
+         Needs to be less that or equal to 1. -->
+    <item name="config_pipLargeScreenOptimizedAspectRatio" format="float" type="dimen">0.5625</item>
+
     <!-- The default gravity for the picture-in-picture window.
          Currently, this maps to Gravity.BOTTOM | Gravity.RIGHT -->
     <integer name="config_defaultPictureInPictureGravity">0x55</integer>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index a1da649..1f9b6cf 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -228,7 +228,7 @@
     <dimen name="bubble_user_education_stack_padding">16dp</dimen>
 
     <!-- Bottom and end margin for compat buttons. -->
-    <dimen name="compat_button_margin">16dp</dimen>
+    <dimen name="compat_button_margin">24dp</dimen>
 
     <!-- The radius of the corners of the compat hint bubble. -->
     <dimen name="compat_hint_corner_radius">28dp</dimen>
@@ -331,30 +331,6 @@
     -->
     <dimen name="overridable_minimal_size_pip_resizable_task">48dp</dimen>
 
-    <!-- The size of the drag handle / menu shown along with a floating task. -->
-    <dimen name="floating_task_menu_size">32dp</dimen>
-
-    <!-- The size of menu items in the floating task menu. -->
-    <dimen name="floating_task_menu_item_size">24dp</dimen>
-
-    <!-- The horizontal margin of menu items in the floating task menu. -->
-    <dimen name="floating_task_menu_item_padding">5dp</dimen>
-
-    <!-- The width of visible floating view region when stashed. -->
-    <dimen name="floating_task_stash_offset">32dp</dimen>
-
-    <!-- The amount of elevation for a floating task. -->
-    <dimen name="floating_task_elevation">8dp</dimen>
-
-    <!-- The amount of padding around the bottom and top of the task. -->
-    <dimen name="floating_task_vertical_padding">8dp</dimen>
-
-    <!-- The normal size of the dismiss target. -->
-    <dimen name="floating_task_dismiss_circle_size">150dp</dimen>
-
-    <!-- The smaller size of the dismiss target (shrinks when something is in the target). -->
-    <dimen name="floating_dismiss_circle_small">120dp</dimen>
-
     <!-- The thickness of shadows of a window that has focus in DIP. -->
     <dimen name="freeform_decor_shadow_focused_thickness">20dp</dimen>
 
@@ -364,11 +340,15 @@
     <!-- Height of button (32dp)  + 2 * margin (5dp each). -->
     <dimen name="freeform_decor_caption_height">42dp</dimen>
 
-    <!-- Width of buttons (64dp) + handle (128dp) + padding (24dp total). -->
-    <dimen name="freeform_decor_caption_width">216dp</dimen>
+    <!-- Width of buttons (32dp each) + padding (128dp total). -->
+    <dimen name="freeform_decor_caption_menu_width">256dp</dimen>
+
+    <dimen name="freeform_decor_caption_menu_height">250dp</dimen>
 
     <dimen name="freeform_resize_handle">30dp</dimen>
 
     <dimen name="freeform_resize_corner">44dp</dimen>
 
+    <dimen name="caption_menu_elevation">4dp</dimen>
+
 </resources>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 250dac6..63992329 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -100,6 +100,15 @@
     <!-- Accessibility action for moving docked stack divider to make the bottom screen full screen [CHAR LIMIT=NONE] -->
     <string name="accessibility_action_divider_bottom_full">Bottom full screen</string>
 
+    <!-- Accessibility label for splitting to the left drop zone [CHAR LIMIT=NONE] -->
+    <string name="accessibility_split_left">Split left</string>
+    <!-- Accessibility label for splitting to the right drop zone [CHAR LIMIT=NONE] -->
+    <string name="accessibility_split_right">Split right</string>
+    <!-- Accessibility label for splitting to the top drop zone [CHAR LIMIT=NONE] -->
+    <string name="accessibility_split_top">Split top</string>
+    <!-- Accessibility label for splitting to the bottom drop zone [CHAR LIMIT=NONE] -->
+    <string name="accessibility_split_bottom">Split bottom</string>
+
     <!-- One-Handed Tutorial title [CHAR LIMIT=60] -->
     <string name="one_handed_tutorial_title">Using one-handed mode</string>
     <!-- One-Handed Tutorial description [CHAR LIMIT=NONE] -->
@@ -216,6 +225,8 @@
     <string name="back_button_text">Back</string>
     <!-- Accessibility text for the caption handle [CHAR LIMIT=NONE] -->
     <string name="handle_text">Handle</string>
+    <!-- Accessibility text for the handle menu app icon [CHAR LIMIT=NONE] -->
+    <string name="app_icon_text">App Icon</string>
     <!-- Accessibility text for the handle fullscreen button [CHAR LIMIT=NONE] -->
     <string name="fullscreen_text">Fullscreen</string>
     <!-- Accessibility text for the handle desktop button [CHAR LIMIT=NONE] -->
@@ -226,4 +237,12 @@
     <string name="more_button_text">More</string>
     <!-- Accessibility text for the handle floating window button [CHAR LIMIT=NONE] -->
     <string name="float_button_text">Float</string>
+    <!-- Accessibility text for the handle menu select button [CHAR LIMIT=NONE] -->
+    <string name="select_text">Select</string>
+    <!-- Accessibility text for the handle menu screenshot button [CHAR LIMIT=NONE] -->
+    <string name="screenshot_text">Screenshot</string>
+    <!-- Accessibility text for the handle menu close button [CHAR LIMIT=NONE] -->
+    <string name="close_text">Close</string>
+    <!-- Accessibility text for the handle menu close menu button [CHAR LIMIT=NONE] -->
+    <string name="collapse_menu_text">Close Menu</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml
index e8f340c..bae009a 100644
--- a/libs/WindowManager/Shell/res/values/styles.xml
+++ b/libs/WindowManager/Shell/res/values/styles.xml
@@ -37,6 +37,22 @@
         <item name="android:padding">4dp</item>
     </style>
 
+    <style name="CaptionWindowingButtonStyle">
+        <item name="android:layout_width">32dp</item>
+        <item name="android:layout_height">32dp</item>
+        <item name="android:padding">4dp</item>
+        <item name="android:layout_marginTop">5dp</item>
+        <item name="android:layout_marginBottom">5dp</item>
+    </style>
+
+    <style name="CaptionMenuButtonStyle" parent="@style/Widget.AppCompat.Button.Borderless">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">52dp</item>
+        <item name="android:layout_marginStart">10dp</item>
+        <item name="android:padding">4dp</item>
+        <item name="android:gravity">start|center_vertical</item>
+    </style>
+
     <style name="DockedDividerBackground">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">@dimen/split_divider_bar_width</item>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java
index d276002..e2012b4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java
@@ -16,6 +16,8 @@
 
 package com.android.wm.shell;
 
+import android.os.Build;
+
 import com.android.wm.shell.protolog.ShellProtoLogImpl;
 import com.android.wm.shell.sysui.ShellCommandHandler;
 import com.android.wm.shell.sysui.ShellInit;
@@ -41,6 +43,9 @@
 
     void onInit() {
         mShellCommandHandler.addCommandCallback("protolog", this, this);
+        if (Build.IS_DEBUGGABLE) {
+            mShellProtoLog.startProtoLog(null /* PrintWriter */);
+        }
     }
 
     @Override
@@ -84,6 +89,15 @@
                 String[] groups = Arrays.copyOfRange(args, 1, args.length);
                 return mShellProtoLog.stopTextLogging(groups, pw) == 0;
             }
+            case "save-for-bugreport": {
+                if (!mShellProtoLog.isProtoEnabled()) {
+                    pw.println("Logging to proto is not enabled for WMShell.");
+                    return false;
+                }
+                mShellProtoLog.stopProtoLog(pw, true /* writeToFile */);
+                mShellProtoLog.startProtoLog(pw);
+                return true;
+            }
             default: {
                 pw.println("Invalid command: " + args[0]);
                 printShellCommandHelp(pw, "");
@@ -108,5 +122,7 @@
         pw.println(prefix + "  Enable logcat logging for given groups.");
         pw.println(prefix + "disable-text [group...]");
         pw.println(prefix + "  Disable logcat logging for given groups.");
+        pw.println(prefix + "save-for-bugreport");
+        pw.println(prefix + "  Flush proto logging to file, only if it's enabled.");
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 2363092..aaeef19 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -82,7 +82,7 @@
     /** Flag for U animation features */
     public static boolean IS_U_ANIMATION_ENABLED =
             SystemProperties.getInt("persist.wm.debug.predictive_back_anim",
-                    SETTING_VALUE_OFF) == SETTING_VALUE_ON;
+                    SETTING_VALUE_ON) == SETTING_VALUE_ON;
     /** Predictive back animation developer option */
     private final AtomicBoolean mEnableAnimations = new AtomicBoolean(false);
     // TODO (b/241808055) Find a appropriate time to remove during refactor
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 09dc68a..e24c228 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -21,6 +21,7 @@
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
 
 import android.annotation.DimenRes;
+import android.annotation.Hide;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Notification;
@@ -125,7 +126,7 @@
     private Icon mIcon;
     private boolean mIsBubble;
     private boolean mIsTextChanged;
-    private boolean mIsClearable;
+    private boolean mIsDismissable;
     private boolean mShouldSuppressNotificationDot;
     private boolean mShouldSuppressNotificationList;
     private boolean mShouldSuppressPeek;
@@ -180,7 +181,7 @@
     @VisibleForTesting(visibility = PRIVATE)
     public Bubble(@NonNull final String key, @NonNull final ShortcutInfo shortcutInfo,
             final int desiredHeight, final int desiredHeightResId, @Nullable final String title,
-            int taskId, @Nullable final String locus, Executor mainExecutor,
+            int taskId, @Nullable final String locus, boolean isDismissable, Executor mainExecutor,
             final Bubbles.BubbleMetadataFlagListener listener) {
         Objects.requireNonNull(key);
         Objects.requireNonNull(shortcutInfo);
@@ -189,6 +190,7 @@
         mKey = key;
         mGroupKey = null;
         mLocusId = locus != null ? new LocusId(locus) : null;
+        mIsDismissable = isDismissable;
         mFlags = 0;
         mUser = shortcutInfo.getUserHandle();
         mPackageName = shortcutInfo.getPackage();
@@ -245,6 +247,11 @@
         return mKey;
     }
 
+    @Hide
+    public boolean isDismissable() {
+        return mIsDismissable;
+    }
+
     /**
      * @see StatusBarNotification#getGroupKey()
      * @return the group key for this bubble, if one exists.
@@ -526,7 +533,7 @@
             mDeleteIntent = entry.getBubbleMetadata().getDeleteIntent();
         }
 
-        mIsClearable = entry.isClearable();
+        mIsDismissable = entry.isDismissable();
         mShouldSuppressNotificationDot = entry.shouldSuppressNotificationDot();
         mShouldSuppressNotificationList = entry.shouldSuppressNotificationList();
         mShouldSuppressPeek = entry.shouldSuppressPeek();
@@ -605,7 +612,7 @@
      * Whether this notification should be shown in the shade.
      */
     boolean showInShade() {
-        return !shouldSuppressNotification() || !mIsClearable;
+        return !shouldSuppressNotification() || !mIsDismissable;
     }
 
     /**
@@ -870,7 +877,7 @@
         pw.print("  desiredHeight: "); pw.println(getDesiredHeightString());
         pw.print("  suppressNotif: "); pw.println(shouldSuppressNotification());
         pw.print("  autoExpand:    "); pw.println(shouldAutoExpand());
-        pw.print("  isClearable:   "); pw.println(mIsClearable);
+        pw.print("  isDismissable: "); pw.println(mIsDismissable);
         pw.println("  bubbleMetadataFlagListener null: " + (mBubbleMetadataFlagListener == null));
         if (mExpandedView != null) {
             mExpandedView.dump(pw);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
index 3a59614..e37c785 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
@@ -109,7 +109,8 @@
                     b.rawDesiredHeightResId,
                     b.title,
                     b.taskId,
-                    b.locusId?.id
+                    b.locusId?.id,
+                    b.isDismissable
             )
         }
     }
@@ -205,6 +206,7 @@
                                 entity.title,
                                 entity.taskId,
                                 entity.locus,
+                                entity.isDismissable,
                                 mainExecutor,
                                 bubbleMetadataFlagListener
                         )
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleEntry.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleEntry.java
index 5f42826..afe19c4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleEntry.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleEntry.java
@@ -38,18 +38,18 @@
     private StatusBarNotification mSbn;
     private Ranking mRanking;
 
-    private boolean mIsClearable;
+    private boolean mIsDismissable;
     private boolean mShouldSuppressNotificationDot;
     private boolean mShouldSuppressNotificationList;
     private boolean mShouldSuppressPeek;
 
     public BubbleEntry(@NonNull StatusBarNotification sbn,
-            Ranking ranking, boolean isClearable, boolean shouldSuppressNotificationDot,
+            Ranking ranking, boolean isDismissable, boolean shouldSuppressNotificationDot,
             boolean shouldSuppressNotificationList, boolean shouldSuppressPeek) {
         mSbn = sbn;
         mRanking = ranking;
 
-        mIsClearable = isClearable;
+        mIsDismissable = isDismissable;
         mShouldSuppressNotificationDot = shouldSuppressNotificationDot;
         mShouldSuppressNotificationList = shouldSuppressNotificationList;
         mShouldSuppressPeek = shouldSuppressPeek;
@@ -115,9 +115,9 @@
         return mRanking.canBubble();
     }
 
-    /** @return true if this notification is clearable. */
-    public boolean isClearable() {
-        return mIsClearable;
+    /** @return true if this notification can be dismissed. */
+    public boolean isDismissable() {
+        return mIsDismissable;
     }
 
     /** @return true if {@link Policy#SUPPRESSED_EFFECT_BADGE} set for this notification. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleEntity.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleEntity.kt
index 186b9b1..9b2e263 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleEntity.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleEntity.kt
@@ -27,5 +27,6 @@
     @DimenRes val desiredHeightResId: Int,
     val title: String? = null,
     val taskId: Int,
-    val locus: String? = null
+    val locus: String? = null,
+    val isDismissable: Boolean = false
 )
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelper.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelper.kt
index f4fa183..48d8ccf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelper.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelper.kt
@@ -43,6 +43,7 @@
 private const val ATTR_TITLE = "t"
 private const val ATTR_TASK_ID = "tid"
 private const val ATTR_LOCUS = "l"
+private const val ATTR_DISMISSABLE = "d"
 
 /**
  * Writes the bubbles in xml format into given output stream.
@@ -84,6 +85,7 @@
         bubble.title?.let { serializer.attribute(null, ATTR_TITLE, it) }
         serializer.attribute(null, ATTR_TASK_ID, bubble.taskId.toString())
         bubble.locus?.let { serializer.attribute(null, ATTR_LOCUS, it) }
+        serializer.attribute(null, ATTR_DISMISSABLE, bubble.isDismissable.toString())
         serializer.endTag(null, TAG_BUBBLE)
     } catch (e: IOException) {
         throw RuntimeException(e)
@@ -142,7 +144,8 @@
             parser.getAttributeWithName(ATTR_DESIRED_HEIGHT_RES_ID)?.toInt() ?: return null,
             parser.getAttributeWithName(ATTR_TITLE),
             parser.getAttributeWithName(ATTR_TASK_ID)?.toInt() ?: INVALID_TASK_ID,
-            parser.getAttributeWithName(ATTR_LOCUS)
+            parser.getAttributeWithName(ATTR_LOCUS),
+            parser.getAttributeWithName(ATTR_DISMISSABLE)?.toBoolean() ?: false
     )
 }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DevicePostureController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DevicePostureController.java
new file mode 100644
index 0000000..22587f4
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DevicePostureController.java
@@ -0,0 +1,147 @@
+/*
+ * 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.wm.shell.common;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.devicestate.DeviceStateManager;
+import android.util.SparseIntArray;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.wm.shell.sysui.ShellInit;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Wrapper class to track the device posture change on Fold-ables.
+ * See also <a
+ * href="https://developer.android.com/guide/topics/large-screens/learn-about-foldables
+ * #foldable_postures">Foldable states and postures</a> for reference.
+ *
+ * Note that most of the implementation here inherits from
+ * {@link com.android.systemui.statusbar.policy.DevicePostureController}.
+ */
+public class DevicePostureController {
+    @IntDef(prefix = {"DEVICE_POSTURE_"}, value = {
+            DEVICE_POSTURE_UNKNOWN,
+            DEVICE_POSTURE_CLOSED,
+            DEVICE_POSTURE_HALF_OPENED,
+            DEVICE_POSTURE_OPENED,
+            DEVICE_POSTURE_FLIPPED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DevicePostureInt {}
+
+    // NOTE: These constants **must** match those defined for Jetpack Sidecar. This is because we
+    // use the Device State -> Jetpack Posture map to translate between the two.
+    public static final int DEVICE_POSTURE_UNKNOWN = 0;
+    public static final int DEVICE_POSTURE_CLOSED = 1;
+    public static final int DEVICE_POSTURE_HALF_OPENED = 2;
+    public static final int DEVICE_POSTURE_OPENED = 3;
+    public static final int DEVICE_POSTURE_FLIPPED = 4;
+
+    private final Context mContext;
+    private final ShellExecutor mMainExecutor;
+    private final List<OnDevicePostureChangedListener> mListeners = new ArrayList<>();
+    private final SparseIntArray mDeviceStateToPostureMap = new SparseIntArray();
+
+    private int mDevicePosture = DEVICE_POSTURE_UNKNOWN;
+
+    public DevicePostureController(
+            Context context, ShellInit shellInit, ShellExecutor mainExecutor) {
+        mContext = context;
+        mMainExecutor = mainExecutor;
+        shellInit.addInitCallback(this::onInit, this);
+    }
+
+    private void onInit() {
+        // Most of this is borrowed from WindowManager/Jetpack/DeviceStateManagerPostureProducer.
+        // Using the sidecar/extension libraries directly brings in a new dependency that it'd be
+        // good to avoid (along with the fact that sidecar is deprecated, and extensions isn't fully
+        // ready yet), and we'd have to make our own layer over the sidecar library anyway to easily
+        // allow the implementation to change, so it was easier to just interface with
+        // DeviceStateManager directly.
+        String[] deviceStatePosturePairs = mContext.getResources()
+                .getStringArray(R.array.config_device_state_postures);
+        for (String deviceStatePosturePair : deviceStatePosturePairs) {
+            String[] deviceStatePostureMapping = deviceStatePosturePair.split(":");
+            if (deviceStatePostureMapping.length != 2) {
+                continue;
+            }
+
+            int deviceState;
+            int posture;
+            try {
+                deviceState = Integer.parseInt(deviceStatePostureMapping[0]);
+                posture = Integer.parseInt(deviceStatePostureMapping[1]);
+            } catch (NumberFormatException e) {
+                continue;
+            }
+
+            mDeviceStateToPostureMap.put(deviceState, posture);
+        }
+
+        final DeviceStateManager deviceStateManager = mContext.getSystemService(
+                DeviceStateManager.class);
+        if (deviceStateManager != null) {
+            deviceStateManager.registerCallback(mMainExecutor, state -> onDevicePostureChanged(
+                    mDeviceStateToPostureMap.get(state, DEVICE_POSTURE_UNKNOWN)));
+        }
+    }
+
+    @VisibleForTesting
+    void onDevicePostureChanged(int devicePosture) {
+        if (devicePosture == mDevicePosture) return;
+        mDevicePosture = devicePosture;
+        mListeners.forEach(l -> l.onDevicePostureChanged(mDevicePosture));
+    }
+
+    /**
+     * Register {@link OnDevicePostureChangedListener} for device posture changes.
+     * The listener will receive callback with current device posture upon registration.
+     */
+    public void registerOnDevicePostureChangedListener(
+            @NonNull OnDevicePostureChangedListener listener) {
+        if (mListeners.contains(listener)) return;
+        mListeners.add(listener);
+        listener.onDevicePostureChanged(mDevicePosture);
+    }
+
+    /**
+     * Unregister {@link OnDevicePostureChangedListener} for device posture changes.
+     */
+    public void unregisterOnDevicePostureChangedListener(
+            @NonNull OnDevicePostureChangedListener listener) {
+        mListeners.remove(listener);
+    }
+
+    /**
+     * Listener interface for device posture change.
+     */
+    public interface OnDevicePostureChangedListener {
+        /**
+         * Callback when device posture changes.
+         * See {@link DevicePostureInt} for callback values.
+         */
+        void onDevicePostureChanged(@DevicePostureInt int posture);
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java
index 9e0a48b..e2106e4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java
@@ -216,7 +216,6 @@
         args.argi1 = homeTaskVisible ? 1 : 0;
         args.argi2 = clearedTask ? 1 : 0;
         args.argi3 = wasVisible ? 1 : 0;
-        mMainHandler.removeMessages(ON_ACTIVITY_RESTART_ATTEMPT);
         mMainHandler.obtainMessage(ON_ACTIVITY_RESTART_ATTEMPT, args).sendToTarget();
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 45b234a..f616e6f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -699,19 +699,6 @@
         return bounds.width() > bounds.height();
     }
 
-    /** Reverse the split position. */
-    @SplitPosition
-    public static int reversePosition(@SplitPosition int position) {
-        switch (position) {
-            case SPLIT_POSITION_TOP_OR_LEFT:
-                return SPLIT_POSITION_BOTTOM_OR_RIGHT;
-            case SPLIT_POSITION_BOTTOM_OR_RIGHT:
-                return SPLIT_POSITION_TOP_OR_LEFT;
-            default:
-                return SPLIT_POSITION_UNDEFINED;
-        }
-    }
-
     /**
      * Return if this layout is landscape.
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
new file mode 100644
index 0000000..042721c9
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
@@ -0,0 +1,85 @@
+/*
+ * 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.wm.shell.common.split;
+
+import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
+import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.PendingIntent;
+import android.content.Intent;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.wm.shell.ShellTaskOrganizer;
+
+/** Helper utility class for split screen components to use. */
+public class SplitScreenUtils {
+    /** Reverse the split position. */
+    @SplitScreenConstants.SplitPosition
+    public static int reverseSplitPosition(@SplitScreenConstants.SplitPosition int position) {
+        switch (position) {
+            case SPLIT_POSITION_TOP_OR_LEFT:
+                return SPLIT_POSITION_BOTTOM_OR_RIGHT;
+            case SPLIT_POSITION_BOTTOM_OR_RIGHT:
+                return SPLIT_POSITION_TOP_OR_LEFT;
+            case SPLIT_POSITION_UNDEFINED:
+            default:
+                return SPLIT_POSITION_UNDEFINED;
+        }
+    }
+
+    /** Returns true if the task is valid for split screen. */
+    public static boolean isValidToSplit(ActivityManager.RunningTaskInfo taskInfo) {
+        return taskInfo != null && taskInfo.supportsMultiWindow
+                && ArrayUtils.contains(CONTROLLED_ACTIVITY_TYPES, taskInfo.getActivityType())
+                && ArrayUtils.contains(CONTROLLED_WINDOWING_MODES, taskInfo.getWindowingMode());
+    }
+
+    /** Retrieve package name from an intent */
+    @Nullable
+    public static String getPackageName(Intent intent) {
+        if (intent == null || intent.getComponent() == null) {
+            return null;
+        }
+        return intent.getComponent().getPackageName();
+    }
+
+    /** Retrieve package name from a PendingIntent */
+    @Nullable
+    public static String getPackageName(PendingIntent pendingIntent) {
+        if (pendingIntent == null || pendingIntent.getIntent() == null) {
+            return null;
+        }
+        return getPackageName(pendingIntent.getIntent());
+    }
+
+    /** Retrieve package name from a taskId */
+    @Nullable
+    public static String getPackageName(int taskId, ShellTaskOrganizer taskOrganizer) {
+        final ActivityManager.RunningTaskInfo taskInfo = taskOrganizer.getRunningTaskInfo(taskId);
+        return taskInfo != null ? getPackageName(taskInfo.baseIntent) : null;
+    }
+
+    /** Returns true if they are the same package. */
+    public static boolean samePackage(String packageName1, String packageName2) {
+        return packageName1 != null && packageName1.equals(packageName2);
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIConfiguration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIConfiguration.java
index 4f33a71..06f0a70 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIConfiguration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIConfiguration.java
@@ -16,11 +16,12 @@
 
 package com.android.wm.shell.compatui;
 
+import android.annotation.NonNull;
+import android.app.TaskInfo;
 import android.content.Context;
+import android.content.SharedPreferences;
 import android.provider.DeviceConfig;
 
-import androidx.annotation.NonNull;
-
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.annotations.ShellMainThread;
@@ -34,11 +35,27 @@
 @WMSingleton
 public class CompatUIConfiguration implements DeviceConfig.OnPropertiesChangedListener {
 
-    static final String KEY_ENABLE_LETTERBOX_RESTART_DIALOG = "enable_letterbox_restart_dialog";
+    private static final String KEY_ENABLE_LETTERBOX_RESTART_DIALOG =
+            "enable_letterbox_restart_confirmation_dialog";
 
-    static final String KEY_ENABLE_LETTERBOX_REACHABILITY_EDUCATION =
+    private static final String KEY_ENABLE_LETTERBOX_REACHABILITY_EDUCATION =
             "enable_letterbox_reachability_education";
 
+    private static final boolean DEFAULT_VALUE_ENABLE_LETTERBOX_RESTART_DIALOG = true;
+
+    private static final boolean DEFAULT_VALUE_ENABLE_LETTERBOX_REACHABILITY_EDUCATION = false;
+
+    /**
+     * The name of the {@link SharedPreferences} that holds which user has seen the Restart
+     * confirmation dialog.
+     */
+    private static final String DONT_SHOW_RESTART_DIALOG_PREF_NAME = "dont_show_restart_dialog";
+
+    /**
+     * The {@link SharedPreferences} instance for {@link #DONT_SHOW_RESTART_DIALOG_PREF_NAME}.
+     */
+    private final SharedPreferences mSharedPreferences;
+
     // Whether the extended restart dialog is enabled
     private boolean mIsRestartDialogEnabled;
 
@@ -64,12 +81,15 @@
         mIsReachabilityEducationEnabled = context.getResources().getBoolean(
                 R.bool.config_letterboxIsReachabilityEducationEnabled);
         mIsLetterboxRestartDialogAllowed = DeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_LETTERBOX_RESTART_DIALOG, false);
+                DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_LETTERBOX_RESTART_DIALOG,
+                DEFAULT_VALUE_ENABLE_LETTERBOX_RESTART_DIALOG);
         mIsLetterboxReachabilityEducationAllowed = DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_LETTERBOX_REACHABILITY_EDUCATION,
-                false);
+                DEFAULT_VALUE_ENABLE_LETTERBOX_REACHABILITY_EDUCATION);
         DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_APP_COMPAT, mainExecutor,
                 this);
+        mSharedPreferences = context.getSharedPreferences(DONT_SHOW_RESTART_DIALOG_PREF_NAME,
+                Context.MODE_PRIVATE);
     }
 
     /**
@@ -102,18 +122,37 @@
         mIsReachabilityEducationOverrideEnabled = enabled;
     }
 
+    boolean getDontShowRestartDialogAgain(TaskInfo taskInfo) {
+        final int userId = taskInfo.userId;
+        final String packageName = taskInfo.topActivity.getPackageName();
+        return mSharedPreferences.getBoolean(
+                getDontShowAgainRestartKey(userId, packageName), /* default= */ false);
+    }
+
+    void setDontShowRestartDialogAgain(TaskInfo taskInfo) {
+        final int userId = taskInfo.userId;
+        final String packageName = taskInfo.topActivity.getPackageName();
+        mSharedPreferences.edit().putBoolean(getDontShowAgainRestartKey(userId, packageName),
+                true).apply();
+    }
+
     @Override
     public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
-        // TODO(b/263349751): Update flag and default value to true
         if (properties.getKeyset().contains(KEY_ENABLE_LETTERBOX_RESTART_DIALOG)) {
             mIsLetterboxRestartDialogAllowed = DeviceConfig.getBoolean(
                     DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_LETTERBOX_RESTART_DIALOG,
-                    false);
+                    DEFAULT_VALUE_ENABLE_LETTERBOX_RESTART_DIALOG);
         }
+        // TODO(b/263349751): Update flag and default value to true
         if (properties.getKeyset().contains(KEY_ENABLE_LETTERBOX_REACHABILITY_EDUCATION)) {
             mIsLetterboxReachabilityEducationAllowed = DeviceConfig.getBoolean(
                     DeviceConfig.NAMESPACE_WINDOW_MANAGER,
-                    KEY_ENABLE_LETTERBOX_REACHABILITY_EDUCATION, false);
+                    KEY_ENABLE_LETTERBOX_REACHABILITY_EDUCATION,
+                    DEFAULT_VALUE_ENABLE_LETTERBOX_REACHABILITY_EDUCATION);
         }
     }
-}
+
+    private String getDontShowAgainRestartKey(int userId, String packageName) {
+        return packageName + "@" + userId;
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index 6627de5..3b2db51 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -24,6 +24,7 @@
 import android.hardware.display.DisplayManager;
 import android.util.ArraySet;
 import android.util.Log;
+import android.util.Pair;
 import android.util.SparseArray;
 import android.view.Display;
 import android.view.InsetsSourceControl;
@@ -49,6 +50,7 @@
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.function.Consumer;
@@ -91,6 +93,18 @@
     private final SparseArray<CompatUIWindowManager> mActiveCompatLayouts = new SparseArray<>(0);
 
     /**
+     * {@link SparseArray} that maps task ids to {@link RestartDialogWindowManager} that are
+     * currently visible
+     */
+    private final SparseArray<RestartDialogWindowManager> mTaskIdToRestartDialogWindowManagerMap =
+            new SparseArray<>(0);
+
+    /**
+     * {@link Set} of task ids for which we need to display a restart confirmation dialog
+     */
+    private Set<Integer> mSetOfTaskIdsShowingRestartDialog = new HashSet<>();
+
+    /**
      * The active Letterbox Education layout if there is one (there can be at most one active).
      *
      * <p>An active layout is a layout that is eligible to be shown for the associated task but
@@ -111,12 +125,15 @@
     private final ShellExecutor mMainExecutor;
     private final Lazy<Transitions> mTransitionsLazy;
     private final DockStateReader mDockStateReader;
+    private final CompatUIConfiguration mCompatUIConfiguration;
 
     private CompatUICallback mCallback;
 
     // Only show each hint once automatically in the process life.
     private final CompatUIHintsState mCompatUIHintsState;
 
+    private final CompatUIShellCommandHandler mCompatUIShellCommandHandler;
+
     // Indicates if the keyguard is currently showing, in which case compat UIs shouldn't
     // be shown.
     private boolean mKeyguardShowing;
@@ -130,7 +147,9 @@
             SyncTransactionQueue syncQueue,
             ShellExecutor mainExecutor,
             Lazy<Transitions> transitionsLazy,
-            DockStateReader dockStateReader) {
+            DockStateReader dockStateReader,
+            CompatUIConfiguration compatUIConfiguration,
+            CompatUIShellCommandHandler compatUIShellCommandHandler) {
         mContext = context;
         mShellController = shellController;
         mDisplayController = displayController;
@@ -140,14 +159,17 @@
         mMainExecutor = mainExecutor;
         mTransitionsLazy = transitionsLazy;
         mCompatUIHintsState = new CompatUIHintsState();
-        shellInit.addInitCallback(this::onInit, this);
         mDockStateReader = dockStateReader;
+        mCompatUIConfiguration = compatUIConfiguration;
+        mCompatUIShellCommandHandler = compatUIShellCommandHandler;
+        shellInit.addInitCallback(this::onInit, this);
     }
 
     private void onInit() {
         mShellController.addKeyguardChangeListener(this);
         mDisplayController.addDisplayWindowListener(this);
         mImeController.addPositionProcessor(this);
+        mCompatUIShellCommandHandler.onInit();
     }
 
     /** Sets the callback for UI interactions. */
@@ -164,6 +186,9 @@
      */
     public void onCompatInfoChanged(TaskInfo taskInfo,
             @Nullable ShellTaskOrganizer.TaskListener taskListener) {
+        if (taskInfo != null && !taskInfo.topActivityInSizeCompat) {
+            mSetOfTaskIdsShowingRestartDialog.remove(taskInfo.taskId);
+        }
         if (taskInfo.configuration == null || taskListener == null) {
             // Null token means the current foreground activity is not in compatibility mode.
             removeLayouts(taskInfo.taskId);
@@ -172,6 +197,7 @@
 
         createOrUpdateCompatLayout(taskInfo, taskListener);
         createOrUpdateLetterboxEduLayout(taskInfo, taskListener);
+        createOrUpdateRestartDialogLayout(taskInfo, taskListener);
     }
 
     @Override
@@ -278,7 +304,21 @@
             ShellTaskOrganizer.TaskListener taskListener) {
         return new CompatUIWindowManager(context,
                 taskInfo, mSyncQueue, mCallback, taskListener,
-                mDisplayController.getDisplayLayout(taskInfo.displayId), mCompatUIHintsState);
+                mDisplayController.getDisplayLayout(taskInfo.displayId), mCompatUIHintsState,
+                mCompatUIConfiguration, this::onRestartButtonClicked);
+    }
+
+    private void onRestartButtonClicked(
+            Pair<TaskInfo, ShellTaskOrganizer.TaskListener> taskInfoState) {
+        if (mCompatUIConfiguration.isRestartDialogEnabled()
+                && !mCompatUIConfiguration.getDontShowRestartDialogAgain(
+                taskInfoState.first)) {
+            // We need to show the dialog
+            mSetOfTaskIdsShowingRestartDialog.add(taskInfoState.first.taskId);
+            onCompatInfoChanged(taskInfoState.first, taskInfoState.second);
+        } else {
+            mCallback.onSizeCompatRestartButtonClicked(taskInfoState.first.taskId);
+        }
     }
 
     private void createOrUpdateLetterboxEduLayout(TaskInfo taskInfo,
@@ -327,6 +367,60 @@
         mActiveLetterboxEduLayout = null;
     }
 
+    private void createOrUpdateRestartDialogLayout(TaskInfo taskInfo,
+            ShellTaskOrganizer.TaskListener taskListener) {
+        RestartDialogWindowManager layout =
+                mTaskIdToRestartDialogWindowManagerMap.get(taskInfo.taskId);
+        if (layout != null) {
+            // TODO(b/266262111) Handle theme change when taskListener changes
+            if (layout.getTaskListener() != taskListener) {
+                mSetOfTaskIdsShowingRestartDialog.remove(taskInfo.taskId);
+            }
+            layout.setRequestRestartDialog(
+                    mSetOfTaskIdsShowingRestartDialog.contains(taskInfo.taskId));
+            // UI already exists, update the UI layout.
+            if (!layout.updateCompatInfo(taskInfo, taskListener,
+                    showOnDisplay(layout.getDisplayId()))) {
+                // The layout is no longer eligible to be shown, remove from active layouts.
+                mTaskIdToRestartDialogWindowManagerMap.remove(taskInfo.taskId);
+            }
+            return;
+        }
+        // Create a new UI layout.
+        final Context context = getOrCreateDisplayContext(taskInfo.displayId);
+        if (context == null) {
+            return;
+        }
+        layout = createRestartDialogWindowManager(context, taskInfo, taskListener);
+        layout.setRequestRestartDialog(
+                mSetOfTaskIdsShowingRestartDialog.contains(taskInfo.taskId));
+        if (layout.createLayout(showOnDisplay(taskInfo.displayId))) {
+            // The new layout is eligible to be shown, add it the active layouts.
+            mTaskIdToRestartDialogWindowManagerMap.put(taskInfo.taskId, layout);
+        }
+    }
+
+    @VisibleForTesting
+    RestartDialogWindowManager createRestartDialogWindowManager(Context context, TaskInfo taskInfo,
+            ShellTaskOrganizer.TaskListener taskListener) {
+        return new RestartDialogWindowManager(context, taskInfo, mSyncQueue, taskListener,
+                mDisplayController.getDisplayLayout(taskInfo.displayId), mTransitionsLazy.get(),
+                this::onRestartDialogCallback, this::onRestartDialogDismissCallback,
+                mCompatUIConfiguration);
+    }
+
+    private void onRestartDialogCallback(
+            Pair<TaskInfo, ShellTaskOrganizer.TaskListener> stateInfo) {
+        mTaskIdToRestartDialogWindowManagerMap.remove(stateInfo.first.taskId);
+        mCallback.onSizeCompatRestartButtonClicked(stateInfo.first.taskId);
+    }
+
+    private void onRestartDialogDismissCallback(
+            Pair<TaskInfo, ShellTaskOrganizer.TaskListener> stateInfo) {
+        mSetOfTaskIdsShowingRestartDialog.remove(stateInfo.first.taskId);
+        onCompatInfoChanged(stateInfo.first, stateInfo.second);
+    }
+
     private void removeLayouts(int taskId) {
         final CompatUIWindowManager layout = mActiveCompatLayouts.get(taskId);
         if (layout != null) {
@@ -338,6 +432,14 @@
             mActiveLetterboxEduLayout.release();
             mActiveLetterboxEduLayout = null;
         }
+
+        final RestartDialogWindowManager restartLayout =
+                mTaskIdToRestartDialogWindowManagerMap.get(taskId);
+        if (restartLayout != null) {
+            restartLayout.release();
+            mTaskIdToRestartDialogWindowManagerMap.remove(taskId);
+            mSetOfTaskIdsShowingRestartDialog.remove(taskId);
+        }
     }
 
     private Context getOrCreateDisplayContext(int displayId) {
@@ -382,6 +484,14 @@
         if (mActiveLetterboxEduLayout != null && condition.test(mActiveLetterboxEduLayout)) {
             callback.accept(mActiveLetterboxEduLayout);
         }
+        for (int i = 0; i < mTaskIdToRestartDialogWindowManagerMap.size(); i++) {
+            final int taskId = mTaskIdToRestartDialogWindowManagerMap.keyAt(i);
+            final RestartDialogWindowManager layout =
+                    mTaskIdToRestartDialogWindowManagerMap.get(taskId);
+            if (layout != null && condition.test(layout)) {
+                callback.accept(layout);
+            }
+        }
     }
 
     /** An implementation of {@link OnInsetsChangedListener} for a given display id. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
index bce3ec4..fe95d04 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
@@ -21,12 +21,14 @@
 import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
 import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.TaskInfo;
 import android.app.TaskInfo.CameraCompatControlState;
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.Log;
+import android.util.Pair;
 import android.view.LayoutInflater;
 import android.view.View;
 
@@ -38,6 +40,8 @@
 import com.android.wm.shell.compatui.CompatUIController.CompatUICallback;
 import com.android.wm.shell.compatui.letterboxedu.LetterboxEduWindowManager;
 
+import java.util.function.Consumer;
+
 /**
  * Window manager for the Size Compat restart button and Camera Compat control.
  */
@@ -50,6 +54,13 @@
 
     private final CompatUICallback mCallback;
 
+    private final CompatUIConfiguration mCompatUIConfiguration;
+
+    private final Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnRestartButtonClicked;
+
+    @NonNull
+    private TaskInfo mTaskInfo;
+
     // Remember the last reported states in case visibility changes due to keyguard or IME updates.
     @VisibleForTesting
     boolean mHasSizeCompat;
@@ -68,12 +79,16 @@
     CompatUIWindowManager(Context context, TaskInfo taskInfo,
             SyncTransactionQueue syncQueue, CompatUICallback callback,
             ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout,
-            CompatUIHintsState compatUIHintsState) {
+            CompatUIHintsState compatUIHintsState, CompatUIConfiguration compatUIConfiguration,
+            Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> onRestartButtonClicked) {
         super(context, taskInfo, syncQueue, taskListener, displayLayout);
+        mTaskInfo = taskInfo;
         mCallback = callback;
         mHasSizeCompat = taskInfo.topActivityInSizeCompat;
         mCameraCompatControlState = taskInfo.cameraCompatControlState;
         mCompatUIHintsState = compatUIHintsState;
+        mCompatUIConfiguration = compatUIConfiguration;
+        mOnRestartButtonClicked = onRestartButtonClicked;
     }
 
     @Override
@@ -119,6 +134,7 @@
     @Override
     public boolean updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener,
             boolean canShow) {
+        mTaskInfo = taskInfo;
         final boolean prevHasSizeCompat = mHasSizeCompat;
         final int prevCameraCompatControlState = mCameraCompatControlState;
         mHasSizeCompat = taskInfo.topActivityInSizeCompat;
@@ -138,7 +154,7 @@
 
     /** Called when the restart button is clicked. */
     void onRestartButtonClicked() {
-        mCallback.onSizeCompatRestartButtonClicked(mTaskId);
+        mOnRestartButtonClicked.accept(Pair.create(mTaskInfo, getTaskListener()));
     }
 
     /** Called when the camera treatment button is clicked. */
@@ -199,8 +215,14 @@
                 : taskStableBounds.right - taskBounds.left - mLayout.getMeasuredWidth();
         final int positionY = taskStableBounds.bottom - taskBounds.top
                 - mLayout.getMeasuredHeight();
-
+        // To secure a proper visualisation, we hide the layout while updating the position of
+        // the {@link SurfaceControl} it belongs.
+        final int oldVisibility = mLayout.getVisibility();
+        if (oldVisibility == View.VISIBLE) {
+            mLayout.setVisibility(View.GONE);
+        }
         updateSurfacePosition(positionX, positionY);
+        mLayout.setVisibility(oldVisibility);
     }
 
     private void updateVisibilityOfViews() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java
index face243..db87f657 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java
@@ -151,6 +151,7 @@
     @Override
     public void setConfiguration(Configuration configuration) {
         super.setConfiguration(configuration);
+        // TODO(b/266262111): Investigate loss of theme configuration when switching TaskListener
         mContext = mContext.createConfigurationContext(configuration);
     }
 
@@ -169,6 +170,10 @@
         initSurface(mLeash);
     }
 
+    protected ShellTaskOrganizer.TaskListener getTaskListener() {
+        return mTaskListener;
+    }
+
     /** Inits the z-order of the surface. */
     private void initSurface(SurfaceControl leash) {
         final int z = getZOrder();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogLayout.java
new file mode 100644
index 0000000..c53e638
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogLayout.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.TextView;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+
+import com.android.wm.shell.R;
+
+import java.util.function.Consumer;
+
+/**
+ * Container for a SCM restart confirmation dialog and background dim.
+ */
+public class RestartDialogLayout extends ConstraintLayout implements DialogContainerSupplier {
+
+    private View mDialogContainer;
+    private TextView mDialogTitle;
+    private Drawable mBackgroundDim;
+
+    public RestartDialogLayout(Context context) {
+        this(context, null);
+    }
+
+    public RestartDialogLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public RestartDialogLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public RestartDialogLayout(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    public View getDialogContainerView() {
+        return mDialogContainer;
+    }
+
+    TextView getDialogTitle() {
+        return mDialogTitle;
+    }
+
+    @Override
+    public Drawable getBackgroundDimDrawable() {
+        return mBackgroundDim;
+    }
+
+    /**
+     * Register a callback for the dismiss button and background dim.
+     *
+     * @param callback The callback to register or null if all on click listeners should be removed.
+     */
+    void setDismissOnClickListener(@Nullable Runnable callback) {
+        final OnClickListener listener = callback == null ? null : view -> callback.run();
+        findViewById(R.id.letterbox_restart_dialog_dismiss_button).setOnClickListener(listener);
+    }
+
+    /**
+     * Register a callback for the restart button
+     *
+     * @param callback The callback to register or null if all on click listeners should be removed.
+     */
+    void setRestartOnClickListener(@Nullable Consumer<Boolean> callback) {
+        final CheckBox dontShowAgainCheckbox = findViewById(R.id.letterbox_restart_dialog_checkbox);
+        final OnClickListener listener = callback == null ? null : view -> callback.accept(
+                dontShowAgainCheckbox.isChecked());
+        findViewById(R.id.letterbox_restart_dialog_restart_button).setOnClickListener(listener);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mDialogContainer = findViewById(R.id.letterbox_restart_dialog_container);
+        mDialogTitle = findViewById(R.id.letterbox_restart_dialog_title);
+        mBackgroundDim = getBackground().mutate();
+        // Set the alpha of the background dim to 0 for enter animation.
+        mBackgroundDim.setAlpha(0);
+        // We add a no-op on-click listener to the dialog container so that clicks on it won't
+        // propagate to the listener of the layout (which represents the background dim).
+        mDialogContainer.setOnClickListener(view -> {});
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java
new file mode 100644
index 0000000..10f25d0
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui;
+
+import static android.provider.Settings.Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.TaskInfo;
+import android.content.Context;
+import android.graphics.Rect;
+import android.provider.Settings;
+import android.util.Pair;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.transition.Transitions;
+
+import java.util.function.Consumer;
+
+/**
+ * Window manager for the Restart Dialog.
+ *
+ * TODO(b/263484314): Create abstraction of RestartDialogWindowManager and LetterboxEduWindowManager
+ */
+class RestartDialogWindowManager extends CompatUIWindowManagerAbstract {
+
+    /**
+     * The restart dialog should be the topmost child of the Task in case there can be more
+     * than one child.
+     */
+    private static final int Z_ORDER = Integer.MAX_VALUE;
+
+    private final DialogAnimationController<RestartDialogLayout> mAnimationController;
+
+    private final Transitions mTransitions;
+
+    // Remember the last reported state in case visibility changes due to keyguard or IME updates.
+    private boolean mRequestRestartDialog;
+
+    private final Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnDismissCallback;
+
+    private final Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnRestartCallback;
+
+    private final CompatUIConfiguration mCompatUIConfiguration;
+
+    /**
+     * The vertical margin between the dialog container and the task stable bounds (excluding
+     * insets).
+     */
+    private final int mDialogVerticalMargin;
+
+    @NonNull
+    private TaskInfo mTaskInfo;
+
+    @Nullable
+    @VisibleForTesting
+    RestartDialogLayout mLayout;
+
+    RestartDialogWindowManager(Context context, TaskInfo taskInfo,
+            SyncTransactionQueue syncQueue, ShellTaskOrganizer.TaskListener taskListener,
+            DisplayLayout displayLayout, Transitions transitions,
+            Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> onRestartCallback,
+            Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> onDismissCallback,
+            CompatUIConfiguration compatUIConfiguration) {
+        this(context, taskInfo, syncQueue, taskListener, displayLayout, transitions,
+                onRestartCallback, onDismissCallback,
+                new DialogAnimationController<>(context, "RestartDialogWindowManager"),
+                compatUIConfiguration);
+    }
+
+    @VisibleForTesting
+    RestartDialogWindowManager(Context context, TaskInfo taskInfo,
+            SyncTransactionQueue syncQueue, ShellTaskOrganizer.TaskListener taskListener,
+            DisplayLayout displayLayout, Transitions transitions,
+            Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> onRestartCallback,
+            Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> onDismissCallback,
+            DialogAnimationController<RestartDialogLayout> animationController,
+            CompatUIConfiguration compatUIConfiguration) {
+        super(context, taskInfo, syncQueue, taskListener, displayLayout);
+        mTaskInfo = taskInfo;
+        mTransitions = transitions;
+        mOnDismissCallback = onDismissCallback;
+        mOnRestartCallback = onRestartCallback;
+        mAnimationController = animationController;
+        mDialogVerticalMargin = (int) mContext.getResources().getDimension(
+                R.dimen.letterbox_restart_dialog_margin);
+        mCompatUIConfiguration = compatUIConfiguration;
+    }
+
+    @Override
+    protected int getZOrder() {
+        return Z_ORDER;
+    }
+
+    @Override
+    @Nullable
+    protected  View getLayout() {
+        return mLayout;
+    }
+
+    @Override
+    protected void removeLayout() {
+        mLayout = null;
+    }
+
+    @Override
+    protected boolean eligibleToShowLayout() {
+        // We don't show this dialog if the user has explicitly selected so clicking on a checkbox.
+        return mRequestRestartDialog && !isTaskbarEduShowing() && (mLayout != null
+                || !mCompatUIConfiguration.getDontShowRestartDialogAgain(mTaskInfo));
+    }
+
+    @Override
+    protected View createLayout() {
+        mLayout = inflateLayout();
+        updateDialogMargins();
+
+        // startEnterAnimation will be called immediately if shell-transitions are disabled.
+        mTransitions.runOnIdle(this::startEnterAnimation);
+
+        return mLayout;
+    }
+
+    void setRequestRestartDialog(boolean enabled) {
+        mRequestRestartDialog = enabled;
+    }
+
+    @Override
+    public boolean updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener,
+            boolean canShow) {
+        mTaskInfo = taskInfo;
+        return super.updateCompatInfo(taskInfo, taskListener, canShow);
+    }
+
+    private void updateDialogMargins() {
+        if (mLayout == null) {
+            return;
+        }
+        final View dialogContainer = mLayout.getDialogContainerView();
+        ViewGroup.MarginLayoutParams marginParams =
+                (ViewGroup.MarginLayoutParams) dialogContainer.getLayoutParams();
+
+        final Rect taskBounds = getTaskBounds();
+        final Rect taskStableBounds = getTaskStableBounds();
+
+        marginParams.topMargin = taskStableBounds.top - taskBounds.top + mDialogVerticalMargin;
+        marginParams.bottomMargin =
+                taskBounds.bottom - taskStableBounds.bottom + mDialogVerticalMargin;
+        dialogContainer.setLayoutParams(marginParams);
+    }
+
+    private RestartDialogLayout inflateLayout() {
+        return (RestartDialogLayout) LayoutInflater.from(mContext).inflate(
+                R.layout.letterbox_restart_dialog_layout, null);
+    }
+
+    private void startEnterAnimation() {
+        if (mLayout == null) {
+            // Dialog has already been released.
+            return;
+        }
+        mAnimationController.startEnterAnimation(mLayout, /* endCallback= */
+                this::onDialogEnterAnimationEnded);
+    }
+
+    private void onDialogEnterAnimationEnded() {
+        if (mLayout == null) {
+            // Dialog has already been released.
+            return;
+        }
+        mLayout.setDismissOnClickListener(this::onDismiss);
+        mLayout.setRestartOnClickListener(dontShowAgain -> {
+            if (mLayout != null) {
+                mLayout.setDismissOnClickListener(null);
+                mAnimationController.startExitAnimation(mLayout, () -> {
+                    release();
+                });
+            }
+            if (dontShowAgain) {
+                mCompatUIConfiguration.setDontShowRestartDialogAgain(mTaskInfo);
+            }
+            mOnRestartCallback.accept(Pair.create(mTaskInfo, getTaskListener()));
+        });
+        // Focus on the dialog title for accessibility.
+        mLayout.getDialogTitle().sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+    }
+
+    private void onDismiss() {
+        if (mLayout == null) {
+            return;
+        }
+
+        mLayout.setDismissOnClickListener(null);
+        mAnimationController.startExitAnimation(mLayout, () -> {
+            release();
+            mOnDismissCallback.accept(Pair.create(mTaskInfo, getTaskListener()));
+        });
+    }
+
+    @Override
+    public void release() {
+        mAnimationController.cancelAnimation();
+        super.release();
+    }
+
+    @Override
+    protected void onParentBoundsChanged() {
+        if (mLayout == null) {
+            return;
+        }
+        // Both the layout dimensions and dialog margins depend on the parent bounds.
+        WindowManager.LayoutParams windowLayoutParams = getWindowLayoutParams();
+        mLayout.setLayoutParams(windowLayoutParams);
+        updateDialogMargins();
+        relayout(windowLayoutParams);
+    }
+
+    @Override
+    protected void updateSurfacePosition() {
+        // Nothing to do, since the position of the surface is fixed to the top left corner (0,0)
+        // of the task (parent surface), which is the default position of a surface.
+    }
+
+    @Override
+    protected WindowManager.LayoutParams getWindowLayoutParams() {
+        final Rect taskBounds = getTaskBounds();
+        return getWindowLayoutParams(/* width= */ taskBounds.width(), /* height= */
+                taskBounds.height());
+    }
+
+    @VisibleForTesting
+    boolean isTaskbarEduShowing() {
+        return Settings.Secure.getInt(mContext.getContentResolver(),
+                LAUNCHER_TASKBAR_EDUCATION_SHOWING, /* def= */ 0) == 1;
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
index 8022e9b..b0756a0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
@@ -39,6 +39,7 @@
 import com.android.wm.shell.pip.PipTransitionController;
 import com.android.wm.shell.pip.PipTransitionState;
 import com.android.wm.shell.pip.PipUiEventLogger;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
 import com.android.wm.shell.pip.tv.TvPipBoundsAlgorithm;
 import com.android.wm.shell.pip.tv.TvPipBoundsController;
 import com.android.wm.shell.pip.tv.TvPipBoundsState;
@@ -69,6 +70,7 @@
             ShellInit shellInit,
             ShellController shellController,
             TvPipBoundsState tvPipBoundsState,
+            PipSizeSpecHandler pipSizeSpecHandler,
             TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
             TvPipBoundsController tvPipBoundsController,
             PipAppOpsListener pipAppOpsListener,
@@ -88,6 +90,7 @@
                         shellInit,
                         shellController,
                         tvPipBoundsState,
+                        pipSizeSpecHandler,
                         tvPipBoundsAlgorithm,
                         tvPipBoundsController,
                         pipAppOpsListener,
@@ -127,14 +130,23 @@
     @WMSingleton
     @Provides
     static TvPipBoundsAlgorithm provideTvPipBoundsAlgorithm(Context context,
-            TvPipBoundsState tvPipBoundsState, PipSnapAlgorithm pipSnapAlgorithm) {
-        return new TvPipBoundsAlgorithm(context, tvPipBoundsState, pipSnapAlgorithm);
+            TvPipBoundsState tvPipBoundsState, PipSnapAlgorithm pipSnapAlgorithm,
+            PipSizeSpecHandler pipSizeSpecHandler) {
+        return new TvPipBoundsAlgorithm(context, tvPipBoundsState, pipSnapAlgorithm,
+                pipSizeSpecHandler);
     }
 
     @WMSingleton
     @Provides
-    static TvPipBoundsState provideTvPipBoundsState(Context context) {
-        return new TvPipBoundsState(context);
+    static TvPipBoundsState provideTvPipBoundsState(Context context,
+            PipSizeSpecHandler pipSizeSpecHandler) {
+        return new TvPipBoundsState(context, pipSizeSpecHandler);
+    }
+
+    @WMSingleton
+    @Provides
+    static PipSizeSpecHandler providePipSizeSpecHelper(Context context) {
+        return new PipSizeSpecHandler(context);
     }
 
     // Handler needed for loadDrawableAsync() in PipControlsViewController
@@ -194,6 +206,7 @@
             TvPipMenuController tvPipMenuController,
             SyncTransactionQueue syncTransactionQueue,
             TvPipBoundsState tvPipBoundsState,
+            PipSizeSpecHandler pipSizeSpecHandler,
             PipTransitionState pipTransitionState,
             TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
             PipAnimationController pipAnimationController,
@@ -205,10 +218,11 @@
             PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
             @ShellMainThread ShellExecutor mainExecutor) {
         return new TvPipTaskOrganizer(context,
-                syncTransactionQueue, pipTransitionState, tvPipBoundsState, tvPipBoundsAlgorithm,
-                tvPipMenuController, pipAnimationController, pipSurfaceTransactionHelper,
-                pipTransitionController, pipParamsChangedForwarder, splitScreenControllerOptional,
-                displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
+                syncTransactionQueue, pipTransitionState, tvPipBoundsState, pipSizeSpecHandler,
+                tvPipBoundsAlgorithm, tvPipMenuController, pipAnimationController,
+                pipSurfaceTransactionHelper, pipTransitionController, pipParamsChangedForwarder,
+                splitScreenControllerOptional, displayController, pipUiEventLogger,
+                shellTaskOrganizer, mainExecutor);
     }
 
     @WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 09f5cf1..72dc771 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -41,6 +41,7 @@
 import com.android.wm.shell.back.BackAnimationController;
 import com.android.wm.shell.bubbles.BubbleController;
 import com.android.wm.shell.bubbles.Bubbles;
+import com.android.wm.shell.common.DevicePostureController;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayImeController;
 import com.android.wm.shell.common.DisplayInsetsController;
@@ -56,7 +57,9 @@
 import com.android.wm.shell.common.annotations.ShellBackgroundThread;
 import com.android.wm.shell.common.annotations.ShellMainThread;
 import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
+import com.android.wm.shell.compatui.CompatUIConfiguration;
 import com.android.wm.shell.compatui.CompatUIController;
+import com.android.wm.shell.compatui.CompatUIShellCommandHandler;
 import com.android.wm.shell.desktopmode.DesktopMode;
 import com.android.wm.shell.desktopmode.DesktopModeController;
 import com.android.wm.shell.desktopmode.DesktopModeStatus;
@@ -158,6 +161,16 @@
 
     @WMSingleton
     @Provides
+    static DevicePostureController provideDevicePostureController(
+            Context context,
+            ShellInit shellInit,
+            @ShellMainThread ShellExecutor mainExecutor
+    ) {
+        return new DevicePostureController(context, shellInit, mainExecutor);
+    }
+
+    @WMSingleton
+    @Provides
     static DragAndDropController provideDragAndDropController(Context context,
             ShellInit shellInit,
             ShellController shellController,
@@ -196,10 +209,11 @@
             DisplayController displayController, DisplayInsetsController displayInsetsController,
             DisplayImeController imeController, SyncTransactionQueue syncQueue,
             @ShellMainThread ShellExecutor mainExecutor, Lazy<Transitions> transitionsLazy,
-            DockStateReader dockStateReader) {
+            DockStateReader dockStateReader, CompatUIConfiguration compatUIConfiguration,
+            CompatUIShellCommandHandler compatUIShellCommandHandler) {
         return new CompatUIController(context, shellInit, shellController, displayController,
                 displayInsetsController, imeController, syncQueue, mainExecutor, transitionsLazy,
-                dockStateReader);
+                dockStateReader, compatUIConfiguration, compatUIShellCommandHandler);
     }
 
     @WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 512a4ef..1239cdc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -77,6 +77,7 @@
 import com.android.wm.shell.pip.phone.PhonePipMenuController;
 import com.android.wm.shell.pip.phone.PipController;
 import com.android.wm.shell.pip.phone.PipMotionHelper;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
 import com.android.wm.shell.pip.phone.PipTouchHandler;
 import com.android.wm.shell.recents.RecentTasksController;
 import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -338,6 +339,7 @@
             PipBoundsAlgorithm pipBoundsAlgorithm,
             PhonePipKeepClearAlgorithm pipKeepClearAlgorithm,
             PipBoundsState pipBoundsState,
+            PipSizeSpecHandler pipSizeSpecHandler,
             PipMotionHelper pipMotionHelper,
             PipMediaController pipMediaController,
             PhonePipMenuController phonePipMenuController,
@@ -354,17 +356,18 @@
         return Optional.ofNullable(PipController.create(
                 context, shellInit, shellCommandHandler, shellController,
                 displayController, pipAnimationController, pipAppOpsListener, pipBoundsAlgorithm,
-                pipKeepClearAlgorithm, pipBoundsState, pipMotionHelper, pipMediaController,
-                phonePipMenuController, pipTaskOrganizer, pipTransitionState, pipTouchHandler,
-                pipTransitionController, windowManagerShellWrapper, taskStackListener,
-                pipParamsChangedForwarder, displayInsetsController, oneHandedController,
-                mainExecutor));
+                pipKeepClearAlgorithm, pipBoundsState, pipSizeSpecHandler, pipMotionHelper,
+                pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTransitionState,
+                pipTouchHandler, pipTransitionController, windowManagerShellWrapper,
+                taskStackListener, pipParamsChangedForwarder, displayInsetsController,
+                oneHandedController, mainExecutor));
     }
 
     @WMSingleton
     @Provides
-    static PipBoundsState providePipBoundsState(Context context) {
-        return new PipBoundsState(context);
+    static PipBoundsState providePipBoundsState(Context context,
+            PipSizeSpecHandler pipSizeSpecHandler) {
+        return new PipBoundsState(context, pipSizeSpecHandler);
     }
 
     @WMSingleton
@@ -381,11 +384,18 @@
 
     @WMSingleton
     @Provides
+    static PipSizeSpecHandler providePipSizeSpecHelper(Context context) {
+        return new PipSizeSpecHandler(context);
+    }
+
+    @WMSingleton
+    @Provides
     static PipBoundsAlgorithm providesPipBoundsAlgorithm(Context context,
             PipBoundsState pipBoundsState, PipSnapAlgorithm pipSnapAlgorithm,
-            PhonePipKeepClearAlgorithm pipKeepClearAlgorithm) {
+            PhonePipKeepClearAlgorithm pipKeepClearAlgorithm,
+            PipSizeSpecHandler pipSizeSpecHandler) {
         return new PipBoundsAlgorithm(context, pipBoundsState, pipSnapAlgorithm,
-                pipKeepClearAlgorithm);
+                pipKeepClearAlgorithm, pipSizeSpecHandler);
     }
 
     // Handler is used by Icon.loadDrawableAsync
@@ -409,13 +419,14 @@
             PhonePipMenuController menuPhoneController,
             PipBoundsAlgorithm pipBoundsAlgorithm,
             PipBoundsState pipBoundsState,
+            PipSizeSpecHandler pipSizeSpecHandler,
             PipTaskOrganizer pipTaskOrganizer,
             PipMotionHelper pipMotionHelper,
             FloatingContentCoordinator floatingContentCoordinator,
             PipUiEventLogger pipUiEventLogger,
             @ShellMainThread ShellExecutor mainExecutor) {
         return new PipTouchHandler(context, shellInit, menuPhoneController, pipBoundsAlgorithm,
-                pipBoundsState, pipTaskOrganizer, pipMotionHelper,
+                pipBoundsState, pipSizeSpecHandler, pipTaskOrganizer, pipMotionHelper,
                 floatingContentCoordinator, pipUiEventLogger, mainExecutor);
     }
 
@@ -431,6 +442,7 @@
             SyncTransactionQueue syncTransactionQueue,
             PipTransitionState pipTransitionState,
             PipBoundsState pipBoundsState,
+            PipSizeSpecHandler pipSizeSpecHandler,
             PipBoundsAlgorithm pipBoundsAlgorithm,
             PhonePipMenuController menuPhoneController,
             PipAnimationController pipAnimationController,
@@ -442,10 +454,11 @@
             PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
             @ShellMainThread ShellExecutor mainExecutor) {
         return new PipTaskOrganizer(context,
-                syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
-                menuPhoneController, pipAnimationController, pipSurfaceTransactionHelper,
-                pipTransitionController, pipParamsChangedForwarder, splitScreenControllerOptional,
-                displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
+                syncTransactionQueue, pipTransitionState, pipBoundsState, pipSizeSpecHandler,
+                pipBoundsAlgorithm, menuPhoneController, pipAnimationController,
+                pipSurfaceTransactionHelper, pipTransitionController, pipParamsChangedForwarder,
+                splitScreenControllerOptional, displayController, pipUiEventLogger,
+                shellTaskOrganizer, mainExecutor);
     }
 
     @WMSingleton
@@ -460,13 +473,14 @@
     static PipTransitionController providePipTransitionController(Context context,
             ShellInit shellInit, ShellTaskOrganizer shellTaskOrganizer, Transitions transitions,
             PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm,
-            PipBoundsState pipBoundsState, PipTransitionState pipTransitionState,
-            PhonePipMenuController pipMenuController,
+            PipBoundsState pipBoundsState, PipSizeSpecHandler pipSizeSpecHandler,
+            PipTransitionState pipTransitionState, PhonePipMenuController pipMenuController,
             PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
             Optional<SplitScreenController> splitScreenOptional) {
         return new PipTransition(context, shellInit, shellTaskOrganizer, transitions,
-                pipBoundsState, pipTransitionState, pipMenuController, pipBoundsAlgorithm,
-                pipAnimationController, pipSurfaceTransactionHelper, splitScreenOptional);
+                pipBoundsState, pipSizeSpecHandler, pipTransitionState, pipMenuController,
+                pipBoundsAlgorithm, pipAnimationController, pipSurfaceTransactionHelper,
+                splitScreenOptional);
     }
 
     @WMSingleton
@@ -544,16 +558,18 @@
     static FullscreenUnfoldTaskAnimator provideFullscreenUnfoldTaskAnimator(
             Context context,
             UnfoldBackgroundController unfoldBackgroundController,
+            ShellController shellController,
             DisplayInsetsController displayInsetsController
     ) {
         return new FullscreenUnfoldTaskAnimator(context, unfoldBackgroundController,
-                displayInsetsController);
+                shellController, displayInsetsController);
     }
 
     @Provides
     static SplitTaskUnfoldAnimator provideSplitTaskUnfoldAnimatorBase(
             Context context,
             UnfoldBackgroundController backgroundController,
+            ShellController shellController,
             @ShellMainThread ShellExecutor executor,
             Lazy<Optional<SplitScreenController>> splitScreenOptional,
             DisplayInsetsController displayInsetsController
@@ -563,7 +579,7 @@
         // controller directly once we refactor ShellTaskOrganizer to not depend on the unfold
         // animation controller directly.
         return new SplitTaskUnfoldAnimator(context, executor, splitScreenOptional,
-                backgroundController, displayInsetsController);
+                shellController, backgroundController, displayInsetsController);
     }
 
     @WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
index a839a23..bc81710 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
@@ -22,6 +22,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.WindowManager.TRANSIT_CHANGE;
+import static android.view.WindowManager.TRANSIT_NONE;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
 
@@ -253,12 +254,16 @@
      */
     void showDesktopApps() {
         // Bring apps to front, ignoring their visibility status to always ensure they are on top.
-        WindowContainerTransaction wct = bringDesktopAppsToFront(true /* ignoreVisibility */);
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        bringDesktopAppsToFront(wct);
 
-        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
-            mTransitions.startTransition(TRANSIT_TO_FRONT, wct, null /* handler */);
-        } else {
-            mShellTaskOrganizer.applyTransaction(wct);
+        if (!wct.isEmpty()) {
+            if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+                // TODO(b/268662477): add animation for the transition
+                mTransitions.startTransition(TRANSIT_NONE, wct, null /* handler */);
+            } else {
+                mShellTaskOrganizer.applyTransaction(wct);
+            }
         }
     }
 
@@ -267,9 +272,7 @@
         return mDesktopModeTaskRepository.getVisibleTaskCount();
     }
 
-    @NonNull
-    private WindowContainerTransaction bringDesktopAppsToFront(boolean force) {
-        final WindowContainerTransaction wct = new WindowContainerTransaction();
+    private void bringDesktopAppsToFront(WindowContainerTransaction wct) {
         final ArraySet<Integer> activeTasks = mDesktopModeTaskRepository.getActiveTasks();
         ProtoLog.d(WM_SHELL_DESKTOP_MODE, "bringDesktopAppsToFront: tasks=%s", activeTasks.size());
 
@@ -282,18 +285,11 @@
         }
 
         if (taskInfos.isEmpty()) {
-            return wct;
+            return;
         }
 
-        if (!force) {
-            final boolean allActiveTasksAreVisible = taskInfos.stream()
-                    .allMatch(info -> mDesktopModeTaskRepository.isVisibleTask(info.taskId));
-            if (allActiveTasksAreVisible) {
-                ProtoLog.d(WM_SHELL_DESKTOP_MODE,
-                        "bringDesktopAppsToFront: active tasks are already in front, skipping.");
-                return wct;
-            }
-        }
+        moveHomeTaskToFront(wct);
+
         ProtoLog.d(WM_SHELL_DESKTOP_MODE,
                 "bringDesktopAppsToFront: reordering all active tasks to the front");
         final List<Integer> allTasksInZOrder =
@@ -304,7 +300,15 @@
         for (RunningTaskInfo task : taskInfos) {
             wct.reorder(task.token, true);
         }
-        return wct;
+    }
+
+    private void moveHomeTaskToFront(WindowContainerTransaction wct) {
+        for (RunningTaskInfo task : mShellTaskOrganizer.getRunningTasks(mContext.getDisplayId())) {
+            if (task.getActivityType() == ACTIVITY_TYPE_HOME) {
+                wct.reorder(task.token, true /* onTop */);
+                return;
+            }
+        }
     }
 
     /**
@@ -363,7 +367,7 @@
         if (wct == null) {
             wct = new WindowContainerTransaction();
         }
-        wct.merge(bringDesktopAppsToFront(false /* ignoreVisibility */), true /* transfer */);
+        bringDesktopAppsToFront(wct);
         wct.reorder(request.getTriggerTask().token, true /* onTop */);
 
         return wct;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 2303325..fce0138 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -27,6 +27,7 @@
 import android.os.IBinder
 import android.view.SurfaceControl
 import android.view.WindowManager.TRANSIT_CHANGE
+import android.view.WindowManager.TRANSIT_NONE
 import android.view.WindowManager.TRANSIT_OPEN
 import android.view.WindowManager.TRANSIT_TO_FRONT
 import android.window.TransitionInfo
@@ -89,7 +90,8 @@
         // Execute transaction if there are pending operations
         if (!wct.isEmpty) {
             if (Transitions.ENABLE_SHELL_TRANSITIONS) {
-                transitions.startTransition(TRANSIT_TO_FRONT, wct, null /* handler */)
+                // TODO(b/268662477): add animation for the transition
+                transitions.startTransition(TRANSIT_NONE, wct, null /* handler */)
             } else {
                 shellTaskOrganizer.applyTransaction(wct)
             }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
index 55378a8..3ade1ed 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
@@ -22,6 +22,10 @@
 
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_BOTTOM;
+import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT;
+import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
+import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_TOP;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -315,6 +319,25 @@
                 // Switching between targets
                 mDropZoneView1.animateSwitch();
                 mDropZoneView2.animateSwitch();
+                // Announce for accessibility.
+                switch (target.type) {
+                    case TYPE_SPLIT_LEFT:
+                        mDropZoneView1.announceForAccessibility(
+                                mContext.getString(R.string.accessibility_split_left));
+                        break;
+                    case TYPE_SPLIT_RIGHT:
+                        mDropZoneView2.announceForAccessibility(
+                                mContext.getString(R.string.accessibility_split_right));
+                        break;
+                    case TYPE_SPLIT_TOP:
+                        mDropZoneView1.announceForAccessibility(
+                                mContext.getString(R.string.accessibility_split_top));
+                        break;
+                    case TYPE_SPLIT_BOTTOM:
+                        mDropZoneView2.announceForAccessibility(
+                                mContext.getString(R.string.accessibility_split_bottom));
+                        break;
+                }
             }
             mCurrentTarget = target;
         }
@@ -424,12 +447,10 @@
     }
 
     private void animateHighlight(DragAndDropPolicy.Target target) {
-        if (target.type == DragAndDropPolicy.Target.TYPE_SPLIT_LEFT
-                || target.type == DragAndDropPolicy.Target.TYPE_SPLIT_TOP) {
+        if (target.type == TYPE_SPLIT_LEFT || target.type == TYPE_SPLIT_TOP) {
             mDropZoneView1.setShowingHighlight(true);
             mDropZoneView2.setShowingHighlight(false);
-        } else if (target.type == DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT
-                || target.type == DragAndDropPolicy.Target.TYPE_SPLIT_BOTTOM) {
+        } else if (target.type == TYPE_SPLIT_RIGHT || target.type == TYPE_SPLIT_BOTTOM) {
             mDropZoneView1.setShowingHighlight(false);
             mDropZoneView2.setShowingHighlight(true);
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
index e91987d..ac13f96 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.kidsmode;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -68,7 +69,7 @@
     private static final String TAG = "KidsModeTaskOrganizer";
 
     private static final int[] CONTROLLED_ACTIVITY_TYPES =
-            {ACTIVITY_TYPE_UNDEFINED, ACTIVITY_TYPE_STANDARD};
+            {ACTIVITY_TYPE_UNDEFINED, ACTIVITY_TYPE_STANDARD, ACTIVITY_TYPE_HOME};
     private static final int[] CONTROLLED_WINDOWING_MODES =
             {WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED};
 
@@ -93,6 +94,8 @@
     private KidsModeSettingsObserver mKidsModeSettingsObserver;
     private boolean mEnabled;
 
+    private ActivityManager.RunningTaskInfo mHomeTask;
+
     private final BroadcastReceiver mUserSwitchIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -219,6 +222,13 @@
         }
         super.onTaskAppeared(taskInfo, leash);
 
+        // Only allow home to draw under system bars.
+        if (taskInfo.getActivityType() == ACTIVITY_TYPE_HOME) {
+            final WindowContainerTransaction wct = getWindowContainerTransaction();
+            wct.setBounds(taskInfo.token, new Rect(0, 0, mDisplayWidth, mDisplayHeight));
+            mSyncQueue.queue(wct);
+            mHomeTask = taskInfo;
+        }
         mSyncQueue.runInSync(t -> {
             // Reset several properties back to fullscreen (PiP, for example, leaves all these
             // properties in a bad state).
@@ -291,6 +301,13 @@
         }
         mLaunchRootTask = null;
         mLaunchRootLeash = null;
+        if (mHomeTask != null && mHomeTask.token != null) {
+            final WindowContainerToken homeToken = mHomeTask.token;
+            final WindowContainerTransaction wct = getWindowContainerTransaction();
+            wct.setBounds(homeToken, null);
+            mSyncQueue.queue(wct);
+        }
+        mHomeTask = null;
         unregisterOrganizer();
     }
 
@@ -320,7 +337,7 @@
             final SurfaceControl rootLeash = mLaunchRootLeash;
             mSyncQueue.runInSync(t -> {
                 t.setPosition(rootLeash, taskBounds.left, taskBounds.top);
-                t.setWindowCrop(rootLeash, taskBounds.width(), taskBounds.height());
+                t.setWindowCrop(rootLeash, mDisplayWidth, mDisplayHeight);
             });
         }
     }
@@ -351,7 +368,7 @@
         final SurfaceControl finalLeash = mLaunchRootLeash;
         mSyncQueue.runInSync(t -> {
             t.setPosition(finalLeash, taskBounds.left, taskBounds.top);
-            t.setWindowCrop(finalLeash, taskBounds.width(), taskBounds.height());
+            t.setWindowCrop(finalLeash, mDisplayWidth, mDisplayHeight);
         });
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 6728c00..23f73f6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -28,6 +28,7 @@
 import android.annotation.NonNull;
 import android.app.TaskInfo;
 import android.content.Context;
+import android.content.pm.ActivityInfo;
 import android.graphics.Rect;
 import android.view.Surface;
 import android.view.SurfaceControl;
@@ -208,7 +209,7 @@
     /**
      * Quietly cancel the animator by removing the listeners first.
      */
-    static void quietCancel(@NonNull ValueAnimator animator) {
+    public static void quietCancel(@NonNull ValueAnimator animator) {
         animator.removeAllUpdateListeners();
         animator.removeAllListeners();
         animator.cancel();
@@ -361,22 +362,26 @@
         }
 
         void setColorContentOverlay(Context context) {
-            final SurfaceControl.Transaction tx =
-                    mSurfaceControlTransactionFactory.getTransaction();
-            if (mContentOverlay != null) {
-                mContentOverlay.detach(tx);
-            }
-            mContentOverlay = new PipContentOverlay.PipColorOverlay(context);
-            mContentOverlay.attach(tx, mLeash);
+            reattachContentOverlay(new PipContentOverlay.PipColorOverlay(context));
         }
 
         void setSnapshotContentOverlay(TaskSnapshot snapshot, Rect sourceRectHint) {
+            reattachContentOverlay(
+                    new PipContentOverlay.PipSnapshotOverlay(snapshot, sourceRectHint));
+        }
+
+        void setAppIconContentOverlay(Context context, Rect bounds, ActivityInfo activityInfo) {
+            reattachContentOverlay(
+                    new PipContentOverlay.PipAppIconOverlay(context, bounds, activityInfo));
+        }
+
+        private void reattachContentOverlay(PipContentOverlay overlay) {
             final SurfaceControl.Transaction tx =
                     mSurfaceControlTransactionFactory.getTransaction();
             if (mContentOverlay != null) {
                 mContentOverlay.detach(tx);
             }
-            mContentOverlay = new PipContentOverlay.PipSnapshotOverlay(snapshot, sourceRectHint);
+            mContentOverlay = overlay;
             mContentOverlay.attach(tx, mLeash);
         }
 
@@ -570,8 +575,9 @@
                     final Rect base = getBaseValue();
                     final Rect start = getStartValue();
                     final Rect end = getEndValue();
+                    Rect bounds = mRectEvaluator.evaluate(fraction, start, end);
                     if (mContentOverlay != null) {
-                        mContentOverlay.onAnimationUpdate(tx, fraction);
+                        mContentOverlay.onAnimationUpdate(tx, bounds, fraction);
                     }
                     if (rotatedEndRect != null) {
                         // Animate the bounds in a different orientation. It only happens when
@@ -579,7 +585,6 @@
                         applyRotation(tx, leash, fraction, start, end);
                         return;
                     }
-                    Rect bounds = mRectEvaluator.evaluate(fraction, start, end);
                     float angle = (1.0f - fraction) * startingAngle;
                     setCurrentValue(bounds);
                     if (inScaleTransition() || sourceHintRect == null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
index f6d67d8..867162b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
@@ -16,24 +16,19 @@
 
 package com.android.wm.shell.pip;
 
-import static android.util.TypedValue.COMPLEX_UNIT_DIP;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.PictureInPictureParams;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.res.Resources;
-import android.graphics.Point;
-import android.graphics.PointF;
 import android.graphics.Rect;
 import android.util.DisplayMetrics;
 import android.util.Size;
-import android.util.TypedValue;
 import android.view.Gravity;
 
 import com.android.wm.shell.R;
-import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
 
 import java.io.PrintWriter;
 
@@ -45,33 +40,29 @@
     private static final String TAG = PipBoundsAlgorithm.class.getSimpleName();
     private static final float INVALID_SNAP_FRACTION = -1f;
 
-    private final @NonNull PipBoundsState mPipBoundsState;
+    @NonNull private final PipBoundsState mPipBoundsState;
+    @NonNull protected final PipSizeSpecHandler mPipSizeSpecHandler;
     private final PipSnapAlgorithm mSnapAlgorithm;
     private final PipKeepClearAlgorithmInterface mPipKeepClearAlgorithm;
 
-    private float mDefaultSizePercent;
-    private float mMinAspectRatioForMinSize;
-    private float mMaxAspectRatioForMinSize;
     private float mDefaultAspectRatio;
     private float mMinAspectRatio;
     private float mMaxAspectRatio;
     private int mDefaultStackGravity;
-    private int mDefaultMinSize;
-    private int mOverridableMinSize;
-    protected Point mScreenEdgeInsets;
 
     public PipBoundsAlgorithm(Context context, @NonNull PipBoundsState pipBoundsState,
             @NonNull PipSnapAlgorithm pipSnapAlgorithm,
-            @NonNull PipKeepClearAlgorithmInterface pipKeepClearAlgorithm) {
+            @NonNull PipKeepClearAlgorithmInterface pipKeepClearAlgorithm,
+            @NonNull PipSizeSpecHandler pipSizeSpecHandler) {
         mPipBoundsState = pipBoundsState;
         mSnapAlgorithm = pipSnapAlgorithm;
         mPipKeepClearAlgorithm = pipKeepClearAlgorithm;
+        mPipSizeSpecHandler = pipSizeSpecHandler;
         reloadResources(context);
         // Initialize the aspect ratio to the default aspect ratio.  Don't do this in reload
         // resources as it would clobber mAspectRatio when entering PiP from fullscreen which
         // triggers a configuration change and the resources to be reloaded.
         mPipBoundsState.setAspectRatio(mDefaultAspectRatio);
-        mPipBoundsState.setMinEdgeSize(mDefaultMinSize);
     }
 
     /**
@@ -83,27 +74,15 @@
                 R.dimen.config_pictureInPictureDefaultAspectRatio);
         mDefaultStackGravity = res.getInteger(
                 R.integer.config_defaultPictureInPictureGravity);
-        mDefaultMinSize = res.getDimensionPixelSize(
-                R.dimen.default_minimal_size_pip_resizable_task);
-        mOverridableMinSize = res.getDimensionPixelSize(
-                R.dimen.overridable_minimal_size_pip_resizable_task);
         final String screenEdgeInsetsDpString = res.getString(
                 R.string.config_defaultPictureInPictureScreenEdgeInsets);
         final Size screenEdgeInsetsDp = !screenEdgeInsetsDpString.isEmpty()
                 ? Size.parseSize(screenEdgeInsetsDpString)
                 : null;
-        mScreenEdgeInsets = screenEdgeInsetsDp == null ? new Point()
-                : new Point(dpToPx(screenEdgeInsetsDp.getWidth(), res.getDisplayMetrics()),
-                        dpToPx(screenEdgeInsetsDp.getHeight(), res.getDisplayMetrics()));
         mMinAspectRatio = res.getFloat(
                 com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
         mMaxAspectRatio = res.getFloat(
                 com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio);
-        mDefaultSizePercent = res.getFloat(
-                R.dimen.config_pictureInPictureDefaultSizePercent);
-        mMaxAspectRatioForMinSize = res.getFloat(
-                R.dimen.config_pictureInPictureAspectRatioLimitForMinSize);
-        mMinAspectRatioForMinSize = 1f / mMaxAspectRatioForMinSize;
     }
 
     /**
@@ -180,8 +159,9 @@
         if (windowLayout.minWidth > 0 && windowLayout.minHeight > 0) {
             // If either dimension is smaller than the allowed minimum, adjust them
             // according to mOverridableMinSize
-            return new Size(Math.max(windowLayout.minWidth, mOverridableMinSize),
-                    Math.max(windowLayout.minHeight, mOverridableMinSize));
+            return new Size(
+                    Math.max(windowLayout.minWidth, mPipSizeSpecHandler.getOverrideMinEdgeSize()),
+                    Math.max(windowLayout.minHeight, mPipSizeSpecHandler.getOverrideMinEdgeSize()));
         }
         return null;
     }
@@ -243,28 +223,13 @@
         final float snapFraction = mSnapAlgorithm.getSnapFraction(stackBounds,
                 getMovementBounds(stackBounds), mPipBoundsState.getStashedState());
 
-        final Size overrideMinSize = mPipBoundsState.getOverrideMinSize();
         final Size size;
         if (useCurrentMinEdgeSize || useCurrentSize) {
-            // The default minimum edge size, or the override min edge size if set.
-            final int defaultMinEdgeSize = overrideMinSize == null ? mDefaultMinSize
-                    : mPipBoundsState.getOverrideMinEdgeSize();
-            final int minEdgeSize = useCurrentMinEdgeSize ? mPipBoundsState.getMinEdgeSize()
-                    : defaultMinEdgeSize;
-            // Use the existing size but adjusted to the aspect ratio and min edge size.
-            size = getSizeForAspectRatio(
-                    new Size(stackBounds.width(), stackBounds.height()), aspectRatio, minEdgeSize);
+            // Use the existing size but adjusted to the new aspect ratio.
+            size = mPipSizeSpecHandler.getSizeForAspectRatio(
+                    new Size(stackBounds.width(), stackBounds.height()), aspectRatio);
         } else {
-            if (overrideMinSize != null) {
-                // The override minimal size is set, use that as the default size making sure it's
-                // adjusted to the aspect ratio.
-                size = adjustSizeToAspectRatio(overrideMinSize, aspectRatio);
-            } else {
-                // Calculate the default size using the display size and default min edge size.
-                final DisplayLayout displayLayout = mPipBoundsState.getDisplayLayout();
-                size = getSizeForAspectRatio(aspectRatio, mDefaultMinSize,
-                        displayLayout.width(), displayLayout.height());
-            }
+            size = mPipSizeSpecHandler.getDefaultSize(aspectRatio);
         }
 
         final int left = (int) (stackBounds.centerX() - size.getWidth() / 2f);
@@ -273,18 +238,6 @@
         mSnapAlgorithm.applySnapFraction(stackBounds, getMovementBounds(stackBounds), snapFraction);
     }
 
-    /** Adjusts the given size to conform to the given aspect ratio. */
-    private Size adjustSizeToAspectRatio(@NonNull Size size, float aspectRatio) {
-        final float sizeAspectRatio = size.getWidth() / (float) size.getHeight();
-        if (sizeAspectRatio > aspectRatio) {
-            // Size is wider, fix the width and increase the height
-            return new Size(size.getWidth(), (int) (size.getWidth() / aspectRatio));
-        } else {
-            // Size is taller, fix the height and adjust the width.
-            return new Size((int) (size.getHeight() * aspectRatio), size.getHeight());
-        }
-    }
-
     /**
      * @return the default bounds to show the PIP, if a {@param snapFraction} and {@param size} are
      * provided, then it will apply the default bounds to the provided snap fraction and size.
@@ -303,17 +256,9 @@
         final Size defaultSize;
         final Rect insetBounds = new Rect();
         getInsetBounds(insetBounds);
-        final DisplayLayout displayLayout = mPipBoundsState.getDisplayLayout();
-        final Size overrideMinSize = mPipBoundsState.getOverrideMinSize();
-        if (overrideMinSize != null) {
-            // The override minimal size is set, use that as the default size making sure it's
-            // adjusted to the aspect ratio.
-            defaultSize = adjustSizeToAspectRatio(overrideMinSize, mDefaultAspectRatio);
-        } else {
-            // Calculate the default size using the display size and default min edge size.
-            defaultSize = getSizeForAspectRatio(mDefaultAspectRatio,
-                    mDefaultMinSize, displayLayout.width(), displayLayout.height());
-        }
+
+        // Calculate the default size
+        defaultSize = mPipSizeSpecHandler.getDefaultSize(mDefaultAspectRatio);
 
         // Now that we have the default size, apply the snap fraction if valid or position the
         // bounds using the default gravity.
@@ -335,12 +280,7 @@
      * Populates the bounds on the screen that the PIP can be visible in.
      */
     public void getInsetBounds(Rect outRect) {
-        final DisplayLayout displayLayout = mPipBoundsState.getDisplayLayout();
-        Rect insets = mPipBoundsState.getDisplayLayout().stableInsets();
-        outRect.set(insets.left + mScreenEdgeInsets.x,
-                insets.top + mScreenEdgeInsets.y,
-                displayLayout.width() - insets.right - mScreenEdgeInsets.x,
-                displayLayout.height() - insets.bottom - mScreenEdgeInsets.y);
+        outRect.set(mPipSizeSpecHandler.getInsetBounds());
     }
 
     /**
@@ -405,71 +345,11 @@
         mSnapAlgorithm.applySnapFraction(stackBounds, movementBounds, snapFraction);
     }
 
-    public int getDefaultMinSize() {
-        return mDefaultMinSize;
-    }
-
     /**
      * @return the pixels for a given dp value.
      */
     private int dpToPx(float dpValue, DisplayMetrics dm) {
-        return (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, dpValue, dm);
-    }
-
-    /**
-     * @return the size of the PiP at the given aspectRatio, ensuring that the minimum edge
-     * is at least minEdgeSize.
-     */
-    public Size getSizeForAspectRatio(float aspectRatio, float minEdgeSize, int displayWidth,
-            int displayHeight) {
-        final int smallestDisplaySize = Math.min(displayWidth, displayHeight);
-        final int minSize = (int) Math.max(minEdgeSize, smallestDisplaySize * mDefaultSizePercent);
-
-        final int width;
-        final int height;
-        if (aspectRatio <= mMinAspectRatioForMinSize || aspectRatio > mMaxAspectRatioForMinSize) {
-            // Beyond these points, we can just use the min size as the shorter edge
-            if (aspectRatio <= 1) {
-                // Portrait, width is the minimum size
-                width = minSize;
-                height = Math.round(width / aspectRatio);
-            } else {
-                // Landscape, height is the minimum size
-                height = minSize;
-                width = Math.round(height * aspectRatio);
-            }
-        } else {
-            // Within these points, we ensure that the bounds fit within the radius of the limits
-            // at the points
-            final float widthAtMaxAspectRatioForMinSize = mMaxAspectRatioForMinSize * minSize;
-            final float radius = PointF.length(widthAtMaxAspectRatioForMinSize, minSize);
-            height = (int) Math.round(Math.sqrt((radius * radius)
-                    / (aspectRatio * aspectRatio + 1)));
-            width = Math.round(height * aspectRatio);
-        }
-        return new Size(width, height);
-    }
-
-    /**
-     * @return the adjusted size so that it conforms to the given aspectRatio, ensuring that the
-     * minimum edge is at least minEdgeSize.
-     */
-    public Size getSizeForAspectRatio(Size size, float aspectRatio, float minEdgeSize) {
-        final int smallestSize = Math.min(size.getWidth(), size.getHeight());
-        final int minSize = (int) Math.max(minEdgeSize, smallestSize);
-
-        final int width;
-        final int height;
-        if (aspectRatio <= 1) {
-            // Portrait, width is the minimum size.
-            width = minSize;
-            height = Math.round(width / aspectRatio);
-        } else {
-            // Landscape, height is the minimum size
-            height = minSize;
-            width = Math.round(height * aspectRatio);
-        }
-        return new Size(width, height);
+        return PipUtils.dpToPx(dpValue, dm);
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
index 5376ae3..5be18d8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
@@ -37,6 +37,7 @@
 import com.android.internal.util.function.TriConsumer;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 
 import java.io.PrintWriter;
@@ -83,13 +84,10 @@
     private int mStashedState = STASH_TYPE_NONE;
     private int mStashOffset;
     private @Nullable PipReentryState mPipReentryState;
+    private final @Nullable PipSizeSpecHandler mPipSizeSpecHandler;
     private @Nullable ComponentName mLastPipComponentName;
     private int mDisplayId = Display.DEFAULT_DISPLAY;
     private final @NonNull DisplayLayout mDisplayLayout = new DisplayLayout();
-    /** The current minimum edge size of PIP. */
-    private int mMinEdgeSize;
-    /** The preferred minimum (and default) size specified by apps. */
-    private @Nullable Size mOverrideMinSize;
     private final @NonNull MotionBoundsState mMotionBoundsState = new MotionBoundsState();
     private boolean mIsImeShowing;
     private int mImeHeight;
@@ -122,9 +120,10 @@
     private @Nullable TriConsumer<Boolean, Integer, Boolean> mOnShelfVisibilityChangeCallback;
     private List<Consumer<Rect>> mOnPipExclusionBoundsChangeCallbacks = new ArrayList<>();
 
-    public PipBoundsState(@NonNull Context context) {
+    public PipBoundsState(@NonNull Context context, PipSizeSpecHandler pipSizeSpecHandler) {
         mContext = context;
         reloadResources();
+        mPipSizeSpecHandler = pipSizeSpecHandler;
     }
 
     /** Reloads the resources. */
@@ -312,10 +311,10 @@
         mDisplayLayout.set(displayLayout);
     }
 
-    /** Get the display layout. */
+    /** Get a copy of the display layout. */
     @NonNull
     public DisplayLayout getDisplayLayout() {
-        return mDisplayLayout;
+        return new DisplayLayout(mDisplayLayout);
     }
 
     @VisibleForTesting
@@ -323,20 +322,10 @@
         mPipReentryState = null;
     }
 
-    /** Set the PIP minimum edge size. */
-    public void setMinEdgeSize(int minEdgeSize) {
-        mMinEdgeSize = minEdgeSize;
-    }
-
-    /** Returns the PIP's current minimum edge size. */
-    public int getMinEdgeSize() {
-        return mMinEdgeSize;
-    }
-
     /** Sets the preferred size of PIP as specified by the activity in PIP mode. */
     public void setOverrideMinSize(@Nullable Size overrideMinSize) {
-        final boolean changed = !Objects.equals(overrideMinSize, mOverrideMinSize);
-        mOverrideMinSize = overrideMinSize;
+        final boolean changed = !Objects.equals(overrideMinSize, getOverrideMinSize());
+        mPipSizeSpecHandler.setOverrideMinSize(overrideMinSize);
         if (changed && mOnMinimalSizeChangeCallback != null) {
             mOnMinimalSizeChangeCallback.run();
         }
@@ -345,13 +334,12 @@
     /** Returns the preferred minimal size specified by the activity in PIP. */
     @Nullable
     public Size getOverrideMinSize() {
-        return mOverrideMinSize;
+        return mPipSizeSpecHandler.getOverrideMinSize();
     }
 
     /** Returns the minimum edge size of the override minimum size, or 0 if not set. */
     public int getOverrideMinEdgeSize() {
-        if (mOverrideMinSize == null) return 0;
-        return Math.min(mOverrideMinSize.getWidth(), mOverrideMinSize.getHeight());
+        return mPipSizeSpecHandler.getOverrideMinEdgeSize();
     }
 
     /** Get the state of the bounds in motion. */
@@ -581,11 +569,8 @@
         pw.println(innerPrefix + "mLastPipComponentName=" + mLastPipComponentName);
         pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio);
         pw.println(innerPrefix + "mDisplayId=" + mDisplayId);
-        pw.println(innerPrefix + "mDisplayLayout=" + mDisplayLayout);
         pw.println(innerPrefix + "mStashedState=" + mStashedState);
         pw.println(innerPrefix + "mStashOffset=" + mStashOffset);
-        pw.println(innerPrefix + "mMinEdgeSize=" + mMinEdgeSize);
-        pw.println(innerPrefix + "mOverrideMinSize=" + mOverrideMinSize);
         pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
         pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
         pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
index 7096a64..480bf93 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
@@ -16,10 +16,21 @@
 
 package com.android.wm.shell.pip;
 
+import static android.util.TypedValue.COMPLEX_UNIT_DIP;
+
+import android.annotation.Nullable;
 import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
 import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Matrix;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.TypedValue;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 import android.window.TaskSnapshot;
@@ -41,13 +52,20 @@
         }
     }
 
+    @Nullable
+    public SurfaceControl getLeash() {
+        return mLeash;
+    }
+
     /**
      * Animates the internal {@link #mLeash} by a given fraction.
      * @param atomicTx {@link SurfaceControl.Transaction} to operate, you should not explicitly
      *                 call apply on this transaction, it should be applied on the caller side.
+     * @param currentBounds {@link Rect} of the current animation bounds.
      * @param fraction progress of the animation ranged from 0f to 1f.
      */
-    public abstract void onAnimationUpdate(SurfaceControl.Transaction atomicTx, float fraction);
+    public abstract void onAnimationUpdate(SurfaceControl.Transaction atomicTx,
+            Rect currentBounds, float fraction);
 
     /**
      * Callback when reaches the end of animation on the internal {@link #mLeash}.
@@ -60,13 +78,15 @@
 
     /** A {@link PipContentOverlay} uses solid color. */
     public static final class PipColorOverlay extends PipContentOverlay {
+        private static final String TAG = PipColorOverlay.class.getSimpleName();
+
         private final Context mContext;
 
         public PipColorOverlay(Context context) {
             mContext = context;
             mLeash = new SurfaceControl.Builder(new SurfaceSession())
-                    .setCallsite("PipAnimation")
-                    .setName(PipColorOverlay.class.getSimpleName())
+                    .setCallsite(TAG)
+                    .setName(TAG)
                     .setColorLayer()
                     .build();
         }
@@ -82,7 +102,8 @@
         }
 
         @Override
-        public void onAnimationUpdate(SurfaceControl.Transaction atomicTx, float fraction) {
+        public void onAnimationUpdate(SurfaceControl.Transaction atomicTx,
+                Rect currentBounds, float fraction) {
             atomicTx.setAlpha(mLeash, fraction < 0.5f ? 0 : (fraction - 0.5f) * 2);
         }
 
@@ -108,6 +129,8 @@
 
     /** A {@link PipContentOverlay} uses {@link TaskSnapshot}. */
     public static final class PipSnapshotOverlay extends PipContentOverlay {
+        private static final String TAG = PipSnapshotOverlay.class.getSimpleName();
+
         private final TaskSnapshot mSnapshot;
         private final Rect mSourceRectHint;
 
@@ -115,8 +138,8 @@
             mSnapshot = snapshot;
             mSourceRectHint = new Rect(sourceRectHint);
             mLeash = new SurfaceControl.Builder(new SurfaceSession())
-                    .setCallsite("PipAnimation")
-                    .setName(PipSnapshotOverlay.class.getSimpleName())
+                    .setCallsite(TAG)
+                    .setName(TAG)
                     .build();
         }
 
@@ -137,7 +160,8 @@
         }
 
         @Override
-        public void onAnimationUpdate(SurfaceControl.Transaction atomicTx, float fraction) {
+        public void onAnimationUpdate(SurfaceControl.Transaction atomicTx,
+                Rect currentBounds, float fraction) {
             // Do nothing. Keep the snapshot till animation ends.
         }
 
@@ -146,4 +170,113 @@
             atomicTx.remove(mLeash);
         }
     }
+
+    /** A {@link PipContentOverlay} shows app icon on solid color background. */
+    public static final class PipAppIconOverlay extends PipContentOverlay {
+        private static final String TAG = PipAppIconOverlay.class.getSimpleName();
+        private static final int APP_ICON_SIZE_DP = 48;
+
+        private final Context mContext;
+        private final int mAppIconSizePx;
+        private final Rect mAppBounds;
+        private final Matrix mTmpTransform = new Matrix();
+        private final float[] mTmpFloat9 = new float[9];
+
+        private Bitmap mBitmap;
+
+        public PipAppIconOverlay(Context context, Rect appBounds, ActivityInfo activityInfo) {
+            mContext = context;
+            mAppIconSizePx = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, APP_ICON_SIZE_DP,
+                    context.getResources().getDisplayMetrics());
+            mAppBounds = new Rect(appBounds);
+            mBitmap = Bitmap.createBitmap(appBounds.width(), appBounds.height(),
+                    Bitmap.Config.ARGB_8888);
+            prepareAppIconOverlay(activityInfo);
+            mLeash = new SurfaceControl.Builder(new SurfaceSession())
+                    .setCallsite(TAG)
+                    .setName(TAG)
+                    .build();
+        }
+
+        @Override
+        public void attach(SurfaceControl.Transaction tx, SurfaceControl parentLeash) {
+            tx.show(mLeash);
+            tx.setLayer(mLeash, Integer.MAX_VALUE);
+            tx.setBuffer(mLeash, mBitmap.getHardwareBuffer());
+            tx.reparent(mLeash, parentLeash);
+            tx.apply();
+        }
+
+        @Override
+        public void onAnimationUpdate(SurfaceControl.Transaction atomicTx,
+                Rect currentBounds, float fraction) {
+            mTmpTransform.reset();
+            // Scale back the bitmap with the pivot point at center.
+            mTmpTransform.postScale(
+                    (float) mAppBounds.width() / currentBounds.width(),
+                    (float) mAppBounds.height() / currentBounds.height(),
+                    mAppBounds.centerX(),
+                    mAppBounds.centerY());
+            atomicTx.setMatrix(mLeash, mTmpTransform, mTmpFloat9)
+                    .setAlpha(mLeash, fraction < 0.5f ? 0 : (fraction - 0.5f) * 2);
+        }
+
+        @Override
+        public void onAnimationEnd(SurfaceControl.Transaction atomicTx, Rect destinationBounds) {
+            atomicTx.remove(mLeash);
+        }
+
+        @Override
+        public void detach(SurfaceControl.Transaction tx) {
+            super.detach(tx);
+            if (mBitmap != null && !mBitmap.isRecycled()) {
+                mBitmap.recycle();
+            }
+        }
+
+        private void prepareAppIconOverlay(ActivityInfo activityInfo) {
+            final Canvas canvas = new Canvas();
+            canvas.setBitmap(mBitmap);
+            final TypedArray ta = mContext.obtainStyledAttributes(new int[] {
+                    android.R.attr.colorBackground });
+            try {
+                int colorAccent = ta.getColor(0, 0);
+                canvas.drawRGB(
+                        Color.red(colorAccent),
+                        Color.green(colorAccent),
+                        Color.blue(colorAccent));
+            } finally {
+                ta.recycle();
+            }
+            final Drawable appIcon = loadActivityInfoIcon(activityInfo,
+                    mContext.getResources().getConfiguration().densityDpi);
+            final Rect appIconBounds = new Rect(
+                    mAppBounds.centerX() - mAppIconSizePx / 2,
+                    mAppBounds.centerY() - mAppIconSizePx / 2,
+                    mAppBounds.centerX() + mAppIconSizePx / 2,
+                    mAppBounds.centerY() + mAppIconSizePx / 2);
+            appIcon.setBounds(appIconBounds);
+            appIcon.draw(canvas);
+            mBitmap = mBitmap.copy(Bitmap.Config.HARDWARE, false /* mutable */);
+        }
+
+        // Copied from com.android.launcher3.icons.IconProvider#loadActivityInfoIcon
+        private Drawable loadActivityInfoIcon(ActivityInfo ai, int density) {
+            final int iconRes = ai.getIconResource();
+            Drawable icon = null;
+            // Get the preferred density icon from the app's resources
+            if (density != 0 && iconRes != 0) {
+                try {
+                    final Resources resources = mContext.getPackageManager()
+                            .getResourcesForApplication(ai.applicationInfo);
+                    icon = resources.getDrawableForDensity(iconRes, density);
+                } catch (PackageManager.NameNotFoundException | Resources.NotFoundException exc) { }
+            }
+            // Get the default density icon
+            if (icon == null) {
+                icon = ai.loadIcon(mContext.getPackageManager());
+            }
+            return icon;
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 8ba2583..f11836e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -62,6 +62,7 @@
 import android.graphics.Rect;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.util.Log;
 import android.view.Choreographer;
 import android.view.Display;
@@ -78,11 +79,13 @@
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.animation.Interpolators;
 import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.ScreenshotUtils;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.annotations.ShellMainThread;
 import com.android.wm.shell.pip.phone.PipMotionHelper;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.transition.Transitions;
@@ -125,6 +128,7 @@
     private final Context mContext;
     private final SyncTransactionQueue mSyncTransactionQueue;
     private final PipBoundsState mPipBoundsState;
+    private final PipSizeSpecHandler mPipSizeSpecHandler;
     private final PipBoundsAlgorithm mPipBoundsAlgorithm;
     private final @NonNull PipMenuController mPipMenuController;
     private final PipAnimationController mPipAnimationController;
@@ -312,6 +316,7 @@
             @NonNull SyncTransactionQueue syncTransactionQueue,
             @NonNull PipTransitionState pipTransitionState,
             @NonNull PipBoundsState pipBoundsState,
+            @NonNull PipSizeSpecHandler pipSizeSpecHandler,
             @NonNull PipBoundsAlgorithm boundsHandler,
             @NonNull PipMenuController pipMenuController,
             @NonNull PipAnimationController pipAnimationController,
@@ -327,6 +332,7 @@
         mSyncTransactionQueue = syncTransactionQueue;
         mPipTransitionState = pipTransitionState;
         mPipBoundsState = pipBoundsState;
+        mPipSizeSpecHandler = pipSizeSpecHandler;
         mPipBoundsAlgorithm = boundsHandler;
         mPipMenuController = pipMenuController;
         mPipTransitionController = pipTransitionController;
@@ -644,7 +650,6 @@
         }
 
         mPipUiEventLoggerLogger.setTaskInfo(mTaskInfo);
-        mPipUiEventLoggerLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER);
 
         // If the displayId of the task is different than what PipBoundsHandler has, then update
         // it. This is possible if we entered PiP on an external display.
@@ -653,6 +658,17 @@
             mOnDisplayIdChangeCallback.accept(info.displayId);
         }
 
+        // UiEvent logging.
+        final PipUiEventLogger.PipUiEventEnum uiEventEnum;
+        if (isLaunchIntoPipTask()) {
+            uiEventEnum = PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER_CONTENT_PIP;
+        } else if (mPipTransitionState.getInSwipePipToHomeTransition()) {
+            uiEventEnum = PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_AUTO_ENTER;
+        } else {
+            uiEventEnum = PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER;
+        }
+        mPipUiEventLoggerLogger.log(uiEventEnum);
+
         if (mPipTransitionState.getInSwipePipToHomeTransition()) {
             if (!mWaitForFixedRotation) {
                 onEndOfSwipePipToHomeTransition();
@@ -1403,7 +1419,6 @@
             @PipAnimationController.TransitionDirection int direction,
             @PipAnimationController.AnimationType int type) {
         final Rect preResizeBounds = new Rect(mPipBoundsState.getBounds());
-        final boolean isPipTopLeft = isPipTopLeft();
         mPipBoundsState.setBounds(destinationBounds);
         if (direction == TRANSITION_DIRECTION_REMOVE_STACK) {
             removePipImmediately();
@@ -1449,9 +1464,11 @@
                             null /* callback */, false /* withStartDelay */);
                 });
             } else {
-                applyFinishBoundsResize(wct, direction, isPipTopLeft);
+                applyFinishBoundsResize(wct, direction, false);
             }
         } else {
+            final boolean isPipTopLeft =
+                    direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN && isPipToTopLeft();
             applyFinishBoundsResize(wct, direction, isPipTopLeft);
         }
 
@@ -1520,6 +1537,14 @@
         return topLeft.contains(mPipBoundsState.getBounds());
     }
 
+    private boolean isPipToTopLeft() {
+        if (!mSplitScreenOptional.isPresent()) {
+            return false;
+        }
+        return mSplitScreenOptional.get().getActivateSplitPosition(mTaskInfo)
+                == SPLIT_POSITION_TOP_OR_LEFT;
+    }
+
     /**
      * The windowing mode to restore to when resizing out of PIP direction. Defaults to undefined
      * and can be overridden to restore to an alternate windowing mode.
@@ -1568,7 +1593,13 @@
             // Similar to auto-enter-pip transition, we use content overlay when there is no
             // source rect hint to enter PiP use bounds animation.
             if (sourceHintRect == null) {
-                animator.setColorContentOverlay(mContext);
+                if (SystemProperties.getBoolean(
+                        "persist.wm.debug.enable_pip_app_icon_overlay", false)) {
+                    animator.setAppIconContentOverlay(
+                            mContext, currentBounds, mTaskInfo.topActivityInfo);
+                } else {
+                    animator.setColorContentOverlay(mContext);
+                }
             } else {
                 final TaskSnapshot snapshot = PipUtils.getTaskSnapshot(
                         mTaskInfo.launchIntoPipHostTaskId, false /* isLowResolution */);
@@ -1594,7 +1625,12 @@
     private @Nullable Rect computeRotatedBounds(int rotationDelta, int direction,
             Rect outDestinationBounds, Rect sourceHintRect) {
         if (direction == TRANSITION_DIRECTION_TO_PIP) {
-            mPipBoundsState.getDisplayLayout().rotateTo(mContext.getResources(), mNextRotation);
+            DisplayLayout layoutCopy = mPipBoundsState.getDisplayLayout();
+
+            layoutCopy.rotateTo(mContext.getResources(), mNextRotation);
+            mPipBoundsState.setDisplayLayout(layoutCopy);
+            mPipSizeSpecHandler.setDisplayLayout(layoutCopy);
+
             final Rect displayBounds = mPipBoundsState.getDisplayBounds();
             outDestinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds());
             // Transform the destination bounds to current display coordinates.
@@ -1645,8 +1681,7 @@
         final Rect topLeft = new Rect();
         final Rect bottomRight = new Rect();
         mSplitScreenOptional.get().getStageBounds(topLeft, bottomRight);
-        final boolean isPipTopLeft = isPipTopLeft();
-        destinationBoundsOut.set(isPipTopLeft ? topLeft : bottomRight);
+        destinationBoundsOut.set(isPipToTopLeft()  ? topLeft : bottomRight);
         return true;
     }
 
@@ -1730,6 +1765,11 @@
         mSurfaceControlTransactionFactory = factory;
     }
 
+    public boolean isLaunchToSplit(TaskInfo taskInfo) {
+        return mSplitScreenOptional.isPresent()
+                && mSplitScreenOptional.get().isLaunchToSplit(taskInfo);
+    }
+
     /**
      * Dumps internal states.
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 83158ff..e5c0570 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -50,6 +50,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.IBinder;
+import android.os.SystemProperties;
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
@@ -64,6 +65,8 @@
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.sysui.ShellInit;
@@ -82,6 +85,7 @@
 
     private final Context mContext;
     private final PipTransitionState mPipTransitionState;
+    private final PipSizeSpecHandler mPipSizeSpecHandler;
     private final int mEnterExitAnimationDuration;
     private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
     private final Optional<SplitScreenController> mSplitScreenOptional;
@@ -112,6 +116,7 @@
             @NonNull ShellTaskOrganizer shellTaskOrganizer,
             @NonNull Transitions transitions,
             PipBoundsState pipBoundsState,
+            PipSizeSpecHandler pipSizeSpecHandler,
             PipTransitionState pipTransitionState,
             PipMenuController pipMenuController,
             PipBoundsAlgorithm pipBoundsAlgorithm,
@@ -122,6 +127,7 @@
                 pipBoundsAlgorithm, pipAnimationController);
         mContext = context;
         mPipTransitionState = pipTransitionState;
+        mPipSizeSpecHandler = pipSizeSpecHandler;
         mEnterExitAnimationDuration = context.getResources()
                 .getInteger(R.integer.config_pipResizeAnimationDuration);
         mSurfaceTransactionHelper = pipSurfaceTransactionHelper;
@@ -307,7 +313,12 @@
             // initial state under the new rotation.
             int rotationDelta = deltaRotation(startRotation, endRotation);
             if (rotationDelta != Surface.ROTATION_0) {
-                mPipBoundsState.getDisplayLayout().rotateTo(mContext.getResources(), endRotation);
+                DisplayLayout layoutCopy = mPipBoundsState.getDisplayLayout();
+
+                layoutCopy.rotateTo(mContext.getResources(), endRotation);
+                mPipBoundsState.setDisplayLayout(layoutCopy);
+                mPipSizeSpecHandler.setDisplayLayout(layoutCopy);
+
                 final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
                 wct.setBounds(mRequestedEnterTask, destinationBounds);
                 return true;
@@ -792,7 +803,13 @@
             if (sourceHintRect == null) {
                 // We use content overlay when there is no source rect hint to enter PiP use bounds
                 // animation.
-                animator.setColorContentOverlay(mContext);
+                if (SystemProperties.getBoolean(
+                        "persist.wm.debug.enable_pip_app_icon_overlay", false)) {
+                    animator.setAppIconContentOverlay(
+                            mContext, currentBounds, taskInfo.topActivityInfo);
+                } else {
+                    animator.setColorContentOverlay(mContext);
+                }
             }
         } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
             animator = mPipAnimationController.getAnimator(taskInfo, leash, destinationBounds,
@@ -817,7 +834,12 @@
     /** Computes destination bounds in old rotation and updates source hint rect if available. */
     private void computeEnterPipRotatedBounds(int rotationDelta, int startRotation, int endRotation,
             TaskInfo taskInfo, Rect outDestinationBounds, @Nullable Rect outSourceHintRect) {
-        mPipBoundsState.getDisplayLayout().rotateTo(mContext.getResources(), endRotation);
+        DisplayLayout layoutCopy = mPipBoundsState.getDisplayLayout();
+
+        layoutCopy.rotateTo(mContext.getResources(), endRotation);
+        mPipBoundsState.setDisplayLayout(layoutCopy);
+        mPipSizeSpecHandler.setDisplayLayout(layoutCopy);
+
         final Rect displayBounds = mPipBoundsState.getDisplayBounds();
         outDestinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds());
         // Transform the destination bounds to current display coordinates.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
index 513ebba..3e5a19b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
@@ -78,6 +78,12 @@
         @UiEvent(doc = "Activity enters picture-in-picture mode")
         PICTURE_IN_PICTURE_ENTER(603),
 
+        @UiEvent(doc = "Activity enters picture-in-picture mode with auto-enter-pip API")
+        PICTURE_IN_PICTURE_AUTO_ENTER(1313),
+
+        @UiEvent(doc = "Activity enters picture-in-picture mode from content-pip API")
+        PICTURE_IN_PICTURE_ENTER_CONTENT_PIP(1314),
+
         @UiEvent(doc = "Expands from picture-in-picture to fullscreen")
         PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN(604),
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java
index fa00619..8b98790 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java
@@ -18,6 +18,7 @@
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.util.TypedValue.COMPLEX_UNIT_DIP;
 
 import android.annotation.Nullable;
 import android.app.ActivityTaskManager;
@@ -26,8 +27,10 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.RemoteException;
+import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Pair;
+import android.util.TypedValue;
 import android.window.TaskSnapshot;
 
 import com.android.internal.protolog.common.ProtoLog;
@@ -70,6 +73,13 @@
     }
 
     /**
+     * @return the pixels for a given dp value.
+     */
+    public static int dpToPx(float dpValue, DisplayMetrics dm) {
+        return (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, dpValue, dm);
+    }
+
+    /**
      * @return true if the aspect ratios differ
      */
     public static boolean aspectRatioChanged(float aspectRatio1, float aspectRatio2) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 525beb1..fa3efeb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -137,6 +137,7 @@
     private PipBoundsAlgorithm mPipBoundsAlgorithm;
     private PipKeepClearAlgorithmInterface mPipKeepClearAlgorithm;
     private PipBoundsState mPipBoundsState;
+    private PipSizeSpecHandler mPipSizeSpecHandler;
     private PipMotionHelper mPipMotionHelper;
     private PipTouchHandler mTouchHandler;
     private PipTransitionController mPipTransitionController;
@@ -380,6 +381,7 @@
             PipBoundsAlgorithm pipBoundsAlgorithm,
             PipKeepClearAlgorithmInterface pipKeepClearAlgorithm,
             PipBoundsState pipBoundsState,
+            PipSizeSpecHandler pipSizeSpecHandler,
             PipMotionHelper pipMotionHelper,
             PipMediaController pipMediaController,
             PhonePipMenuController phonePipMenuController,
@@ -401,11 +403,11 @@
 
         return new PipController(context, shellInit, shellCommandHandler, shellController,
                 displayController, pipAnimationController, pipAppOpsListener,
-                pipBoundsAlgorithm, pipKeepClearAlgorithm, pipBoundsState, pipMotionHelper,
-                pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTransitionState,
-                pipTouchHandler, pipTransitionController, windowManagerShellWrapper,
-                taskStackListener, pipParamsChangedForwarder, displayInsetsController,
-                oneHandedController, mainExecutor)
+                pipBoundsAlgorithm, pipKeepClearAlgorithm, pipBoundsState, pipSizeSpecHandler,
+                pipMotionHelper, pipMediaController, phonePipMenuController, pipTaskOrganizer,
+                pipTransitionState, pipTouchHandler, pipTransitionController,
+                windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
+                displayInsetsController, oneHandedController, mainExecutor)
                 .mImpl;
     }
 
@@ -419,6 +421,7 @@
             PipBoundsAlgorithm pipBoundsAlgorithm,
             PipKeepClearAlgorithmInterface pipKeepClearAlgorithm,
             @NonNull PipBoundsState pipBoundsState,
+            PipSizeSpecHandler pipSizeSpecHandler,
             PipMotionHelper pipMotionHelper,
             PipMediaController pipMediaController,
             PhonePipMenuController phonePipMenuController,
@@ -444,6 +447,7 @@
         mPipBoundsAlgorithm = pipBoundsAlgorithm;
         mPipKeepClearAlgorithm = pipKeepClearAlgorithm;
         mPipBoundsState = pipBoundsState;
+        mPipSizeSpecHandler = pipSizeSpecHandler;
         mPipMotionHelper = pipMotionHelper;
         mPipTaskOrganizer = pipTaskOrganizer;
         mPipTransitionState = pipTransitionState;
@@ -512,7 +516,10 @@
         // Ensure that we have the display info in case we get calls to update the bounds before the
         // listener calls back
         mPipBoundsState.setDisplayId(mContext.getDisplayId());
-        mPipBoundsState.setDisplayLayout(new DisplayLayout(mContext, mContext.getDisplay()));
+
+        DisplayLayout layout = new DisplayLayout(mContext, mContext.getDisplay());
+        mPipSizeSpecHandler.setDisplayLayout(layout);
+        mPipBoundsState.setDisplayLayout(layout);
 
         try {
             mWindowManagerShellWrapper.addPinnedStackListener(mPinnedTaskListener);
@@ -563,8 +570,12 @@
                         if (task.getWindowingMode() != WINDOWING_MODE_PINNED) {
                             return;
                         }
-                        mTouchHandler.getMotionHelper().expandLeavePip(
-                                clearedTask /* skipAnimation */);
+                        if (mPipTaskOrganizer.isLaunchToSplit(task)) {
+                            mTouchHandler.getMotionHelper().expandIntoSplit();
+                        } else {
+                            mTouchHandler.getMotionHelper().expandLeavePip(
+                                    clearedTask /* skipAnimation */);
+                        }
                     }
                 });
 
@@ -686,6 +697,7 @@
         mPipBoundsAlgorithm.onConfigurationChanged(mContext);
         mTouchHandler.onConfigurationChanged();
         mPipBoundsState.onConfigurationChanged();
+        mPipSizeSpecHandler.onConfigurationChanged();
     }
 
     @Override
@@ -703,6 +715,12 @@
 
     private void onDisplayChanged(DisplayLayout layout, boolean saveRestoreSnapFraction) {
         if (!mPipBoundsState.getDisplayLayout().isSameGeometry(layout)) {
+            PipAnimationController.PipTransitionAnimator animator =
+                    mPipAnimationController.getCurrentAnimator();
+            if (animator != null && animator.isRunning()) {
+                // cancel any running animator, as it is using stale display layout information
+                PipAnimationController.quietCancel(animator);
+            }
             onDisplayChangedUncheck(layout, saveRestoreSnapFraction);
         }
     }
@@ -711,7 +729,11 @@
         Runnable updateDisplayLayout = () -> {
             final boolean fromRotation = Transitions.ENABLE_SHELL_TRANSITIONS
                     && mPipBoundsState.getDisplayLayout().rotation() != layout.rotation();
+
+            // update the internal state of objects subscribed to display changes
+            mPipSizeSpecHandler.setDisplayLayout(layout);
             mPipBoundsState.setDisplayLayout(layout);
+
             final WindowContainerTransaction wct =
                     fromRotation ? new WindowContainerTransaction() : null;
             updateMovementBounds(null /* toBounds */,
@@ -1020,7 +1042,11 @@
     private void onDisplayRotationChangedNotInPip(Context context, int toRotation) {
         // Update the display layout, note that we have to do this on every rotation even if we
         // aren't in PIP since we need to update the display layout to get the right resources
-        mPipBoundsState.getDisplayLayout().rotateTo(context.getResources(), toRotation);
+        DisplayLayout layoutCopy = mPipBoundsState.getDisplayLayout();
+
+        layoutCopy.rotateTo(context.getResources(), toRotation);
+        mPipBoundsState.setDisplayLayout(layoutCopy);
+        mPipSizeSpecHandler.setDisplayLayout(layoutCopy);
     }
 
     /**
@@ -1057,7 +1083,11 @@
                         mPipBoundsState.getStashedState());
 
         // Update the display layout
-        mPipBoundsState.getDisplayLayout().rotateTo(context.getResources(), toRotation);
+        DisplayLayout layoutCopy = mPipBoundsState.getDisplayLayout();
+
+        layoutCopy.rotateTo(context.getResources(), toRotation);
+        mPipBoundsState.setDisplayLayout(layoutCopy);
+        mPipSizeSpecHandler.setDisplayLayout(layoutCopy);
 
         // Calculate the stack bounds in the new orientation based on same fraction along the
         // rotated movement bounds.
@@ -1083,6 +1113,7 @@
         mPipTaskOrganizer.dump(pw, innerPrefix);
         mPipBoundsState.dump(pw, innerPrefix);
         mPipInputConsumer.dump(pw, innerPrefix);
+        mPipSizeSpecHandler.dump(pw, innerPrefix);
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java
new file mode 100644
index 0000000..d03d075
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java
@@ -0,0 +1,529 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip.phone;
+
+import static com.android.wm.shell.pip.PipUtils.dpToPx;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.os.SystemProperties;
+import android.util.Size;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.wm.shell.R;
+import com.android.wm.shell.common.DisplayLayout;
+
+import java.io.PrintWriter;
+
+/**
+ * Acts as a source of truth for appropriate size spec for PIP.
+ */
+public class PipSizeSpecHandler {
+    private static final String TAG = PipSizeSpecHandler.class.getSimpleName();
+
+    @NonNull private final DisplayLayout mDisplayLayout = new DisplayLayout();
+
+    @VisibleForTesting
+    final SizeSpecSource mSizeSpecSourceImpl;
+
+    /** The preferred minimum (and default minimum) size specified by apps. */
+    @Nullable private Size mOverrideMinSize;
+    private int mOverridableMinSize;
+
+    /** Used to store values obtained from resource files. */
+    private Point mScreenEdgeInsets;
+    private float mMinAspectRatioForMinSize;
+    private float mMaxAspectRatioForMinSize;
+    private int mDefaultMinSize;
+
+    @NonNull private final Context mContext;
+
+    private interface SizeSpecSource {
+        /** Returns max size allowed for the PIP window */
+        Size getMaxSize(float aspectRatio);
+
+        /** Returns default size for the PIP window */
+        Size getDefaultSize(float aspectRatio);
+
+        /** Returns min size allowed for the PIP window */
+        Size getMinSize(float aspectRatio);
+
+        /** Returns the adjusted size based on current size and target aspect ratio */
+        Size getSizeForAspectRatio(Size size, float aspectRatio);
+
+        /** Updates internal resources on configuration changes */
+        default void reloadResources() {}
+    }
+
+    /**
+     * Determines PIP window size optimized for large screens and aspect ratios close to 1:1
+     */
+    private class SizeSpecLargeScreenOptimizedImpl implements SizeSpecSource {
+        private static final float DEFAULT_OPTIMIZED_ASPECT_RATIO = 9f / 16;
+
+        /** Default and minimum percentages for the PIP size logic. */
+        private final float mDefaultSizePercent;
+        private final float mMinimumSizePercent;
+
+        /** Aspect ratio that the PIP size spec logic optimizes for. */
+        private float mOptimizedAspectRatio;
+
+        private SizeSpecLargeScreenOptimizedImpl() {
+            mDefaultSizePercent = Float.parseFloat(SystemProperties
+                    .get("com.android.wm.shell.pip.phone.def_percentage", "0.6"));
+            mMinimumSizePercent = Float.parseFloat(SystemProperties
+                    .get("com.android.wm.shell.pip.phone.min_percentage", "0.5"));
+        }
+
+        @Override
+        public void reloadResources() {
+            final Resources res = mContext.getResources();
+
+            mOptimizedAspectRatio = res.getFloat(R.dimen.config_pipLargeScreenOptimizedAspectRatio);
+            // make sure the optimized aspect ratio is valid with a default value to fall back to
+            if (mOptimizedAspectRatio > 1) {
+                mOptimizedAspectRatio = DEFAULT_OPTIMIZED_ASPECT_RATIO;
+            }
+        }
+
+        /**
+         * Calculates the max size of PIP.
+         *
+         * Optimizes for 16:9 aspect ratios, making them take full length of shortest display edge.
+         * As aspect ratio approaches values close to 1:1, the logic does not let PIP occupy the
+         * whole screen. A linear function is used to calculate these sizes.
+         *
+         * @param aspectRatio aspect ratio of the PIP window
+         * @return dimensions of the max size of the PIP
+         */
+        @Override
+        public Size getMaxSize(float aspectRatio) {
+            final int totalHorizontalPadding = getInsetBounds().left
+                    + (getDisplayBounds().width() - getInsetBounds().right);
+            final int totalVerticalPadding = getInsetBounds().top
+                    + (getDisplayBounds().height() - getInsetBounds().bottom);
+
+            final int shorterLength = (int) (1f * Math.min(
+                    getDisplayBounds().width() - totalHorizontalPadding,
+                    getDisplayBounds().height() - totalVerticalPadding));
+
+            int maxWidth, maxHeight;
+
+            // use the optimized max sizing logic only within a certain aspect ratio range
+            if (aspectRatio >= mOptimizedAspectRatio && aspectRatio <= 1 / mOptimizedAspectRatio) {
+                // this formula and its derivation is explained in b/198643358#comment16
+                maxWidth = (int) (mOptimizedAspectRatio * shorterLength
+                        + shorterLength * (aspectRatio - mOptimizedAspectRatio) / (1
+                        + aspectRatio));
+                maxHeight = (int) (maxWidth / aspectRatio);
+            } else {
+                if (aspectRatio > 1f) {
+                    maxWidth = shorterLength;
+                    maxHeight = (int) (maxWidth / aspectRatio);
+                } else {
+                    maxHeight = shorterLength;
+                    maxWidth = (int) (maxHeight * aspectRatio);
+                }
+            }
+
+            return new Size(maxWidth, maxHeight);
+        }
+
+        /**
+         * Decreases the dimensions by a percentage relative to max size to get default size.
+         *
+         * @param aspectRatio aspect ratio of the PIP window
+         * @return dimensions of the default size of the PIP
+         */
+        @Override
+        public Size getDefaultSize(float aspectRatio) {
+            Size minSize = this.getMinSize(aspectRatio);
+
+            if (mOverrideMinSize != null) {
+                return minSize;
+            }
+
+            Size maxSize = this.getMaxSize(aspectRatio);
+
+            int defaultWidth = Math.max((int) (maxSize.getWidth() * mDefaultSizePercent),
+                    minSize.getWidth());
+            int defaultHeight = Math.max((int) (maxSize.getHeight() * mDefaultSizePercent),
+                    minSize.getHeight());
+
+            return new Size(defaultWidth, defaultHeight);
+        }
+
+        /**
+         * Decreases the dimensions by a certain percentage relative to max size to get min size.
+         *
+         * @param aspectRatio aspect ratio of the PIP window
+         * @return dimensions of the min size of the PIP
+         */
+        @Override
+        public Size getMinSize(float aspectRatio) {
+            // if there is an overridden min size provided, return that
+            if (mOverrideMinSize != null) {
+                return adjustOverrideMinSizeToAspectRatio(aspectRatio);
+            }
+
+            Size maxSize = this.getMaxSize(aspectRatio);
+
+            int minWidth = (int) (maxSize.getWidth() * mMinimumSizePercent);
+            int minHeight = (int) (maxSize.getHeight() * mMinimumSizePercent);
+
+            // make sure the calculated min size is not smaller than the allowed default min size
+            if (aspectRatio > 1f) {
+                minHeight = (int) Math.max(minHeight, mDefaultMinSize);
+                minWidth = (int) (minHeight * aspectRatio);
+            } else {
+                minWidth = (int) Math.max(minWidth, mDefaultMinSize);
+                minHeight = (int) (minWidth / aspectRatio);
+            }
+            return new Size(minWidth, minHeight);
+        }
+
+        /**
+         * Returns the size for target aspect ratio making sure new size conforms with the rules.
+         *
+         * <p>Recalculates the dimensions such that the target aspect ratio is achieved, while
+         * maintaining the same maximum size to current size ratio.
+         *
+         * @param size current size
+         * @param aspectRatio target aspect ratio
+         */
+        @Override
+        public Size getSizeForAspectRatio(Size size, float aspectRatio) {
+            // getting the percentage of the max size that current size takes
+            float currAspectRatio = (float) size.getWidth() / size.getHeight();
+            Size currentMaxSize = getMaxSize(currAspectRatio);
+            float currentPercent = (float) size.getWidth() / currentMaxSize.getWidth();
+
+            // getting the max size for the target aspect ratio
+            Size updatedMaxSize = getMaxSize(aspectRatio);
+
+            int width = (int) (updatedMaxSize.getWidth() * currentPercent);
+            int height = (int) (updatedMaxSize.getHeight() * currentPercent);
+
+            // adjust the dimensions if below allowed min edge size
+            if (width < getMinEdgeSize() && aspectRatio <= 1) {
+                width = getMinEdgeSize();
+                height = (int) (width / aspectRatio);
+            } else if (height < getMinEdgeSize() && aspectRatio > 1) {
+                height = getMinEdgeSize();
+                width = (int) (height * aspectRatio);
+            }
+
+            // reduce the dimensions of the updated size to the calculated percentage
+            return new Size(width, height);
+        }
+    }
+
+    private class SizeSpecDefaultImpl implements SizeSpecSource {
+        private float mDefaultSizePercent;
+        private float mMinimumSizePercent;
+
+        @Override
+        public void reloadResources() {
+            final Resources res = mContext.getResources();
+
+            mMaxAspectRatioForMinSize = res.getFloat(
+                    R.dimen.config_pictureInPictureAspectRatioLimitForMinSize);
+            mMinAspectRatioForMinSize = 1f / mMaxAspectRatioForMinSize;
+
+            mDefaultSizePercent = res.getFloat(R.dimen.config_pictureInPictureDefaultSizePercent);
+            mMinimumSizePercent = res.getFraction(R.fraction.config_pipShortestEdgePercent, 1, 1);
+        }
+
+        @Override
+        public Size getMaxSize(float aspectRatio) {
+            final int shorterLength = Math.min(getDisplayBounds().width(),
+                    getDisplayBounds().height());
+
+            final int totalHorizontalPadding = getInsetBounds().left
+                    + (getDisplayBounds().width() - getInsetBounds().right);
+            final int totalVerticalPadding = getInsetBounds().top
+                    + (getDisplayBounds().height() - getInsetBounds().bottom);
+
+            final int maxWidth, maxHeight;
+
+            if (aspectRatio > 1f) {
+                maxWidth = (int) Math.max(getDefaultSize(aspectRatio).getWidth(),
+                        shorterLength - totalHorizontalPadding);
+                maxHeight = (int) (maxWidth / aspectRatio);
+            } else {
+                maxHeight = (int) Math.max(getDefaultSize(aspectRatio).getHeight(),
+                        shorterLength - totalVerticalPadding);
+                maxWidth = (int) (maxHeight * aspectRatio);
+            }
+
+            return new Size(maxWidth, maxHeight);
+        }
+
+        @Override
+        public Size getDefaultSize(float aspectRatio) {
+            if (mOverrideMinSize != null) {
+                return this.getMinSize(aspectRatio);
+            }
+
+            final int smallestDisplaySize = Math.min(getDisplayBounds().width(),
+                    getDisplayBounds().height());
+            final int minSize = (int) Math.max(getMinEdgeSize(),
+                    smallestDisplaySize * mDefaultSizePercent);
+
+            final int width;
+            final int height;
+
+            if (aspectRatio <= mMinAspectRatioForMinSize
+                    || aspectRatio > mMaxAspectRatioForMinSize) {
+                // Beyond these points, we can just use the min size as the shorter edge
+                if (aspectRatio <= 1) {
+                    // Portrait, width is the minimum size
+                    width = minSize;
+                    height = Math.round(width / aspectRatio);
+                } else {
+                    // Landscape, height is the minimum size
+                    height = minSize;
+                    width = Math.round(height * aspectRatio);
+                }
+            } else {
+                // Within these points, ensure that the bounds fit within the radius of the limits
+                // at the points
+                final float widthAtMaxAspectRatioForMinSize = mMaxAspectRatioForMinSize * minSize;
+                final float radius = PointF.length(widthAtMaxAspectRatioForMinSize, minSize);
+                height = (int) Math.round(Math.sqrt((radius * radius)
+                        / (aspectRatio * aspectRatio + 1)));
+                width = Math.round(height * aspectRatio);
+            }
+
+            return new Size(width, height);
+        }
+
+        @Override
+        public Size getMinSize(float aspectRatio) {
+            if (mOverrideMinSize != null) {
+                return adjustOverrideMinSizeToAspectRatio(aspectRatio);
+            }
+
+            final int shorterLength = Math.min(getDisplayBounds().width(),
+                    getDisplayBounds().height());
+            final int minWidth, minHeight;
+
+            if (aspectRatio > 1f) {
+                minWidth = (int) Math.min(getDefaultSize(aspectRatio).getWidth(),
+                        shorterLength * mMinimumSizePercent);
+                minHeight = (int) (minWidth / aspectRatio);
+            } else {
+                minHeight = (int) Math.min(getDefaultSize(aspectRatio).getHeight(),
+                        shorterLength * mMinimumSizePercent);
+                minWidth = (int) (minHeight * aspectRatio);
+            }
+
+            return new Size(minWidth, minHeight);
+        }
+
+        @Override
+        public Size getSizeForAspectRatio(Size size, float aspectRatio) {
+            final int smallestSize = Math.min(size.getWidth(), size.getHeight());
+            final int minSize = Math.max(getMinEdgeSize(), smallestSize);
+
+            final int width;
+            final int height;
+            if (aspectRatio <= 1) {
+                // Portrait, width is the minimum size.
+                width = minSize;
+                height = Math.round(width / aspectRatio);
+            } else {
+                // Landscape, height is the minimum size
+                height = minSize;
+                width = Math.round(height * aspectRatio);
+            }
+
+            return new Size(width, height);
+        }
+    }
+
+    public PipSizeSpecHandler(Context context) {
+        mContext = context;
+
+        boolean enablePipSizeLargeScreen = SystemProperties
+                .getBoolean("persist.wm.debug.enable_pip_size_large_screen", false);
+
+        // choose between two implementations of size spec logic
+        if (enablePipSizeLargeScreen) {
+            mSizeSpecSourceImpl = new SizeSpecLargeScreenOptimizedImpl();
+        } else {
+            mSizeSpecSourceImpl = new SizeSpecDefaultImpl();
+        }
+
+        reloadResources();
+    }
+
+    /** Reloads the resources */
+    public void onConfigurationChanged() {
+        reloadResources();
+    }
+
+    private void reloadResources() {
+        final Resources res = mContext.getResources();
+
+        mDefaultMinSize = res.getDimensionPixelSize(
+                R.dimen.default_minimal_size_pip_resizable_task);
+        mOverridableMinSize = res.getDimensionPixelSize(
+                R.dimen.overridable_minimal_size_pip_resizable_task);
+
+        final String screenEdgeInsetsDpString = res.getString(
+                R.string.config_defaultPictureInPictureScreenEdgeInsets);
+        final Size screenEdgeInsetsDp = !screenEdgeInsetsDpString.isEmpty()
+                ? Size.parseSize(screenEdgeInsetsDpString)
+                : null;
+        mScreenEdgeInsets = screenEdgeInsetsDp == null ? new Point()
+                : new Point(dpToPx(screenEdgeInsetsDp.getWidth(), res.getDisplayMetrics()),
+                        dpToPx(screenEdgeInsetsDp.getHeight(), res.getDisplayMetrics()));
+
+        // update the internal resources of the size spec source's stub
+        mSizeSpecSourceImpl.reloadResources();
+    }
+
+    /** Returns the display's bounds. */
+    @NonNull
+    public Rect getDisplayBounds() {
+        return new Rect(0, 0, mDisplayLayout.width(), mDisplayLayout.height());
+    }
+
+    /** Update the display layout. */
+    public void setDisplayLayout(@NonNull DisplayLayout displayLayout) {
+        mDisplayLayout.set(displayLayout);
+    }
+
+    public Point getScreenEdgeInsets() {
+        return mScreenEdgeInsets;
+    }
+
+    /**
+     * Returns the inset bounds the PIP window can be visible in.
+     */
+    public Rect getInsetBounds() {
+        Rect insetBounds = new Rect();
+        Rect insets = mDisplayLayout.stableInsets();
+        insetBounds.set(insets.left + mScreenEdgeInsets.x,
+                insets.top + mScreenEdgeInsets.y,
+                mDisplayLayout.width() - insets.right - mScreenEdgeInsets.x,
+                mDisplayLayout.height() - insets.bottom - mScreenEdgeInsets.y);
+        return insetBounds;
+    }
+
+    /** Sets the preferred size of PIP as specified by the activity in PIP mode. */
+    public void setOverrideMinSize(@Nullable Size overrideMinSize) {
+        mOverrideMinSize = overrideMinSize;
+    }
+
+    /** Returns the preferred minimal size specified by the activity in PIP. */
+    @Nullable
+    public Size getOverrideMinSize() {
+        if (mOverrideMinSize != null
+                && (mOverrideMinSize.getWidth() < mOverridableMinSize
+                || mOverrideMinSize.getHeight() < mOverridableMinSize)) {
+            return new Size(mOverridableMinSize, mOverridableMinSize);
+        }
+
+        return mOverrideMinSize;
+    }
+
+    /** Returns the minimum edge size of the override minimum size, or 0 if not set. */
+    public int getOverrideMinEdgeSize() {
+        if (mOverrideMinSize == null) return 0;
+        return Math.min(getOverrideMinSize().getWidth(), getOverrideMinSize().getHeight());
+    }
+
+    public int getMinEdgeSize() {
+        return mOverrideMinSize == null ? mDefaultMinSize : getOverrideMinEdgeSize();
+    }
+
+    /**
+     * Returns the size for the max size spec.
+     */
+    public Size getMaxSize(float aspectRatio) {
+        return mSizeSpecSourceImpl.getMaxSize(aspectRatio);
+    }
+
+    /**
+     * Returns the size for the default size spec.
+     */
+    public Size getDefaultSize(float aspectRatio) {
+        return mSizeSpecSourceImpl.getDefaultSize(aspectRatio);
+    }
+
+    /**
+     * Returns the size for the min size spec.
+     */
+    public Size getMinSize(float aspectRatio) {
+        return mSizeSpecSourceImpl.getMinSize(aspectRatio);
+    }
+
+    /**
+     * Returns the adjusted size so that it conforms to the given aspectRatio.
+     *
+     * @param size current size
+     * @param aspectRatio target aspect ratio
+     */
+    public Size getSizeForAspectRatio(@NonNull Size size, float aspectRatio) {
+        if (size.equals(mOverrideMinSize)) {
+            return adjustOverrideMinSizeToAspectRatio(aspectRatio);
+        }
+
+        return mSizeSpecSourceImpl.getSizeForAspectRatio(size, aspectRatio);
+    }
+
+    /**
+     * Returns the adjusted overridden min size if it is set; otherwise, returns null.
+     *
+     * <p>Overridden min size needs to be adjusted in its own way while making sure that the target
+     * aspect ratio is maintained
+     *
+     * @param aspectRatio target aspect ratio
+     */
+    @Nullable
+    @VisibleForTesting
+    Size adjustOverrideMinSizeToAspectRatio(float aspectRatio) {
+        if (mOverrideMinSize == null) {
+            return null;
+        }
+        final Size size = getOverrideMinSize();
+        final float sizeAspectRatio = size.getWidth() / (float) size.getHeight();
+        if (sizeAspectRatio > aspectRatio) {
+            // Size is wider, fix the width and increase the height
+            return new Size(size.getWidth(), (int) (size.getWidth() / aspectRatio));
+        } else {
+            // Size is taller, fix the height and adjust the width.
+            return new Size((int) (size.getHeight() * aspectRatio), size.getHeight());
+        }
+    }
+
+    /** Dumps internal state. */
+    public void dump(PrintWriter pw, String prefix) {
+        final String innerPrefix = prefix + "  ";
+        pw.println(prefix + TAG);
+        pw.println(innerPrefix + "mSizeSpecSourceImpl=" + mSizeSpecSourceImpl.toString());
+        pw.println(innerPrefix + "mDisplayLayout=" + mDisplayLayout);
+        pw.println(innerPrefix + "mOverrideMinSize=" + mOverrideMinSize);
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 850c561..0e8d13d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -78,7 +78,8 @@
     private boolean mEnableResize;
     private final Context mContext;
     private final PipBoundsAlgorithm mPipBoundsAlgorithm;
-    private final @NonNull PipBoundsState mPipBoundsState;
+    @NonNull private final PipBoundsState mPipBoundsState;
+    @NonNull private final PipSizeSpecHandler mPipSizeSpecHandler;
     private final PipUiEventLogger mPipUiEventLogger;
     private final PipDismissTargetHandler mPipDismissTargetHandler;
     private final PipTaskOrganizer mPipTaskOrganizer;
@@ -99,7 +100,6 @@
 
     // The reference inset bounds, used to determine the dismiss fraction
     private final Rect mInsetBounds = new Rect();
-    private int mExpandedShortestEdgeSize;
 
     // Used to workaround an issue where the WM rotation happens before we are notified, allowing
     // us to send stale bounds
@@ -120,7 +120,6 @@
     private float mSavedSnapFraction = -1f;
     private boolean mSendingHoverAccessibilityEvents;
     private boolean mMovementWithinDismiss;
-    private float mMinimumSizePercent;
 
     // Touch state
     private final PipTouchState mTouchState;
@@ -174,6 +173,7 @@
             PhonePipMenuController menuController,
             PipBoundsAlgorithm pipBoundsAlgorithm,
             @NonNull PipBoundsState pipBoundsState,
+            @NonNull PipSizeSpecHandler pipSizeSpecHandler,
             PipTaskOrganizer pipTaskOrganizer,
             PipMotionHelper pipMotionHelper,
             FloatingContentCoordinator floatingContentCoordinator,
@@ -184,6 +184,7 @@
         mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
         mPipBoundsAlgorithm = pipBoundsAlgorithm;
         mPipBoundsState = pipBoundsState;
+        mPipSizeSpecHandler = pipSizeSpecHandler;
         mPipTaskOrganizer = pipTaskOrganizer;
         mMenuController = menuController;
         mPipUiEventLogger = pipUiEventLogger;
@@ -271,10 +272,7 @@
     private void reloadResources() {
         final Resources res = mContext.getResources();
         mBottomOffsetBufferPx = res.getDimensionPixelSize(R.dimen.pip_bottom_offset_buffer);
-        mExpandedShortestEdgeSize = res.getDimensionPixelSize(
-                R.dimen.pip_expanded_shortest_edge_size);
         mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset);
-        mMinimumSizePercent = res.getFraction(R.fraction.config_pipShortestEdgePercent, 1, 1);
         mPipDismissTargetHandler.updateMagneticTargetSize();
     }
 
@@ -407,10 +405,7 @@
 
         // Calculate the expanded size
         float aspectRatio = (float) normalBounds.width() / normalBounds.height();
-        Point displaySize = new Point();
-        mContext.getDisplay().getRealSize(displaySize);
-        Size expandedSize = mPipBoundsAlgorithm.getSizeForAspectRatio(
-                aspectRatio, mExpandedShortestEdgeSize, displaySize.x, displaySize.y);
+        Size expandedSize = mPipSizeSpecHandler.getDefaultSize(aspectRatio);
         mPipBoundsState.setExpandedBounds(
                 new Rect(0, 0, expandedSize.getWidth(), expandedSize.getHeight()));
         Rect expandedMovementBounds = new Rect();
@@ -418,7 +413,7 @@
                 mPipBoundsState.getExpandedBounds(), insetBounds, expandedMovementBounds,
                 bottomOffset);
 
-        updatePipSizeConstraints(insetBounds, normalBounds, aspectRatio);
+        updatePipSizeConstraints(normalBounds, aspectRatio);
 
         // The extra offset does not really affect the movement bounds, but are applied based on the
         // current state (ime showing, or shelf offset) when we need to actually shift
@@ -496,14 +491,14 @@
      * @param aspectRatio aspect ratio to use for the calculation of min/max size
      */
     public void updateMinMaxSize(float aspectRatio) {
-        updatePipSizeConstraints(mInsetBounds, mPipBoundsState.getNormalBounds(),
+        updatePipSizeConstraints(mPipBoundsState.getNormalBounds(),
                 aspectRatio);
     }
 
-    private void updatePipSizeConstraints(Rect insetBounds, Rect normalBounds,
+    private void updatePipSizeConstraints(Rect normalBounds,
             float aspectRatio) {
         if (mPipResizeGestureHandler.isUsingPinchToZoom()) {
-            updatePinchResizeSizeConstraints(insetBounds, normalBounds, aspectRatio);
+            updatePinchResizeSizeConstraints(aspectRatio);
         } else {
             mPipResizeGestureHandler.updateMinSize(normalBounds.width(), normalBounds.height());
             mPipResizeGestureHandler.updateMaxSize(mPipBoundsState.getExpandedBounds().width(),
@@ -511,26 +506,13 @@
         }
     }
 
-    private void updatePinchResizeSizeConstraints(Rect insetBounds, Rect normalBounds,
-            float aspectRatio) {
-        final int shorterLength = Math.min(mPipBoundsState.getDisplayBounds().width(),
-                mPipBoundsState.getDisplayBounds().height());
-        final int totalHorizontalPadding = insetBounds.left
-                + (mPipBoundsState.getDisplayBounds().width() - insetBounds.right);
-        final int totalVerticalPadding = insetBounds.top
-                + (mPipBoundsState.getDisplayBounds().height() - insetBounds.bottom);
+    private void updatePinchResizeSizeConstraints(float aspectRatio) {
         final int minWidth, minHeight, maxWidth, maxHeight;
-        if (aspectRatio > 1f) {
-            minWidth = (int) Math.min(normalBounds.width(), shorterLength * mMinimumSizePercent);
-            minHeight = (int) (minWidth / aspectRatio);
-            maxWidth = (int) Math.max(normalBounds.width(), shorterLength - totalHorizontalPadding);
-            maxHeight = (int) (maxWidth / aspectRatio);
-        } else {
-            minHeight = (int) Math.min(normalBounds.height(), shorterLength * mMinimumSizePercent);
-            minWidth = (int) (minHeight * aspectRatio);
-            maxHeight = (int) Math.max(normalBounds.height(), shorterLength - totalVerticalPadding);
-            maxWidth = (int) (maxHeight * aspectRatio);
-        }
+
+        minWidth = mPipSizeSpecHandler.getMinSize(aspectRatio).getWidth();
+        minHeight = mPipSizeSpecHandler.getMinSize(aspectRatio).getHeight();
+        maxWidth = mPipSizeSpecHandler.getMaxSize(aspectRatio).getWidth();
+        maxHeight = mPipSizeSpecHandler.getMaxSize(aspectRatio).getHeight();
 
         mPipResizeGestureHandler.updateMinSize(minWidth, minHeight);
         mPipResizeGestureHandler.updateMaxSize(maxWidth, maxHeight);
@@ -1064,11 +1046,6 @@
         mPipBoundsAlgorithm.getMovementBounds(mPipBoundsState.getBounds(),
                 mInsetBounds, mPipBoundsState.getMovementBounds(), mIsImeShowing ? mImeHeight : 0);
         mMotionHelper.onMovementBoundsChanged();
-
-        boolean isMenuExpanded = mMenuState == MENU_STATE_FULL;
-        mPipBoundsState.setMinEdgeSize(
-                isMenuExpanded && willResizeMenu() ? mExpandedShortestEdgeSize
-                        : mPipBoundsAlgorithm.getDefaultMinSize());
     }
 
     private Rect getMovementBounds(Rect curBounds) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
index 1ff77f7..22feb43 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
@@ -41,6 +41,7 @@
 import com.android.wm.shell.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.pip.PipKeepClearAlgorithmInterface;
 import com.android.wm.shell.pip.PipSnapAlgorithm;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
 import com.android.wm.shell.pip.tv.TvPipKeepClearAlgorithm.Placement;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 
@@ -63,9 +64,10 @@
 
     public TvPipBoundsAlgorithm(Context context,
             @NonNull TvPipBoundsState tvPipBoundsState,
-            @NonNull PipSnapAlgorithm pipSnapAlgorithm) {
+            @NonNull PipSnapAlgorithm pipSnapAlgorithm,
+            @NonNull PipSizeSpecHandler pipSizeSpecHandler) {
         super(context, tvPipBoundsState, pipSnapAlgorithm,
-                new PipKeepClearAlgorithmInterface() {});
+                new PipKeepClearAlgorithmInterface() {}, pipSizeSpecHandler);
         this.mTvPipBoundsState = tvPipBoundsState;
         this.mKeepClearAlgorithm = new TvPipKeepClearAlgorithm();
         reloadResources(context);
@@ -370,7 +372,8 @@
             if (mTvPipBoundsState.getTvFixedPipOrientation() == ORIENTATION_HORIZONTAL) {
                 expandedSize = mTvPipBoundsState.getTvExpandedSize();
             } else {
-                int maxHeight = displayLayout.height() - (2 * mScreenEdgeInsets.y)
+                int maxHeight = displayLayout.height()
+                        - (2 * mPipSizeSpecHandler.getScreenEdgeInsets().y)
                         - pipDecorations.top - pipDecorations.bottom;
                 float aspectRatioHeight = mFixedExpandedWidthInPx / expandedRatio;
 
@@ -393,7 +396,8 @@
             if (mTvPipBoundsState.getTvFixedPipOrientation() == ORIENTATION_VERTICAL) {
                 expandedSize = mTvPipBoundsState.getTvExpandedSize();
             } else {
-                int maxWidth = displayLayout.width() - (2 * mScreenEdgeInsets.x)
+                int maxWidth = displayLayout.width()
+                        - (2 * mPipSizeSpecHandler.getScreenEdgeInsets().x)
                         - pipDecorations.left - pipDecorations.right;
                 float aspectRatioWidth = mFixedExpandedHeightInPx * expandedRatio;
                 if (maxWidth > aspectRatioWidth) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
index ca22882..4e3ee51 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
@@ -30,6 +30,7 @@
 
 import com.android.wm.shell.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -64,8 +65,9 @@
     private @NonNull Insets mPipMenuPermanentDecorInsets = Insets.NONE;
     private @NonNull Insets mPipMenuTemporaryDecorInsets = Insets.NONE;
 
-    public TvPipBoundsState(@NonNull Context context) {
-        super(context);
+    public TvPipBoundsState(@NonNull Context context,
+            @NonNull PipSizeSpecHandler pipSizeSpecHandler) {
+        super(context, pipSizeSpecHandler);
         mIsTvExpandedPipSupported = context.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_EXPANDED_PICTURE_IN_PICTURE);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 4e1b046..6bc666f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -50,6 +50,7 @@
 import com.android.wm.shell.pip.PipParamsChangedForwarder;
 import com.android.wm.shell.pip.PipTaskOrganizer;
 import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.sysui.ConfigurationChangeListener;
 import com.android.wm.shell.sysui.ShellController;
@@ -102,6 +103,7 @@
 
     private final ShellController mShellController;
     private final TvPipBoundsState mTvPipBoundsState;
+    private final PipSizeSpecHandler mPipSizeSpecHandler;
     private final TvPipBoundsAlgorithm mTvPipBoundsAlgorithm;
     private final TvPipBoundsController mTvPipBoundsController;
     private final PipAppOpsListener mAppOpsListener;
@@ -133,6 +135,7 @@
             ShellInit shellInit,
             ShellController shellController,
             TvPipBoundsState tvPipBoundsState,
+            PipSizeSpecHandler pipSizeSpecHandler,
             TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
             TvPipBoundsController tvPipBoundsController,
             PipAppOpsListener pipAppOpsListener,
@@ -151,6 +154,7 @@
                 shellInit,
                 shellController,
                 tvPipBoundsState,
+                pipSizeSpecHandler,
                 tvPipBoundsAlgorithm,
                 tvPipBoundsController,
                 pipAppOpsListener,
@@ -171,6 +175,7 @@
             ShellInit shellInit,
             ShellController shellController,
             TvPipBoundsState tvPipBoundsState,
+            PipSizeSpecHandler pipSizeSpecHandler,
             TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
             TvPipBoundsController tvPipBoundsController,
             PipAppOpsListener pipAppOpsListener,
@@ -189,9 +194,13 @@
         mShellController = shellController;
         mDisplayController = displayController;
 
+        DisplayLayout layout = new DisplayLayout(context, context.getDisplay());
+
         mTvPipBoundsState = tvPipBoundsState;
+        mTvPipBoundsState.setDisplayLayout(layout);
         mTvPipBoundsState.setDisplayId(context.getDisplayId());
-        mTvPipBoundsState.setDisplayLayout(new DisplayLayout(context, context.getDisplay()));
+        mPipSizeSpecHandler = pipSizeSpecHandler;
+        mPipSizeSpecHandler.setDisplayLayout(layout);
         mTvPipBoundsAlgorithm = tvPipBoundsAlgorithm;
         mTvPipBoundsController = tvPipBoundsController;
         mTvPipBoundsController.setListener(this);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
index 42fd1aa..be9b936 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
@@ -36,6 +36,7 @@
 import com.android.wm.shell.pip.PipTransitionState;
 import com.android.wm.shell.pip.PipUiEventLogger;
 import com.android.wm.shell.pip.PipUtils;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 
 import java.util.Objects;
@@ -50,6 +51,7 @@
             @NonNull SyncTransactionQueue syncTransactionQueue,
             @NonNull PipTransitionState pipTransitionState,
             @NonNull PipBoundsState pipBoundsState,
+            @NonNull PipSizeSpecHandler pipSizeSpecHandler,
             @NonNull PipBoundsAlgorithm boundsHandler,
             @NonNull PipMenuController pipMenuController,
             @NonNull PipAnimationController pipAnimationController,
@@ -61,10 +63,11 @@
             @NonNull PipUiEventLogger pipUiEventLogger,
             @NonNull ShellTaskOrganizer shellTaskOrganizer,
             ShellExecutor mainExecutor) {
-        super(context, syncTransactionQueue, pipTransitionState, pipBoundsState, boundsHandler,
-                pipMenuController, pipAnimationController, surfaceTransactionHelper,
-                pipTransitionController, pipParamsChangedForwarder, splitScreenOptional,
-                displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
+        super(context, syncTransactionQueue, pipTransitionState, pipBoundsState, pipSizeSpecHandler,
+                boundsHandler, pipMenuController, pipAnimationController,
+                surfaceTransactionHelper, pipTransitionController, pipParamsChangedForwarder,
+                splitScreenOptional, displayController, pipUiEventLogger, shellTaskOrganizer,
+                mainExecutor);
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java
index 93ffb3d..c59c42d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java
@@ -31,8 +31,7 @@
  */
 public class ShellProtoLogImpl extends BaseProtoLogImpl {
     private static final String TAG = "ProtoLogImpl";
-    private static final int BUFFER_CAPACITY = 1024 * 1024;
-    // TODO: find a proper location to save the protolog message file
+    private static final int BUFFER_CAPACITY = 128 * 1024;
     private static final String LOG_FILENAME = "/data/misc/wmtrace/shell_log.winscope";
     private static final String VIEWER_CONFIG_FILENAME = "/system_ext/etc/wmshell.protolog.json.gz";
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 8490f9f..0d9faa3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -308,7 +308,6 @@
             rawMapping.put(taskInfo.taskId, taskInfo);
         }
 
-        boolean desktopModeActive = DesktopModeStatus.isActive(mContext);
         ArrayList<ActivityManager.RecentTaskInfo> freeformTasks = new ArrayList<>();
 
         // Pull out the pairs as we iterate back in the list
@@ -320,7 +319,7 @@
                 continue;
             }
 
-            if (desktopModeActive && mDesktopModeTaskRepository.isPresent()
+            if (DesktopModeStatus.isProto2Enabled() && mDesktopModeTaskRepository.isPresent()
                     && mDesktopModeTaskRepository.get().isActiveTask(taskInfo.taskId)) {
                 // Freeform tasks will be added as a separate entry
                 freeformTasks.add(taskInfo);
@@ -328,7 +327,7 @@
             }
 
             final int pairedTaskId = mSplitTasks.get(taskInfo.taskId);
-            if (!desktopModeActive && pairedTaskId != INVALID_TASK_ID && rawMapping.contains(
+            if (pairedTaskId != INVALID_TASK_ID && rawMapping.contains(
                     pairedTaskId)) {
                 final ActivityManager.RecentTaskInfo pairedTaskInfo = rawMapping.get(pairedTaskId);
                 rawMapping.remove(pairedTaskId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
index 56aa742..81e118a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
@@ -122,9 +122,9 @@
      * Start a pair of intents using legacy transition system.
      */
     oneway void startIntentsWithLegacyTransition(in PendingIntent pendingIntent1,
-            in Bundle options1, in PendingIntent pendingIntent2, in Bundle options2,
-            int splitPosition, float splitRatio, in RemoteAnimationAdapter adapter,
-            in InstanceId instanceId) = 18;
+            in ShortcutInfo shortcutInfo1, in Bundle options1, in PendingIntent pendingIntent2,
+            in ShortcutInfo shortcutInfo2, in Bundle options2, int splitPosition, float splitRatio,
+            in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 18;
 
     /**
      * Start a pair of intents in one transition.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 21eeaa2..c7ad4fd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -28,6 +28,9 @@
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
+import static com.android.wm.shell.common.split.SplitScreenUtils.isValidToSplit;
+import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition;
+import static com.android.wm.shell.common.split.SplitScreenUtils.samePackage;
 import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
 import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SPLIT_SCREEN;
@@ -39,11 +42,9 @@
 import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
 import android.app.PendingIntent;
-import android.content.ActivityNotFoundException;
-import android.content.ComponentName;
+import android.app.TaskInfo;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.LauncherApps;
 import android.content.pm.ShortcutInfo;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -82,8 +83,8 @@
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.common.annotations.ExternalThread;
-import com.android.wm.shell.common.split.SplitLayout;
 import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
+import com.android.wm.shell.common.split.SplitScreenUtils;
 import com.android.wm.shell.draganddrop.DragAndDropController;
 import com.android.wm.shell.draganddrop.DragAndDropPolicy;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
@@ -318,10 +319,6 @@
         return mStageCoordinator;
     }
 
-    public boolean isValidToEnterSplitScreen(@NonNull ActivityManager.RunningTaskInfo taskInfo) {
-        return mStageCoordinator.isValidToEnterSplitScreen(taskInfo);
-    }
-
     @Nullable
     public ActivityManager.RunningTaskInfo getTaskInfo(@SplitPosition int splitPosition) {
         if (!isSplitScreenVisible() || splitPosition == SPLIT_POSITION_UNDEFINED) {
@@ -425,6 +422,14 @@
         mStageCoordinator.goToFullscreenFromSplit();
     }
 
+    public boolean isLaunchToSplit(TaskInfo taskInfo) {
+        return mStageCoordinator.isLaunchToSplit(taskInfo);
+    }
+
+    public int getActivateSplitPosition(TaskInfo taskInfo) {
+        return mStageCoordinator.getActivateSplitPosition(taskInfo);
+    }
+
     public void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options) {
         final int[] result = new int[1];
         IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
@@ -480,39 +485,54 @@
     @Override
     public void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
             @Nullable Bundle options, UserHandle user) {
-        IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
-            @Override
-            public void onAnimationStart(@WindowManager.TransitionOldType int transit,
-                    RemoteAnimationTarget[] apps,
-                    RemoteAnimationTarget[] wallpapers,
-                    RemoteAnimationTarget[] nonApps,
-                    final IRemoteAnimationFinishedCallback finishedCallback) {
-                try {
-                    finishedCallback.onAnimationFinished();
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Failed to invoke onAnimationFinished", e);
-                }
-                final WindowContainerTransaction evictWct = new WindowContainerTransaction();
-                mStageCoordinator.prepareEvictNonOpeningChildTasks(position, apps, evictWct);
-                mSyncQueue.queue(evictWct);
+        if (options == null) options = new Bundle();
+        final ActivityOptions activityOptions = ActivityOptions.fromBundle(options);
+
+        if (samePackage(packageName, getPackageName(reverseSplitPosition(position)))) {
+            if (supportMultiInstancesSplit(packageName)) {
+                activityOptions.setApplyMultipleTaskFlagForShortcut(true);
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
+            } else if (isSplitScreenVisible()) {
+                mStageCoordinator.switchSplitPosition("startShortcut");
+                return;
+            } else {
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+                        "Cancel entering split as not supporting multi-instances");
+                Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
+                        Toast.LENGTH_SHORT).show();
+                return;
             }
-            @Override
-            public void onAnimationCancelled(boolean isKeyguardOccluded) {
-            }
-        };
-        options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
-                null /* wct */);
-        RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(wrapper,
-                0 /* duration */, 0 /* statusBarTransitionDelay */);
-        ActivityOptions activityOptions = ActivityOptions.fromBundle(options);
-        activityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
-        try {
-            LauncherApps launcherApps = mContext.getSystemService(LauncherApps.class);
-            launcherApps.startShortcut(packageName, shortcutId, null /* sourceBounds */,
-                    activityOptions.toBundle(), user);
-        } catch (ActivityNotFoundException e) {
-            Slog.e(TAG, "Failed to launch shortcut", e);
         }
+
+        mStageCoordinator.startShortcut(packageName, shortcutId, position,
+                activityOptions.toBundle(), user);
+    }
+
+    void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo,
+            @Nullable Bundle options1, int taskId, @Nullable Bundle options2,
+            @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
+            InstanceId instanceId) {
+        if (options1 == null) options1 = new Bundle();
+        final ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
+
+        final String packageName1 = shortcutInfo.getPackage();
+        final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
+        if (samePackage(packageName1, packageName2)) {
+            if (supportMultiInstancesSplit(shortcutInfo.getPackage())) {
+                activityOptions.setApplyMultipleTaskFlagForShortcut(true);
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
+            } else {
+                taskId = INVALID_TASK_ID;
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+                        "Cancel entering split as not supporting multi-instances");
+                Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
+                        Toast.LENGTH_SHORT).show();
+            }
+        }
+
+        mStageCoordinator.startShortcutAndTaskWithLegacyTransition(shortcutInfo,
+                activityOptions.toBundle(), taskId, options2, splitPosition, splitRatio, adapter,
+                instanceId);
     }
 
     /**
@@ -530,8 +550,10 @@
             @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
             InstanceId instanceId) {
         Intent fillInIntent = null;
-        if (launchSameAppAdjacently(pendingIntent, taskId)) {
-            if (supportMultiInstancesSplit(pendingIntent.getIntent().getComponent())) {
+        final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent);
+        final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
+        if (samePackage(packageName1, packageName2)) {
+            if (supportMultiInstancesSplit(packageName1)) {
                 fillInIntent = new Intent();
                 fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
@@ -551,8 +573,10 @@
             int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
             float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
         Intent fillInIntent = null;
-        if (launchSameAppAdjacently(pendingIntent, taskId)) {
-            if (supportMultiInstancesSplit(pendingIntent.getIntent().getComponent())) {
+        final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent);
+        final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
+        if (samePackage(packageName1, packageName2)) {
+            if (supportMultiInstancesSplit(packageName1)) {
                 fillInIntent = new Intent();
                 fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
@@ -568,13 +592,16 @@
     }
 
     private void startIntentsWithLegacyTransition(PendingIntent pendingIntent1,
-            @Nullable Bundle options1, PendingIntent pendingIntent2,
-            @Nullable Bundle options2, @SplitPosition int splitPosition,
-            float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) {
+            @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
+            PendingIntent pendingIntent2, @Nullable ShortcutInfo shortcutInfo2,
+            @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
+            RemoteAnimationAdapter adapter, InstanceId instanceId) {
         Intent fillInIntent1 = null;
         Intent fillInIntent2 = null;
-        if (launchSameAppAdjacently(pendingIntent1, pendingIntent2)) {
-            if (supportMultiInstancesSplit(pendingIntent1.getIntent().getComponent())) {
+        final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1);
+        final String packageName2 = SplitScreenUtils.getPackageName(pendingIntent2);
+        if (samePackage(packageName1, packageName2)) {
+            if (supportMultiInstancesSplit(packageName1)) {
                 fillInIntent1 = new Intent();
                 fillInIntent1.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
                 fillInIntent2 = new Intent();
@@ -588,9 +615,9 @@
                         Toast.LENGTH_SHORT).show();
             }
         }
-        mStageCoordinator.startIntentsWithLegacyTransition(pendingIntent1, fillInIntent1, options1,
-                pendingIntent2, fillInIntent2, options2, splitPosition, splitRatio, adapter,
-                instanceId);
+        mStageCoordinator.startIntentsWithLegacyTransition(pendingIntent1, fillInIntent1,
+                shortcutInfo1, options1, pendingIntent2, fillInIntent2, shortcutInfo2, options2,
+                splitPosition, splitRatio, adapter, instanceId);
     }
 
     @Override
@@ -602,13 +629,15 @@
         if (fillInIntent == null) fillInIntent = new Intent();
         fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION);
 
-        if (launchSameAppAdjacently(position, intent)) {
-            final ComponentName launching = intent.getIntent().getComponent();
-            if (supportMultiInstancesSplit(launching)) {
+        final String packageName1 = SplitScreenUtils.getPackageName(intent);
+        final String packageName2 = getPackageName(reverseSplitPosition(position));
+        if (SplitScreenUtils.samePackage(packageName1, packageName2)) {
+            if (supportMultiInstancesSplit(packageName1)) {
                 // To prevent accumulating large number of instances in the background, reuse task
                 // in the background with priority.
                 final ActivityManager.RecentTaskInfo taskInfo = mRecentTasksOptional
-                        .map(recentTasks -> recentTasks.findTaskInBackground(launching))
+                        .map(recentTasks -> recentTasks.findTaskInBackground(
+                                intent.getIntent().getComponent()))
                         .orElse(null);
                 if (taskInfo != null) {
                     startTask(taskInfo.taskId, position, options);
@@ -636,63 +665,32 @@
         mStageCoordinator.startIntent(intent, fillInIntent, position, options);
     }
 
+    /** Retrieve package name of a specific split position if split screen is activated, otherwise
+     *  returns the package name of the top running task. */
     @Nullable
-    private String getPackageName(Intent intent) {
-        if (intent == null || intent.getComponent() == null) {
-            return null;
-        }
-        return intent.getComponent().getPackageName();
-    }
-
-    private boolean launchSameAppAdjacently(@SplitPosition int position,
-            PendingIntent pendingIntent) {
-        ActivityManager.RunningTaskInfo adjacentTaskInfo = null;
+    private String getPackageName(@SplitPosition int position) {
+        ActivityManager.RunningTaskInfo taskInfo;
         if (isSplitScreenVisible()) {
-            adjacentTaskInfo = getTaskInfo(SplitLayout.reversePosition(position));
+            taskInfo = getTaskInfo(position);
         } else {
-            adjacentTaskInfo = mRecentTasksOptional
-                    .map(recentTasks -> recentTasks.getTopRunningTask()).orElse(null);
-            if (!isValidToEnterSplitScreen(adjacentTaskInfo)) {
-                return false;
+            taskInfo = mRecentTasksOptional
+                    .map(recentTasks -> recentTasks.getTopRunningTask())
+                    .orElse(null);
+            if (!isValidToSplit(taskInfo)) {
+                return null;
             }
         }
 
-        if (adjacentTaskInfo == null) {
-            return false;
-        }
-
-        final String targetPackageName = getPackageName(pendingIntent.getIntent());
-        final String adjacentPackageName = getPackageName(adjacentTaskInfo.baseIntent);
-        return targetPackageName != null && targetPackageName.equals(adjacentPackageName);
-    }
-
-    private boolean launchSameAppAdjacently(PendingIntent pendingIntent, int taskId) {
-        final ActivityManager.RunningTaskInfo adjacentTaskInfo =
-                mTaskOrganizer.getRunningTaskInfo(taskId);
-        if (adjacentTaskInfo == null) {
-            return false;
-        }
-        final String targetPackageName = getPackageName(pendingIntent.getIntent());
-        final String adjacentPackageName = getPackageName(adjacentTaskInfo.baseIntent);
-        return targetPackageName != null && targetPackageName.equals(adjacentPackageName);
-    }
-
-    private boolean launchSameAppAdjacently(PendingIntent pendingIntent1,
-            PendingIntent pendingIntent2) {
-        final String targetPackageName = getPackageName(pendingIntent1.getIntent());
-        final String adjacentPackageName = getPackageName(pendingIntent2.getIntent());
-        return targetPackageName != null && targetPackageName.equals(adjacentPackageName);
+        return taskInfo != null ? SplitScreenUtils.getPackageName(taskInfo.baseIntent) : null;
     }
 
     @VisibleForTesting
-    /** Returns {@code true} if the component supports multi-instances split. */
-    boolean supportMultiInstancesSplit(@Nullable ComponentName launching) {
-        if (launching == null) return false;
-
-        final String packageName = launching.getPackageName();
-        for (int i = 0; i < mAppsSupportMultiInstances.length; i++) {
-            if (mAppsSupportMultiInstances[i].equals(packageName)) {
-                return true;
+    boolean supportMultiInstancesSplit(String packageName) {
+        if (packageName != null) {
+            for (int i = 0; i < mAppsSupportMultiInstances.length; i++) {
+                if (mAppsSupportMultiInstances[i].equals(packageName)) {
+                    return true;
+                }
             }
         }
 
@@ -1011,7 +1009,7 @@
                 InstanceId instanceId) {
             executeRemoteCallWithTaskPermission(mController,
                     "startShortcutAndTaskWithLegacyTransition", (controller) ->
-                            controller.mStageCoordinator.startShortcutAndTaskWithLegacyTransition(
+                            controller.startShortcutAndTaskWithLegacyTransition(
                                     shortcutInfo, options1, taskId, options2, splitPosition,
                                     splitRatio, adapter, instanceId));
         }
@@ -1049,13 +1047,14 @@
 
         @Override
         public void startIntentsWithLegacyTransition(PendingIntent pendingIntent1,
-                @Nullable Bundle options1, PendingIntent pendingIntent2, @Nullable Bundle options2,
-                @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
-                InstanceId instanceId) {
+                @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
+                PendingIntent pendingIntent2, @Nullable ShortcutInfo shortcutInfo2,
+                @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
+                RemoteAnimationAdapter adapter, InstanceId instanceId) {
             executeRemoteCallWithTaskPermission(mController, "startIntentsWithLegacyTransition",
                     (controller) ->
-                        controller.startIntentsWithLegacyTransition(
-                                pendingIntent1, options1, pendingIntent2, options2, splitPosition,
+                        controller.startIntentsWithLegacyTransition(pendingIntent1, shortcutInfo1,
+                                options1, pendingIntent2, shortcutInfo2, options2, splitPosition,
                                 splitRatio, adapter, instanceId)
                     );
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 39cf5f1..2a6fbd2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -36,12 +36,11 @@
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
 
 import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER;
-import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
-import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES;
 import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
+import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition;
 import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
 import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
 import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
@@ -75,9 +74,12 @@
 import android.app.ActivityOptions;
 import android.app.IActivityTaskManager;
 import android.app.PendingIntent;
+import android.app.TaskInfo;
 import android.app.WindowConfiguration;
+import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.LauncherApps;
 import android.content.pm.ShortcutInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -87,6 +89,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.util.Log;
 import android.util.Slog;
 import android.view.Choreographer;
@@ -122,6 +125,7 @@
 import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.common.split.SplitLayout;
 import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
+import com.android.wm.shell.common.split.SplitScreenUtils;
 import com.android.wm.shell.common.split.SplitWindowManager;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.recents.RecentTasksController;
@@ -206,6 +210,36 @@
 
     private DefaultMixedHandler mMixedHandler;
     private final Toast mSplitUnsupportedToast;
+    private SplitRequest mSplitRequest;
+
+    class SplitRequest {
+        @SplitPosition
+        int mActivatePosition;
+        int mActivateTaskId;
+        int mActivateTaskId2;
+        Intent mStartIntent;
+        Intent mStartIntent2;
+
+        SplitRequest(int taskId, Intent startIntent, int position) {
+            mActivateTaskId = taskId;
+            mStartIntent = startIntent;
+            mActivatePosition = position;
+        }
+        SplitRequest(Intent startIntent, int position) {
+            mStartIntent = startIntent;
+            mActivatePosition = position;
+        }
+        SplitRequest(Intent startIntent, Intent startIntent2, int position) {
+            mStartIntent = startIntent;
+            mStartIntent2 = startIntent2;
+            mActivatePosition = position;
+        }
+        SplitRequest(int taskId1, int taskId2, int position) {
+            mActivateTaskId = taskId1;
+            mActivateTaskId2 = taskId2;
+            mActivatePosition = position;
+        }
+    }
 
     private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks =
             new SplitWindowManager.ParentContainerCallbacks() {
@@ -370,7 +404,7 @@
         int sideStagePosition;
         if (stageType == STAGE_TYPE_MAIN) {
             targetStage = mMainStage;
-            sideStagePosition = SplitLayout.reversePosition(stagePosition);
+            sideStagePosition = reverseSplitPosition(stagePosition);
         } else if (stageType == STAGE_TYPE_SIDE) {
             targetStage = mSideStage;
             sideStagePosition = stagePosition;
@@ -391,6 +425,23 @@
         setSideStagePosition(sideStagePosition, wct);
         final WindowContainerTransaction evictWct = new WindowContainerTransaction();
         targetStage.evictAllChildren(evictWct);
+
+        // Apply surface bounds before animation start.
+        SurfaceControl.Transaction startT = mTransactionPool.acquire();
+        if (startT != null) {
+            updateSurfaceBounds(mSplitLayout, startT, false /* applyResizingOffset */);
+            startT.apply();
+            mTransactionPool.release(startT);
+        }
+        // reparent the task to an invisible split root will make the activity invisible.  Reorder
+        // the root task to front to make the entering transition from pip to split smooth.
+        wct.reorder(mRootTaskInfo.token, true);
+        wct.setForceTranslucent(mRootTaskInfo.token, true);
+        wct.reorder(targetStage.mRootTaskInfo.token, true);
+        wct.setForceTranslucent(targetStage.mRootTaskInfo.token, true);
+        // prevent the fling divider to center transition
+        mIsDropEntering = true;
+
         targetStage.addTask(task, wct);
 
         if (ENABLE_SHELL_TRANSITIONS) {
@@ -428,6 +479,81 @@
         return mLogger;
     }
 
+    void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
+            Bundle options, UserHandle user) {
+        final boolean isEnteringSplit = !isSplitActive();
+
+        IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
+            @Override
+            public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+                    RemoteAnimationTarget[] apps,
+                    RemoteAnimationTarget[] wallpapers,
+                    RemoteAnimationTarget[] nonApps,
+                    final IRemoteAnimationFinishedCallback finishedCallback) {
+                boolean openingToSide = false;
+                if (apps != null) {
+                    for (int i = 0; i < apps.length; ++i) {
+                        if (apps[i].mode == MODE_OPENING
+                                && mSideStage.containsTask(apps[i].taskId)) {
+                            openingToSide = true;
+                            break;
+                        }
+                    }
+                } else if (mSideStage.getChildCount() != 0) {
+                    // There are chances the entering app transition got canceled by performing
+                    // rotation transition. Checks if there is any child task existed in split
+                    // screen before fallback to cancel entering flow.
+                    openingToSide = true;
+                }
+
+                if (isEnteringSplit && !openingToSide) {
+                    mMainExecutor.execute(() -> exitSplitScreen(
+                            mSideStage.getChildCount() == 0 ? mMainStage : mSideStage,
+                            EXIT_REASON_UNKNOWN));
+                }
+
+                if (finishedCallback != null) {
+                    try {
+                        finishedCallback.onAnimationFinished();
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "Error finishing legacy transition: ", e);
+                    }
+                }
+
+                if (!isEnteringSplit && apps != null) {
+                    final WindowContainerTransaction evictWct = new WindowContainerTransaction();
+                    prepareEvictNonOpeningChildTasks(position, apps, evictWct);
+                    mSyncQueue.queue(evictWct);
+                }
+            }
+            @Override
+            public void onAnimationCancelled(boolean isKeyguardOccluded) {
+                if (isEnteringSplit) {
+                    mMainExecutor.execute(() -> exitSplitScreen(
+                            mSideStage.getChildCount() == 0 ? mMainStage : mSideStage,
+                            EXIT_REASON_UNKNOWN));
+                }
+            }
+        };
+        options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
+                null /* wct */);
+        RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(wrapper,
+                0 /* duration */, 0 /* statusBarTransitionDelay */);
+        ActivityOptions activityOptions = ActivityOptions.fromBundle(options);
+        // Flag this as a no-user-action launch to prevent sending user leaving event to the current
+        // top activity since it's going to be put into another side of the split. This prevents the
+        // current top activity from going into pip mode due to user leaving event.
+        activityOptions.setApplyNoUserActionFlagForShortcut(true);
+        activityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
+        try {
+            LauncherApps launcherApps = mContext.getSystemService(LauncherApps.class);
+            launcherApps.startShortcut(packageName, shortcutId, null /* sourceBounds */,
+                    activityOptions.toBundle(), user);
+        } catch (ActivityNotFoundException e) {
+            Slog.e(TAG, "Failed to launch shortcut", e);
+        }
+    }
+
     /** Launches an activity into split. */
     void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position,
             @Nullable Bundle options) {
@@ -477,9 +603,14 @@
                             break;
                         }
                     }
+                } else if (mSideStage.getChildCount() != 0) {
+                    // There are chances the entering app transition got canceled by performing
+                    // rotation transition. Checks if there is any child task existed in split
+                    // screen before fallback to cancel entering flow.
+                    openingToSide = true;
                 }
 
-                if (isEnteringSplit && !openingToSide) {
+                if (isEnteringSplit && !openingToSide && apps != null) {
                     mMainExecutor.execute(() -> exitSplitScreen(
                             mSideStage.getChildCount() == 0 ? mMainStage : mSideStage,
                             EXIT_REASON_UNKNOWN));
@@ -503,7 +634,7 @@
                 }
 
 
-                if (!isEnteringSplit && openingToSide) {
+                if (!isEnteringSplit && apps != null) {
                     final WindowContainerTransaction evictWct = new WindowContainerTransaction();
                     prepareEvictNonOpeningChildTasks(position, apps, evictWct);
                     mSyncQueue.queue(evictWct);
@@ -519,7 +650,7 @@
         if (isEnteringSplit && mLogger.isEnterRequestedByDrag()) {
             updateWindowBounds(mSplitLayout, wct);
         }
-
+        mSplitRequest = new SplitRequest(intent.getIntent(), position);
         wct.sendPendingIntent(intent, fillInIntent, options);
         mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
     }
@@ -618,15 +749,18 @@
 
         addActivityOptions(options1, mSideStage);
         wct.startTask(taskId1, options1);
+        mSplitRequest = new SplitRequest(taskId1, taskId2, splitPosition);
         startWithLegacyTransition(wct, taskId2, options2, splitPosition, splitRatio, adapter,
                 instanceId);
     }
 
     /** Starts a pair of intents using legacy transition. */
     void startIntentsWithLegacyTransition(PendingIntent pendingIntent1, Intent fillInIntent1,
-            @Nullable Bundle options1, PendingIntent pendingIntent2, Intent fillInIntent2,
-            @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
-            RemoteAnimationAdapter adapter, InstanceId instanceId) {
+            @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
+            @Nullable PendingIntent pendingIntent2, Intent fillInIntent2,
+            @Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2,
+            @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
+            InstanceId instanceId) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         if (options1 == null) options1 = new Bundle();
         if (pendingIntent2 == null) {
@@ -635,15 +769,25 @@
             activityOptions.update(ActivityOptions.makeRemoteAnimation(adapter));
             options1 = activityOptions.toBundle();
             addActivityOptions(options1, null /* launchTarget */);
-            wct.sendPendingIntent(pendingIntent1, fillInIntent1, options1);
+            if (shortcutInfo1 != null) {
+                wct.startShortcut(mContext.getPackageName(), shortcutInfo1, options1);
+            } else {
+                wct.sendPendingIntent(pendingIntent1, fillInIntent1, options1);
+            }
             mSyncQueue.queue(wct);
             return;
         }
 
         addActivityOptions(options1, mSideStage);
-        wct.sendPendingIntent(pendingIntent1, fillInIntent1, options1);
-        startWithLegacyTransition(wct, pendingIntent2, fillInIntent2, options2, splitPosition,
-                splitRatio, adapter, instanceId);
+        if (shortcutInfo1 != null) {
+            wct.startShortcut(mContext.getPackageName(), shortcutInfo1, options1);
+        } else {
+            wct.sendPendingIntent(pendingIntent1, fillInIntent1, options1);
+            mSplitRequest = new SplitRequest(pendingIntent1.getIntent(),
+                    pendingIntent2 != null ? pendingIntent2.getIntent() : null, splitPosition);
+        }
+        startWithLegacyTransition(wct, pendingIntent2, fillInIntent2, shortcutInfo2, options2,
+                splitPosition, splitRatio, adapter, instanceId);
     }
 
     void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, Intent fillInIntent,
@@ -665,6 +809,7 @@
 
         addActivityOptions(options1, mSideStage);
         wct.sendPendingIntent(pendingIntent, fillInIntent, options1);
+        mSplitRequest = new SplitRequest(taskId, pendingIntent.getIntent(), splitPosition);
         startWithLegacyTransition(wct, taskId, options2, splitPosition, splitRatio, adapter,
                 instanceId);
     }
@@ -695,18 +840,19 @@
 
     private void startWithLegacyTransition(WindowContainerTransaction wct,
             @Nullable PendingIntent mainPendingIntent, @Nullable Intent mainFillInIntent,
-            @Nullable Bundle mainOptions, @SplitPosition int sidePosition, float splitRatio,
-            RemoteAnimationAdapter adapter, InstanceId instanceId) {
+            @Nullable ShortcutInfo mainShortcutInfo, @Nullable Bundle mainOptions,
+            @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter,
+            InstanceId instanceId) {
         startWithLegacyTransition(wct, INVALID_TASK_ID, mainPendingIntent, mainFillInIntent,
-                mainOptions, sidePosition, splitRatio, adapter, instanceId);
+                mainShortcutInfo, mainOptions, sidePosition, splitRatio, adapter, instanceId);
     }
 
     private void startWithLegacyTransition(WindowContainerTransaction wct, int mainTaskId,
             @Nullable Bundle mainOptions, @SplitPosition int sidePosition, float splitRatio,
             RemoteAnimationAdapter adapter, InstanceId instanceId) {
         startWithLegacyTransition(wct, mainTaskId, null /* mainPendingIntent */,
-                null /* mainFillInIntent */, mainOptions, sidePosition, splitRatio, adapter,
-                instanceId);
+                null /* mainFillInIntent */, null /* mainShortcutInfo */, mainOptions, sidePosition,
+                splitRatio, adapter, instanceId);
     }
 
     /**
@@ -716,8 +862,9 @@
      */
     private void startWithLegacyTransition(WindowContainerTransaction wct, int mainTaskId,
             @Nullable PendingIntent mainPendingIntent, @Nullable Intent mainFillInIntent,
-            @Nullable Bundle mainOptions, @SplitPosition int sidePosition, float splitRatio,
-            RemoteAnimationAdapter adapter, InstanceId instanceId) {
+            @Nullable ShortcutInfo mainShortcutInfo, @Nullable Bundle options,
+            @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter,
+            InstanceId instanceId) {
         if (!isSplitScreenVisible()) {
             exitSplitScreen(null /* childrenToTop */, EXIT_REASON_RECREATE_SPLIT);
         }
@@ -735,21 +882,29 @@
         // Set false to avoid record new bounds with old task still on top;
         mShouldUpdateRecents = false;
         mIsSplitEntering = true;
-
+        if (mSplitRequest == null) {
+            mSplitRequest = new SplitRequest(mainTaskId,
+                    mainPendingIntent != null ? mainPendingIntent.getIntent() : null,
+                    sidePosition);
+        }
         setSideStagePosition(sidePosition, wct);
         if (!mMainStage.isActive()) {
             mMainStage.activate(wct, false /* reparent */);
         }
 
-        if (mainOptions == null) mainOptions = new Bundle();
-        addActivityOptions(mainOptions, mMainStage);
-        mainOptions = wrapAsSplitRemoteAnimation(adapter, mainOptions);
+        if (options == null) options = new Bundle();
+        addActivityOptions(options, mMainStage);
+        options = wrapAsSplitRemoteAnimation(adapter, options);
 
         updateWindowBounds(mSplitLayout, wct);
-        if (mainTaskId == INVALID_TASK_ID) {
-            wct.sendPendingIntent(mainPendingIntent, mainFillInIntent, mainOptions);
+
+        // TODO(b/268008375): Merge APIs to start a split pair into one.
+        if (mainTaskId != INVALID_TASK_ID) {
+            wct.startTask(mainTaskId, options);
+        } else if (mainShortcutInfo != null) {
+            wct.startShortcut(mContext.getPackageName(), mainShortcutInfo, options);
         } else {
-            wct.startTask(mainTaskId, mainOptions);
+            wct.sendPendingIntent(mainPendingIntent, mainFillInIntent, options);
         }
 
         wct.reorder(mRootTaskInfo.token, true);
@@ -822,6 +977,7 @@
             WindowContainerTransaction evictWct) {
         mIsSplitEntering = false;
         mShouldUpdateRecents = true;
+        mSplitRequest = null;
         // If any stage has no child after animation finished, it means that split will display
         // nothing, such status will happen if task and intent is same app but not support
         // multi-instance, we should exit split and expand that app as full screen.
@@ -899,7 +1055,7 @@
             case STAGE_TYPE_MAIN: {
                 if (position != SPLIT_POSITION_UNDEFINED) {
                     // Set the side stage opposite of what we want to the main stage.
-                    setSideStagePosition(SplitLayout.reversePosition(position), wct);
+                    setSideStagePosition(reverseSplitPosition(position), wct);
                 } else {
                     position = getMainStagePosition();
                 }
@@ -923,7 +1079,7 @@
 
     @SplitPosition
     int getMainStagePosition() {
-        return SplitLayout.reversePosition(mSideStagePosition);
+        return reverseSplitPosition(mSideStagePosition);
     }
 
     int getTaskId(@SplitPosition int splitPosition) {
@@ -950,7 +1106,7 @@
         mSplitLayout.splitSwitching(t, topLeftStage.mRootLeash, bottomRightStage.mRootLeash,
                 insets -> {
                     WindowContainerTransaction wct = new WindowContainerTransaction();
-                    setSideStagePosition(SplitLayout.reversePosition(mSideStagePosition), wct);
+                    setSideStagePosition(reverseSplitPosition(mSideStagePosition), wct);
                     mSyncQueue.queue(wct);
                     mSyncQueue.runInSync(st -> {
                         updateSurfaceBounds(mSplitLayout, st, false /* applyResizingOffset */);
@@ -1080,8 +1236,8 @@
             // Notify recents if we are exiting in a way that breaks the pair, and disable further
             // updates to splits in the recents until we enter split again
             if (shouldBreakPairedTaskInRecents(exitReason) && mShouldUpdateRecents) {
-                recentTasks.removeSplitPair(mMainStage.getTopVisibleChildTaskId());
-                recentTasks.removeSplitPair(mSideStage.getTopVisibleChildTaskId());
+                recentTasks.removeSplitPair(mMainStage.getLastVisibleTaskId());
+                recentTasks.removeSplitPair(mSideStage.getLastVisibleTaskId());
             }
         });
         mShouldUpdateRecents = false;
@@ -1655,7 +1811,7 @@
             mSplitLayout.init();
 
             final WindowContainerTransaction wct = new WindowContainerTransaction();
-            if (mLogger.isEnterRequestedByDrag()) {
+            if (mIsDropEntering) {
                 prepareEnterSplitScreen(wct);
             } else {
                 // TODO (b/238697912) : Add the validation to prevent entering non-recovered status
@@ -1680,6 +1836,7 @@
         }
         if (mMainStageListener.mHasChildren && mSideStageListener.mHasChildren) {
             mShouldUpdateRecents = true;
+            mSplitRequest = null;
             updateRecentTasksSplitPair();
 
             if (!mLogger.hasStartedSession()) {
@@ -1694,12 +1851,6 @@
         }
     }
 
-    boolean isValidToEnterSplitScreen(@NonNull ActivityManager.RunningTaskInfo taskInfo) {
-        return taskInfo.supportsMultiWindow
-                && ArrayUtils.contains(CONTROLLED_ACTIVITY_TYPES, taskInfo.getActivityType())
-                && ArrayUtils.contains(CONTROLLED_WINDOWING_MODES, taskInfo.getWindowingMode());
-    }
-
     @Override
     public void onSnappedToDismiss(boolean bottomOrRight, int reason) {
         final boolean mainStageToTop =
@@ -2238,6 +2389,33 @@
         mSplitLayout.flingDividerToDismiss(!leftOrTop, EXIT_REASON_FULLSCREEN_SHORTCUT);
     }
 
+    boolean isLaunchToSplit(TaskInfo taskInfo) {
+        return getActivateSplitPosition(taskInfo) != SPLIT_POSITION_UNDEFINED;
+    }
+
+    int getActivateSplitPosition(TaskInfo taskInfo) {
+        if (mSplitRequest == null || taskInfo == null) {
+            return SPLIT_POSITION_UNDEFINED;
+        }
+        if (mSplitRequest.mActivateTaskId != 0
+                && mSplitRequest.mActivateTaskId2 == taskInfo.taskId) {
+            return mSplitRequest.mActivatePosition;
+        }
+        if (mSplitRequest.mActivateTaskId == taskInfo.taskId) {
+            return mSplitRequest.mActivatePosition;
+        }
+        final String packageName1 = SplitScreenUtils.getPackageName(mSplitRequest.mStartIntent);
+        final String basePackageName = SplitScreenUtils.getPackageName(taskInfo.baseIntent);
+        if (packageName1 != null && packageName1.equals(basePackageName)) {
+            return mSplitRequest.mActivatePosition;
+        }
+        final String packageName2 = SplitScreenUtils.getPackageName(mSplitRequest.mStartIntent2);
+        if (packageName2 != null && packageName2.equals(basePackageName)) {
+            return mSplitRequest.mActivatePosition;
+        }
+        return SPLIT_POSITION_UNDEFINED;
+    }
+
     /** Synchronize split-screen state with transition and make appropriate preparations. */
     public void prepareDismissAnimation(@StageType int toStage, @ExitReason int dismissReason,
             @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index a841b7f..0359761 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -92,6 +92,7 @@
     protected SurfaceControl mDimLayer;
     protected SparseArray<ActivityManager.RunningTaskInfo> mChildrenTaskInfo = new SparseArray<>();
     private final SparseArray<SurfaceControl> mChildrenLeashes = new SparseArray<>();
+    private int mLastVisibleTaskId = INVALID_TASK_ID;
     // TODO(b/204308910): Extracts SplitDecorManager related code to common package.
     private SplitDecorManager mSplitDecorManager;
 
@@ -123,6 +124,13 @@
     }
 
     /**
+     * Returns the last visible task's id.
+     */
+    int getLastVisibleTaskId() {
+        return mLastVisibleTaskId;
+    }
+
+    /**
      * Returns the top visible child task's id.
      */
     int getTopVisibleChildTaskId() {
@@ -221,6 +229,9 @@
                 return;
             }
             mChildrenTaskInfo.put(taskInfo.taskId, taskInfo);
+            if (taskInfo.isVisible && taskInfo.taskId != mLastVisibleTaskId) {
+                mLastVisibleTaskId = taskInfo.taskId;
+            }
             mCallbacks.onChildTaskStatusChanged(taskInfo.taskId, true /* present */,
                     taskInfo.isVisible);
             if (!ENABLE_SHELL_TRANSITIONS) {
@@ -253,6 +264,9 @@
         } else if (mChildrenTaskInfo.contains(taskId)) {
             mChildrenTaskInfo.remove(taskId);
             mChildrenLeashes.remove(taskId);
+            if (taskId == mLastVisibleTaskId) {
+                mLastVisibleTaskId = INVALID_TASK_ID;
+            }
             mCallbacks.onChildTaskStatusChanged(taskId, false /* present */, taskInfo.isVisible);
             if (ENABLE_SHELL_TRANSITIONS) {
                 // Status is managed/synchronized by the transition lifecycle.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 053491e..22e8045 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -430,7 +430,8 @@
         }
 
         @Override
-        public @Nullable SplashScreenView get() {
+        @Nullable
+        public SplashScreenView get() {
             synchronized (this) {
                 while (!mIsViewSet) {
                     try {
@@ -691,7 +692,7 @@
         private final TaskSnapshotWindow mTaskSnapshotWindow;
         private SplashScreenView mContentView;
         private boolean mSetSplashScreen;
-        private @StartingWindowType int mSuggestType;
+        @StartingWindowType private int mSuggestType;
         private int mBGColor;
         private final long mCreateTime;
         private int mSystemBarAppearance;
@@ -732,7 +733,7 @@
 
         // Reset the system bar color which set by splash screen, make it align to the app.
         private void clearSystemBarColor() {
-            if (mDecorView == null) {
+            if (mDecorView == null || !mDecorView.isAttachedToWindow()) {
                 return;
             }
             if (mDecorView.getLayoutParams() instanceof WindowManager.LayoutParams) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/FullscreenUnfoldTaskAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/FullscreenUnfoldTaskAnimator.java
index eab82f0..e0f3fcd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/FullscreenUnfoldTaskAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/FullscreenUnfoldTaskAnimator.java
@@ -26,8 +26,10 @@
 import android.annotation.NonNull;
 import android.app.TaskInfo;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.graphics.Matrix;
 import android.graphics.Rect;
+import android.os.Trace;
 import android.util.SparseArray;
 import android.view.InsetsSource;
 import android.view.InsetsState;
@@ -36,6 +38,8 @@
 
 import com.android.internal.policy.ScreenDecorationsUtils;
 import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.sysui.ConfigurationChangeListener;
+import com.android.wm.shell.sysui.ShellController;
 import com.android.wm.shell.unfold.UnfoldAnimationController;
 import com.android.wm.shell.unfold.UnfoldBackgroundController;
 
@@ -51,7 +55,7 @@
  * instances of FullscreenUnfoldTaskAnimator.
  */
 public class FullscreenUnfoldTaskAnimator implements UnfoldTaskAnimator,
-        DisplayInsetsController.OnInsetsChangedListener {
+        DisplayInsetsController.OnInsetsChangedListener, ConfigurationChangeListener {
 
     private static final float[] FLOAT_9 = new float[9];
     private static final TypeEvaluator<Rect> RECT_EVALUATOR = new RectEvaluator(new Rect());
@@ -63,17 +67,21 @@
 
     private final SparseArray<AnimationContext> mAnimationContextByTaskId = new SparseArray<>();
     private final int mExpandedTaskBarHeight;
-    private final float mWindowCornerRadiusPx;
     private final DisplayInsetsController mDisplayInsetsController;
     private final UnfoldBackgroundController mBackgroundController;
+    private final Context mContext;
+    private final ShellController mShellController;
 
     private InsetsSource mTaskbarInsetsSource;
+    private float mWindowCornerRadiusPx;
 
     public FullscreenUnfoldTaskAnimator(Context context,
             @NonNull UnfoldBackgroundController backgroundController,
-            DisplayInsetsController displayInsetsController) {
+            ShellController shellController, DisplayInsetsController displayInsetsController) {
+        mContext = context;
         mDisplayInsetsController = displayInsetsController;
         mBackgroundController = backgroundController;
+        mShellController = shellController;
         mExpandedTaskBarHeight = context.getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.taskbar_frame_height);
         mWindowCornerRadiusPx = ScreenDecorationsUtils.getWindowCornerRadius(context);
@@ -81,6 +89,14 @@
 
     public void init() {
         mDisplayInsetsController.addInsetsChangedListener(DEFAULT_DISPLAY, this);
+        mShellController.addConfigurationChangeListener(this);
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfiguration) {
+        Trace.beginSection("FullscreenUnfoldTaskAnimator#onConfigurationChanged");
+        mWindowCornerRadiusPx = ScreenDecorationsUtils.getWindowCornerRadius(mContext);
+        Trace.endSection();
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java
index 6e10ebe..addd0a6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java
@@ -28,8 +28,10 @@
 import android.animation.TypeEvaluator;
 import android.app.TaskInfo;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.graphics.Insets;
 import android.graphics.Rect;
+import android.os.Trace;
 import android.util.SparseArray;
 import android.view.InsetsSource;
 import android.view.InsetsState;
@@ -42,6 +44,8 @@
 import com.android.wm.shell.splitscreen.SplitScreen;
 import com.android.wm.shell.splitscreen.SplitScreen.SplitScreenListener;
 import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.sysui.ConfigurationChangeListener;
+import com.android.wm.shell.sysui.ShellController;
 import com.android.wm.shell.unfold.UnfoldAnimationController;
 import com.android.wm.shell.unfold.UnfoldBackgroundController;
 
@@ -62,16 +66,18 @@
  * They use independent instances of SplitTaskUnfoldAnimator.
  */
 public class SplitTaskUnfoldAnimator implements UnfoldTaskAnimator,
-        DisplayInsetsController.OnInsetsChangedListener, SplitScreenListener {
+        DisplayInsetsController.OnInsetsChangedListener, SplitScreenListener,
+        ConfigurationChangeListener {
 
     private static final TypeEvaluator<Rect> RECT_EVALUATOR = new RectEvaluator(new Rect());
     private static final float CROPPING_START_MARGIN_FRACTION = 0.05f;
 
+    private final Context mContext;
     private final Executor mExecutor;
     private final DisplayInsetsController mDisplayInsetsController;
     private final SparseArray<AnimationContext> mAnimationContextByTaskId = new SparseArray<>();
     private final int mExpandedTaskBarHeight;
-    private final float mWindowCornerRadiusPx;
+    private final ShellController mShellController;
     private final Lazy<Optional<SplitScreenController>> mSplitScreenController;
     private final UnfoldBackgroundController mUnfoldBackgroundController;
 
@@ -79,6 +85,7 @@
     private final Rect mSideStageBounds = new Rect();
     private final Rect mRootStageBounds = new Rect();
 
+    private float mWindowCornerRadiusPx;
     private InsetsSource mTaskbarInsetsSource;
 
     @SplitPosition
@@ -88,10 +95,12 @@
 
     public SplitTaskUnfoldAnimator(Context context, Executor executor,
             Lazy<Optional<SplitScreenController>> splitScreenController,
-            UnfoldBackgroundController unfoldBackgroundController,
+            ShellController shellController, UnfoldBackgroundController unfoldBackgroundController,
             DisplayInsetsController displayInsetsController) {
         mDisplayInsetsController = displayInsetsController;
         mExecutor = executor;
+        mContext = context;
+        mShellController = shellController;
         mUnfoldBackgroundController = unfoldBackgroundController;
         mSplitScreenController = splitScreenController;
         mExpandedTaskBarHeight = context.getResources().getDimensionPixelSize(
@@ -103,6 +112,14 @@
     @Override
     public void init() {
         mDisplayInsetsController.addInsetsChangedListener(DEFAULT_DISPLAY, this);
+        mShellController.addConfigurationChangeListener(this);
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfiguration) {
+        Trace.beginSection("SplitTaskUnfoldAnimator#onConfigurationChanged");
+        mWindowCornerRadiusPx = ScreenDecorationsUtils.getWindowCornerRadius(mContext);
+        Trace.endSection();
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 129924a..2981f5e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -36,6 +36,7 @@
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
+import com.android.wm.shell.transition.Transitions;
 
 /**
  * View model for the window decoration with a caption and shadows. Works with
@@ -65,6 +66,9 @@
         mTaskOrganizer = taskOrganizer;
         mDisplayController = displayController;
         mSyncQueue = syncQueue;
+        if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
+            mTaskOperations = new TaskOperations(null, mContext, mSyncQueue);
+        }
     }
 
     @Override
@@ -143,8 +147,8 @@
     private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
         return taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
                 || (taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD
-                    && taskInfo.configuration.windowConfiguration.getDisplayWindowingMode()
-                        == WINDOWING_MODE_FREEFORM);
+                && taskInfo.configuration.windowConfiguration.getDisplayWindowingMode()
+                == WINDOWING_MODE_FREEFORM);
     }
 
     private void createWindowDecoration(
@@ -174,26 +178,29 @@
         final CaptionTouchEventListener touchEventListener =
                 new CaptionTouchEventListener(taskInfo, taskPositioner);
         windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
-        windowDecoration.setDragResizeCallback(taskPositioner);
+        windowDecoration.setDragPositioningCallback(taskPositioner);
+        windowDecoration.setDragDetector(touchEventListener.mDragDetector);
         windowDecoration.relayout(taskInfo, startT, finishT);
         setupCaptionColor(taskInfo, windowDecoration);
     }
 
     private class CaptionTouchEventListener implements
-            View.OnClickListener, View.OnTouchListener {
+            View.OnClickListener, View.OnTouchListener, DragDetector.MotionEventHandler {
 
         private final int mTaskId;
         private final WindowContainerToken mTaskToken;
-        private final DragResizeCallback mDragResizeCallback;
+        private final DragPositioningCallback mDragPositioningCallback;
+        private final DragDetector mDragDetector;
 
         private int mDragPointerId = -1;
 
         private CaptionTouchEventListener(
                 RunningTaskInfo taskInfo,
-                DragResizeCallback dragResizeCallback) {
+                DragPositioningCallback dragPositioningCallback) {
             mTaskId = taskInfo.taskId;
             mTaskToken = taskInfo.token;
-            mDragResizeCallback = dragResizeCallback;
+            mDragPositioningCallback = dragPositioningCallback;
+            mDragDetector = new DragDetector(this);
         }
 
         @Override
@@ -216,7 +223,7 @@
             if (v.getId() != R.id.caption) {
                 return false;
             }
-            handleEventForMove(e);
+            mDragDetector.onMotionEvent(e);
 
             if (e.getAction() != MotionEvent.ACTION_DOWN) {
                 return false;
@@ -235,32 +242,34 @@
          * @param e {@link MotionEvent} to process
          * @return {@code true} if a drag is happening; or {@code false} if it is not
          */
-        private void handleEventForMove(MotionEvent e) {
+        @Override
+        public boolean handleMotionEvent(MotionEvent e) {
             final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
             if (taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
-                return;
+                return false;
             }
             switch (e.getActionMasked()) {
                 case MotionEvent.ACTION_DOWN: {
                     mDragPointerId = e.getPointerId(0);
-                    mDragResizeCallback.onDragResizeStart(
+                    mDragPositioningCallback.onDragPositioningStart(
                             0 /* ctrlType */, e.getRawX(0), e.getRawY(0));
                     break;
                 }
                 case MotionEvent.ACTION_MOVE: {
                     int dragPointerIdx = e.findPointerIndex(mDragPointerId);
-                    mDragResizeCallback.onDragResizeMove(
+                    mDragPositioningCallback.onDragPositioningMove(
                             e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
                     break;
                 }
                 case MotionEvent.ACTION_UP:
                 case MotionEvent.ACTION_CANCEL: {
                     int dragPointerIdx = e.findPointerIndex(mDragPointerId);
-                    mDragResizeCallback.onDragResizeEnd(
+                    mDragPositioningCallback.onDragPositioningEnd(
                             e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
                     break;
                 }
             }
+            return true;
         }
     }
-}
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index d26f1fc..060dc4e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -47,9 +47,9 @@
 
     private View.OnClickListener mOnCaptionButtonClickListener;
     private View.OnTouchListener mOnCaptionTouchListener;
-    private DragResizeCallback mDragResizeCallback;
+    private DragPositioningCallback mDragPositioningCallback;
     private DragResizeInputListener mDragResizeListener;
-    private final DragDetector mDragDetector;
+    private DragDetector mDragDetector;
 
     private RelayoutParams mRelayoutParams = new RelayoutParams();
     private final RelayoutResult<WindowDecorLinearLayout> mResult =
@@ -69,7 +69,6 @@
         mHandler = handler;
         mChoreographer = choreographer;
         mSyncQueue = syncQueue;
-        mDragDetector = new DragDetector(ViewConfiguration.get(context).getScaledTouchSlop());
     }
 
     void setCaptionListeners(
@@ -79,8 +78,13 @@
         mOnCaptionTouchListener = onCaptionTouchListener;
     }
 
-    void setDragResizeCallback(DragResizeCallback dragResizeCallback) {
-        mDragResizeCallback = dragResizeCallback;
+    void setDragPositioningCallback(DragPositioningCallback dragPositioningCallback) {
+        mDragPositioningCallback = dragPositioningCallback;
+    }
+
+    void setDragDetector(DragDetector dragDetector) {
+        mDragDetector = dragDetector;
+        mDragDetector.setTouchSlop(ViewConfiguration.get(mContext).getScaledTouchSlop());
     }
 
     @Override
@@ -147,7 +151,7 @@
                     mChoreographer,
                     mDisplay.getDisplayId(),
                     mDecorationContainerSurface,
-                    mDragResizeCallback);
+                    mDragPositioningCallback);
         }
 
         final int touchSlop = ViewConfiguration.get(mResult.mRootView.getContext())
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 2863adc..de5f2f4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -156,6 +156,7 @@
         }
 
         decoration.relayout(taskInfo);
+        setupCaptionColor(taskInfo, decoration);
     }
 
     @Override
@@ -205,30 +206,29 @@
     }
 
     private class DesktopModeTouchEventListener implements
-            View.OnClickListener, View.OnTouchListener {
+            View.OnClickListener, View.OnTouchListener, DragDetector.MotionEventHandler {
 
         private final int mTaskId;
         private final WindowContainerToken mTaskToken;
-        private final DragResizeCallback mDragResizeCallback;
+        private final DragPositioningCallback mDragPositioningCallback;
         private final DragDetector mDragDetector;
 
         private int mDragPointerId = -1;
 
         private DesktopModeTouchEventListener(
                 RunningTaskInfo taskInfo,
-                DragResizeCallback dragResizeCallback,
-                DragDetector dragDetector) {
+                DragPositioningCallback dragPositioningCallback) {
             mTaskId = taskInfo.taskId;
             mTaskToken = taskInfo.token;
-            mDragResizeCallback = dragResizeCallback;
-            mDragDetector = dragDetector;
+            mDragPositioningCallback = dragPositioningCallback;
+            mDragDetector = new DragDetector(this);
         }
 
         @Override
         public void onClick(View v) {
             final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
             final int id = v.getId();
-            if (id == R.id.close_window) {
+            if (id == R.id.close_window || id == R.id.close_button) {
                 mTaskOperations.closeTask(mTaskToken);
             } else if (id == R.id.back_button) {
                 mTaskOperations.injectBackKey();
@@ -243,59 +243,65 @@
                 mDesktopTasksController.ifPresent(c -> c.moveToFullscreen(mTaskId));
                 decoration.closeHandleMenu();
                 decoration.setButtonVisibility(false);
+            } else if (id == R.id.collapse_menu_button) {
+                decoration.closeHandleMenu();
             }
         }
 
         @Override
         public boolean onTouch(View v, MotionEvent e) {
-            boolean isDrag = false;
             final int id = v.getId();
             if (id != R.id.caption_handle && id != R.id.desktop_mode_caption) {
                 return false;
             }
-            if (id == R.id.caption_handle) {
-                isDrag = mDragDetector.detectDragEvent(e);
-                handleEventForMove(e);
+            switch (e.getAction()) {
+                case MotionEvent.ACTION_DOWN:
+                    mDragDetector.onMotionEvent(e);
+                    final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
+                    if (taskInfo.isFocused) {
+                        return mDragDetector.isDragEvent();
+                    }
+                    final WindowContainerTransaction wct = new WindowContainerTransaction();
+                    wct.reorder(mTaskToken, true /* onTop */);
+                    mSyncQueue.queue(wct);
+                    return false;
+                case MotionEvent.ACTION_UP:
+                case MotionEvent.ACTION_CANCEL:
+                    boolean res = mDragDetector.isDragEvent();
+                    mDragDetector.onMotionEvent(e);
+                    return res;
+                default:
+                    mDragDetector.onMotionEvent(e);
+                    return mDragDetector.isDragEvent();
             }
-            if (e.getAction() != MotionEvent.ACTION_DOWN) {
-                return isDrag;
-            }
-            final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
-            if (taskInfo.isFocused) {
-                return isDrag;
-            }
-            final WindowContainerTransaction wct = new WindowContainerTransaction();
-            wct.reorder(mTaskToken, true /* onTop */);
-            mSyncQueue.queue(wct);
-            return true;
         }
 
         /**
          * @param e {@link MotionEvent} to process
-         * @return {@code true} if a drag is happening; or {@code false} if it is not
+         * @return {@code true} if the motion event is handled.
          */
-        private void handleEventForMove(MotionEvent e) {
+        @Override
+        public boolean handleMotionEvent(MotionEvent e) {
             final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
             if (DesktopModeStatus.isProto2Enabled()
                     && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
-                return;
+                return false;
             }
             if (DesktopModeStatus.isProto1Enabled() && mDesktopModeController.isPresent()
-                    && mDesktopModeController.get().getDisplayAreaWindowingMode(
-                    taskInfo.displayId)
+                    && mDesktopModeController.get().getDisplayAreaWindowingMode(taskInfo.displayId)
                     == WINDOWING_MODE_FULLSCREEN) {
-                return;
+                return false;
             }
             switch (e.getActionMasked()) {
                 case MotionEvent.ACTION_DOWN: {
                     mDragPointerId = e.getPointerId(0);
-                    mDragResizeCallback.onDragResizeStart(
+                    mDragPositioningCallback.onDragPositioningStart(
                             0 /* ctrlType */, e.getRawX(0), e.getRawY(0));
                     break;
                 }
                 case MotionEvent.ACTION_MOVE: {
                     final int dragPointerIdx = e.findPointerIndex(mDragPointerId);
-                    mDragResizeCallback.onDragResizeMove(
+                    mDragPositioningCallback.onDragPositioningMove(
                             e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
                     break;
                 }
@@ -304,26 +310,20 @@
                     final int dragPointerIdx = e.findPointerIndex(mDragPointerId);
                     final int statusBarHeight = mDisplayController
                             .getDisplayLayout(taskInfo.displayId).stableInsets().top;
-                    mDragResizeCallback.onDragResizeEnd(
+                    mDragPositioningCallback.onDragPositioningEnd(
                             e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
                     if (e.getRawY(dragPointerIdx) <= statusBarHeight) {
-                        if (DesktopModeStatus.isProto2Enabled()) {
-                            if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
-                                // Switch a single task to fullscreen
-                                mDesktopTasksController.ifPresent(
-                                        c -> c.moveToFullscreen(taskInfo));
-                            }
-                        } else if (DesktopModeStatus.isProto1Enabled()) {
-                            if (DesktopModeStatus.isActive(mContext)) {
-                                // Turn off desktop mode
-                                mDesktopModeController.ifPresent(
-                                        c -> c.setDesktopModeActive(false));
-                            }
+                        if (DesktopModeStatus.isProto2Enabled()
+                                && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
+                            // Switch a single task to fullscreen
+                            mDesktopTasksController.ifPresent(
+                                    c -> c.moveToFullscreen(taskInfo));
                         }
                     }
                     break;
                 }
             }
+            return true;
         }
     }
 
@@ -407,10 +407,6 @@
                     || focusedDecor.mTaskInfo.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
                 handleCaptionThroughStatusBar(ev);
             }
-        } else if (DesktopModeStatus.isProto1Enabled()) {
-            if (!DesktopModeStatus.isActive(mContext)) {
-                handleCaptionThroughStatusBar(ev);
-            }
         }
         handleEventOutsideFocusedCaption(ev);
         // Prevent status bar from reacting to a caption drag.
@@ -456,9 +452,6 @@
                         // In proto2 any full screen task can be dragged to freeform
                         dragFromStatusBarAllowed = focusedDecor.mTaskInfo.getWindowingMode()
                                 == WINDOWING_MODE_FULLSCREEN;
-                    } else if (DesktopModeStatus.isProto1Enabled()) {
-                        // In proto1 task can be dragged to freeform when not in desktop mode
-                        dragFromStatusBarAllowed = !DesktopModeStatus.isActive(mContext);
                     }
 
                     if (dragFromStatusBarAllowed && focusedDecor.checkTouchEventInHandle(ev)) {
@@ -527,9 +520,16 @@
         }
     }
 
+    private void setupCaptionColor(RunningTaskInfo taskInfo,
+            DesktopModeWindowDecoration decoration) {
+        if (taskInfo == null || taskInfo.taskDescription == null) return;
+        final int statusBarColor = taskInfo.taskDescription.getStatusBarColor();
+        decoration.setCaptionColor(statusBarColor);
+    }
+
     private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
         if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true;
-        return DesktopModeStatus.isAnyEnabled()
+        return DesktopModeStatus.isProto2Enabled()
                 && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD
                 && mDisplayController.getDisplayContext(taskInfo.displayId)
                 .getResources().getConfiguration().smallestScreenWidthDp >= 600;
@@ -560,11 +560,12 @@
         final TaskPositioner taskPositioner =
                 new TaskPositioner(mTaskOrganizer, windowDecoration, mDragStartListener);
         final DesktopModeTouchEventListener touchEventListener =
-                new DesktopModeTouchEventListener(
-                        taskInfo, taskPositioner, windowDecoration.getDragDetector());
+                new DesktopModeTouchEventListener(taskInfo, taskPositioner);
         windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
-        windowDecoration.setDragResizeCallback(taskPositioner);
+        windowDecoration.setDragPositioningCallback(taskPositioner);
+        windowDecoration.setDragDetector(touchEventListener.mDragDetector);
         windowDecoration.relayout(taskInfo, startT, finishT);
+        setupCaptionColor(taskInfo, windowDecoration);
         incrementEventReceiverTasks(taskInfo.displayId);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 1a38d24..245cc8d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -20,19 +20,25 @@
 
 import android.app.ActivityManager;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.Point;
 import android.graphics.PointF;
-import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.VectorDrawable;
 import android.os.Handler;
+import android.util.Log;
 import android.view.Choreographer;
 import android.view.MotionEvent;
 import android.view.SurfaceControl;
 import android.view.View;
 import android.view.ViewConfiguration;
+import android.widget.ImageView;
+import android.widget.TextView;
 import android.window.WindowContainerTransaction;
 
 import com.android.wm.shell.R;
@@ -49,22 +55,26 @@
  * The shadow's thickness is 20dp when the window is in focus and 5dp when the window isn't.
  */
 public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLinearLayout> {
+    private static final String TAG = "DesktopModeWindowDecoration";
     private final Handler mHandler;
     private final Choreographer mChoreographer;
     private final SyncTransactionQueue mSyncQueue;
 
     private View.OnClickListener mOnCaptionButtonClickListener;
     private View.OnTouchListener mOnCaptionTouchListener;
-    private DragResizeCallback mDragResizeCallback;
+    private DragPositioningCallback mDragPositioningCallback;
     private DragResizeInputListener mDragResizeListener;
-    private final DragDetector mDragDetector;
+    private DragDetector mDragDetector;
 
     private RelayoutParams mRelayoutParams = new RelayoutParams();
+    private final int mCaptionMenuHeightId = R.dimen.freeform_decor_caption_menu_height;
     private final WindowDecoration.RelayoutResult<WindowDecorLinearLayout> mResult =
             new WindowDecoration.RelayoutResult<>();
 
     private boolean mDesktopActive;
     private AdditionalWindow mHandleMenu;
+    private final int mHandleMenuWidthId = R.dimen.freeform_decor_caption_menu_width;
+    private PointF mHandleMenuPosition = new PointF();
 
     DesktopModeWindowDecoration(
             Context context,
@@ -81,7 +91,6 @@
         mChoreographer = choreographer;
         mSyncQueue = syncQueue;
         mDesktopActive = DesktopModeStatus.isActive(mContext);
-        mDragDetector = new DragDetector(ViewConfiguration.get(context).getScaledTouchSlop());
     }
 
     void setCaptionListeners(
@@ -91,12 +100,13 @@
         mOnCaptionTouchListener = onCaptionTouchListener;
     }
 
-    void setDragResizeCallback(DragResizeCallback dragResizeCallback) {
-        mDragResizeCallback = dragResizeCallback;
+    void setDragPositioningCallback(DragPositioningCallback dragPositioningCallback) {
+        mDragPositioningCallback = dragPositioningCallback;
     }
 
-    DragDetector getDragDetector() {
-        return mDragDetector;
+    void setDragDetector(DragDetector dragDetector) {
+        mDragDetector = dragDetector;
+        mDragDetector.setTouchSlop(ViewConfiguration.get(mContext).getScaledTouchSlop());
     }
 
     @Override
@@ -131,22 +141,10 @@
         mRelayoutParams.mRunningTaskInfo = taskInfo;
         mRelayoutParams.mLayoutResId = R.layout.desktop_mode_window_decor;
         mRelayoutParams.mCaptionHeightId = R.dimen.freeform_decor_caption_height;
-        mRelayoutParams.mCaptionWidthId = R.dimen.freeform_decor_caption_width;
         mRelayoutParams.mShadowRadiusId = shadowRadiusID;
         if (isDragResizeable) {
             mRelayoutParams.setOutsets(outsetLeftId, outsetTopId, outsetRightId, outsetBottomId);
         }
-        final Resources resources = mDecorWindowContext.getResources();
-        final Rect taskBounds = taskInfo.configuration.windowConfiguration.getBounds();
-        final int captionHeight = loadDimensionPixelSize(resources,
-                mRelayoutParams.mCaptionHeightId);
-        final int captionWidth = loadDimensionPixelSize(resources,
-                mRelayoutParams.mCaptionWidthId);
-        final int captionLeft = taskBounds.width() / 2
-                - captionWidth / 2;
-        final int captionTop = taskBounds.top
-                <= captionHeight / 2 ? 0 : -captionHeight / 2;
-        mRelayoutParams.setCaptionPosition(captionLeft, captionTop);
 
         relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
         // After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
@@ -191,7 +189,7 @@
                     mChoreographer,
                     mDisplay.getDisplayId(),
                     mDecorationContainerSurface,
-                    mDragResizeCallback);
+                    mDragPositioningCallback);
         }
 
         final int touchSlop = ViewConfiguration.get(mResult.mRootView.getContext())
@@ -212,10 +210,6 @@
     private void setupRootView() {
         final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
         caption.setOnTouchListener(mOnCaptionTouchListener);
-        final View close = caption.findViewById(R.id.close_window);
-        close.setOnClickListener(mOnCaptionButtonClickListener);
-        final View back = caption.findViewById(R.id.back_button);
-        back.setOnClickListener(mOnCaptionButtonClickListener);
         final View handle = caption.findViewById(R.id.caption_handle);
         handle.setOnTouchListener(mOnCaptionTouchListener);
         handle.setOnClickListener(mOnCaptionButtonClickListener);
@@ -227,23 +221,46 @@
         final View fullscreen = menu.findViewById(R.id.fullscreen_button);
         fullscreen.setOnClickListener(mOnCaptionButtonClickListener);
         final View desktop = menu.findViewById(R.id.desktop_button);
-        desktop.setOnClickListener(mOnCaptionButtonClickListener);
+        if (DesktopModeStatus.isProto2Enabled()) {
+            desktop.setOnClickListener(mOnCaptionButtonClickListener);
+        } else if (DesktopModeStatus.isProto1Enabled()) {
+            desktop.setVisibility(View.GONE);
+        }
         final View split = menu.findViewById(R.id.split_screen_button);
         split.setOnClickListener(mOnCaptionButtonClickListener);
-        final View more = menu.findViewById(R.id.more_button);
-        more.setOnClickListener(mOnCaptionButtonClickListener);
+        final View close = menu.findViewById(R.id.close_button);
+        close.setOnClickListener(mOnCaptionButtonClickListener);
+        final View collapse = menu.findViewById(R.id.collapse_menu_button);
+        collapse.setOnClickListener(mOnCaptionButtonClickListener);
+        menu.setOnTouchListener(mOnCaptionTouchListener);
+
+        String packageName = mTaskInfo.baseActivity.getPackageName();
+        PackageManager pm = mContext.getApplicationContext().getPackageManager();
+        // TODO(b/268363572): Use IconProvider or BaseIconCache to set drawable/name.
+        try {
+            ApplicationInfo applicationInfo = pm.getApplicationInfo(packageName,
+                    PackageManager.ApplicationInfoFlags.of(0));
+            final ImageView appIcon = menu.findViewById(R.id.application_icon);
+            appIcon.setImageDrawable(pm.getApplicationIcon(applicationInfo));
+            final TextView appName = menu.findViewById(R.id.application_name);
+            appName.setText(pm.getApplicationLabel(applicationInfo));
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.w(TAG, "Package not found: " + packageName, e);
+        }
     }
 
     /**
      * Sets caption visibility based on task focus.
-     *
+     * Note: Only applicable to Desktop Proto 1; Proto 2 only closes handle menu on focus loss
      * @param visible whether or not the caption should be visible
      */
     private void setCaptionVisibility(boolean visible) {
+        if (!visible) closeHandleMenu();
+        if (!DesktopModeStatus.isProto1Enabled()) return;
         final int v = visible ? View.VISIBLE : View.GONE;
         final View captionView = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
         captionView.setVisibility(v);
-        if (!visible) closeHandleMenu();
+
     }
 
     /**
@@ -262,7 +279,8 @@
      * Show or hide buttons
      */
     void setButtonVisibility(boolean visible) {
-        final int visibility = visible ? View.VISIBLE : View.GONE;
+        final int visibility = visible && DesktopModeStatus.isProto1Enabled()
+                ? View.VISIBLE : View.GONE;
         final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
         final View back = caption.findViewById(R.id.back_button);
         final View close = caption.findViewById(R.id.close_window);
@@ -276,13 +294,33 @@
         final View handle = caption.findViewById(R.id.caption_handle);
         final VectorDrawable handleBackground = (VectorDrawable) handle.getBackground();
         handleBackground.setTintList(buttonTintColor);
-        caption.getBackground().setTint(visible ? Color.WHITE : Color.TRANSPARENT);
     }
 
     boolean isHandleMenuActive() {
         return mHandleMenu != null;
     }
 
+    void setCaptionColor(int captionColor) {
+        if (mResult.mRootView == null) {
+            return;
+        }
+
+        final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
+        final GradientDrawable captionDrawable = (GradientDrawable) caption.getBackground();
+        captionDrawable.setColor(captionColor);
+
+        final int buttonTintColorRes =
+                Color.valueOf(captionColor).luminance() < 0.5
+                        ? R.color.decor_button_light_color
+                        : R.color.decor_button_dark_color;
+        final ColorStateList buttonTintColor =
+                caption.getResources().getColorStateList(buttonTintColorRes, null /* theme */);
+
+        final View handle = caption.findViewById(R.id.caption_handle);
+        final Drawable handleBackground = handle.getBackground();
+        handleBackground.setTintList(buttonTintColor);
+    }
+
     private void closeDragResizeListener() {
         if (mDragResizeListener == null) {
             return;
@@ -297,14 +335,23 @@
     void createHandleMenu() {
         final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
         final Resources resources = mDecorWindowContext.getResources();
-        final int x = mRelayoutParams.mCaptionX;
-        final int y = mRelayoutParams.mCaptionY;
-        final int width = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionWidthId);
-        final int height = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionHeightId);
+        final int captionWidth = mTaskInfo.getConfiguration()
+                .windowConfiguration.getBounds().width();
+        final int menuWidth = loadDimensionPixelSize(resources, mHandleMenuWidthId);
+        final int menuHeight = loadDimensionPixelSize(resources, mCaptionMenuHeightId);
+
+        // Elevation gives the appearance of a changed x/y coordinate; this is to fix that
+        int elevationOffset = 2 * loadDimensionPixelSize(resources,
+                R.dimen.caption_menu_elevation);
+
+        final int x = mRelayoutParams.mCaptionX + (captionWidth / 2) - (menuWidth / 2)
+                - mResult.mDecorContainerOffsetX - elevationOffset;
+        final int y =
+                mRelayoutParams.mCaptionY - mResult.mDecorContainerOffsetY - elevationOffset;
+        mHandleMenuPosition.set(x, y);
         String namePrefix = "Caption Menu";
-        mHandleMenu = addWindow(R.layout.desktop_mode_decor_handle_menu, namePrefix, t,
-                x - mResult.mDecorContainerOffsetX, y - mResult.mDecorContainerOffsetY,
-                width, height);
+        mHandleMenu = addWindow(R.layout.desktop_mode_decor_handle_menu, namePrefix, t, x, y,
+                menuWidth, menuHeight, 2 * elevationOffset);
         mSyncQueue.runInSync(transaction -> {
             transaction.merge(t);
             t.close();
@@ -333,10 +380,17 @@
      * @param ev the tapped point to compare against
      */
     void closeHandleMenuIfNeeded(MotionEvent ev) {
-        if (isHandleMenuActive()) {
-            if (!checkEventInCaptionView(ev, R.id.desktop_mode_caption)) {
-                closeHandleMenu();
-            }
+        if (!isHandleMenuActive()) return;
+
+        // When this is called before the layout is fully inflated, width will be 0.
+        // Menu is not visible in this scenario, so skip the check if that is the case.
+        if (mHandleMenu.mWindowViewHost.getView().getWidth() == 0) return;
+
+        PointF inputPoint = offsetCaptionLocation(ev);
+        if (!pointInView(mHandleMenu.mWindowViewHost.getView(),
+                inputPoint.x - mHandleMenuPosition.x - mResult.mDecorContainerOffsetX,
+                inputPoint.y - mHandleMenuPosition.y - mResult.mDecorContainerOffsetY)) {
+            closeHandleMenu();
         }
     }
 
@@ -370,7 +424,7 @@
         if (mResult.mRootView == null) return false;
         final PointF inputPoint = offsetCaptionLocation(ev);
         final View view = mResult.mRootView.findViewById(layoutId);
-        return view != null && view.pointInView(inputPoint.x, inputPoint.y, 0);
+        return view != null && pointInView(view, inputPoint.x, inputPoint.y);
     }
 
     boolean checkTouchEventInHandle(MotionEvent ev) {
@@ -387,32 +441,35 @@
      */
     void checkClickEvent(MotionEvent ev) {
         if (mResult.mRootView == null) return;
-        final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
-        final PointF inputPoint = offsetCaptionLocation(ev);
         if (!isHandleMenuActive()) {
+            final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
             final View handle = caption.findViewById(R.id.caption_handle);
-            clickIfPointInView(inputPoint, handle);
+            clickIfPointInView(new PointF(ev.getX(), ev.getY()), handle);
         } else {
             final View menu = mHandleMenu.mWindowViewHost.getView();
-            final View fullscreen = menu.findViewById(R.id.fullscreen_button);
-            if (clickIfPointInView(inputPoint, fullscreen)) return;
-            final View desktop = menu.findViewById(R.id.desktop_button);
-            if (clickIfPointInView(inputPoint, desktop)) return;
-            final View split = menu.findViewById(R.id.split_screen_button);
-            if (clickIfPointInView(inputPoint, split)) return;
-            final View more = menu.findViewById(R.id.more_button);
-            clickIfPointInView(inputPoint, more);
+            final int captionWidth = mTaskInfo.getConfiguration().windowConfiguration
+                    .getBounds().width();
+            final int menuX = mRelayoutParams.mCaptionX + (captionWidth / 2)
+                    - (menu.getWidth() / 2);
+            final PointF inputPoint = new PointF(ev.getX() - menuX, ev.getY());
+            final View collapse = menu.findViewById(R.id.collapse_menu_button);
+            if (clickIfPointInView(inputPoint, collapse)) return;
         }
     }
 
     private boolean clickIfPointInView(PointF inputPoint, View v) {
-        if (v.pointInView(inputPoint.x - v.getLeft(), inputPoint.y, 0)) {
+        if (pointInView(v, inputPoint.x, inputPoint.y)) {
             mOnCaptionButtonClickListener.onClick(v);
             return true;
         }
         return false;
     }
 
+    private boolean pointInView(View v, float x, float y) {
+        return v != null && v.getLeft() <= x && v.getRight() >= x
+                && v.getTop() <= y && v.getBottom() >= y;
+    }
+
     @Override
     public void close() {
         closeDragResizeListener();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
index 0abe8ab..cf1850b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.windowdecor;
 
+import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
 import static android.view.MotionEvent.ACTION_CANCEL;
 import static android.view.MotionEvent.ACTION_DOWN;
 import static android.view.MotionEvent.ACTION_MOVE;
@@ -25,63 +26,86 @@
 import android.view.MotionEvent;
 
 /**
- * A detector for touch inputs that differentiates between drag and click inputs.
+ * A detector for touch inputs that differentiates between drag and click inputs. It receives a flow
+ * of {@link MotionEvent} and generates a new flow of motion events with slop in consideration to
+ * the event handler. In particular, it always passes down, up and cancel events. It'll pass move
+ * events only when there is at least one move event that's beyond the slop threshold. For the
+ * purpose of convenience it also passes all events of other actions.
+ *
  * All touch events must be passed through this class to track a drag event.
  */
-public class DragDetector {
+class DragDetector {
+    private final MotionEventHandler mEventHandler;
+
+    private final PointF mInputDownPoint = new PointF();
     private int mTouchSlop;
-    private PointF mInputDownPoint;
     private boolean mIsDragEvent;
     private int mDragPointerId;
-    public DragDetector(int touchSlop) {
-        mTouchSlop = touchSlop;
-        mInputDownPoint = new PointF();
-        mIsDragEvent = false;
-        mDragPointerId = -1;
+
+    private boolean mResultOfDownAction;
+
+    DragDetector(MotionEventHandler eventHandler) {
+        resetState();
+        mEventHandler = eventHandler;
     }
 
     /**
-     * Determine if {@link MotionEvent} is part of a drag event.
-     * @return {@code true} if this is a drag event, {@code false} if not
-     */
-    public boolean detectDragEvent(MotionEvent ev) {
-        switch (ev.getAction()) {
+     * The receiver of the {@link MotionEvent} flow.
+     *
+     * @return the result returned by {@link #mEventHandler}, or the result when
+     * {@link #mEventHandler} handles the previous down event if the event shouldn't be passed
+    */
+    boolean onMotionEvent(MotionEvent ev) {
+        switch (ev.getActionMasked()) {
             case ACTION_DOWN: {
+                // Only touch screens generate noisy moves.
+                mIsDragEvent = (ev.getSource() & SOURCE_TOUCHSCREEN) != SOURCE_TOUCHSCREEN;
                 mDragPointerId = ev.getPointerId(0);
                 float rawX = ev.getRawX(0);
                 float rawY = ev.getRawY(0);
                 mInputDownPoint.set(rawX, rawY);
-                return false;
+                mResultOfDownAction = mEventHandler.handleMotionEvent(ev);
+                return mResultOfDownAction;
             }
             case ACTION_MOVE: {
                 if (!mIsDragEvent) {
                     int dragPointerIndex = ev.findPointerIndex(mDragPointerId);
                     float dx = ev.getRawX(dragPointerIndex) - mInputDownPoint.x;
                     float dy = ev.getRawY(dragPointerIndex) - mInputDownPoint.y;
-                    if (Math.hypot(dx, dy) > mTouchSlop) {
-                        mIsDragEvent = true;
-                    }
+                    mIsDragEvent = Math.hypot(dx, dy) > mTouchSlop;
                 }
-                return mIsDragEvent;
+                if (mIsDragEvent) {
+                    return mEventHandler.handleMotionEvent(ev);
+                } else {
+                    return mResultOfDownAction;
+                }
             }
-            case ACTION_UP: {
-                boolean result = mIsDragEvent;
-                mIsDragEvent = false;
-                mInputDownPoint.set(0, 0);
-                mDragPointerId = -1;
-                return result;
-            }
+            case ACTION_UP:
             case ACTION_CANCEL: {
-                mIsDragEvent = false;
-                mInputDownPoint.set(0, 0);
-                mDragPointerId = -1;
-                return false;
+                resetState();
+                return mEventHandler.handleMotionEvent(ev);
             }
+            default:
+                return mEventHandler.handleMotionEvent(ev);
         }
+    }
+
+    void setTouchSlop(int touchSlop) {
+        mTouchSlop = touchSlop;
+    }
+
+    boolean isDragEvent() {
         return mIsDragEvent;
     }
 
-    public void setTouchSlop(int touchSlop) {
-        mTouchSlop = touchSlop;
+    private void resetState() {
+        mIsDragEvent = false;
+        mInputDownPoint.set(0, 0);
+        mDragPointerId = -1;
+        mResultOfDownAction = false;
+    }
+
+    interface MotionEventHandler {
+        boolean handleMotionEvent(MotionEvent ev);
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java
similarity index 76%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeCallback.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java
index ee160a1..0191c60 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeCallback.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java
@@ -19,28 +19,28 @@
 /**
  * Callback called when receiving drag-resize or drag-move related input events.
  */
-public interface DragResizeCallback {
+public interface DragPositioningCallback {
     /**
-     * Called when a drag resize starts.
+     * Called when a drag-resize or drag-move starts.
      *
      * @param ctrlType {@link TaskPositioner.CtrlType} indicating the direction of resizing, use
      *                 {@code 0} to indicate it's a move
-     * @param x x coordinate in window decoration coordinate system where the drag resize starts
-     * @param y y coordinate in window decoration coordinate system where the drag resize starts
+     * @param x x coordinate in window decoration coordinate system where the drag starts
+     * @param y y coordinate in window decoration coordinate system where the drag starts
      */
-    void onDragResizeStart(@TaskPositioner.CtrlType int ctrlType, float x, float y);
+    void onDragPositioningStart(@TaskPositioner.CtrlType int ctrlType, float x, float y);
 
     /**
-     * Called when the pointer moves during a drag resize.
+     * Called when the pointer moves during a drag-resize or drag-move.
      * @param x x coordinate in window decoration coordinate system of the new pointer location
      * @param y y coordinate in window decoration coordinate system of the new pointer location
      */
-    void onDragResizeMove(float x, float y);
+    void onDragPositioningMove(float x, float y);
 
     /**
-     * Called when a drag resize stops.
+     * Called when a drag-resize or drag-move stops.
      * @param x x coordinate in window decoration coordinate system where the drag resize stops
      * @param y y coordinate in window decoration coordinate system where the drag resize stops
      */
-    void onDragResizeEnd(float x, float y);
+    void onDragPositioningEnd(float x, float y);
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index d3f1332..81c4176 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -48,7 +48,6 @@
  * Task edges are for resizing with a mouse.
  * Task corners are for resizing with touch input.
  */
-// TODO(b/251270585): investigate how to pass taps in corners to the tasks
 class DragResizeInputListener implements AutoCloseable {
     private static final String TAG = "DragResizeInputListener";
 
@@ -63,7 +62,7 @@
     private final SurfaceControl mDecorationSurface;
     private final InputChannel mInputChannel;
     private final TaskResizeInputEventReceiver mInputEventReceiver;
-    private final com.android.wm.shell.windowdecor.DragResizeCallback mCallback;
+    private final DragPositioningCallback mCallback;
 
     private int mWidth;
     private int mHeight;
@@ -84,7 +83,7 @@
             Choreographer choreographer,
             int displayId,
             SurfaceControl decorationSurface,
-            DragResizeCallback callback) {
+            DragPositioningCallback callback) {
         mInputManager = context.getSystemService(InputManager.class);
         mHandler = handler;
         mChoreographer = choreographer;
@@ -115,7 +114,8 @@
         mInputEventReceiver = new TaskResizeInputEventReceiver(
                 mInputChannel, mHandler, mChoreographer);
         mCallback = callback;
-        mDragDetector = new DragDetector(ViewConfiguration.get(context).getScaledTouchSlop());
+        mDragDetector = new DragDetector(mInputEventReceiver);
+        mDragDetector.setTouchSlop(ViewConfiguration.get(context).getScaledTouchSlop());
     }
 
     /**
@@ -215,6 +215,7 @@
 
     @Override
     public void close() {
+        mInputEventReceiver.dispose();
         mInputChannel.dispose();
         try {
             mWindowSession.remove(mFakeWindow);
@@ -223,12 +224,12 @@
         }
     }
 
-    private class TaskResizeInputEventReceiver extends InputEventReceiver {
+    private class TaskResizeInputEventReceiver extends InputEventReceiver
+            implements DragDetector.MotionEventHandler {
         private final Choreographer mChoreographer;
         private final Runnable mConsumeBatchEventRunnable;
         private boolean mConsumeBatchEventScheduled;
         private boolean mShouldHandleEvents;
-        private boolean mDragging;
 
         private TaskResizeInputEventReceiver(
                 InputChannel inputChannel, Handler handler, Choreographer choreographer) {
@@ -270,15 +271,15 @@
             if (!(inputEvent instanceof MotionEvent)) {
                 return false;
             }
+            return mDragDetector.onMotionEvent((MotionEvent) inputEvent);
+        }
 
-            MotionEvent e = (MotionEvent) inputEvent;
+        @Override
+        public boolean handleMotionEvent(MotionEvent e) {
             boolean result = false;
             // Check if this is a touch event vs mouse event.
             // Touch events are tracked in four corners. Other events are tracked in resize edges.
             boolean isTouch = (e.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN;
-            if (isTouch) {
-                mDragging = mDragDetector.detectDragEvent(e);
-            }
             switch (e.getActionMasked()) {
                 case MotionEvent.ACTION_DOWN: {
                     float x = e.getX(0);
@@ -293,7 +294,7 @@
                         float rawX = e.getRawX(0);
                         float rawY = e.getRawY(0);
                         int ctrlType = calculateCtrlType(isTouch, x, y);
-                        mCallback.onDragResizeStart(ctrlType, rawX, rawY);
+                        mCallback.onDragPositioningStart(ctrlType, rawX, rawY);
                         result = true;
                     }
                     break;
@@ -305,24 +306,17 @@
                     int dragPointerIndex = e.findPointerIndex(mDragPointerId);
                     float rawX = e.getRawX(dragPointerIndex);
                     float rawY = e.getRawY(dragPointerIndex);
-                    if (!isTouch) {
-                        // For all other types allow immediate dragging.
-                        mDragging = true;
-                    }
-                    if (mDragging) {
-                        mCallback.onDragResizeMove(rawX, rawY);
-                        result = true;
-                    }
+                    mCallback.onDragPositioningMove(rawX, rawY);
+                    result = true;
                     break;
                 }
                 case MotionEvent.ACTION_UP:
                 case MotionEvent.ACTION_CANCEL: {
-                    if (mShouldHandleEvents && mDragging) {
+                    if (mShouldHandleEvents) {
                         int dragPointerIndex = e.findPointerIndex(mDragPointerId);
-                        mCallback.onDragResizeEnd(
+                        mCallback.onDragPositioningEnd(
                                 e.getRawX(dragPointerIndex), e.getRawY(dragPointerIndex));
                     }
-                    mDragging = false;
                     mShouldHandleEvents = false;
                     mDragPointerId = -1;
                     result = true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
index 20631f8..d3f9227 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
@@ -23,7 +23,7 @@
 
 import com.android.wm.shell.ShellTaskOrganizer;
 
-class TaskPositioner implements DragResizeCallback {
+class TaskPositioner implements DragPositioningCallback {
 
     @IntDef({CTRL_TYPE_UNDEFINED, CTRL_TYPE_LEFT, CTRL_TYPE_RIGHT, CTRL_TYPE_TOP, CTRL_TYPE_BOTTOM})
     @interface CtrlType {}
@@ -38,11 +38,9 @@
     private final WindowDecoration mWindowDecoration;
 
     private final Rect mTaskBoundsAtDragStart = new Rect();
-    private final PointF mResizeStartPoint = new PointF();
-    private final Rect mResizeTaskBounds = new Rect();
-    // Whether the |dragResizing| hint should be sent with the next bounds change WCT.
-    // Used to optimized fluid resizing of freeform tasks.
-    private boolean mPendingDragResizeHint = false;
+    private final PointF mRepositionStartPoint = new PointF();
+    private final Rect mRepositionTaskBounds = new Rect();
+    private boolean mHasMoved = false;
 
     private int mCtrlType;
     private DragStartListener mDragStartListener;
@@ -59,72 +57,84 @@
     }
 
     @Override
-    public void onDragResizeStart(int ctrlType, float x, float y) {
-        if (ctrlType != CTRL_TYPE_UNDEFINED) {
-            // The task is being resized, send the |dragResizing| hint to core with the first
-            // bounds-change wct.
-            mPendingDragResizeHint = true;
-        }
+    public void onDragPositioningStart(int ctrlType, float x, float y) {
+        mHasMoved = false;
 
         mDragStartListener.onDragStart(mWindowDecoration.mTaskInfo.taskId);
         mCtrlType = ctrlType;
 
         mTaskBoundsAtDragStart.set(
                 mWindowDecoration.mTaskInfo.configuration.windowConfiguration.getBounds());
-        mResizeStartPoint.set(x, y);
+        mRepositionStartPoint.set(x, y);
     }
 
     @Override
-    public void onDragResizeMove(float x, float y) {
+    public void onDragPositioningMove(float x, float y) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         if (changeBounds(wct, x, y)) {
-            if (mPendingDragResizeHint) {
+            // The task is being resized, send the |dragResizing| hint to core with the first
+            // bounds-change wct.
+            if (!mHasMoved && mCtrlType != CTRL_TYPE_UNDEFINED) {
                 // This is the first bounds change since drag resize operation started.
                 wct.setDragResizing(mWindowDecoration.mTaskInfo.token, true /* dragResizing */);
-                mPendingDragResizeHint = false;
             }
             mTaskOrganizer.applyTransaction(wct);
+            mHasMoved = true;
         }
     }
 
     @Override
-    public void onDragResizeEnd(float x, float y) {
-        final WindowContainerTransaction wct = new WindowContainerTransaction();
-        wct.setDragResizing(mWindowDecoration.mTaskInfo.token, false /* dragResizing */);
-        changeBounds(wct, x, y);
-        mTaskOrganizer.applyTransaction(wct);
+    public void onDragPositioningEnd(float x, float y) {
+        // |mHasMoved| being false means there is no real change to the task bounds in WM core, so
+        // we don't need a WCT to finish it.
+        if (mHasMoved) {
+            final WindowContainerTransaction wct = new WindowContainerTransaction();
+            wct.setDragResizing(mWindowDecoration.mTaskInfo.token, false /* dragResizing */);
+            changeBounds(wct, x, y);
+            mTaskOrganizer.applyTransaction(wct);
+        }
 
-        mCtrlType = 0;
+        mCtrlType = CTRL_TYPE_UNDEFINED;
         mTaskBoundsAtDragStart.setEmpty();
-        mResizeStartPoint.set(0, 0);
-        mPendingDragResizeHint = false;
+        mRepositionStartPoint.set(0, 0);
+        mHasMoved = false;
     }
 
     private boolean changeBounds(WindowContainerTransaction wct, float x, float y) {
-        float deltaX = x - mResizeStartPoint.x;
-        mResizeTaskBounds.set(mTaskBoundsAtDragStart);
+        // |mRepositionTaskBounds| is the bounds last reported if |mHasMoved| is true. If it's not
+        // true, we can compare it against |mTaskBoundsAtDragStart|.
+        final int oldLeft = mHasMoved ? mRepositionTaskBounds.left : mTaskBoundsAtDragStart.left;
+        final int oldTop = mHasMoved ? mRepositionTaskBounds.top : mTaskBoundsAtDragStart.top;
+        final int oldRight = mHasMoved ? mRepositionTaskBounds.right : mTaskBoundsAtDragStart.right;
+        final int oldBottom =
+                mHasMoved ? mRepositionTaskBounds.bottom : mTaskBoundsAtDragStart.bottom;
+
+        final float deltaX = x - mRepositionStartPoint.x;
+        final float deltaY = y - mRepositionStartPoint.y;
+        mRepositionTaskBounds.set(mTaskBoundsAtDragStart);
         if ((mCtrlType & CTRL_TYPE_LEFT) != 0) {
-            mResizeTaskBounds.left += deltaX;
+            mRepositionTaskBounds.left += deltaX;
         }
         if ((mCtrlType & CTRL_TYPE_RIGHT) != 0) {
-            mResizeTaskBounds.right += deltaX;
+            mRepositionTaskBounds.right += deltaX;
         }
-        float deltaY = y - mResizeStartPoint.y;
         if ((mCtrlType & CTRL_TYPE_TOP) != 0) {
-            mResizeTaskBounds.top += deltaY;
+            mRepositionTaskBounds.top += deltaY;
         }
         if ((mCtrlType & CTRL_TYPE_BOTTOM) != 0) {
-            mResizeTaskBounds.bottom += deltaY;
+            mRepositionTaskBounds.bottom += deltaY;
         }
-        if (mCtrlType == 0) {
-            mResizeTaskBounds.offset((int) deltaX, (int) deltaY);
+        if (mCtrlType == CTRL_TYPE_UNDEFINED) {
+            mRepositionTaskBounds.offset((int) deltaX, (int) deltaY);
         }
 
-        if (!mResizeTaskBounds.isEmpty()) {
-            wct.setBounds(mWindowDecoration.mTaskInfo.token, mResizeTaskBounds);
-            return true;
+        if (oldLeft == mRepositionTaskBounds.left && oldTop == mRepositionTaskBounds.top
+                && oldRight == mRepositionTaskBounds.right
+                && oldBottom == mRepositionTaskBounds.bottom) {
+            return false;
         }
-        return false;
+        wct.setBounds(mWindowDecoration.mTaskInfo.token, mRepositionTaskBounds);
+        return true;
     }
 
     interface DragStartListener {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
index 907977c..3734487 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
@@ -29,7 +29,8 @@
  */
 public interface WindowDecorViewModel {
     /**
-     * Sets the transition starter that starts freeform task transitions.
+     * Sets the transition starter that starts freeform task transitions. Only called when
+     * {@link com.android.wm.shell.transition.Transitions#ENABLE_SHELL_TRANSITIONS} is {@code true}.
      *
      * @param transitionStarter the transition starter that starts freeform task transitions
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 7f85988..ae685ad 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -252,9 +252,7 @@
         }
 
         final int captionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId);
-        final int captionWidth = params.mCaptionWidthId == Resources.ID_NULL
-                ? taskBounds.width()
-                : loadDimensionPixelSize(resources, params.mCaptionWidthId);
+        final int captionWidth = taskBounds.width();
 
         startT.setPosition(
                         mCaptionContainerSurface,
@@ -393,10 +391,11 @@
      * @param yPos y position of new window
      * @param width width of new window
      * @param height height of new window
+     * @param cropPadding padding to add to window crop to ensure shadows display properly
      * @return
      */
-    AdditionalWindow addWindow(int layoutId, String namePrefix,
-            SurfaceControl.Transaction t, int xPos, int yPos, int width, int height) {
+    AdditionalWindow addWindow(int layoutId, String namePrefix, SurfaceControl.Transaction t,
+            int xPos, int yPos, int width, int height, int cropPadding) {
         final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
         SurfaceControl windowSurfaceControl = builder
                 .setName(namePrefix + " of Task=" + mTaskInfo.taskId)
@@ -407,7 +406,7 @@
 
         t.setPosition(
                 windowSurfaceControl, xPos, yPos)
-                .setWindowCrop(windowSurfaceControl, width, height)
+                .setWindowCrop(windowSurfaceControl, width + cropPadding, height + cropPadding)
                 .show(windowSurfaceControl);
         final WindowManager.LayoutParams lp =
                 new WindowManager.LayoutParams(width, height,
diff --git a/libs/WindowManager/Shell/tests/unittest/res/values/dimen.xml b/libs/WindowManager/Shell/tests/unittest/res/values/dimen.xml
index 8949a75..27d40b2 100644
--- a/libs/WindowManager/Shell/tests/unittest/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/tests/unittest/res/values/dimen.xml
@@ -17,7 +17,7 @@
 <resources>
     <!-- Resources used in WindowDecorationTests -->
     <dimen name="test_freeform_decor_caption_height">32dp</dimen>
-    <dimen name="test_freeform_decor_caption_width">216dp</dimen>
+    <dimen name="test_freeform_decor_caption_menu_width">216dp</dimen>
     <dimen name="test_window_decor_left_outset">10dp</dimen>
     <dimen name="test_window_decor_top_outset">20dp</dimen>
     <dimen name="test_window_decor_right_outset">30dp</dimen>
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt
index 1636c5f..0a31338 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt
@@ -21,7 +21,6 @@
 import android.util.SparseArray
 import androidx.test.filters.SmallTest
 import com.android.wm.shell.ShellTestCase
-import com.android.wm.shell.bubbles.storage.BubbleXmlHelperTest.Companion.sparseArraysEqual
 import junit.framework.Assert.assertEquals
 import junit.framework.Assert.assertNotNull
 import junit.framework.Assert.assertTrue
@@ -36,7 +35,8 @@
 
     // user, package, shortcut, notification key, height, res-height, title, taskId, locusId
     private val user0Bubbles = listOf(
-            BubbleEntity(0, "com.example.messenger", "shortcut-1", "0k1", 120, 0, null, 1, null),
+            BubbleEntity(0, "com.example.messenger", "shortcut-1", "0k1", 120, 0, null, 1, null,
+                    true),
             BubbleEntity(10, "com.example.chat", "alice and bob", "0k2", 0, 16537428, "title", 2,
                     null),
             BubbleEntity(0, "com.example.messenger", "shortcut-2", "0k3", 120, 0, null,
@@ -44,7 +44,8 @@
     )
 
     private val user1Bubbles = listOf(
-            BubbleEntity(1, "com.example.messenger", "shortcut-1", "1k1", 120, 0, null, 3, null),
+            BubbleEntity(1, "com.example.messenger", "shortcut-1", "1k1", 120, 0, null, 3, null,
+                    true),
             BubbleEntity(12, "com.example.chat", "alice and bob", "1k2", 0, 16537428, "title", 4,
                     null),
             BubbleEntity(1, "com.example.messenger", "shortcut-2", "1k3", 120, 0, null,
@@ -76,6 +77,6 @@
         assertEquals(actual.size(), 0)
 
         repository.persistsToDisk(bubbles)
-        assertTrue(sparseArraysEqual(bubbles, repository.readFromDisk()))
+        assertTrue(bubbles.contentEquals(repository.readFromDisk()))
     }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelperTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelperTest.kt
index 4ab9f87..3bfbcd2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelperTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelperTest.kt
@@ -34,7 +34,8 @@
 class BubbleXmlHelperTest : ShellTestCase() {
 
     private val user0Bubbles = listOf(
-            BubbleEntity(0, "com.example.messenger", "shortcut-1", "0k1", 120, 0, null, 1),
+            BubbleEntity(0, "com.example.messenger", "shortcut-1", "0k1", 120, 0, null, 1,
+                    isDismissable = true),
             BubbleEntity(10, "com.example.chat", "alice and bob", "0k2", 0, 16537428, "title", 2,
                     null),
             BubbleEntity(0, "com.example.messenger", "shortcut-2", "0k3", 120, 0, null,
@@ -42,7 +43,8 @@
     )
 
     private val user1Bubbles = listOf(
-            BubbleEntity(1, "com.example.messenger", "shortcut-1", "1k1", 120, 0, null, 3),
+            BubbleEntity(1, "com.example.messenger", "shortcut-1", "1k1", 120, 0, null, 3,
+                    isDismissable = true),
             BubbleEntity(12, "com.example.chat", "alice and bob", "1k2", 0, 16537428, "title", 4,
                     null),
             BubbleEntity(1, "com.example.messenger", "shortcut-2", "1k3", 120, 0, null,
@@ -51,28 +53,6 @@
 
     private val bubbles = SparseArray<List<BubbleEntity>>()
 
-    // Checks that the contents of the two sparse arrays are the same.
-    companion object {
-        fun sparseArraysEqual(
-            one: SparseArray<List<BubbleEntity>>?,
-            two: SparseArray<List<BubbleEntity>>?
-        ): Boolean {
-            if (one == null && two == null) return true
-            if ((one == null) != (two == null)) return false
-            if (one!!.size() != two!!.size()) return false
-            for (i in 0 until one.size()) {
-                val k1 = one.keyAt(i)
-                val v1 = one.valueAt(i)
-                val k2 = two.keyAt(i)
-                val v2 = two.valueAt(i)
-                if (k1 != k2 && v1 != v2) {
-                    return false
-                }
-            }
-            return true
-        }
-    }
-
     @Before
     fun setup() {
         bubbles.put(0, user0Bubbles)
@@ -83,14 +63,14 @@
     fun testWriteXml() {
         val expectedEntries = """
 <bs uid="0">
-<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="0k1" h="120" hid="0" tid="1" />
-<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="0k2" h="0" hid="16537428" t="title" tid="2" />
-<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="0k3" h="120" hid="0" tid="-1" l="l3" />
+<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="0k1" h="120" hid="0" tid="1" d="true" />
+<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="0k2" h="0" hid="16537428" t="title" tid="2" d="false" />
+<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="0k3" h="120" hid="0" tid="-1" l="l3" d="false" />
 </bs>
 <bs uid="1">
-<bb uid="1" pkg="com.example.messenger" sid="shortcut-1" key="1k1" h="120" hid="0" tid="3" />
-<bb uid="12" pkg="com.example.chat" sid="alice and bob" key="1k2" h="0" hid="16537428" t="title" tid="4" />
-<bb uid="1" pkg="com.example.messenger" sid="shortcut-2" key="1k3" h="120" hid="0" tid="-1" l="l4" />
+<bb uid="1" pkg="com.example.messenger" sid="shortcut-1" key="1k1" h="120" hid="0" tid="3" d="true" />
+<bb uid="12" pkg="com.example.chat" sid="alice and bob" key="1k2" h="0" hid="16537428" t="title" tid="4" d="false" />
+<bb uid="1" pkg="com.example.messenger" sid="shortcut-2" key="1k3" h="120" hid="0" tid="-1" l="l4" d="false" />
 </bs>
         """.trimIndent()
         ByteArrayOutputStream().use {
@@ -107,19 +87,19 @@
 <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
 <bs v="2">
 <bs uid="0">
-<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="0k1" h="120" hid="0" tid="1" />
-<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="0k2" h="0" hid="16537428" t="title" tid="2" />
-<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="0k3" h="120" hid="0" tid="-1" l="l3" />
+<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="0k1" h="120" hid="0" tid="1" d="true" />
+<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="0k2" h="0" hid="16537428" t="title" tid="2" d="false" />
+<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="0k3" h="120" hid="0" tid="-1" l="l3" d="false" />
 </bs>
 <bs uid="1">
-<bb uid="1" pkg="com.example.messenger" sid="shortcut-1" key="1k1" h="120" hid="0" tid="3" />
-<bb uid="12" pkg="com.example.chat" sid="alice and bob" key="1k2" h="0" hid="16537428" t="title" tid="4" />
-<bb uid="1" pkg="com.example.messenger" sid="shortcut-2" key="1k3" h="120" hid="0" tid="-1" l="l4" />
+<bb uid="1" pkg="com.example.messenger" sid="shortcut-1" key="1k1" h="120" hid="0" tid="3" d="true" />
+<bb uid="12" pkg="com.example.chat" sid="alice and bob" key="1k2" h="0" hid="16537428" t="title" tid="4" d="false" />
+<bb uid="1" pkg="com.example.messenger" sid="shortcut-2" key="1k3" h="120" hid="0" tid="-1" l="l4" d="false" />
 </bs>
 </bs>
         """.trimIndent()
         val actual = readXml(ByteArrayInputStream(src.toByteArray(Charsets.UTF_8)))
-        assertTrue("failed parsing bubbles from xml\n$src", sparseArraysEqual(bubbles, actual))
+        assertTrue("failed parsing bubbles from xml\n$src", bubbles.contentEquals(actual))
     }
 
     // V0 -> V1 happened prior to release / during dogfood so nothing is saved
@@ -161,8 +141,7 @@
 </bs>
         """.trimIndent()
         val actual = readXml(ByteArrayInputStream(src.toByteArray(Charsets.UTF_8)))
-        assertTrue("failed parsing bubbles from xml\n$src",
-                sparseArraysEqual(expectedBubbles, actual))
+        assertTrue("failed parsing bubbles from xml\n$src", expectedBubbles.contentEquals(actual))
     }
 
     /**
@@ -187,7 +166,7 @@
         """.trimIndent()
         val actual = readXml(ByteArrayInputStream(src.toByteArray(Charsets.UTF_8)))
         assertTrue("failed parsing bubbles from xml\n$src",
-                sparseArraysEqual(expectedBubbles, actual))
+                expectedBubbles.contentEquals(actual))
     }
 
     @Test
@@ -210,6 +189,6 @@
         )
         val actual = readXml(ByteArrayInputStream(src.toByteArray(Charsets.UTF_8)))
         assertTrue("failed parsing bubbles from xml\n$src",
-                sparseArraysEqual(expectedBubbles, actual))
+                expectedBubbles.contentEquals(actual))
     }
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java
new file mode 100644
index 0000000..f8ee300
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.wm.shell.common;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.content.Context;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.sysui.ShellInit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DevicePostureControllerTest {
+    @Mock
+    private Context mContext;
+
+    @Mock
+    private ShellInit mShellInit;
+
+    @Mock
+    private ShellExecutor mMainExecutor;
+
+    @Captor
+    private ArgumentCaptor<Integer> mDevicePostureCaptor;
+
+    @Mock
+    private DevicePostureController.OnDevicePostureChangedListener mOnDevicePostureChangedListener;
+
+    private DevicePostureController mDevicePostureController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mDevicePostureController = new DevicePostureController(mContext, mShellInit, mMainExecutor);
+    }
+
+    @Test
+    public void instantiateController_addInitCallback() {
+        verify(mShellInit, times(1)).addInitCallback(any(), eq(mDevicePostureController));
+    }
+
+    @Test
+    public void registerOnDevicePostureChangedListener_callbackCurrentPosture() {
+        mDevicePostureController.registerOnDevicePostureChangedListener(
+                mOnDevicePostureChangedListener);
+        verify(mOnDevicePostureChangedListener, times(1))
+                .onDevicePostureChanged(anyInt());
+    }
+
+    @Test
+    public void onDevicePostureChanged_differentPosture_callbackListener() {
+        mDevicePostureController.registerOnDevicePostureChangedListener(
+                mOnDevicePostureChangedListener);
+        verify(mOnDevicePostureChangedListener).onDevicePostureChanged(
+                mDevicePostureCaptor.capture());
+        clearInvocations(mOnDevicePostureChangedListener);
+
+        int differentDevicePosture = mDevicePostureCaptor.getValue() + 1;
+        mDevicePostureController.onDevicePostureChanged(differentDevicePosture);
+
+        verify(mOnDevicePostureChangedListener, times(1))
+                .onDevicePostureChanged(differentDevicePosture);
+    }
+
+    @Test
+    public void onDevicePostureChanged_samePosture_doesNotCallbackListener() {
+        mDevicePostureController.registerOnDevicePostureChangedListener(
+                mOnDevicePostureChangedListener);
+        verify(mOnDevicePostureChangedListener).onDevicePostureChanged(
+                mDevicePostureCaptor.capture());
+        clearInvocations(mOnDevicePostureChangedListener);
+
+        int sameDevicePosture = mDevicePostureCaptor.getValue();
+        mDevicePostureController.onDevicePostureChanged(sameDevicePosture);
+
+        verifyZeroInteractions(mOnDevicePostureChangedListener);
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index 2fc0914..875e610 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -94,7 +94,10 @@
     private @Mock Lazy<Transitions> mMockTransitionsLazy;
     private @Mock CompatUIWindowManager mMockCompatLayout;
     private @Mock LetterboxEduWindowManager mMockLetterboxEduLayout;
+    private @Mock RestartDialogWindowManager mMockRestartDialogLayout;
     private @Mock DockStateReader mDockStateReader;
+    private @Mock CompatUIConfiguration mCompatUIConfiguration;
+    private @Mock CompatUIShellCommandHandler mCompatUIShellCommandHandler;
 
     @Captor
     ArgumentCaptor<OnInsetsChangedListener> mOnInsetsChangedListenerCaptor;
@@ -112,10 +115,17 @@
         doReturn(TASK_ID).when(mMockLetterboxEduLayout).getTaskId();
         doReturn(true).when(mMockLetterboxEduLayout).createLayout(anyBoolean());
         doReturn(true).when(mMockLetterboxEduLayout).updateCompatInfo(any(), any(), anyBoolean());
+
+        doReturn(DISPLAY_ID).when(mMockRestartDialogLayout).getDisplayId();
+        doReturn(TASK_ID).when(mMockRestartDialogLayout).getTaskId();
+        doReturn(true).when(mMockRestartDialogLayout).createLayout(anyBoolean());
+        doReturn(true).when(mMockRestartDialogLayout).updateCompatInfo(any(), any(), anyBoolean());
+
         mShellInit = spy(new ShellInit(mMockExecutor));
         mController = new CompatUIController(mContext, mShellInit, mMockShellController,
                 mMockDisplayController, mMockDisplayInsetsController, mMockImeController,
-                mMockSyncQueue, mMockExecutor, mMockTransitionsLazy, mDockStateReader) {
+                mMockSyncQueue, mMockExecutor, mMockTransitionsLazy, mDockStateReader,
+                mCompatUIConfiguration, mCompatUIShellCommandHandler) {
             @Override
             CompatUIWindowManager createCompatUiWindowManager(Context context, TaskInfo taskInfo,
                     ShellTaskOrganizer.TaskListener taskListener) {
@@ -127,6 +137,12 @@
                     TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) {
                 return mMockLetterboxEduLayout;
             }
+
+            @Override
+            RestartDialogWindowManager createRestartDialogWindowManager(Context context,
+                    TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) {
+                return mMockRestartDialogLayout;
+            }
         };
         mShellInit.init();
         spyOn(mController);
@@ -159,6 +175,8 @@
         verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
         verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
                 eq(mMockTaskListener));
+        verify(mController).createRestartDialogWindowManager(any(), eq(taskInfo),
+                eq(mMockTaskListener));
 
         // Verify that the compat controls and letterbox education are updated with new size compat
         // info.
@@ -167,10 +185,12 @@
                 CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
         mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
 
-        verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
-                true);
-        verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
-                true);
+        verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+                /* canShow= */ true);
+        verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+                /* canShow= */ true);
+        verify(mMockRestartDialogLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+                /* canShow= */ true);
 
         // Verify that compat controls and letterbox education are removed with null task listener.
         clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mController);
@@ -180,12 +200,14 @@
 
         verify(mMockCompatLayout).release();
         verify(mMockLetterboxEduLayout).release();
+        verify(mMockRestartDialogLayout).release();
     }
 
     @Test
     public void testOnCompatInfoChanged_createLayoutReturnsFalse() {
         doReturn(false).when(mMockCompatLayout).createLayout(anyBoolean());
         doReturn(false).when(mMockLetterboxEduLayout).createLayout(anyBoolean());
+        doReturn(false).when(mMockRestartDialogLayout).createLayout(anyBoolean());
 
         TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
                 CAMERA_COMPAT_CONTROL_HIDDEN);
@@ -194,6 +216,8 @@
         verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
         verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
                 eq(mMockTaskListener));
+        verify(mController).createRestartDialogWindowManager(any(), eq(taskInfo),
+                eq(mMockTaskListener));
 
         // Verify that the layout is created again.
         clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mController);
@@ -201,15 +225,19 @@
 
         verify(mMockCompatLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
         verify(mMockLetterboxEduLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
+        verify(mMockRestartDialogLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
         verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
         verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
                 eq(mMockTaskListener));
+        verify(mController).createRestartDialogWindowManager(any(), eq(taskInfo),
+                eq(mMockTaskListener));
     }
 
     @Test
     public void testOnCompatInfoChanged_updateCompatInfoReturnsFalse() {
         doReturn(false).when(mMockCompatLayout).updateCompatInfo(any(), any(), anyBoolean());
         doReturn(false).when(mMockLetterboxEduLayout).updateCompatInfo(any(), any(), anyBoolean());
+        doReturn(false).when(mMockRestartDialogLayout).updateCompatInfo(any(), any(), anyBoolean());
 
         TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
                 CAMERA_COMPAT_CONTROL_HIDDEN);
@@ -218,24 +246,33 @@
         verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
         verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
                 eq(mMockTaskListener));
+        verify(mController).createRestartDialogWindowManager(any(), eq(taskInfo),
+                eq(mMockTaskListener));
 
-        clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mController);
+        clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mMockRestartDialogLayout,
+                mController);
         mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
 
-        verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
-                true);
-        verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
-                true);
+        verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+                /* canShow= */ true);
+        verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+                /* canShow= */ true);
+        verify(mMockRestartDialogLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+                /* canShow= */ true);
 
         // Verify that the layout is created again.
-        clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mController);
+        clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mMockRestartDialogLayout,
+                mController);
         mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
 
         verify(mMockCompatLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
         verify(mMockLetterboxEduLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
+        verify(mMockRestartDialogLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
         verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
         verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
                 eq(mMockTaskListener));
+        verify(mController).createRestartDialogWindowManager(any(), eq(taskInfo),
+                eq(mMockTaskListener));
     }
 
 
@@ -259,6 +296,7 @@
 
         verify(mMockCompatLayout, never()).release();
         verify(mMockLetterboxEduLayout, never()).release();
+        verify(mMockRestartDialogLayout, never()).release();
         verify(mMockDisplayInsetsController, never()).removeInsetsChangedListener(eq(DISPLAY_ID),
                 any());
 
@@ -267,6 +305,7 @@
         verify(mMockDisplayInsetsController).removeInsetsChangedListener(eq(DISPLAY_ID), any());
         verify(mMockCompatLayout).release();
         verify(mMockLetterboxEduLayout).release();
+        verify(mMockRestartDialogLayout).release();
     }
 
     @Test
@@ -278,11 +317,13 @@
 
         verify(mMockCompatLayout, never()).updateDisplayLayout(any());
         verify(mMockLetterboxEduLayout, never()).updateDisplayLayout(any());
+        verify(mMockRestartDialogLayout, never()).updateDisplayLayout(any());
 
         mController.onDisplayConfigurationChanged(DISPLAY_ID, new Configuration());
 
         verify(mMockCompatLayout).updateDisplayLayout(mMockDisplayLayout);
         verify(mMockLetterboxEduLayout).updateDisplayLayout(mMockDisplayLayout);
+        verify(mMockRestartDialogLayout).updateDisplayLayout(mMockDisplayLayout);
     }
 
     @Test
@@ -301,12 +342,14 @@
 
         verify(mMockCompatLayout).updateDisplayLayout(mMockDisplayLayout);
         verify(mMockLetterboxEduLayout).updateDisplayLayout(mMockDisplayLayout);
+        verify(mMockRestartDialogLayout).updateDisplayLayout(mMockDisplayLayout);
 
         // No update if the insets state is the same.
-        clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout);
+        clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mMockRestartDialogLayout);
         mOnInsetsChangedListenerCaptor.getValue().insetsChanged(new InsetsState(insetsState));
         verify(mMockCompatLayout, never()).updateDisplayLayout(mMockDisplayLayout);
         verify(mMockLetterboxEduLayout, never()).updateDisplayLayout(mMockDisplayLayout);
+        verify(mMockRestartDialogLayout, never()).updateDisplayLayout(mMockDisplayLayout);
     }
 
     @Test
@@ -319,22 +362,26 @@
 
         verify(mMockCompatLayout).updateVisibility(false);
         verify(mMockLetterboxEduLayout).updateVisibility(false);
+        verify(mMockRestartDialogLayout).updateVisibility(false);
 
         // Verify button remains hidden while IME is showing.
         TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
                 CAMERA_COMPAT_CONTROL_HIDDEN);
         mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
 
-        verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
-                false);
-        verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
-                false);
+        verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+                /* canShow= */ false);
+        verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+                /* canShow= */ false);
+        verify(mMockRestartDialogLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+                /* canShow= */ false);
 
         // Verify button is shown after IME is hidden.
         mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ false);
 
         verify(mMockCompatLayout).updateVisibility(true);
         verify(mMockLetterboxEduLayout).updateVisibility(true);
+        verify(mMockRestartDialogLayout).updateVisibility(true);
     }
 
     @Test
@@ -347,22 +394,26 @@
 
         verify(mMockCompatLayout).updateVisibility(false);
         verify(mMockLetterboxEduLayout).updateVisibility(false);
+        verify(mMockRestartDialogLayout).updateVisibility(false);
 
         // Verify button remains hidden while keyguard is showing.
         TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
                 CAMERA_COMPAT_CONTROL_HIDDEN);
         mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
 
-        verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
-                false);
-        verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
-                false);
+        verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+                /* canShow= */ false);
+        verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+                /* canShow= */ false);
+        verify(mMockRestartDialogLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+                /* canShow= */ false);
 
         // Verify button is shown after keyguard becomes not showing.
         mController.onKeyguardVisibilityChanged(false, false, false);
 
         verify(mMockCompatLayout).updateVisibility(true);
         verify(mMockLetterboxEduLayout).updateVisibility(true);
+        verify(mMockRestartDialogLayout).updateVisibility(true);
     }
 
     @Test
@@ -375,20 +426,23 @@
 
         verify(mMockCompatLayout, times(2)).updateVisibility(false);
         verify(mMockLetterboxEduLayout, times(2)).updateVisibility(false);
+        verify(mMockRestartDialogLayout, times(2)).updateVisibility(false);
 
-        clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout);
+        clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mMockRestartDialogLayout);
 
         // Verify button remains hidden after keyguard becomes not showing since IME is showing.
         mController.onKeyguardVisibilityChanged(false, false, false);
 
         verify(mMockCompatLayout).updateVisibility(false);
         verify(mMockLetterboxEduLayout).updateVisibility(false);
+        verify(mMockRestartDialogLayout).updateVisibility(false);
 
         // Verify button is shown after IME is not showing.
         mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ false);
 
         verify(mMockCompatLayout).updateVisibility(true);
         verify(mMockLetterboxEduLayout).updateVisibility(true);
+        verify(mMockRestartDialogLayout).updateVisibility(true);
     }
 
     @Test
@@ -401,20 +455,23 @@
 
         verify(mMockCompatLayout, times(2)).updateVisibility(false);
         verify(mMockLetterboxEduLayout, times(2)).updateVisibility(false);
+        verify(mMockRestartDialogLayout, times(2)).updateVisibility(false);
 
-        clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout);
+        clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mMockRestartDialogLayout);
 
         // Verify button remains hidden after IME is hidden since keyguard is showing.
         mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ false);
 
         verify(mMockCompatLayout).updateVisibility(false);
         verify(mMockLetterboxEduLayout).updateVisibility(false);
+        verify(mMockRestartDialogLayout).updateVisibility(false);
 
         // Verify button is shown after keyguard becomes not showing.
         mController.onKeyguardVisibilityChanged(false, false, false);
 
         verify(mMockCompatLayout).updateVisibility(true);
         verify(mMockLetterboxEduLayout).updateVisibility(true);
+        verify(mMockRestartDialogLayout).updateVisibility(true);
     }
 
     private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
index 7d3e718..5f294d5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
@@ -31,6 +31,7 @@
 import android.app.TaskInfo;
 import android.app.TaskInfo.CameraCompatControlState;
 import android.testing.AndroidTestingRunner;
+import android.util.Pair;
 import android.view.LayoutInflater;
 import android.view.SurfaceControlViewHost;
 import android.widget.ImageButton;
@@ -45,12 +46,17 @@
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.compatui.CompatUIWindowManager.CompatUIHintsState;
 
+import junit.framework.Assert;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.function.Consumer;
+
 /**
  * Tests for {@link CompatUILayout}.
  *
@@ -65,20 +71,22 @@
 
     @Mock private SyncTransactionQueue mSyncTransactionQueue;
     @Mock private CompatUIController.CompatUICallback mCallback;
+    @Mock private Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnRestartButtonClicked;
     @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
     @Mock private SurfaceControlViewHost mViewHost;
+    @Mock private CompatUIConfiguration mCompatUIConfiguration;
 
     private CompatUIWindowManager mWindowManager;
     private CompatUILayout mLayout;
+    private TaskInfo mTaskInfo;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-
-        mWindowManager = new CompatUIWindowManager(mContext,
-                createTaskInfo(/* hasSizeCompat= */ false, CAMERA_COMPAT_CONTROL_HIDDEN),
-                mSyncTransactionQueue, mCallback, mTaskListener,
-                new DisplayLayout(), new CompatUIHintsState());
+        mTaskInfo = createTaskInfo(/* hasSizeCompat= */ false, CAMERA_COMPAT_CONTROL_HIDDEN);
+        mWindowManager = new CompatUIWindowManager(mContext, mTaskInfo, mSyncTransactionQueue,
+                mCallback, mTaskListener, new DisplayLayout(), new CompatUIHintsState(),
+                mCompatUIConfiguration, mOnRestartButtonClicked);
 
         mLayout = (CompatUILayout)
                 LayoutInflater.from(mContext).inflate(R.layout.compat_ui_layout, null);
@@ -95,8 +103,15 @@
         final ImageButton button = mLayout.findViewById(R.id.size_compat_restart_button);
         button.performClick();
 
+        @SuppressWarnings("unchecked")
+        ArgumentCaptor<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> restartCaptor =
+                ArgumentCaptor.forClass(Pair.class);
+
         verify(mWindowManager).onRestartButtonClicked();
-        verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID);
+        verify(mOnRestartButtonClicked).accept(restartCaptor.capture());
+        final Pair<TaskInfo, ShellTaskOrganizer.TaskListener> result = restartCaptor.getValue();
+        Assert.assertEquals(mTaskInfo, result.first);
+        Assert.assertEquals(mTaskListener, result.second);
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
index e79b803..0c5edc3f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
@@ -28,6 +28,7 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -38,6 +39,7 @@
 import android.app.TaskInfo;
 import android.graphics.Rect;
 import android.testing.AndroidTestingRunner;
+import android.util.Pair;
 import android.view.DisplayInfo;
 import android.view.InsetsSource;
 import android.view.InsetsState;
@@ -53,12 +55,17 @@
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.compatui.CompatUIWindowManager.CompatUIHintsState;
 
+import junit.framework.Assert;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.function.Consumer;
+
 /**
  * Tests for {@link CompatUIWindowManager}.
  *
@@ -73,20 +80,22 @@
 
     @Mock private SyncTransactionQueue mSyncTransactionQueue;
     @Mock private CompatUIController.CompatUICallback mCallback;
+    @Mock private Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnRestartButtonClicked;
     @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
     @Mock private CompatUILayout mLayout;
     @Mock private SurfaceControlViewHost mViewHost;
+    @Mock private CompatUIConfiguration mCompatUIConfiguration;
 
     private CompatUIWindowManager mWindowManager;
+    private TaskInfo mTaskInfo;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-
-        mWindowManager = new CompatUIWindowManager(mContext,
-                createTaskInfo(/* hasSizeCompat= */ false, CAMERA_COMPAT_CONTROL_HIDDEN),
-                mSyncTransactionQueue, mCallback, mTaskListener,
-                new DisplayLayout(), new CompatUIHintsState());
+        mTaskInfo = createTaskInfo(/* hasSizeCompat= */ false, CAMERA_COMPAT_CONTROL_HIDDEN);
+        mWindowManager = new CompatUIWindowManager(mContext, mTaskInfo, mSyncTransactionQueue,
+                mCallback, mTaskListener, new DisplayLayout(), new CompatUIHintsState(),
+                mCompatUIConfiguration, mOnRestartButtonClicked);
 
         spyOn(mWindowManager);
         doReturn(mLayout).when(mWindowManager).inflateLayout();
@@ -351,14 +360,14 @@
         mWindowManager.updateVisibility(/* canShow= */ false);
 
         verify(mWindowManager, never()).createLayout(anyBoolean());
-        verify(mLayout).setVisibility(View.GONE);
+        verify(mLayout, atLeastOnce()).setVisibility(View.GONE);
 
         // Show button.
         doReturn(View.GONE).when(mLayout).getVisibility();
         mWindowManager.updateVisibility(/* canShow= */ true);
 
         verify(mWindowManager, never()).createLayout(anyBoolean());
-        verify(mLayout).setVisibility(View.VISIBLE);
+        verify(mLayout, atLeastOnce()).setVisibility(View.VISIBLE);
     }
 
     @Test
@@ -404,7 +413,14 @@
     public void testOnRestartButtonClicked() {
         mWindowManager.onRestartButtonClicked();
 
-        verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID);
+        @SuppressWarnings("unchecked")
+        ArgumentCaptor<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> restartCaptor =
+                ArgumentCaptor.forClass(Pair.class);
+
+        verify(mOnRestartButtonClicked).accept(restartCaptor.capture());
+        final Pair<TaskInfo, ShellTaskOrganizer.TaskListener> result = restartCaptor.getValue();
+        Assert.assertEquals(mTaskInfo, result.first);
+        Assert.assertEquals(mTaskListener, result.second);
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/RestartDialogLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/RestartDialogLayoutTest.java
new file mode 100644
index 0000000..e2dcdb0
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/RestartDialogLayoutTest.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.CheckBox;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.function.Consumer;
+
+/**
+ * Tests for {@link RestartDialogLayout}.
+ *
+ * Build/Install/Run:
+ *  atest WMShellUnitTests:RestartDialogLayoutTest
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class RestartDialogLayoutTest extends ShellTestCase  {
+
+    @Mock private Runnable mDismissCallback;
+    @Mock private Consumer<Boolean> mRestartCallback;
+
+    private RestartDialogLayout mLayout;
+    private View mDismissButton;
+    private View mRestartButton;
+    private View mDialogContainer;
+    private CheckBox mDontRepeatCheckBox;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mLayout = (RestartDialogLayout)
+                LayoutInflater.from(mContext).inflate(R.layout.letterbox_restart_dialog_layout,
+                        null);
+        mDismissButton = mLayout.findViewById(R.id.letterbox_restart_dialog_dismiss_button);
+        mRestartButton = mLayout.findViewById(R.id.letterbox_restart_dialog_restart_button);
+        mDialogContainer = mLayout.findViewById(R.id.letterbox_restart_dialog_container);
+        mDontRepeatCheckBox = mLayout.findViewById(R.id.letterbox_restart_dialog_checkbox);
+        mLayout.setDismissOnClickListener(mDismissCallback);
+        mLayout.setRestartOnClickListener(mRestartCallback);
+    }
+
+    @Test
+    public void testOnFinishInflate() {
+        assertEquals(mLayout.getDialogContainerView(),
+                mLayout.findViewById(R.id.letterbox_restart_dialog_container));
+        assertEquals(mLayout.getDialogTitle(),
+                mLayout.findViewById(R.id.letterbox_restart_dialog_title));
+        assertEquals(mLayout.getBackgroundDimDrawable(), mLayout.getBackground());
+        assertEquals(mLayout.getBackground().getAlpha(), 0);
+    }
+
+    @Test
+    public void testOnDismissButtonClicked() {
+        assertTrue(mDismissButton.performClick());
+
+        verify(mDismissCallback).run();
+    }
+
+    @Test
+    public void testOnRestartButtonClickedWithoutCheckbox() {
+        mDontRepeatCheckBox.setChecked(false);
+        assertTrue(mRestartButton.performClick());
+
+        verify(mRestartCallback).accept(false);
+    }
+
+    @Test
+    public void testOnRestartButtonClickedWithCheckbox() {
+        mDontRepeatCheckBox.setChecked(true);
+        assertTrue(mRestartButton.performClick());
+
+        verify(mRestartCallback).accept(true);
+    }
+
+    @Test
+    public void testOnBackgroundClickedDoesntDismiss() {
+        assertFalse(mLayout.performClick());
+
+        verify(mDismissCallback, never()).run();
+    }
+
+    @Test
+    public void testOnDialogContainerClicked() {
+        assertTrue(mDialogContainer.performClick());
+
+        verify(mDismissCallback, never()).run();
+        verify(mRestartCallback, never()).accept(anyBoolean());
+    }
+
+    @Test
+    public void testSetDismissOnClickListenerNull() {
+        mLayout.setDismissOnClickListener(null);
+
+        assertFalse(mDismissButton.performClick());
+        assertFalse(mLayout.performClick());
+        assertTrue(mDialogContainer.performClick());
+
+        verify(mDismissCallback, never()).run();
+    }
+
+    @Test
+    public void testSetRestartOnClickListenerNull() {
+        mLayout.setRestartOnClickListener(null);
+
+        assertFalse(mRestartButton.performClick());
+        assertFalse(mLayout.performClick());
+        assertTrue(mDialogContainer.performClick());
+
+        verify(mRestartCallback, never()).accept(anyBoolean());
+    }
+
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
index 7997a7e..43f8f7b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
@@ -22,6 +22,7 @@
 import static android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS;
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_NONE;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
@@ -446,7 +447,7 @@
         final ArgumentCaptor<WindowContainerTransaction> arg = ArgumentCaptor.forClass(
                 WindowContainerTransaction.class);
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
-            verify(mTransitions).startTransition(eq(TRANSIT_TO_FRONT), arg.capture(), any());
+            verify(mTransitions).startTransition(eq(TRANSIT_NONE), arg.capture(), any());
         } else {
             verify(mShellTaskOrganizer).applyTransaction(arg.capture());
         }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index f16beee..95e78a8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -26,6 +26,8 @@
 import android.os.Binder
 import android.testing.AndroidTestingRunner
 import android.view.WindowManager
+import android.view.WindowManager.TRANSIT_CHANGE
+import android.view.WindowManager.TRANSIT_NONE
 import android.view.WindowManager.TRANSIT_OPEN
 import android.view.WindowManager.TRANSIT_TO_FRONT
 import android.window.TransitionRequestInfo
@@ -55,6 +57,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.eq
 import org.mockito.ArgumentMatchers.isNull
 import org.mockito.Mock
 import org.mockito.Mockito
@@ -141,7 +144,7 @@
 
         controller.showDesktopApps()
 
-        val wct = getLatestWct()
+        val wct = getLatestWct(expectTransition = TRANSIT_NONE)
         assertThat(wct.hierarchyOps).hasSize(3)
         // Expect order to be from bottom: home, task1, task2
         wct.assertReorderAt(index = 0, homeTask)
@@ -159,7 +162,7 @@
 
         controller.showDesktopApps()
 
-        val wct = getLatestWct()
+        val wct = getLatestWct(expectTransition = TRANSIT_NONE)
         assertThat(wct.hierarchyOps).hasSize(3)
         // Expect order to be from bottom: home, task1, task2
         wct.assertReorderAt(index = 0, homeTask)
@@ -177,7 +180,7 @@
 
         controller.showDesktopApps()
 
-        val wct = getLatestWct()
+        val wct = getLatestWct(expectTransition = TRANSIT_NONE)
         assertThat(wct.hierarchyOps).hasSize(3)
         // Expect order to be from bottom: home, task1, task2
         wct.assertReorderAt(index = 0, homeTask)
@@ -191,7 +194,7 @@
 
         controller.showDesktopApps()
 
-        val wct = getLatestWct()
+        val wct = getLatestWct(expectTransition = TRANSIT_NONE)
         assertThat(wct.hierarchyOps).hasSize(1)
         wct.assertReorderAt(index = 0, homeTask)
     }
@@ -221,7 +224,7 @@
     fun moveToDesktop() {
         val task = setUpFullscreenTask()
         controller.moveToDesktop(task)
-        val wct = getLatestWct()
+        val wct = getLatestWct(expectTransition = TRANSIT_CHANGE)
         assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
             .isEqualTo(WINDOWING_MODE_FREEFORM)
     }
@@ -241,7 +244,7 @@
 
         controller.moveToDesktop(fullscreenTask)
 
-        with(getLatestWct()) {
+        with(getLatestWct(expectTransition = TRANSIT_CHANGE)) {
             assertThat(hierarchyOps).hasSize(3)
             assertReorderSequence(homeTask, freeformTask, fullscreenTask)
             assertThat(changes[fullscreenTask.token.asBinder()]?.windowingMode)
@@ -253,7 +256,7 @@
     fun moveToFullscreen() {
         val task = setUpFreeformTask()
         controller.moveToFullscreen(task)
-        val wct = getLatestWct()
+        val wct = getLatestWct(expectTransition = TRANSIT_CHANGE)
         assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
             .isEqualTo(WINDOWING_MODE_FULLSCREEN)
     }
@@ -415,10 +418,12 @@
         desktopModeTaskRepository.updateVisibleFreeformTasks(task.taskId, visible = false)
     }
 
-    private fun getLatestWct(): WindowContainerTransaction {
+    private fun getLatestWct(
+        @WindowManager.TransitionType expectTransition: Int = TRANSIT_OPEN
+    ): WindowContainerTransaction {
         val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
         if (ENABLE_SHELL_TRANSITIONS) {
-            verify(transitions).startTransition(anyInt(), arg.capture(), isNull())
+            verify(transitions).startTransition(eq(expectTransition), arg.capture(), isNull())
         } else {
             verify(shellTaskOrganizer).applyTransaction(arg.capture())
         }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
index 298d0a6..ec264a6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
@@ -32,6 +32,7 @@
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -57,17 +58,22 @@
     private PipBoundsAlgorithm mPipBoundsAlgorithm;
     private DisplayInfo mDefaultDisplayInfo;
     private PipBoundsState mPipBoundsState;
+    private PipSizeSpecHandler mPipSizeSpecHandler;
 
 
     @Before
     public void setUp() throws Exception {
         initializeMockResources();
-        mPipBoundsState = new PipBoundsState(mContext);
+        mPipSizeSpecHandler = new PipSizeSpecHandler(mContext);
+        mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler);
         mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState,
-                new PipSnapAlgorithm(), new PipKeepClearAlgorithmInterface() {});
+                new PipSnapAlgorithm(), new PipKeepClearAlgorithmInterface() {},
+                mPipSizeSpecHandler);
 
-        mPipBoundsState.setDisplayLayout(
-                new DisplayLayout(mDefaultDisplayInfo, mContext.getResources(), true, true));
+        DisplayLayout layout =
+                new DisplayLayout(mDefaultDisplayInfo, mContext.getResources(), true, true);
+        mPipBoundsState.setDisplayLayout(layout);
+        mPipSizeSpecHandler.setDisplayLayout(layout);
     }
 
     private void initializeMockResources() {
@@ -120,9 +126,7 @@
 
     @Test
     public void getDefaultBounds_noOverrideMinSize_matchesDefaultSizeAndAspectRatio() {
-        final Size defaultSize = mPipBoundsAlgorithm.getSizeForAspectRatio(DEFAULT_ASPECT_RATIO,
-                DEFAULT_MIN_EDGE_SIZE, mDefaultDisplayInfo.logicalWidth,
-                mDefaultDisplayInfo.logicalHeight);
+        final Size defaultSize = mPipSizeSpecHandler.getDefaultSize(DEFAULT_ASPECT_RATIO);
 
         mPipBoundsState.setOverrideMinSize(null);
         final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds();
@@ -296,9 +300,9 @@
                 (MAX_ASPECT_RATIO + DEFAULT_ASPECT_RATIO) / 2
         };
         final Size[] minimalSizes = new Size[] {
-                new Size((int) (100 * aspectRatios[0]), 100),
-                new Size((int) (100 * aspectRatios[1]), 100),
-                new Size((int) (100 * aspectRatios[2]), 100)
+                new Size((int) (200 * aspectRatios[0]), 200),
+                new Size((int) (200 * aspectRatios[1]), 200),
+                new Size((int) (200 * aspectRatios[2]), 200)
         };
         for (int i = 0; i < aspectRatios.length; i++) {
             final float aspectRatio = aspectRatios[i];
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
index 8e30f65..341a451 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
@@ -33,6 +33,7 @@
 
 import com.android.internal.util.function.TriConsumer;
 import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -57,7 +58,7 @@
 
     @Before
     public void setUp() {
-        mPipBoundsState = new PipBoundsState(mContext);
+        mPipBoundsState = new PipBoundsState(mContext, new PipSizeSpecHandler(mContext));
         mTestComponentName1 = new ComponentName(mContext, "component1");
         mTestComponentName2 = new ComponentName(mContext, "component2");
     }
@@ -161,10 +162,10 @@
     @Test
     public void testSetOverrideMinSize_notChanged_callbackNotInvoked() {
         final Runnable callback = mock(Runnable.class);
-        mPipBoundsState.setOverrideMinSize(new Size(5, 5));
+        mPipBoundsState.setOverrideMinSize(new Size(100, 150));
         mPipBoundsState.setOnMinimalSizeChangeCallback(callback);
 
-        mPipBoundsState.setOverrideMinSize(new Size(5, 5));
+        mPipBoundsState.setOverrideMinSize(new Size(100, 150));
 
         verify(callback, never()).run();
     }
@@ -174,11 +175,11 @@
         mPipBoundsState.setOverrideMinSize(null);
         assertEquals(0, mPipBoundsState.getOverrideMinEdgeSize());
 
-        mPipBoundsState.setOverrideMinSize(new Size(5, 10));
-        assertEquals(5, mPipBoundsState.getOverrideMinEdgeSize());
+        mPipBoundsState.setOverrideMinSize(new Size(100, 110));
+        assertEquals(100, mPipBoundsState.getOverrideMinEdgeSize());
 
-        mPipBoundsState.setOverrideMinSize(new Size(15, 10));
-        assertEquals(10, mPipBoundsState.getOverrideMinEdgeSize());
+        mPipBoundsState.setOverrideMinSize(new Size(150, 200));
+        assertEquals(150, mPipBoundsState.getOverrideMinEdgeSize());
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 17e7d74..e907cd3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -53,6 +53,7 @@
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.pip.phone.PhonePipMenuController;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 
 import org.junit.Before;
@@ -86,6 +87,7 @@
     private PipBoundsState mPipBoundsState;
     private PipTransitionState mPipTransitionState;
     private PipBoundsAlgorithm mPipBoundsAlgorithm;
+    private PipSizeSpecHandler mPipSizeSpecHandler;
 
     private ComponentName mComponent1;
     private ComponentName mComponent2;
@@ -95,13 +97,15 @@
         MockitoAnnotations.initMocks(this);
         mComponent1 = new ComponentName(mContext, "component1");
         mComponent2 = new ComponentName(mContext, "component2");
-        mPipBoundsState = new PipBoundsState(mContext);
+        mPipSizeSpecHandler = new PipSizeSpecHandler(mContext);
+        mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler);
         mPipTransitionState = new PipTransitionState();
         mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState,
-                new PipSnapAlgorithm(), new PipKeepClearAlgorithmInterface() {});
+                new PipSnapAlgorithm(), new PipKeepClearAlgorithmInterface() {},
+                mPipSizeSpecHandler);
         mMainExecutor = new TestShellExecutor();
-        mPipTaskOrganizer = new PipTaskOrganizer(mContext,
-                mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState,
+        mPipTaskOrganizer = new PipTaskOrganizer(mContext, mMockSyncTransactionQueue,
+                mPipTransitionState, mPipBoundsState, mPipSizeSpecHandler,
                 mPipBoundsAlgorithm, mMockPhonePipMenuController, mMockPipAnimationController,
                 mMockPipSurfaceTransactionHelper, mMockPipTransitionController,
                 mMockPipParamsChangedForwarder, mMockOptionalSplitScreen, mMockDisplayController,
@@ -253,8 +257,10 @@
 
     private void preparePipTaskOrg() {
         final DisplayInfo info = new DisplayInfo();
-        mPipBoundsState.setDisplayLayout(new DisplayLayout(info,
-                mContext.getResources(), true, true));
+        DisplayLayout layout = new DisplayLayout(info,
+                mContext.getResources(), true, true);
+        mPipBoundsState.setDisplayLayout(layout);
+        mPipSizeSpecHandler.setDisplayLayout(layout);
         mPipTaskOrganizer.setOneShotAnimationType(PipAnimationController.ANIM_TYPE_ALPHA);
         mPipTaskOrganizer.setSurfaceControlTransactionFactory(
                 MockSurfaceControlHelper::createMockSurfaceControlTransaction);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 35c09a1..4a68287 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -65,7 +65,6 @@
 import com.android.wm.shell.pip.PipTaskOrganizer;
 import com.android.wm.shell.pip.PipTransitionController;
 import com.android.wm.shell.pip.PipTransitionState;
-import com.android.wm.shell.recents.IRecentTasksListener;
 import com.android.wm.shell.sysui.ShellCommandHandler;
 import com.android.wm.shell.sysui.ShellController;
 import com.android.wm.shell.sysui.ShellInit;
@@ -108,6 +107,7 @@
     @Mock private PipMotionHelper mMockPipMotionHelper;
     @Mock private WindowManagerShellWrapper mMockWindowManagerShellWrapper;
     @Mock private PipBoundsState mMockPipBoundsState;
+    @Mock private PipSizeSpecHandler mMockPipSizeSpecHandler;
     @Mock private TaskStackListenerImpl mMockTaskStackListener;
     @Mock private ShellExecutor mMockExecutor;
     @Mock private Optional<OneHandedController> mMockOneHandedController;
@@ -130,11 +130,12 @@
         mPipController = new PipController(mContext, mShellInit, mMockShellCommandHandler,
                 mShellController, mMockDisplayController, mMockPipAnimationController,
                 mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
-                mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
-                mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTransitionState,
-                mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
-                mMockTaskStackListener, mMockPipParamsChangedForwarder,
-                mMockDisplayInsetsController, mMockOneHandedController, mMockExecutor);
+                mMockPipBoundsState, mMockPipSizeSpecHandler, mMockPipMotionHelper,
+                mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer,
+                mMockPipTransitionState, mMockPipTouchHandler, mMockPipTransitionController,
+                mMockWindowManagerShellWrapper, mMockTaskStackListener,
+                mMockPipParamsChangedForwarder, mMockDisplayInsetsController,
+                mMockOneHandedController, mMockExecutor);
         mShellInit.init();
         when(mMockPipBoundsAlgorithm.getSnapAlgorithm()).thenReturn(mMockPipSnapAlgorithm);
         when(mMockPipTouchHandler.getMotionHelper()).thenReturn(mMockPipMotionHelper);
@@ -220,11 +221,12 @@
         assertNull(PipController.create(spyContext, shellInit, mMockShellCommandHandler,
                 mShellController, mMockDisplayController, mMockPipAnimationController,
                 mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
-                mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
-                mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTransitionState,
-                mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
-                mMockTaskStackListener, mMockPipParamsChangedForwarder,
-                mMockDisplayInsetsController, mMockOneHandedController, mMockExecutor));
+                mMockPipBoundsState, mMockPipSizeSpecHandler, mMockPipMotionHelper,
+                mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer,
+                mMockPipTransitionState, mMockPipTouchHandler, mMockPipTransitionController,
+                mMockWindowManagerShellWrapper, mMockTaskStackListener,
+                mMockPipParamsChangedForwarder, mMockDisplayInsetsController,
+                mMockOneHandedController, mMockExecutor));
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
index c1993b2..c7b9eb3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
@@ -85,15 +85,18 @@
 
     private PipBoundsState mPipBoundsState;
 
+    private PipSizeSpecHandler mPipSizeSpecHandler;
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mPipBoundsState = new PipBoundsState(mContext);
+        mPipSizeSpecHandler = new PipSizeSpecHandler(mContext);
+        mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler);
         final PipSnapAlgorithm pipSnapAlgorithm = new PipSnapAlgorithm();
         final PipKeepClearAlgorithmInterface pipKeepClearAlgorithm =
                 new PipKeepClearAlgorithmInterface() {};
         final PipBoundsAlgorithm pipBoundsAlgorithm = new PipBoundsAlgorithm(mContext,
-                mPipBoundsState, pipSnapAlgorithm, pipKeepClearAlgorithm);
+                mPipBoundsState, pipSnapAlgorithm, pipKeepClearAlgorithm, mPipSizeSpecHandler);
         final PipMotionHelper motionHelper = new PipMotionHelper(mContext, mPipBoundsState,
                 mPipTaskOrganizer, mPhonePipMenuController, pipSnapAlgorithm,
                 mMockPipTransitionController, mFloatingContentCoordinator);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipSizeSpecHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipSizeSpecHandlerTest.java
new file mode 100644
index 0000000..d9ff7d1
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipSizeSpecHandlerTest.java
@@ -0,0 +1,214 @@
+/*
+ * 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.wm.shell.pip.phone;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.SystemProperties;
+import android.testing.AndroidTestingRunner;
+import android.util.Size;
+import android.view.DisplayInfo;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayLayout;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.exceptions.misusing.InvalidUseOfMatchersException;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Function;
+
+/**
+ * Unit test against {@link PipSizeSpecHandler} with feature flag on.
+ */
+@RunWith(AndroidTestingRunner.class)
+public class PipSizeSpecHandlerTest extends ShellTestCase {
+    /** A sample overridden min edge size. */
+    private static final int OVERRIDE_MIN_EDGE_SIZE = 40;
+    /** A sample default min edge size */
+    private static final int DEFAULT_MIN_EDGE_SIZE = 40;
+    /** Display edge size */
+    private static final int DISPLAY_EDGE_SIZE = 1000;
+    /** Default sizing percentage */
+    private static final float DEFAULT_PERCENT = 0.6f;
+    /** Minimum sizing percentage */
+    private static final float MIN_PERCENT = 0.5f;
+    /** Aspect ratio that the new PIP size spec logic optimizes for. */
+    private static final float OPTIMIZED_ASPECT_RATIO = 9f / 16;
+
+    /** A map of aspect ratios to be tested to expected sizes */
+    private static Map<Float, Size> sExpectedMaxSizes;
+    private static Map<Float, Size> sExpectedDefaultSizes;
+    private static Map<Float, Size> sExpectedMinSizes;
+    /** A static mockito session object to mock {@link SystemProperties} */
+    private static StaticMockitoSession sStaticMockitoSession;
+
+    @Mock private Context mContext;
+    @Mock private Resources mResources;
+
+    private PipSizeSpecHandler mPipSizeSpecHandler;
+
+    /**
+     * Sets up static Mockito session for SystemProperties and mocks necessary static methods.
+     */
+    private static void setUpStaticSystemPropertiesSession() {
+        sStaticMockitoSession = mockitoSession()
+                .mockStatic(SystemProperties.class).startMocking();
+        // make sure the feature flag is on
+        when(SystemProperties.getBoolean(anyString(), anyBoolean())).thenReturn(true);
+        when(SystemProperties.get(anyString(), anyString())).thenAnswer(invocation -> {
+            String property = invocation.getArgument(0);
+            if (property.equals("com.android.wm.shell.pip.phone.def_percentage")) {
+                return Float.toString(DEFAULT_PERCENT);
+            } else if (property.equals("com.android.wm.shell.pip.phone.min_percentage")) {
+                return Float.toString(MIN_PERCENT);
+            }
+
+            // throw an exception if illegal arguments are used for these tests
+            throw new InvalidUseOfMatchersException(
+                String.format("Argument %s does not match", property)
+            );
+        });
+    }
+
+    /**
+     * Initializes the map with the aspect ratios to be tested and corresponding expected max sizes.
+     */
+    private static void initExpectedSizes() {
+        sExpectedMaxSizes = new HashMap<>();
+        sExpectedDefaultSizes = new HashMap<>();
+        sExpectedMinSizes = new HashMap<>();
+
+        sExpectedMaxSizes.put(16f / 9, new Size(1000, 562));
+        sExpectedDefaultSizes.put(16f / 9, new Size(600, 337));
+        sExpectedMinSizes.put(16f / 9, new Size(499, 281));
+
+        sExpectedMaxSizes.put(4f / 3, new Size(892, 669));
+        sExpectedDefaultSizes.put(4f / 3, new Size(535, 401));
+        sExpectedMinSizes.put(4f / 3, new Size(445, 334));
+
+        sExpectedMaxSizes.put(3f / 4, new Size(669, 892));
+        sExpectedDefaultSizes.put(3f / 4, new Size(401, 535));
+        sExpectedMinSizes.put(3f / 4, new Size(334, 445));
+
+        sExpectedMaxSizes.put(9f / 16, new Size(562, 999));
+        sExpectedDefaultSizes.put(9f / 16, new Size(337, 599));
+        sExpectedMinSizes.put(9f / 16, new Size(281, 499));
+    }
+
+    private void forEveryTestCaseCheck(Map<Float, Size> expectedSizes,
+            Function<Float, Size> callback) {
+        for (Map.Entry<Float, Size> expectedSizesEntry : expectedSizes.entrySet()) {
+            float aspectRatio = expectedSizesEntry.getKey();
+            Size expectedSize = expectedSizesEntry.getValue();
+
+            Assert.assertEquals(expectedSize, callback.apply(aspectRatio));
+        }
+    }
+
+    @Before
+    public void setUp() {
+        initExpectedSizes();
+        setUpStaticSystemPropertiesSession();
+
+        when(mResources.getDimensionPixelSize(anyInt())).thenReturn(DEFAULT_MIN_EDGE_SIZE);
+        when(mResources.getFloat(anyInt())).thenReturn(OPTIMIZED_ASPECT_RATIO);
+        when(mResources.getString(anyInt())).thenReturn("0x0");
+        when(mResources.getDisplayMetrics())
+                .thenReturn(getContext().getResources().getDisplayMetrics());
+
+        // set up the mock context for spec handler specifically
+        when(mContext.getResources()).thenReturn(mResources);
+
+        mPipSizeSpecHandler = new PipSizeSpecHandler(mContext);
+
+        // no overridden min edge size by default
+        mPipSizeSpecHandler.setOverrideMinSize(null);
+
+        DisplayInfo displayInfo = new DisplayInfo();
+        displayInfo.logicalWidth = DISPLAY_EDGE_SIZE;
+        displayInfo.logicalHeight = DISPLAY_EDGE_SIZE;
+
+        // use the parent context (not the mocked one) to obtain the display layout
+        // this is done to avoid unnecessary mocking while allowing for custom display dimensions
+        DisplayLayout displayLayout = new DisplayLayout(displayInfo, getContext().getResources(),
+                false, false);
+        mPipSizeSpecHandler.setDisplayLayout(displayLayout);
+    }
+
+    @After
+    public void cleanUp() {
+        sStaticMockitoSession.finishMocking();
+    }
+
+    @Test
+    public void testGetMaxSize() {
+        forEveryTestCaseCheck(sExpectedMaxSizes,
+                (aspectRatio) -> mPipSizeSpecHandler.getMaxSize(aspectRatio));
+    }
+
+    @Test
+    public void testGetDefaultSize() {
+        forEveryTestCaseCheck(sExpectedDefaultSizes,
+                (aspectRatio) -> mPipSizeSpecHandler.getDefaultSize(aspectRatio));
+    }
+
+    @Test
+    public void testGetMinSize() {
+        forEveryTestCaseCheck(sExpectedMinSizes,
+                (aspectRatio) -> mPipSizeSpecHandler.getMinSize(aspectRatio));
+    }
+
+    @Test
+    public void testGetSizeForAspectRatio_noOverrideMinSize() {
+        // an initial size with 16:9 aspect ratio
+        Size initSize = new Size(600, 337);
+
+        Size expectedSize = new Size(337, 599);
+        Size actualSize = mPipSizeSpecHandler.getSizeForAspectRatio(initSize, 9f / 16);
+
+        Assert.assertEquals(expectedSize, actualSize);
+    }
+
+    @Test
+    public void testGetSizeForAspectRatio_withOverrideMinSize() {
+        // an initial size with a 1:1 aspect ratio
+        mPipSizeSpecHandler.setOverrideMinSize(new Size(OVERRIDE_MIN_EDGE_SIZE,
+                OVERRIDE_MIN_EDGE_SIZE));
+        // make sure initial size is same as override min size
+        Size initSize = mPipSizeSpecHandler.getOverrideMinSize();
+
+        Size expectedSize = new Size(40, 71);
+        Size actualSize = mPipSizeSpecHandler.getSizeForAspectRatio(initSize, 9f / 16);
+
+        Assert.assertEquals(expectedSize, actualSize);
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
index 8ad2932..5c4863f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
@@ -25,6 +25,7 @@
 import android.graphics.Rect;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.util.Size;
 
 import androidx.test.filters.SmallTest;
 
@@ -90,6 +91,7 @@
     private PipSnapAlgorithm mPipSnapAlgorithm;
     private PipMotionHelper mMotionHelper;
     private PipResizeGestureHandler mPipResizeGestureHandler;
+    private PipSizeSpecHandler mPipSizeSpecHandler;
 
     private DisplayLayout mDisplayLayout;
     private Rect mInsetBounds;
@@ -103,16 +105,17 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mPipBoundsState = new PipBoundsState(mContext);
+        mPipSizeSpecHandler = new PipSizeSpecHandler(mContext);
+        mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler);
         mPipSnapAlgorithm = new PipSnapAlgorithm();
         mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState, mPipSnapAlgorithm,
-                new PipKeepClearAlgorithmInterface() {});
+                new PipKeepClearAlgorithmInterface() {}, mPipSizeSpecHandler);
         PipMotionHelper pipMotionHelper = new PipMotionHelper(mContext, mPipBoundsState,
                 mPipTaskOrganizer, mPhonePipMenuController, mPipSnapAlgorithm,
                 mMockPipTransitionController, mFloatingContentCoordinator);
         mPipTouchHandler = new PipTouchHandler(mContext, mShellInit, mPhonePipMenuController,
-                mPipBoundsAlgorithm, mPipBoundsState, mPipTaskOrganizer, pipMotionHelper,
-                mFloatingContentCoordinator, mPipUiEventLogger, mMainExecutor);
+                mPipBoundsAlgorithm, mPipBoundsState, mPipSizeSpecHandler, mPipTaskOrganizer,
+                pipMotionHelper, mFloatingContentCoordinator, mPipUiEventLogger, mMainExecutor);
         // We aren't actually using ShellInit, so just call init directly
         mPipTouchHandler.onInit();
         mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper());
@@ -122,6 +125,7 @@
 
         mDisplayLayout = new DisplayLayout(mContext, mContext.getDisplay());
         mPipBoundsState.setDisplayLayout(mDisplayLayout);
+        mPipSizeSpecHandler.setDisplayLayout(mDisplayLayout);
         mInsetBounds = new Rect(mPipBoundsState.getDisplayBounds().left + INSET,
                 mPipBoundsState.getDisplayBounds().top + INSET,
                 mPipBoundsState.getDisplayBounds().right - INSET,
@@ -154,13 +158,17 @@
         mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mPipBounds, mCurBounds,
                 mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
 
+        // getting the expected min and max size
+        float aspectRatio = (float) mPipBounds.width() / mPipBounds.height();
+        Size expectedMinSize = mPipSizeSpecHandler.getMinSize(aspectRatio);
+        Size expectedMaxSize = mPipSizeSpecHandler.getMaxSize(aspectRatio);
+
         assertEquals(expectedMovementBounds, mPipBoundsState.getNormalMovementBounds());
         verify(mPipResizeGestureHandler, times(1))
-                .updateMinSize(mPipBounds.width(), mPipBounds.height());
+                .updateMinSize(expectedMinSize.getWidth(), expectedMinSize.getHeight());
 
         verify(mPipResizeGestureHandler, times(1))
-                .updateMaxSize(shorterLength - 2 * mInsetBounds.left,
-                        shorterLength - 2 * mInsetBounds.left);
+                .updateMaxSize(expectedMaxSize.getWidth(), expectedMaxSize.getHeight());
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 82392ad9..b542fae 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -253,10 +253,10 @@
     }
 
     @Test
-    public void testGetRecentTasks_groupActiveFreeformTasks() {
+    public void testGetRecentTasks_hasActiveDesktopTasks_proto2Enabled_groupFreeformTasks() {
         StaticMockitoSession mockitoSession = mockitoSession().mockStatic(
                 DesktopModeStatus.class).startMocking();
-        when(DesktopModeStatus.isActive(any())).thenReturn(true);
+        when(DesktopModeStatus.isProto2Enabled()).thenReturn(true);
 
         ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
         ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
@@ -293,6 +293,39 @@
     }
 
     @Test
+    public void testGetRecentTasks_hasActiveDesktopTasks_proto2Disabled_doNotGroupFreeformTasks() {
+        StaticMockitoSession mockitoSession = mockitoSession().mockStatic(
+                DesktopModeStatus.class).startMocking();
+        when(DesktopModeStatus.isProto2Enabled()).thenReturn(false);
+
+        ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
+        ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
+        ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
+        ActivityManager.RecentTaskInfo t4 = makeTaskInfo(4);
+        setRawList(t1, t2, t3, t4);
+
+        when(mDesktopModeTaskRepository.isActiveTask(1)).thenReturn(true);
+        when(mDesktopModeTaskRepository.isActiveTask(3)).thenReturn(true);
+
+        ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks(
+                MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
+
+        // Expect no grouping of tasks
+        assertEquals(4, recentTasks.size());
+        assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, recentTasks.get(0).getType());
+        assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, recentTasks.get(1).getType());
+        assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, recentTasks.get(2).getType());
+        assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, recentTasks.get(3).getType());
+
+        assertEquals(t1, recentTasks.get(0).getTaskInfo1());
+        assertEquals(t2, recentTasks.get(1).getTaskInfo1());
+        assertEquals(t3, recentTasks.get(2).getTaskInfo1());
+        assertEquals(t4, recentTasks.get(3).getTaskInfo1());
+
+        mockitoSession.finishMocking();
+    }
+
+    @Test
     public void testRemovedTaskRemovesSplit() {
         ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
         ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index ea3af9d..d0e2601 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -27,8 +27,6 @@
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -201,7 +199,6 @@
         ActivityManager.RunningTaskInfo topRunningTask =
                 createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
         doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask();
-        doReturn(true).when(mStageCoordinator).isValidToEnterSplitScreen(any());
 
         mSplitScreenController.startIntent(pendingIntent, null, SPLIT_POSITION_TOP_OR_LEFT, null);
 
@@ -222,7 +219,6 @@
         ActivityManager.RunningTaskInfo topRunningTask =
                 createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
         doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask();
-        doReturn(true).when(mStageCoordinator).isValidToEnterSplitScreen(any());
         // Put the same component into a task in the background
         ActivityManager.RecentTaskInfo sameTaskInfo = new ActivityManager.RecentTaskInfo();
         doReturn(sameTaskInfo).when(mRecentTasks).findTaskInBackground(any());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt
new file mode 100644
index 0000000..8f84008
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt
@@ -0,0 +1,210 @@
+/*
+ * 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.wm.shell.windowdecor
+
+import android.os.SystemClock
+import android.testing.AndroidTestingRunner
+import android.view.MotionEvent
+import android.view.InputDevice
+import androidx.test.filters.SmallTest
+import org.junit.After
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.any
+import org.mockito.Mockito.argThat
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+
+/**
+ * Tests for [DragDetector].
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:DragDetectorTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DragDetectorTest {
+    private val motionEvents = mutableListOf<MotionEvent>()
+
+    @Mock
+    private lateinit var eventHandler: DragDetector.MotionEventHandler
+
+    private lateinit var dragDetector: DragDetector
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        `when`(eventHandler.handleMotionEvent(any())).thenReturn(true)
+
+        dragDetector = DragDetector(eventHandler)
+        dragDetector.setTouchSlop(SLOP)
+    }
+
+    @After
+    fun tearDown() {
+        motionEvents.forEach {
+            it.recycle()
+        }
+        motionEvents.clear()
+    }
+
+    @Test
+    fun testNoMove_passesDownAndUp() {
+        assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
+        verify(eventHandler).handleMotionEvent(argThat {
+            return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
+                    it.source == InputDevice.SOURCE_TOUCHSCREEN
+        })
+
+        assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_UP)))
+        verify(eventHandler).handleMotionEvent(argThat {
+            return@argThat it.action == MotionEvent.ACTION_UP && it.x == X && it.y == Y &&
+                    it.source == InputDevice.SOURCE_TOUCHSCREEN
+        })
+    }
+
+    @Test
+    fun testMoveInSlop_touch_passesDownAndUp() {
+        `when`(eventHandler.handleMotionEvent(argThat {
+            return@argThat it.action == MotionEvent.ACTION_DOWN
+        })).thenReturn(false)
+
+        assertFalse(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
+        verify(eventHandler).handleMotionEvent(argThat {
+            return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
+                    it.source == InputDevice.SOURCE_TOUCHSCREEN
+        })
+
+        val newX = X + SLOP - 1
+        assertFalse(
+                dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y)))
+        verify(eventHandler, never()).handleMotionEvent(argThat {
+            return@argThat it.action == MotionEvent.ACTION_MOVE
+        })
+
+        assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_UP, newX, Y)))
+        verify(eventHandler).handleMotionEvent(argThat {
+            return@argThat it.action == MotionEvent.ACTION_UP && it.x == newX && it.y == Y &&
+                    it.source == InputDevice.SOURCE_TOUCHSCREEN
+        })
+    }
+
+    @Test
+    fun testMoveInSlop_mouse_passesDownMoveAndUp() {
+        `when`(eventHandler.handleMotionEvent(argThat {
+            it.action == MotionEvent.ACTION_DOWN
+        })).thenReturn(false)
+
+        assertFalse(dragDetector.onMotionEvent(
+                createMotionEvent(MotionEvent.ACTION_DOWN, isTouch = false)))
+        verify(eventHandler).handleMotionEvent(argThat {
+            return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
+                    it.source == InputDevice.SOURCE_MOUSE
+        })
+
+        val newX = X + SLOP - 1
+        assertTrue(dragDetector.onMotionEvent(
+                createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y, isTouch = false)))
+        verify(eventHandler).handleMotionEvent(argThat {
+            return@argThat it.action == MotionEvent.ACTION_MOVE && it.x == newX && it.y == Y &&
+                    it.source == InputDevice.SOURCE_MOUSE
+        })
+
+        assertTrue(dragDetector.onMotionEvent(
+                createMotionEvent(MotionEvent.ACTION_UP, newX, Y, isTouch = false)))
+        verify(eventHandler).handleMotionEvent(argThat {
+            return@argThat it.action == MotionEvent.ACTION_UP && it.x == newX && it.y == Y &&
+                    it.source == InputDevice.SOURCE_MOUSE
+        })
+    }
+
+    @Test
+    fun testMoveBeyondSlop_passesDownMoveAndUp() {
+        `when`(eventHandler.handleMotionEvent(argThat {
+            it.action == MotionEvent.ACTION_DOWN
+        })).thenReturn(false)
+
+        assertFalse(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
+        verify(eventHandler).handleMotionEvent(argThat {
+            return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
+                    it.source == InputDevice.SOURCE_TOUCHSCREEN
+        })
+
+        val newX = X + SLOP + 1
+        assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y)))
+        verify(eventHandler).handleMotionEvent(argThat {
+            return@argThat it.action == MotionEvent.ACTION_MOVE && it.x == newX && it.y == Y &&
+                    it.source == InputDevice.SOURCE_TOUCHSCREEN
+        })
+
+        assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_UP, newX, Y)))
+        verify(eventHandler).handleMotionEvent(argThat {
+            return@argThat it.action == MotionEvent.ACTION_UP && it.x == newX && it.y == Y &&
+                    it.source == InputDevice.SOURCE_TOUCHSCREEN
+        })
+    }
+
+    @Test
+    fun testPassesHoverEnter() {
+        `when`(eventHandler.handleMotionEvent(argThat {
+            it.action == MotionEvent.ACTION_HOVER_ENTER
+        })).thenReturn(false)
+
+        assertFalse(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_HOVER_ENTER)))
+        verify(eventHandler).handleMotionEvent(argThat {
+            return@argThat it.action == MotionEvent.ACTION_HOVER_ENTER && it.x == X && it.y == Y
+        })
+    }
+
+    @Test
+    fun testPassesHoverMove() {
+        assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_HOVER_MOVE)))
+        verify(eventHandler).handleMotionEvent(argThat {
+            return@argThat it.action == MotionEvent.ACTION_HOVER_MOVE && it.x == X && it.y == Y
+        })
+    }
+
+    @Test
+    fun testPassesHoverExit() {
+        assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_HOVER_EXIT)))
+        verify(eventHandler).handleMotionEvent(argThat {
+            return@argThat it.action == MotionEvent.ACTION_HOVER_EXIT && it.x == X && it.y == Y
+        })
+    }
+
+    private fun createMotionEvent(action: Int, x: Float = X, y: Float = Y, isTouch: Boolean = true):
+            MotionEvent {
+        val time = SystemClock.uptimeMillis()
+        val ev = MotionEvent.obtain(time, time, action, x, y, 0)
+        ev.source = if (isTouch) InputDevice.SOURCE_TOUCHSCREEN else InputDevice.SOURCE_MOUSE
+        motionEvents.add(ev)
+        return ev
+    }
+
+    companion object {
+        private const val SLOP = 10
+        private const val X = 123f
+        private const val Y = 234f
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
index ac10ddb..f185a8a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
@@ -1,6 +1,7 @@
 package com.android.wm.shell.windowdecor
 
 import android.app.ActivityManager
+import android.app.WindowConfiguration
 import android.graphics.Rect
 import android.os.IBinder
 import android.testing.AndroidTestingRunner
@@ -10,6 +11,7 @@
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_RIGHT
+import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_TOP
 import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_UNDEFINED
 import org.junit.Before
 import org.junit.Test
@@ -63,8 +65,92 @@
     }
 
     @Test
+    fun testDragResize_notMove_skipsTransactionOnEnd() {
+        taskPositioner.onDragPositioningStart(
+                CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+                STARTING_BOUNDS.left.toFloat(),
+                STARTING_BOUNDS.top.toFloat()
+        )
+
+        taskPositioner.onDragPositioningEnd(
+                STARTING_BOUNDS.left.toFloat() + 10,
+                STARTING_BOUNDS.top.toFloat() + 10
+        )
+
+        verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
+            return@argThat wct.changes.any { (token, change) ->
+                token == taskBinder &&
+                        ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)
+            }
+        })
+    }
+
+    @Test
+    fun testDragResize_noEffectiveMove_skipsTransactionOnMoveAndEnd() {
+        taskPositioner.onDragPositioningStart(
+                CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+                STARTING_BOUNDS.left.toFloat(),
+                STARTING_BOUNDS.top.toFloat()
+        )
+
+        taskPositioner.onDragPositioningMove(
+                STARTING_BOUNDS.left.toFloat(),
+                STARTING_BOUNDS.top.toFloat()
+        )
+
+        taskPositioner.onDragPositioningEnd(
+                STARTING_BOUNDS.left.toFloat() + 10,
+                STARTING_BOUNDS.top.toFloat() + 10
+        )
+
+        verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
+            return@argThat wct.changes.any { (token, change) ->
+                token == taskBinder &&
+                        ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)
+            }
+        })
+    }
+
+    @Test
+    fun testDragResize_hasEffectiveMove_issuesTransactionOnMoveAndEnd() {
+        taskPositioner.onDragPositioningStart(
+                CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+                STARTING_BOUNDS.left.toFloat(),
+                STARTING_BOUNDS.top.toFloat()
+        )
+
+        taskPositioner.onDragPositioningMove(
+                STARTING_BOUNDS.left.toFloat() + 10,
+                STARTING_BOUNDS.top.toFloat()
+        )
+        val rectAfterMove = Rect(STARTING_BOUNDS)
+        rectAfterMove.right += 10
+        verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+            return@argThat wct.changes.any { (token, change) ->
+                token == taskBinder &&
+                        (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
+                        change.configuration.windowConfiguration.bounds == rectAfterMove
+            }
+        })
+
+        taskPositioner.onDragPositioningEnd(
+                STARTING_BOUNDS.left.toFloat() + 10,
+                STARTING_BOUNDS.top.toFloat() + 10
+        )
+        val rectAfterEnd = Rect(rectAfterMove)
+        rectAfterEnd.top += 10
+        verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+            return@argThat wct.changes.any { (token, change) ->
+                token == taskBinder &&
+                        (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
+                        change.configuration.windowConfiguration.bounds == rectAfterEnd
+            }
+        })
+    }
+
+    @Test
     fun testDragResize_move_skipsDragResizingFlag() {
-        taskPositioner.onDragResizeStart(
+        taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_UNDEFINED, // Move
                 STARTING_BOUNDS.left.toFloat(),
                 STARTING_BOUNDS.top.toFloat()
@@ -73,12 +159,12 @@
         // Move the task 10px to the right.
         val newX = STARTING_BOUNDS.left.toFloat() + 10
         val newY = STARTING_BOUNDS.top.toFloat()
-        taskPositioner.onDragResizeMove(
+        taskPositioner.onDragPositioningMove(
                 newX,
                 newY
         )
 
-        taskPositioner.onDragResizeEnd(newX, newY)
+        taskPositioner.onDragPositioningEnd(newX, newY)
 
         verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
             return@argThat wct.changes.any { (token, change) ->
@@ -91,7 +177,7 @@
 
     @Test
     fun testDragResize_resize_setsDragResizingFlag() {
-        taskPositioner.onDragResizeStart(
+        taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_RIGHT, // Resize right
                 STARTING_BOUNDS.left.toFloat(),
                 STARTING_BOUNDS.top.toFloat()
@@ -100,12 +186,12 @@
         // Resize the task by 10px to the right.
         val newX = STARTING_BOUNDS.right.toFloat() + 10
         val newY = STARTING_BOUNDS.top.toFloat()
-        taskPositioner.onDragResizeMove(
+        taskPositioner.onDragPositioningMove(
                 newX,
                 newY
         )
 
-        taskPositioner.onDragResizeEnd(newX, newY)
+        taskPositioner.onDragPositioningEnd(newX, newY)
 
         verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
             return@argThat wct.changes.any { (token, change) ->
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index ec4f17f..b80edce 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -107,6 +107,7 @@
     private SurfaceControl.Transaction mMockSurfaceControlFinishT;
     private SurfaceControl.Transaction mMockSurfaceControlAddWindowT;
     private WindowDecoration.RelayoutParams mRelayoutParams = new WindowDecoration.RelayoutParams();
+    private int mCaptionMenuWidthId;
 
     @Before
     public void setUp() {
@@ -116,8 +117,7 @@
 
         mRelayoutParams.mLayoutResId = 0;
         mRelayoutParams.mCaptionHeightId = R.dimen.test_freeform_decor_caption_height;
-        // Caption should have fixed width except in testLayoutResultCalculation_fullWidthCaption()
-        mRelayoutParams.mCaptionWidthId = R.dimen.test_freeform_decor_caption_width;
+        mCaptionMenuWidthId = R.dimen.test_freeform_decor_caption_menu_width;
         mRelayoutParams.mShadowRadiusId = R.dimen.test_window_decor_shadow_radius;
 
         doReturn(mMockSurfaceControlViewHost).when(mMockSurfaceControlViewHostFactory)
@@ -240,7 +240,7 @@
         verify(captionContainerSurfaceBuilder).setParent(decorContainerSurface);
         verify(captionContainerSurfaceBuilder).setContainerLayer();
         verify(mMockSurfaceControlStartT).setPosition(captionContainerSurface, 20, 40);
-        verify(mMockSurfaceControlStartT).setWindowCrop(captionContainerSurface, 432, 64);
+        verify(mMockSurfaceControlStartT).setWindowCrop(captionContainerSurface, 300, 64);
         verify(mMockSurfaceControlStartT).show(captionContainerSurface);
 
         verify(mMockSurfaceControlViewHostFactory).create(any(), eq(defaultDisplay), any());
@@ -248,7 +248,7 @@
         verify(mMockSurfaceControlViewHost)
                 .setView(same(mMockView),
                         argThat(lp -> lp.height == 64
-                                && lp.width == 432
+                                && lp.width == 300
                                 && (lp.flags & LayoutParams.FLAG_NOT_FOCUSABLE) != 0));
         if (ViewRootImpl.CAPTION_ON_SHELL) {
             verify(mMockView).setTaskFocusState(true);
@@ -431,7 +431,7 @@
         verify(additionalWindowSurfaceBuilder).setParent(decorContainerSurface);
         verify(additionalWindowSurfaceBuilder).build();
         verify(mMockSurfaceControlAddWindowT).setPosition(additionalWindowSurface, 20, 40);
-        verify(mMockSurfaceControlAddWindowT).setWindowCrop(additionalWindowSurface, 432, 64);
+        verify(mMockSurfaceControlAddWindowT).setWindowCrop(additionalWindowSurface, 442, 74);
         verify(mMockSurfaceControlAddWindowT).show(additionalWindowSurface);
         verify(mMockSurfaceControlViewHostFactory, Mockito.times(2))
                 .create(any(), eq(defaultDisplay), any());
@@ -484,7 +484,6 @@
         final SurfaceControl taskSurface = mock(SurfaceControl.class);
         final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface);
 
-        mRelayoutParams.mCaptionWidthId = Resources.ID_NULL;
         windowDecor.relayout(taskInfo);
 
         verify(captionContainerSurfaceBuilder).setParent(decorContainerSurface);
@@ -558,7 +557,7 @@
             final Resources resources = mDecorWindowContext.getResources();
             int x = mRelayoutParams.mCaptionX;
             int y = mRelayoutParams.mCaptionY;
-            int width = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionWidthId);
+            int width = loadDimensionPixelSize(resources, mCaptionMenuWidthId);
             int height = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionHeightId);
             String name = "Test Window";
             WindowDecoration.AdditionalWindow additionalWindow =
@@ -566,7 +565,7 @@
                             mMockSurfaceControlAddWindowT,
                             x - mRelayoutResult.mDecorContainerOffsetX,
                             y - mRelayoutResult.mDecorContainerOffsetY,
-                            width, height);
+                            width, height, 10);
             return additionalWindow;
         }
     }
diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp
index 90c4440..2ab7a58 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.cpp
+++ b/libs/hwui/pipeline/skia/ShaderCache.cpp
@@ -174,14 +174,13 @@
 
 void ShaderCache::saveToDiskLocked() {
     ATRACE_NAME("ShaderCache::saveToDiskLocked");
-    if (mInitialized && mBlobCache && mSavePending) {
+    if (mInitialized && mBlobCache) {
         if (mIDHash.size()) {
             auto key = sIDKey;
             set(mBlobCache.get(), &key, sizeof(key), mIDHash.data(), mIDHash.size());
         }
         mBlobCache->writeToFile();
     }
-    mSavePending = false;
 }
 
 void ShaderCache::store(const SkData& key, const SkData& data, const SkString& /*description*/) {
@@ -224,10 +223,10 @@
     }
     set(bc, key.data(), keySize, value, valueSize);
 
-    if (!mSavePending && mDeferredSaveDelay > 0) {
+    if (!mSavePending && mDeferredSaveDelayMs > 0) {
         mSavePending = true;
         std::thread deferredSaveThread([this]() {
-            sleep(mDeferredSaveDelay);
+            usleep(mDeferredSaveDelayMs * 1000);  // milliseconds to microseconds
             std::lock_guard<std::mutex> lock(mMutex);
             // Store file on disk if there a new shader or Vulkan pipeline cache size changed.
             if (mCacheDirty || mNewPipelineCacheSize != mOldPipelineCacheSize) {
@@ -236,6 +235,7 @@
                 mTryToStorePipelineCache = false;
                 mCacheDirty = false;
             }
+            mSavePending = false;
         });
         deferredSaveThread.detach();
     }
diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h
index 3e0fd51..4e3eb81 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.h
+++ b/libs/hwui/pipeline/skia/ShaderCache.h
@@ -153,7 +153,8 @@
      * pending.  Each time a key/value pair is inserted into the cache via
      * load, a deferred save is initiated if one is not already pending.
      * This will wait some amount of time and then trigger a save of the cache
-     * contents to disk.
+     * contents to disk, unless mDeferredSaveDelayMs is 0 in which case saving
+     * is disabled.
      */
     bool mSavePending = false;
 
@@ -163,9 +164,11 @@
     size_t mObservedBlobValueSize = 20 * 1024;
 
     /**
-     *  The time in seconds to wait before saving newly inserted cache entries.
+     *  The time in milliseconds to wait before saving newly inserted cache entries.
+     *
+     *  WARNING: setting this to 0 will disable writing the cache to disk.
      */
-    unsigned int mDeferredSaveDelay = 4;
+    unsigned int mDeferredSaveDelayMs = 4 * 1000;
 
     /**
      * "mMutex" is the mutex used to prevent concurrent access to the member
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 75d3ff7..602554a 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -537,7 +537,7 @@
             const auto inputEventId =
                     static_cast<int32_t>(mCurrentFrameInfo->get(FrameInfoIndex::InputEventId));
             native_window_set_frame_timeline_info(
-                    mNativeSurface->getNativeWindow(), vsyncId, inputEventId,
+                    mNativeSurface->getNativeWindow(), frameCompleteNr, vsyncId, inputEventId,
                     mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime));
         }
     }
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 59c914f..4ceb13e 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -278,6 +278,11 @@
     mSignal.signal();
 }
 
+void DrawFrameTask::createHintSession(pid_t uiThreadId, pid_t renderThreadId) {
+    if (mHintSessionWrapper) return;
+    mHintSessionWrapper.emplace(uiThreadId, renderThreadId);
+}
+
 DrawFrameTask::HintSessionWrapper::HintSessionWrapper(int32_t uiThreadId, int32_t renderThreadId) {
     if (!Properties::useHintManager) return;
     if (uiThreadId < 0 || renderThreadId < 0) return;
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index d6fc292..b135a21 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -90,6 +90,8 @@
 
     void forceDrawNextFrame() { mForceDrawFrame = true; }
 
+    void createHintSession(pid_t uiThreadId, pid_t renderThreadId);
+
 private:
     class HintSessionWrapper {
     public:
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index a44b498..d9b650c 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -38,11 +38,18 @@
 RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode,
                          IContextFactory* contextFactory)
         : mRenderThread(RenderThread::getInstance()), mContext(nullptr) {
-    mContext = mRenderThread.queue().runSync([&]() -> CanvasContext* {
-        return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory);
+    pid_t uiThreadId = pthread_gettid_np(pthread_self());
+    pid_t renderThreadId = getRenderThreadTid();
+    mContext = mRenderThread.queue().runSync([=, this]() -> CanvasContext* {
+        CanvasContext* context =
+                CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory);
+        if (context != nullptr) {
+            mRenderThread.queue().post(
+                    [=] { mDrawFrameTask.createHintSession(uiThreadId, renderThreadId); });
+        }
+        return context;
     });
-    mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode,
-                              pthread_gettid_np(pthread_self()), getRenderThreadTid());
+    mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode, uiThreadId, renderThreadId);
 }
 
 RenderProxy::~RenderProxy() {
diff --git a/libs/hwui/tests/unit/ShaderCacheTests.cpp b/libs/hwui/tests/unit/ShaderCacheTests.cpp
index 974d85a..7bcd45c 100644
--- a/libs/hwui/tests/unit/ShaderCacheTests.cpp
+++ b/libs/hwui/tests/unit/ShaderCacheTests.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+#include <GrDirectContext.h>
+#include <Properties.h>
+#include <SkData.h>
+#include <SkRefCnt.h>
 #include <cutils/properties.h>
 #include <dirent.h>
 #include <errno.h>
@@ -22,9 +26,12 @@
 #include <stdlib.h>
 #include <sys/types.h>
 #include <utils/Log.h>
+
 #include <cstdint>
+
 #include "FileBlobCache.h"
 #include "pipeline/skia/ShaderCache.h"
+#include "tests/common/TestUtils.h"
 
 using namespace android::uirenderer::skiapipeline;
 
@@ -35,11 +42,38 @@
 class ShaderCacheTestUtils {
 public:
     /**
-     * "setSaveDelay" sets the time in seconds to wait before saving newly inserted cache entries.
-     * If set to 0, then deferred save is disabled.
+     * Hack to reset all member variables of the given cache to their default / initial values.
+     *
+     * WARNING: this must be kept up to date manually, since ShaderCache's parent disables just
+     * reassigning a new instance.
      */
-    static void setSaveDelay(ShaderCache& cache, unsigned int saveDelay) {
-        cache.mDeferredSaveDelay = saveDelay;
+    static void reinitializeAllFields(ShaderCache& cache) {
+        ShaderCache newCache = ShaderCache();
+        std::lock_guard<std::mutex> lock(cache.mMutex);
+        // By order of declaration
+        cache.mInitialized = newCache.mInitialized;
+        cache.mBlobCache.reset(nullptr);
+        cache.mFilename = newCache.mFilename;
+        cache.mIDHash.clear();
+        cache.mSavePending = newCache.mSavePending;
+        cache.mObservedBlobValueSize = newCache.mObservedBlobValueSize;
+        cache.mDeferredSaveDelayMs = newCache.mDeferredSaveDelayMs;
+        cache.mTryToStorePipelineCache = newCache.mTryToStorePipelineCache;
+        cache.mInStoreVkPipelineInProgress = newCache.mInStoreVkPipelineInProgress;
+        cache.mNewPipelineCacheSize = newCache.mNewPipelineCacheSize;
+        cache.mOldPipelineCacheSize = newCache.mOldPipelineCacheSize;
+        cache.mCacheDirty = newCache.mCacheDirty;
+        cache.mNumShadersCachedInRam = newCache.mNumShadersCachedInRam;
+    }
+
+    /**
+     * "setSaveDelayMs" sets the time in milliseconds to wait before saving newly inserted cache
+     * entries. If set to 0, then deferred save is disabled, and "saveToDiskLocked" must be called
+     * manually, as seen in the "terminate" testing helper function.
+     */
+    static void setSaveDelayMs(ShaderCache& cache, unsigned int saveDelayMs) {
+        std::lock_guard<std::mutex> lock(cache.mMutex);
+        cache.mDeferredSaveDelayMs = saveDelayMs;
     }
 
     /**
@@ -48,8 +82,9 @@
      */
     static void terminate(ShaderCache& cache, bool saveContent) {
         std::lock_guard<std::mutex> lock(cache.mMutex);
-        cache.mSavePending = saveContent;
-        cache.saveToDiskLocked();
+        if (saveContent) {
+            cache.saveToDiskLocked();
+        }
         cache.mBlobCache = NULL;
     }
 
@@ -60,6 +95,38 @@
     static bool validateCache(ShaderCache& cache, std::vector<T> hash) {
         return cache.validateCache(hash.data(), hash.size() * sizeof(T));
     }
+
+    /**
+     * Waits until cache::mSavePending is false, checking every 0.1 ms *while the mutex is free*.
+     *
+     * Fails if there was no save pending, or if the cache was already being written to disk, or if
+     * timeoutMs is exceeded.
+     *
+     * Note: timeoutMs only guards against mSavePending getting stuck like in b/268205519, and
+     * cannot protect against mutex-based deadlock. Reaching timeoutMs implies something is broken,
+     * so setting it to a sufficiently large value will not delay execution in the happy state.
+     */
+    static void waitForPendingSave(ShaderCache& cache, const int timeoutMs = 50) {
+        {
+            std::lock_guard<std::mutex> lock(cache.mMutex);
+            ASSERT_TRUE(cache.mSavePending);
+        }
+        bool saving = true;
+        float elapsedMilliseconds = 0;
+        while (saving) {
+            if (elapsedMilliseconds >= timeoutMs) {
+                FAIL() << "Timed out after waiting " << timeoutMs << " ms for a pending save";
+            }
+            // This small (0.1 ms) delay is to avoid working too much while waiting for
+            // deferredSaveThread to take the mutex and start the disk write.
+            const int delayMicroseconds = 100;
+            usleep(delayMicroseconds);
+            elapsedMilliseconds += (float)delayMicroseconds / 1000;
+
+            std::lock_guard<std::mutex> lock(cache.mMutex);
+            saving = cache.mSavePending;
+        }
+    }
 };
 
 } /* namespace skiapipeline */
@@ -81,6 +148,18 @@
     return false;
 }
 
+/**
+ * Attempts to delete the given file, and asserts that either:
+ * 1. Deletion was successful, OR
+ * 2. The file did not exist.
+ *
+ * Tip: wrap calls to this in ASSERT_NO_FATAL_FAILURE(x) if a test should exit early if this fails.
+ */
+void deleteFileAssertSuccess(const std::string& filePath) {
+    int deleteResult = remove(filePath.c_str());
+    ASSERT_TRUE(0 == deleteResult || ENOENT == errno);
+}
+
 inline bool checkShader(const sk_sp<SkData>& shader1, const sk_sp<SkData>& shader2) {
     return nullptr != shader1 && nullptr != shader2 && shader1->size() == shader2->size() &&
            0 == memcmp(shader1->data(), shader2->data(), shader1->size());
@@ -91,6 +170,10 @@
     return checkShader(shader, shader2);
 }
 
+inline bool checkShader(const sk_sp<SkData>& shader, const std::string& program) {
+    return checkShader(shader, program.c_str());
+}
+
 template <typename T>
 bool checkShader(const sk_sp<SkData>& shader, std::vector<T>& program) {
     sk_sp<SkData> shader2 = SkData::MakeWithCopy(program.data(), program.size() * sizeof(T));
@@ -101,6 +184,10 @@
     shader = SkData::MakeWithCString(program);
 }
 
+void setShader(sk_sp<SkData>& shader, const std::string& program) {
+    setShader(shader, program.c_str());
+}
+
 template <typename T>
 void setShader(sk_sp<SkData>& shader, std::vector<T>& buffer) {
     shader = SkData::MakeWithCopy(buffer.data(), buffer.size() * sizeof(T));
@@ -124,13 +211,13 @@
     std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
 
     // remove any test files from previous test run
-    int deleteFile = remove(cacheFile1.c_str());
-    ASSERT_TRUE(0 == deleteFile || ENOENT == errno);
+    ASSERT_NO_FATAL_FAILURE(deleteFileAssertSuccess(cacheFile1));
+    ASSERT_NO_FATAL_FAILURE(deleteFileAssertSuccess(cacheFile2));
     std::srand(0);
 
     // read the cache from a file that does not exist
     ShaderCache::get().setFilename(cacheFile1.c_str());
-    ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0);  // disable deferred save
+    ShaderCacheTestUtils::setSaveDelayMs(ShaderCache::get(), 0);  // disable deferred save
     ShaderCache::get().initShaderDiskCache();
 
     // read a key - should not be found since the cache is empty
@@ -184,7 +271,8 @@
     ASSERT_TRUE(checkShader(outVS2, dataBuffer));
 
     ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
-    remove(cacheFile1.c_str());
+    ASSERT_NO_FATAL_FAILURE(deleteFileAssertSuccess(cacheFile1));
+    ASSERT_NO_FATAL_FAILURE(deleteFileAssertSuccess(cacheFile2));
 }
 
 TEST(ShaderCacheTest, testCacheValidation) {
@@ -196,13 +284,13 @@
     std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
 
     // remove any test files from previous test run
-    int deleteFile = remove(cacheFile1.c_str());
-    ASSERT_TRUE(0 == deleteFile || ENOENT == errno);
+    ASSERT_NO_FATAL_FAILURE(deleteFileAssertSuccess(cacheFile1));
+    ASSERT_NO_FATAL_FAILURE(deleteFileAssertSuccess(cacheFile2));
     std::srand(0);
 
     // generate identity and read the cache from a file that does not exist
     ShaderCache::get().setFilename(cacheFile1.c_str());
-    ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0);  // disable deferred save
+    ShaderCacheTestUtils::setSaveDelayMs(ShaderCache::get(), 0);  // disable deferred save
     std::vector<uint8_t> identity(1024);
     genRandomData(identity);
     ShaderCache::get().initShaderDiskCache(
@@ -276,7 +364,81 @@
     }
 
     ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
-    remove(cacheFile1.c_str());
+    ASSERT_NO_FATAL_FAILURE(deleteFileAssertSuccess(cacheFile1));
+    ASSERT_NO_FATAL_FAILURE(deleteFileAssertSuccess(cacheFile2));
+}
+
+using namespace android::uirenderer;
+RENDERTHREAD_SKIA_PIPELINE_TEST(ShaderCacheTest, testOnVkFrameFlushed) {
+    if (Properties::getRenderPipelineType() != RenderPipelineType::SkiaVulkan) {
+        // RENDERTHREAD_SKIA_PIPELINE_TEST declares both SkiaVK and SkiaGL variants.
+        GTEST_SKIP() << "This test is only applicable to RenderPipelineType::SkiaVulkan";
+    }
+    if (!folderExist(getExternalStorageFolder())) {
+        // Don't run the test if external storage folder is not available
+        return;
+    }
+    std::string cacheFile = getExternalStorageFolder() + "/shaderCacheTest";
+    GrDirectContext* grContext = renderThread.getGrContext();
+
+    // Remove any test files from previous test run
+    ASSERT_NO_FATAL_FAILURE(deleteFileAssertSuccess(cacheFile));
+
+    // The first iteration of this loop is to save an initial VkPipelineCache data blob to disk,
+    // which sets up the second iteration for a common scenario of comparing a "new" VkPipelineCache
+    // blob passed to "store" against the same blob that's already in the persistent cache from a
+    // previous launch. "reinitializeAllFields" is critical to emulate each iteration being as close
+    // to the state of a freshly launched app as possible, as the initial values of member variables
+    // like mInStoreVkPipelineInProgress and mOldPipelineCacheSize are critical to catch issues
+    // such as b/268205519
+    for (int flushIteration = 1; flushIteration <= 2; flushIteration++) {
+        SCOPED_TRACE("Frame flush iteration " + std::to_string(flushIteration));
+        // Reset *all* in-memory data and reload the cache from disk.
+        ShaderCacheTestUtils::reinitializeAllFields(ShaderCache::get());
+        ShaderCacheTestUtils::setSaveDelayMs(ShaderCache::get(), 10);  // Delay must be > 0 to save.
+        ShaderCache::get().setFilename(cacheFile.c_str());
+        ShaderCache::get().initShaderDiskCache();
+
+        // 1st iteration: store pipeline data to be read back on a subsequent "boot" of the "app".
+        // 2nd iteration: ensure that an initial frame flush (without storing any shaders) given the
+        // same pipeline data that's already on disk doesn't break the cache.
+        ShaderCache::get().onVkFrameFlushed(grContext);
+        ASSERT_NO_FATAL_FAILURE(ShaderCacheTestUtils::waitForPendingSave(ShaderCache::get()));
+    }
+
+    constexpr char shader1[] = "sassas";
+    constexpr char shader2[] = "someVS";
+    constexpr int numIterations = 3;
+    // Also do n iterations of separate "store some shaders then flush the frame" pairs to just
+    // double-check the cache also doesn't get stuck from that use case.
+    for (int saveIteration = 1; saveIteration <= numIterations; saveIteration++) {
+        SCOPED_TRACE("Shader save iteration " + std::to_string(saveIteration));
+        // Write twice to the in-memory cache, which should start a deferred save with both queued.
+        sk_sp<SkData> inVS;
+        setShader(inVS, shader1 + std::to_string(saveIteration));
+        ShaderCache::get().store(GrProgramDescTest(100), *inVS.get(), SkString());
+        setShader(inVS, shader2 + std::to_string(saveIteration));
+        ShaderCache::get().store(GrProgramDescTest(432), *inVS.get(), SkString());
+
+        // Simulate flush to also save latest pipeline info.
+        ShaderCache::get().onVkFrameFlushed(grContext);
+        ASSERT_NO_FATAL_FAILURE(ShaderCacheTestUtils::waitForPendingSave(ShaderCache::get()));
+    }
+
+    // Reload from disk to ensure saving succeeded.
+    ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
+    ShaderCache::get().initShaderDiskCache();
+
+    // Read twice, ensure equal to last store.
+    sk_sp<SkData> outVS;
+    ASSERT_NE((outVS = ShaderCache::get().load(GrProgramDescTest(100))), sk_sp<SkData>());
+    ASSERT_TRUE(checkShader(outVS, shader1 + std::to_string(numIterations)));
+    ASSERT_NE((outVS = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
+    ASSERT_TRUE(checkShader(outVS, shader2 + std::to_string(numIterations)));
+
+    // Clean up.
+    ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
+    ASSERT_NO_FATAL_FAILURE(deleteFileAssertSuccess(cacheFile));
 }
 
 }  // namespace
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index ded9597..a5757b9 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -1275,7 +1275,10 @@
                     || (preset == MediaRecorder.AudioSource.VOICE_UPLINK)
                     || (preset == MediaRecorder.AudioSource.VOICE_CALL)
                     || (preset == MediaRecorder.AudioSource.ECHO_REFERENCE)
-                    || (preset == MediaRecorder.AudioSource.ULTRASOUND)) {
+                    || (preset == MediaRecorder.AudioSource.ULTRASOUND)
+                    // AUDIO_SOURCE_INVALID is used by convention on default initialized
+                    // audio attributes
+                    || (preset == MediaRecorder.AudioSource.AUDIO_SOURCE_INVALID)) {
                 mSource = preset;
             } else {
                 setCapturePreset(preset);
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 798688e..67efe3a 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1337,12 +1337,8 @@
     public void setVolumeIndexForAttributes(@NonNull AudioAttributes attr, int index, int flags) {
         Preconditions.checkNotNull(attr, "attr must not be null");
         final IAudioService service = getService();
-        try {
-            service.setVolumeIndexForAttributes(attr, index, flags,
-                    getContext().getOpPackageName(), getContext().getAttributionTag());
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        int groupId = getVolumeGroupIdForAttributes(attr);
+        setVolumeGroupVolumeIndex(groupId, index, flags);
     }
 
     /**
@@ -1361,11 +1357,8 @@
     public int getVolumeIndexForAttributes(@NonNull AudioAttributes attr) {
         Preconditions.checkNotNull(attr, "attr must not be null");
         final IAudioService service = getService();
-        try {
-            return service.getVolumeIndexForAttributes(attr);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        int groupId = getVolumeGroupIdForAttributes(attr);
+        return getVolumeGroupVolumeIndex(groupId);
     }
 
     /**
@@ -1382,11 +1375,8 @@
     public int getMaxVolumeIndexForAttributes(@NonNull AudioAttributes attr) {
         Preconditions.checkNotNull(attr, "attr must not be null");
         final IAudioService service = getService();
-        try {
-            return service.getMaxVolumeIndexForAttributes(attr);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        int groupId = getVolumeGroupIdForAttributes(attr);
+        return getVolumeGroupMaxVolumeIndex(groupId);
     }
 
     /**
@@ -1403,8 +1393,168 @@
     public int getMinVolumeIndexForAttributes(@NonNull AudioAttributes attr) {
         Preconditions.checkNotNull(attr, "attr must not be null");
         final IAudioService service = getService();
+        int groupId = getVolumeGroupIdForAttributes(attr);
+        return getVolumeGroupMinVolumeIndex(groupId);
+    }
+
+    /**
+     * Returns the volume group id associated to the given {@link AudioAttributes}.
+     *
+     * @param attributes The {@link AudioAttributes} to consider.
+     * @return {@link android.media.audiopolicy.AudioVolumeGroup} id supporting the given
+     * {@link AudioAttributes} if found,
+     * {@code android.media.audiopolicy.AudioVolumeGroup.DEFAULT_VOLUME_GROUP} otherwise.
+     * @hide
+     */
+    public int getVolumeGroupIdForAttributes(@NonNull AudioAttributes attributes) {
+        Preconditions.checkNotNull(attributes, "Audio Attributes must not be null");
+        return AudioProductStrategy.getVolumeGroupIdForAudioAttributes(attributes,
+                /* fallbackOnDefault= */ true);
+    }
+
+    /**
+     * Sets the volume index for a particular group associated to given id.
+     * <p> Call first in prior {@link getVolumeGroupIdForAttributes} to retrieve the volume group
+     * id supporting the given {@link AudioAttributes}.
+     *
+     * @param groupId of the {@link android.media.audiopolicy.AudioVolumeGroup} to consider.
+     * @param index The volume index to set. See
+     *          {@link #getVolumeGroupMaxVolumeIndex(id)} for the largest valid value
+     *          {@link #getVolumeGroupMinVolumeIndex(id)} for the lowest valid value.
+     * @param flags One or more flags.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public void setVolumeGroupVolumeIndex(int groupId, int index, int flags) {
+        final IAudioService service = getService();
         try {
-            return service.getMinVolumeIndexForAttributes(attr);
+            service.setVolumeGroupVolumeIndex(groupId, index, flags,
+                    getContext().getOpPackageName(), getContext().getAttributionTag());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the current volume index for a particular group associated to given id.
+     * <p> Call first in prior {@link getVolumeGroupIdForAttributes} to retrieve the volume group
+     * id supporting the given {@link AudioAttributes}.
+     *
+     * @param groupId of the {@link android.media.audiopolicy.AudioVolumeGroup} to consider.
+     * @return The current volume index for the stream.
+     * @hide
+     */
+    @IntRange(from = 0)
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public int getVolumeGroupVolumeIndex(int groupId) {
+        final IAudioService service = getService();
+        try {
+            return service.getVolumeGroupVolumeIndex(groupId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the maximum volume index for a particular group associated to given id.
+     * <p> Call first in prior {@link getVolumeGroupIdForAttributes} to retrieve the volume group
+     * id supporting the given {@link AudioAttributes}.
+     *
+     * @param groupId of the {@link android.media.audiopolicy.AudioVolumeGroup} to consider.
+     * @return The maximum valid volume index for the {@link AudioAttributes}.
+     * @hide
+     */
+    @IntRange(from = 0)
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public int getVolumeGroupMaxVolumeIndex(int groupId) {
+        final IAudioService service = getService();
+        try {
+            return service.getVolumeGroupMaxVolumeIndex(groupId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the minimum volume index for a particular group associated to given id.
+     * <p> Call first in prior {@link getVolumeGroupIdForAttributes} to retrieve the volume group
+     * id supporting the given {@link AudioAttributes}.
+     *
+     * @param groupId of the {@link android.media.audiopolicy.AudioVolumeGroup} to consider.
+     * @return The minimum valid volume index for the {@link AudioAttributes}.
+     * @hide
+     */
+    @IntRange(from = 0)
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public int getVolumeGroupMinVolumeIndex(int groupId) {
+        final IAudioService service = getService();
+        try {
+            return service.getVolumeGroupMinVolumeIndex(groupId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Adjusts the volume of a particular group associated to given id by one step in a direction.
+     * <p> If the volume group is associated to a stream type, it fallbacks on
+     * {@link AudioManager#adjustStreamVolume()} for compatibility reason.
+     * <p> Call first in prior {@link getVolumeGroupIdForAttributes} to retrieve the volume group
+     * id supporting the given {@link AudioAttributes}.
+     *
+     * @param groupId of the {@link android.media.audiopolicy.AudioVolumeGroup} to consider.
+     * @param direction The direction to adjust the volume. One of
+     *            {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or
+     *            {@link #ADJUST_SAME}.
+     * @param flags One or more flags.
+     * @throws SecurityException if the adjustment triggers a Do Not Disturb change and the caller
+     * is not granted notification policy access.
+     * @hide
+     */
+    public void adjustVolumeGroupVolume(int groupId, int direction, int flags) {
+        IAudioService service = getService();
+        try {
+            service.adjustVolumeGroupVolume(groupId, direction, flags,
+                    getContext().getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get last audible volume of the group associated to given id before it was muted.
+     * <p> Call first in prior {@link getVolumeGroupIdForAttributes} to retrieve the volume group
+     * id supporting the given {@link AudioAttributes}.
+     *
+     * @param groupId of the {@link android.media.audiopolicy.AudioVolumeGroup} to consider.
+     * @return current volume if not muted, volume before muted otherwise.
+     * @hide
+     */
+    @RequiresPermission("android.permission.QUERY_AUDIO_STATE")
+    @IntRange(from = 0)
+    public int getLastAudibleVolumeGroupVolume(int groupId) {
+        IAudioService service = getService();
+        try {
+            return service.getLastAudibleVolumeGroupVolume(groupId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the current mute state for a particular volume group associated to the given id.
+     * <p> Call first in prior {@link getVolumeGroupIdForAttributes} to retrieve the volume group
+     * id supporting the given {@link AudioAttributes}.
+     *
+     * @param groupId of the {@link android.media.audiopolicy.AudioVolumeGroup} to consider.
+     * @return The mute state for the given {@link android.media.audiopolicy.AudioVolumeGroup} id.
+     * @see #adjustAttributesVolume(AudioAttributes, int, int)
+     * @hide
+     */
+    public boolean isVolumeGroupMuted(int groupId) {
+        IAudioService service = getService();
+        try {
+            return service.isVolumeGroupMuted(groupId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -7687,7 +7837,8 @@
         Objects.requireNonNull(device);
         try {
             if (device.getId() == 0) {
-                throw new IllegalArgumentException("In valid device: " + device);
+                Log.w(TAG, "setCommunicationDevice: device not found: " + device);
+                return false;
             }
             return getService().setCommunicationDevice(mICallBack, device.getId());
         } catch (RemoteException e) {
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index ad933e0..88a0321 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -126,14 +126,20 @@
 
     List<AudioVolumeGroup> getAudioVolumeGroups();
 
-    void setVolumeIndexForAttributes(in AudioAttributes aa, int index, int flags,
-            String callingPackage, in String attributionTag);
+    void setVolumeGroupVolumeIndex(int groupId, int index, int flags, String callingPackage,
+            in String attributionTag);
 
-    int getVolumeIndexForAttributes(in AudioAttributes aa);
+    int getVolumeGroupVolumeIndex(int groupId);
 
-    int getMaxVolumeIndexForAttributes(in AudioAttributes aa);
+    int getVolumeGroupMaxVolumeIndex(int groupId);
 
-    int getMinVolumeIndexForAttributes(in AudioAttributes aa);
+    int getVolumeGroupMinVolumeIndex(int groupId);
+
+    int getLastAudibleVolumeGroupVolume(int groupId);
+
+    boolean isVolumeGroupMuted(int groupId);
+
+    void adjustVolumeGroupVolume(int groupId, int direction, int flags, String callingPackage);
 
     int getLastAudibleStreamVolume(int streamType);
 
diff --git a/media/java/android/media/audiopolicy/AudioProductStrategy.java b/media/java/android/media/audiopolicy/AudioProductStrategy.java
index 31d5967..a792501 100644
--- a/media/java/android/media/audiopolicy/AudioProductStrategy.java
+++ b/media/java/android/media/audiopolicy/AudioProductStrategy.java
@@ -29,10 +29,10 @@
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * @hide
@@ -142,7 +142,7 @@
      */
     public static int getLegacyStreamTypeForStrategyWithAudioAttributes(
             @NonNull AudioAttributes audioAttributes) {
-        Preconditions.checkNotNull(audioAttributes, "AudioAttributes must not be null");
+        Objects.requireNonNull(audioAttributes, "AudioAttributes must not be null");
         for (final AudioProductStrategy productStrategy :
                 AudioProductStrategy.getAudioProductStrategies()) {
             if (productStrategy.supportsAudioAttributes(audioAttributes)) {
@@ -162,6 +162,30 @@
         return AudioSystem.STREAM_MUSIC;
     }
 
+    /**
+     * @hide
+     * @param attributes the {@link AudioAttributes} to identify VolumeGroupId with
+     * @param fallbackOnDefault if set, allows to fallback on the default group (e.g. the group
+     *                          associated to {@link AudioManager#STREAM_MUSIC}).
+     * @return volume group id associated with the given {@link AudioAttributes} if found,
+     *     default volume group id if fallbackOnDefault is set
+     * <p>By convention, the product strategy with default attributes will be associated to the
+     * default volume group (e.g. associated to {@link AudioManager#STREAM_MUSIC})
+     * or {@link AudioVolumeGroup#DEFAULT_VOLUME_GROUP} if not found.
+     */
+    public static int getVolumeGroupIdForAudioAttributes(
+            @NonNull AudioAttributes attributes, boolean fallbackOnDefault) {
+        Objects.requireNonNull(attributes, "attributes must not be null");
+        int volumeGroupId = getVolumeGroupIdForAudioAttributesInt(attributes);
+        if (volumeGroupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP) {
+            return volumeGroupId;
+        }
+        if (fallbackOnDefault) {
+            return getVolumeGroupIdForAudioAttributesInt(getDefaultAttributes());
+        }
+        return AudioVolumeGroup.DEFAULT_VOLUME_GROUP;
+    }
+
     private static List<AudioProductStrategy> initializeAudioProductStrategies() {
         ArrayList<AudioProductStrategy> apsList = new ArrayList<AudioProductStrategy>();
         int status = native_list_audio_product_strategies(apsList);
@@ -192,8 +216,8 @@
      */
     private AudioProductStrategy(@NonNull String name, int id,
             @NonNull AudioAttributesGroup[] aag) {
-        Preconditions.checkNotNull(name, "name must not be null");
-        Preconditions.checkNotNull(aag, "AudioAttributesGroups must not be null");
+        Objects.requireNonNull(name, "name must not be null");
+        Objects.requireNonNull(aag, "AudioAttributesGroups must not be null");
         mName = name;
         mId = id;
         mAudioAttributesGroups = aag;
@@ -211,6 +235,15 @@
 
     /**
      * @hide
+     * @return the product strategy ID (which is the generalisation of Car Audio Usage / legacy
+     *         routing_strategy linked to {@link AudioAttributes#getUsage()}).
+     */
+    @NonNull public String getName() {
+        return mName;
+    }
+
+    /**
+     * @hide
      * @return first {@link AudioAttributes} associated to this product strategy.
      */
     @SystemApi
@@ -243,7 +276,7 @@
      */
     @TestApi
     public int getLegacyStreamTypeForAudioAttributes(@NonNull AudioAttributes aa) {
-        Preconditions.checkNotNull(aa, "AudioAttributes must not be null");
+        Objects.requireNonNull(aa, "AudioAttributes must not be null");
         for (final AudioAttributesGroup aag : mAudioAttributesGroups) {
             if (aag.supportsAttributes(aa)) {
                 return aag.getStreamType();
@@ -260,7 +293,7 @@
      */
     @SystemApi
     public boolean supportsAudioAttributes(@NonNull AudioAttributes aa) {
-        Preconditions.checkNotNull(aa, "AudioAttributes must not be null");
+        Objects.requireNonNull(aa, "AudioAttributes must not be null");
         for (final AudioAttributesGroup aag : mAudioAttributesGroups) {
             if (aag.supportsAttributes(aa)) {
                 return true;
@@ -293,7 +326,7 @@
      */
     @TestApi
     public int getVolumeGroupIdForAudioAttributes(@NonNull AudioAttributes aa) {
-        Preconditions.checkNotNull(aa, "AudioAttributes must not be null");
+        Objects.requireNonNull(aa, "AudioAttributes must not be null");
         for (final AudioAttributesGroup aag : mAudioAttributesGroups) {
             if (aag.supportsAttributes(aa)) {
                 return aag.getVolumeGroupId();
@@ -302,6 +335,17 @@
         return AudioVolumeGroup.DEFAULT_VOLUME_GROUP;
     }
 
+    private static int getVolumeGroupIdForAudioAttributesInt(@NonNull AudioAttributes attributes) {
+        Objects.requireNonNull(attributes, "attributes must not be null");
+        for (AudioProductStrategy productStrategy : getAudioProductStrategies()) {
+            int volumeGroupId = productStrategy.getVolumeGroupIdForAudioAttributes(attributes);
+            if (volumeGroupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP) {
+                return volumeGroupId;
+            }
+        }
+        return AudioVolumeGroup.DEFAULT_VOLUME_GROUP;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -377,8 +421,8 @@
      */
     private static boolean attributesMatches(@NonNull AudioAttributes refAttr,
             @NonNull AudioAttributes attr) {
-        Preconditions.checkNotNull(refAttr, "refAttr must not be null");
-        Preconditions.checkNotNull(attr, "attr must not be null");
+        Objects.requireNonNull(refAttr, "reference AudioAttributes must not be null");
+        Objects.requireNonNull(attr, "requester's AudioAttributes must not be null");
         String refFormattedTags = TextUtils.join(";", refAttr.getTags());
         String cliFormattedTags = TextUtils.join(";", attr.getTags());
         if (refAttr.equals(DEFAULT_ATTRIBUTES)) {
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 2f4dd8f..408883e 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -1004,9 +1004,10 @@
 
 static jbyteArray android_media_MediaDrm_getSupportedCryptoSchemesNative(JNIEnv *env) {
     sp<IDrm> drm = android::DrmUtils::MakeDrm();
+    if (drm == NULL) return env->NewByteArray(0);
+
     std::vector<uint8_t> bv;
     drm->getSupportedSchemes(bv);
-
     jbyteArray jUuidBytes = env->NewByteArray(bv.size());
     env->SetByteArrayRegion(jUuidBytes, 0, bv.size(), reinterpret_cast<const jbyte *>(bv.data()));
     return jUuidBytes;
diff --git a/media/tests/MediaFrameworkTest/AndroidManifest.xml b/media/tests/MediaFrameworkTest/AndroidManifest.xml
index 33872ed..0da5de7 100644
--- a/media/tests/MediaFrameworkTest/AndroidManifest.xml
+++ b/media/tests/MediaFrameworkTest/AndroidManifest.xml
@@ -37,7 +37,6 @@
         </activity>
         <activity android:label="Camera2CtsActivity"
              android:name="Camera2SurfaceViewActivity"
-             android:screenOrientation="landscape"
              android:configChanges="keyboardHidden|orientation|screenSize">
         </activity>
     </application>
diff --git a/packages/CompanionDeviceManager/res/values-eu/strings.xml b/packages/CompanionDeviceManager/res/values-eu/strings.xml
index 03c32ba..41f680e 100644
--- a/packages/CompanionDeviceManager/res/values-eu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-eu/strings.xml
@@ -17,10 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Gailu osagarriaren kudeatzailea"</string>
-    <string name="confirmation_title" msgid="3785000297483688997">"Eman &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; atzitzeko baimena &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aplikazioari"</string>
+    <string name="confirmation_title" msgid="3785000297483688997">"Eman &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; erabiltzeko baimena &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aplikazioari"</string>
     <string name="profile_name_watch" msgid="576290739483672360">"erlojua"</string>
     <string name="chooser_title" msgid="2262294130493605839">"Aukeratu &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; aplikazioak kudeatu beharreko <xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
-    <string name="summary_watch" msgid="3002344206574997652">"Aplikazio hau <xliff:g id="DEVICE_NAME">%1$s</xliff:g> kudeatzeko beharrezkoa da. Jakinarazpenekin interakzioan aritzeko eta telefonoa, SMSak, kontaktuak, egutegia, deien erregistroa eta inguruko gailuak atzitzeko baimenak izango ditu <xliff:g id="APP_NAME">%2$s</xliff:g> aplikazioak."</string>
+    <string name="summary_watch" msgid="3002344206574997652">"Aplikazio hau <xliff:g id="DEVICE_NAME">%1$s</xliff:g> kudeatzeko beharrezkoa da. Jakinarazpenekin interakzioan aritzeko eta telefonoa, SMSak, kontaktuak, egutegia, deien erregistroa eta inguruko gailuak erabiltzeko baimenak izango ditu <xliff:g id="APP_NAME">%2$s</xliff:g> aplikazioak."</string>
     <string name="permission_apps" msgid="6142133265286656158">"Aplikazioak"</string>
     <string name="permission_apps_summary" msgid="798718816711515431">"Igorri zuzenean telefonoko aplikazioak"</string>
     <string name="title_app_streaming" msgid="2270331024626446950">"Eman informazioa telefonotik hartzeko baimena &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aplikazioari"</string>
@@ -28,21 +28,21 @@
     <string name="helper_summary_app_streaming" msgid="5977509499890099">"Gailu batetik bestera aplikazioak igortzeko baimena eskatzen ari da <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> gailuaren izenean"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Eman telefonoko informazio hau atzitzeko baimena &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aplikazioari"</string>
+    <string name="title_computer" msgid="4693714143506569253">"Eman telefonoko informazio hau erabiltzeko baimena &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aplikazioari"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="permission_notification" msgid="693762568127741203">"Jakinarazpenak"</string>
     <string name="permission_notification_summary" msgid="884075314530071011">"Jakinarazpen guztiak irakur ditzake; besteak beste, kontaktuak, mezuak, argazkiak eta antzeko informazioa"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Argazkiak eta multimedia-edukia"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="helper_title_computer" msgid="4671071173916176037">"Google Play Services"</string>
-    <string name="helper_summary_computer" msgid="9050724687678157852">"Telefonoko argazkiak, multimedia-edukia eta jakinarazpenak atzitzeko baimena eskatzen ari da <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> gailuaren izenean"</string>
+    <string name="helper_summary_computer" msgid="9050724687678157852">"Telefonoko argazkiak, multimedia-edukia eta jakinarazpenak erabiltzeko baimena eskatzen ari da <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> gailuaren izenean"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"gailua"</string>
     <string name="summary_generic" msgid="2346762210105903720"></string>
     <string name="consent_yes" msgid="8344487259618762872">"Eman baimena"</string>
     <string name="consent_no" msgid="2640796915611404382">"Ez eman baimenik"</string>
     <string name="consent_back" msgid="2560683030046918882">"Atzera"</string>
     <string name="permission_sync_confirmation_title" msgid="667074294393493186">"Transferitu aplikazio-baimenak erlojura"</string>
-    <string name="permission_sync_summary" msgid="8873391306499120778">"Erlojua errazago konfiguratzeko, konfigurazio-prozesua abian zen bitartean erlojuan instalatutako aplikazioek telefonoak darabiltzan baimen berak erabiliko dituzte.\n\n Baliteke baimen horien artean erlojuaren mikrofonoa eta kokapena atzitzeko baimenak egotea."</string>
+    <string name="permission_sync_summary" msgid="8873391306499120778">"Erlojua errazago konfiguratzeko, konfigurazio-prozesua abian zen bitartean erlojuan instalatutako aplikazioek telefonoak darabiltzan baimen berak erabiliko dituzte.\n\n Baliteke baimen horien artean erlojuaren mikrofonoa eta kokapena erabiltzeko baimenak egotea."</string>
     <string name="vendor_icon_description" msgid="4445875290032225965">"Aplikazioaren ikonoa"</string>
     <string name="vendor_header_button_description" msgid="6566660389500630608">"Informazio gehiagorako botoia"</string>
 </resources>
diff --git a/packages/PackageInstaller/res/values-pt-rBR/strings.xml b/packages/PackageInstaller/res/values-pt-rBR/strings.xml
index b9e5be8..43dd331 100644
--- a/packages/PackageInstaller/res/values-pt-rBR/strings.xml
+++ b/packages/PackageInstaller/res/values-pt-rBR/strings.xml
@@ -53,7 +53,7 @@
     <string name="uninstall_application_title" msgid="4045420072401428123">"Desinstalar app"</string>
     <string name="uninstall_update_title" msgid="824411791011583031">"Desinstalar atualização"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> é parte do seguinte app:"</string>
-    <string name="uninstall_application_text" msgid="3816830743706143980">"Quer desinstalar este app?"</string>
+    <string name="uninstall_application_text" msgid="3816830743706143980">"Você quer desinstalar este app?"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Quer desinstalar este app para "<b>"todos"</b>" os usuários? O aplicativo e os dados dele serão removidos para "<b>"todos"</b>" os usuários do dispositivo."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Quer desinstalar este app para o usuário <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Você quer desinstalar esse app do seu perfil de trabalho?"</string>
diff --git a/packages/PackageInstaller/res/values-pt/strings.xml b/packages/PackageInstaller/res/values-pt/strings.xml
index b9e5be8..43dd331 100644
--- a/packages/PackageInstaller/res/values-pt/strings.xml
+++ b/packages/PackageInstaller/res/values-pt/strings.xml
@@ -53,7 +53,7 @@
     <string name="uninstall_application_title" msgid="4045420072401428123">"Desinstalar app"</string>
     <string name="uninstall_update_title" msgid="824411791011583031">"Desinstalar atualização"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> é parte do seguinte app:"</string>
-    <string name="uninstall_application_text" msgid="3816830743706143980">"Quer desinstalar este app?"</string>
+    <string name="uninstall_application_text" msgid="3816830743706143980">"Você quer desinstalar este app?"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Quer desinstalar este app para "<b>"todos"</b>" os usuários? O aplicativo e os dados dele serão removidos para "<b>"todos"</b>" os usuários do dispositivo."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Quer desinstalar este app para o usuário <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Você quer desinstalar esse app do seu perfil de trabalho?"</string>
diff --git a/packages/PrintSpooler/res/values-ca/strings.xml b/packages/PrintSpooler/res/values-ca/strings.xml
index 4ee4323..483a522 100644
--- a/packages/PrintSpooler/res/values-ca/strings.xml
+++ b/packages/PrintSpooler/res/values-ca/strings.xml
@@ -56,7 +56,7 @@
     <string name="print_select_printer" msgid="7388760939873368698">"Selecciona una impressora"</string>
     <string name="print_forget_printer" msgid="5035287497291910766">"Oblida la impressora"</string>
     <plurals name="print_search_result_count_utterance" formatted="false" msgid="6997663738361080868">
-      <item quantity="many"><xliff:g id="COUNT_1">%1$s</xliff:g> printers found</item>
+      <item quantity="many">S\'han trobat <xliff:g id="COUNT_1">%1$s</xliff:g> impressores</item>
       <item quantity="other">S\'han trobat <xliff:g id="COUNT_1">%1$s</xliff:g> impressores</item>
       <item quantity="one">S\'ha trobat <xliff:g id="COUNT_0">%1$s</xliff:g> impressora</item>
     </plurals>
@@ -77,7 +77,7 @@
     <string name="disabled_services_title" msgid="7313253167968363211">"Serveis desactivats"</string>
     <string name="all_services_title" msgid="5578662754874906455">"Tots els serveis"</string>
     <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
-      <item quantity="many">Install to discover <xliff:g id="COUNT_1">%1$s</xliff:g> printers</item>
+      <item quantity="many">Instal·la\'l per detectar <xliff:g id="COUNT_1">%1$s</xliff:g> impressores</item>
       <item quantity="other">Instal·la\'l per detectar <xliff:g id="COUNT_1">%1$s</xliff:g> impressores</item>
       <item quantity="one">Instal·la\'l per detectar <xliff:g id="COUNT_0">%1$s</xliff:g> impressora</item>
     </plurals>
diff --git a/packages/PrintSpooler/res/values-es-rUS/strings.xml b/packages/PrintSpooler/res/values-es-rUS/strings.xml
index 90c1937..476614b 100644
--- a/packages/PrintSpooler/res/values-es-rUS/strings.xml
+++ b/packages/PrintSpooler/res/values-es-rUS/strings.xml
@@ -56,7 +56,7 @@
     <string name="print_select_printer" msgid="7388760939873368698">"Seleccionar impresora"</string>
     <string name="print_forget_printer" msgid="5035287497291910766">"No recordar impresora"</string>
     <plurals name="print_search_result_count_utterance" formatted="false" msgid="6997663738361080868">
-      <item quantity="many"><xliff:g id="COUNT_1">%1$s</xliff:g> printers found</item>
+      <item quantity="many">Se encontraron <xliff:g id="COUNT_1">%1$s</xliff:g> impresoras.</item>
       <item quantity="other">Se encontraron <xliff:g id="COUNT_1">%1$s</xliff:g> impresoras.</item>
       <item quantity="one">Se encontró <xliff:g id="COUNT_0">%1$s</xliff:g> impresora.</item>
     </plurals>
@@ -77,7 +77,7 @@
     <string name="disabled_services_title" msgid="7313253167968363211">"Servicios inhabilitados"</string>
     <string name="all_services_title" msgid="5578662754874906455">"Todos los servicios"</string>
     <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
-      <item quantity="many">Install to discover <xliff:g id="COUNT_1">%1$s</xliff:g> printers</item>
+      <item quantity="many">Instala para ver <xliff:g id="COUNT_1">%1$s</xliff:g> impresoras</item>
       <item quantity="other">Instala para ver <xliff:g id="COUNT_1">%1$s</xliff:g> impresoras</item>
       <item quantity="one">Instala para ver <xliff:g id="COUNT_0">%1$s</xliff:g> impresora</item>
     </plurals>
diff --git a/packages/PrintSpooler/res/values-es/strings.xml b/packages/PrintSpooler/res/values-es/strings.xml
index 18e56db..507b2a7 100644
--- a/packages/PrintSpooler/res/values-es/strings.xml
+++ b/packages/PrintSpooler/res/values-es/strings.xml
@@ -56,7 +56,7 @@
     <string name="print_select_printer" msgid="7388760939873368698">"Seleccionar impresora"</string>
     <string name="print_forget_printer" msgid="5035287497291910766">"Olvidar impresora"</string>
     <plurals name="print_search_result_count_utterance" formatted="false" msgid="6997663738361080868">
-      <item quantity="many"><xliff:g id="COUNT_1">%1$s</xliff:g> printers found</item>
+      <item quantity="many">Se han encontrado <xliff:g id="COUNT_1">%1$s</xliff:g> impresoras</item>
       <item quantity="other">Se han encontrado <xliff:g id="COUNT_1">%1$s</xliff:g> impresoras</item>
       <item quantity="one">Se ha encontrado <xliff:g id="COUNT_0">%1$s</xliff:g> impresora</item>
     </plurals>
@@ -77,7 +77,7 @@
     <string name="disabled_services_title" msgid="7313253167968363211">"Servicios inhabilitados"</string>
     <string name="all_services_title" msgid="5578662754874906455">"Todos los servicios"</string>
     <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
-      <item quantity="many">Install to discover <xliff:g id="COUNT_1">%1$s</xliff:g> printers</item>
+      <item quantity="many">Instalar para descubrir <xliff:g id="COUNT_1">%1$s</xliff:g> impresoras</item>
       <item quantity="other">Instalar para descubrir <xliff:g id="COUNT_1">%1$s</xliff:g> impresoras</item>
       <item quantity="one">Instalar para descubrir <xliff:g id="COUNT_0">%1$s</xliff:g> impresora</item>
     </plurals>
diff --git a/packages/PrintSpooler/res/values-fr-rCA/strings.xml b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
index 082c148..5298f8b 100644
--- a/packages/PrintSpooler/res/values-fr-rCA/strings.xml
+++ b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
@@ -57,7 +57,7 @@
     <string name="print_forget_printer" msgid="5035287497291910766">"Supprimer l\'imprimante"</string>
     <plurals name="print_search_result_count_utterance" formatted="false" msgid="6997663738361080868">
       <item quantity="one"><xliff:g id="COUNT_1">%1$s</xliff:g> imprimante trouvée</item>
-      <item quantity="many"><xliff:g id="COUNT_1">%1$s</xliff:g> printers found</item>
+      <item quantity="many"><xliff:g id="COUNT_1">%1$s</xliff:g> imprimante trouvées</item>
       <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> imprimante trouvées</item>
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
@@ -78,7 +78,7 @@
     <string name="all_services_title" msgid="5578662754874906455">"Tous les services"</string>
     <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
       <item quantity="one">Installer pour détecter <xliff:g id="COUNT_1">%1$s</xliff:g> imprimante</item>
-      <item quantity="many">Install to discover <xliff:g id="COUNT_1">%1$s</xliff:g> printers</item>
+      <item quantity="many">Installer pour détecter <xliff:g id="COUNT_1">%1$s</xliff:g> imprimantes</item>
       <item quantity="other">Installer pour détecter <xliff:g id="COUNT_1">%1$s</xliff:g> imprimantes</item>
     </plurals>
     <string name="printing_notification_title_template" msgid="295903957762447362">"Impression de <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> en cours…"</string>
diff --git a/packages/PrintSpooler/res/values-fr/strings.xml b/packages/PrintSpooler/res/values-fr/strings.xml
index 560c5dc..4b7e83c 100644
--- a/packages/PrintSpooler/res/values-fr/strings.xml
+++ b/packages/PrintSpooler/res/values-fr/strings.xml
@@ -57,7 +57,7 @@
     <string name="print_forget_printer" msgid="5035287497291910766">"Supprimer l\'imprimante"</string>
     <plurals name="print_search_result_count_utterance" formatted="false" msgid="6997663738361080868">
       <item quantity="one"><xliff:g id="COUNT_1">%1$s</xliff:g> imprimante trouvée</item>
-      <item quantity="many"><xliff:g id="COUNT_1">%1$s</xliff:g> printers found</item>
+      <item quantity="many"><xliff:g id="COUNT_1">%1$s</xliff:g> imprimantes trouvées</item>
       <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> imprimantes trouvées</item>
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
@@ -78,7 +78,7 @@
     <string name="all_services_title" msgid="5578662754874906455">"Tous les services"</string>
     <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
       <item quantity="one">Installer pour détecter <xliff:g id="COUNT_1">%1$s</xliff:g> imprimante</item>
-      <item quantity="many">Install to discover <xliff:g id="COUNT_1">%1$s</xliff:g> printers</item>
+      <item quantity="many">Installer pour détecter <xliff:g id="COUNT_1">%1$s</xliff:g> imprimantes</item>
       <item quantity="other">Installer pour détecter <xliff:g id="COUNT_1">%1$s</xliff:g> imprimantes</item>
     </plurals>
     <string name="printing_notification_title_template" msgid="295903957762447362">"Impression de \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\" en cours…"</string>
diff --git a/packages/PrintSpooler/res/values-it/strings.xml b/packages/PrintSpooler/res/values-it/strings.xml
index 569bbc2..2a64d3d 100644
--- a/packages/PrintSpooler/res/values-it/strings.xml
+++ b/packages/PrintSpooler/res/values-it/strings.xml
@@ -56,7 +56,7 @@
     <string name="print_select_printer" msgid="7388760939873368698">"Seleziona stampante"</string>
     <string name="print_forget_printer" msgid="5035287497291910766">"Elimina stampante"</string>
     <plurals name="print_search_result_count_utterance" formatted="false" msgid="6997663738361080868">
-      <item quantity="many"><xliff:g id="COUNT_1">%1$s</xliff:g> printers found</item>
+      <item quantity="many"><xliff:g id="COUNT_1">%1$s</xliff:g> stampanti trovate</item>
       <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> stampanti trovate</item>
       <item quantity="one"><xliff:g id="COUNT_0">%1$s</xliff:g> stampante trovata</item>
     </plurals>
@@ -77,7 +77,7 @@
     <string name="disabled_services_title" msgid="7313253167968363211">"Servizi disattivati"</string>
     <string name="all_services_title" msgid="5578662754874906455">"Tutti i servizi"</string>
     <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
-      <item quantity="many">Install to discover <xliff:g id="COUNT_1">%1$s</xliff:g> printers</item>
+      <item quantity="many">Installa per rilevare <xliff:g id="COUNT_1">%1$s</xliff:g> stampanti</item>
       <item quantity="other">Installa per rilevare <xliff:g id="COUNT_1">%1$s</xliff:g> stampanti</item>
       <item quantity="one">Installa per rilevare <xliff:g id="COUNT_0">%1$s</xliff:g> stampante</item>
     </plurals>
diff --git a/packages/PrintSpooler/res/values-or/strings.xml b/packages/PrintSpooler/res/values-or/strings.xml
index 6f215d3..dd29700 100644
--- a/packages/PrintSpooler/res/values-or/strings.xml
+++ b/packages/PrintSpooler/res/values-or/strings.xml
@@ -47,7 +47,7 @@
     <string name="savetopdf_button" msgid="2976186791686924743">"PDFàŹ°à­‡ àŹžà­‡àŹ­à­‍ àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="print_options_expanded" msgid="6944679157471691859">"àŹȘà­àŹ°àŹżàŹŁà­àŹŸ àŹŹàŹżàŹ•àŹłà­àŹȘàŹ•à­ àŹŹàŹĄàŹŒ àŹ•àŹ°àŹŸàŹŻàŹŸàŹ‡àŹ›àŹż"</string>
     <string name="print_options_collapsed" msgid="7455930445670414332">"àŹȘà­àŹ°àŹżàŹŁà­àŹŸ àŹŹàŹżàŹ•àŹłà­àŹȘàŹ•à­ àŹ›à­‹àŹŸ àŹ•àŹ°àŹŸàŹŻàŹŸàŹ‡àŹ›àŹż"</string>
-    <string name="search" msgid="5421724265322228497">"àŹžàŹšà­àŹ§àŹŸàŹš àŹ•àŹ°àŹšà­àŹ€à­"</string>
+    <string name="search" msgid="5421724265322228497">"àŹžàŹ°à­àŹšà­àŹš àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="all_printers_label" msgid="3178848870161526399">"àŹžàŹźàŹžà­àŹ€ àŹȘà­àŹ°àŹżàŹŁà­àŹŸàŹ°à­‌"</string>
     <string name="add_print_service_label" msgid="5356702546188981940">"àŹžà­‡àŹŹàŹŸ àŹŻà­‹àŹ— àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"àŹžàŹ°à­àŹšà­àŹš àŹŹàŹ•à­àŹž àŹŠà­‡àŹ–àŹŸàŹŻàŹŸàŹ‡àŹ›àŹż"</string>
diff --git a/packages/PrintSpooler/res/values-pt-rBR/strings.xml b/packages/PrintSpooler/res/values-pt-rBR/strings.xml
index 3b460a1..855701b 100644
--- a/packages/PrintSpooler/res/values-pt-rBR/strings.xml
+++ b/packages/PrintSpooler/res/values-pt-rBR/strings.xml
@@ -57,7 +57,7 @@
     <string name="print_forget_printer" msgid="5035287497291910766">"Esquecer impressora"</string>
     <plurals name="print_search_result_count_utterance" formatted="false" msgid="6997663738361080868">
       <item quantity="one"><xliff:g id="COUNT_1">%1$s</xliff:g> impressoras encontradas</item>
-      <item quantity="many"><xliff:g id="COUNT_1">%1$s</xliff:g> printers found</item>
+      <item quantity="many"><xliff:g id="COUNT_1">%1$s</xliff:g> impressoras encontradas</item>
       <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> impressoras encontradas</item>
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
@@ -78,7 +78,7 @@
     <string name="all_services_title" msgid="5578662754874906455">"Todos os serviços"</string>
     <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
       <item quantity="one">Instale para encontrar <xliff:g id="COUNT_1">%1$s</xliff:g> impressoras</item>
-      <item quantity="many">Install to discover <xliff:g id="COUNT_1">%1$s</xliff:g> printers</item>
+      <item quantity="many">Instale para encontrar <xliff:g id="COUNT_1">%1$s</xliff:g> impressoras</item>
       <item quantity="other">Instale para encontrar <xliff:g id="COUNT_1">%1$s</xliff:g> impressoras</item>
     </plurals>
     <string name="printing_notification_title_template" msgid="295903957762447362">"Imprimindo <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-pt-rPT/strings.xml b/packages/PrintSpooler/res/values-pt-rPT/strings.xml
index 8c1087e..c9a52a8 100644
--- a/packages/PrintSpooler/res/values-pt-rPT/strings.xml
+++ b/packages/PrintSpooler/res/values-pt-rPT/strings.xml
@@ -56,7 +56,7 @@
     <string name="print_select_printer" msgid="7388760939873368698">"Selecionar impressora"</string>
     <string name="print_forget_printer" msgid="5035287497291910766">"Esquecer impressora"</string>
     <plurals name="print_search_result_count_utterance" formatted="false" msgid="6997663738361080868">
-      <item quantity="many"><xliff:g id="COUNT_1">%1$s</xliff:g> printers found</item>
+      <item quantity="many"><xliff:g id="COUNT_1">%1$s</xliff:g> impressoras encontradas</item>
       <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> impressoras encontradas</item>
       <item quantity="one"><xliff:g id="COUNT_0">%1$s</xliff:g> impressora encontrada</item>
     </plurals>
@@ -77,7 +77,7 @@
     <string name="disabled_services_title" msgid="7313253167968363211">"Serviços desativados"</string>
     <string name="all_services_title" msgid="5578662754874906455">"Todos os serviços"</string>
     <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
-      <item quantity="many">Install to discover <xliff:g id="COUNT_1">%1$s</xliff:g> printers</item>
+      <item quantity="many">Instale para detetar <xliff:g id="COUNT_1">%1$s</xliff:g> impressoras</item>
       <item quantity="other">Instale para detetar <xliff:g id="COUNT_1">%1$s</xliff:g> impressoras</item>
       <item quantity="one">Instale para detetar <xliff:g id="COUNT_0">%1$s</xliff:g> impressora</item>
     </plurals>
diff --git a/packages/PrintSpooler/res/values-pt/strings.xml b/packages/PrintSpooler/res/values-pt/strings.xml
index 3b460a1..855701b 100644
--- a/packages/PrintSpooler/res/values-pt/strings.xml
+++ b/packages/PrintSpooler/res/values-pt/strings.xml
@@ -57,7 +57,7 @@
     <string name="print_forget_printer" msgid="5035287497291910766">"Esquecer impressora"</string>
     <plurals name="print_search_result_count_utterance" formatted="false" msgid="6997663738361080868">
       <item quantity="one"><xliff:g id="COUNT_1">%1$s</xliff:g> impressoras encontradas</item>
-      <item quantity="many"><xliff:g id="COUNT_1">%1$s</xliff:g> printers found</item>
+      <item quantity="many"><xliff:g id="COUNT_1">%1$s</xliff:g> impressoras encontradas</item>
       <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> impressoras encontradas</item>
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
@@ -78,7 +78,7 @@
     <string name="all_services_title" msgid="5578662754874906455">"Todos os serviços"</string>
     <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
       <item quantity="one">Instale para encontrar <xliff:g id="COUNT_1">%1$s</xliff:g> impressoras</item>
-      <item quantity="many">Install to discover <xliff:g id="COUNT_1">%1$s</xliff:g> printers</item>
+      <item quantity="many">Instale para encontrar <xliff:g id="COUNT_1">%1$s</xliff:g> impressoras</item>
       <item quantity="other">Instale para encontrar <xliff:g id="COUNT_1">%1$s</xliff:g> impressoras</item>
     </plurals>
     <string name="printing_notification_title_template" msgid="295903957762447362">"Imprimindo <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java b/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
index 2db0a8f..33fc37e 100644
--- a/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
+++ b/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
@@ -25,7 +25,7 @@
 import android.util.Log;
 
 import androidx.core.os.BuildCompat;
-import androidx.window.embedding.SplitController;
+import androidx.window.embedding.ActivityEmbeddingController;
 
 import com.android.settingslib.utils.BuildCompatUtils;
 
@@ -86,7 +86,7 @@
      * @param activity Activity that needs the check
      */
     public static boolean isActivityEmbedded(Activity activity) {
-        return SplitController.getInstance().isActivityEmbedded(activity);
+        return ActivityEmbeddingController.getInstance(activity).isActivityEmbedded(activity);
     }
 
     /**
diff --git a/packages/SettingsLib/IllustrationPreference/res/values/attrs.xml b/packages/SettingsLib/IllustrationPreference/res/values/attrs.xml
new file mode 100644
index 0000000..141886c
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/res/values/attrs.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+
+<resources>
+    <declare-styleable name="IllustrationPreference">
+        <attr name="dynamicColor" format="boolean" />
+    </declare-styleable>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
index 1592094..3b90275 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
@@ -61,6 +61,8 @@
     private View mMiddleGroundView;
     private OnBindListener mOnBindListener;
 
+    private boolean mLottieDynamicColor;
+
     /**
      * Interface to listen in on when {@link #onBindViewHolder(PreferenceViewHolder)} occurs.
      */
@@ -146,6 +148,10 @@
             ColorUtils.applyDynamicColors(getContext(), illustrationView);
         }
 
+        if (mLottieDynamicColor) {
+            LottieColorUtils.applyDynamicColors(getContext(), illustrationView);
+        }
+
         if (mOnBindListener != null) {
             mOnBindListener.onBind(illustrationView);
         }
@@ -262,6 +268,21 @@
         }
     }
 
+    /**
+     * Sets the lottie illustration apply dynamic color.
+     */
+    public void applyDynamicColor() {
+        mLottieDynamicColor = true;
+        notifyChanged();
+    }
+
+    /**
+     * Return if the lottie illustration apply dynamic color or not.
+     */
+    public boolean isApplyDynamicColor() {
+        return mLottieDynamicColor;
+    }
+
     private void resetImageResourceCache() {
         mImageDrawable = null;
         mImageUri = null;
@@ -403,9 +424,15 @@
 
         mIsAutoScale = false;
         if (attrs != null) {
-            final TypedArray a = context.obtainStyledAttributes(attrs,
+            TypedArray a = context.obtainStyledAttributes(attrs,
                     R.styleable.LottieAnimationView, 0 /*defStyleAttr*/, 0 /*defStyleRes*/);
             mImageResId = a.getResourceId(R.styleable.LottieAnimationView_lottie_rawRes, 0);
+
+            a = context.obtainStyledAttributes(attrs,
+                    R.styleable.IllustrationPreference, 0 /*defStyleAttr*/, 0 /*defStyleRes*/);
+            mLottieDynamicColor = a.getBoolean(R.styleable.IllustrationPreference_dynamicColor,
+                    false);
+
             a.recycle();
         }
     }
diff --git a/packages/SettingsLib/res/values-af/arrays.xml b/packages/SettingsLib/res/values-af/arrays.xml
index a7e44d3..a28340e 100644
--- a/packages/SettingsLib/res/values-af/arrays.xml
+++ b/packages/SettingsLib/res/values-af/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-am/arrays.xml b/packages/SettingsLib/res/values-am/arrays.xml
index 7bef7fa..a201ab0 100644
--- a/packages/SettingsLib/res/values-am/arrays.xml
+++ b/packages/SettingsLib/res/values-am/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-ar/arrays.xml b/packages/SettingsLib/res/values-ar/arrays.xml
index 0720cf5..7852b04 100644
--- a/packages/SettingsLib/res/values-ar/arrays.xml
+++ b/packages/SettingsLib/res/values-ar/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-as/arrays.xml b/packages/SettingsLib/res/values-as/arrays.xml
index cbacce8..b273d8e 100644
--- a/packages/SettingsLib/res/values-as/arrays.xml
+++ b/packages/SettingsLib/res/values-as/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-az/arrays.xml b/packages/SettingsLib/res/values-az/arrays.xml
index d1f157a..dc8be1d 100644
--- a/packages/SettingsLib/res/values-az/arrays.xml
+++ b/packages/SettingsLib/res/values-az/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml b/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml
index 63b08fa..2fc7afb 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-be/arrays.xml b/packages/SettingsLib/res/values-be/arrays.xml
index f16e1c5..e180b44 100644
--- a/packages/SettingsLib/res/values-be/arrays.xml
+++ b/packages/SettingsLib/res/values-be/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-bg/arrays.xml b/packages/SettingsLib/res/values-bg/arrays.xml
index 849e694..cdad4ac 100644
--- a/packages/SettingsLib/res/values-bg/arrays.xml
+++ b/packages/SettingsLib/res/values-bg/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-bn/arrays.xml b/packages/SettingsLib/res/values-bn/arrays.xml
index a3bc4fd..0d79aa7 100644
--- a/packages/SettingsLib/res/values-bn/arrays.xml
+++ b/packages/SettingsLib/res/values-bn/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-bs/arrays.xml b/packages/SettingsLib/res/values-bs/arrays.xml
index 926ad84..4ef0981 100644
--- a/packages/SettingsLib/res/values-bs/arrays.xml
+++ b/packages/SettingsLib/res/values-bs/arrays.xml
@@ -273,4 +273,10 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+  <string-array name="entries_font_size">
+    <item msgid="9181293769180886675">"Malo"</item>
+    <item msgid="1484561228522634915">"Zadano"</item>
+    <item msgid="4014311587094503943">"Veliko"</item>
+    <item msgid="2904569270831156685">"Najveće"</item>
+  </string-array>
 </resources>
diff --git a/packages/SettingsLib/res/values-ca/arrays.xml b/packages/SettingsLib/res/values-ca/arrays.xml
index b6f1590..e0bfec5 100644
--- a/packages/SettingsLib/res/values-ca/arrays.xml
+++ b/packages/SettingsLib/res/values-ca/arrays.xml
@@ -232,7 +232,7 @@
     <item msgid="8612549335720461635">"4K (segur)"</item>
     <item msgid="7322156123728520872">"4K (ampliat)"</item>
     <item msgid="7735692090314849188">"4K (ampliat, segur)"</item>
-    <item msgid="7346816300608639624">"720p, 1080p (pantalla doble)"</item>
+    <item msgid="7346816300608639624">"720p, 1080p (pantalla dual)"</item>
   </string-array>
   <string-array name="enable_opengl_traces_entries">
     <item msgid="4433736508877934305">"Cap"</item>
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-cs/arrays.xml b/packages/SettingsLib/res/values-cs/arrays.xml
index e1d033c..f8cdb68 100644
--- a/packages/SettingsLib/res/values-cs/arrays.xml
+++ b/packages/SettingsLib/res/values-cs/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-da/arrays.xml b/packages/SettingsLib/res/values-da/arrays.xml
index 48a33f6..4d656ca 100644
--- a/packages/SettingsLib/res/values-da/arrays.xml
+++ b/packages/SettingsLib/res/values-da/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-de/arrays.xml b/packages/SettingsLib/res/values-de/arrays.xml
index ca999db..250f0e4 100644
--- a/packages/SettingsLib/res/values-de/arrays.xml
+++ b/packages/SettingsLib/res/values-de/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-el/arrays.xml b/packages/SettingsLib/res/values-el/arrays.xml
index b95f6fc..cb6e2e9 100644
--- a/packages/SettingsLib/res/values-el/arrays.xml
+++ b/packages/SettingsLib/res/values-el/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rAU/arrays.xml b/packages/SettingsLib/res/values-en-rAU/arrays.xml
index 327e4e9..a75f05c 100644
--- a/packages/SettingsLib/res/values-en-rAU/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rAU/arrays.xml
@@ -273,4 +273,10 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+  <string-array name="entries_font_size">
+    <item msgid="9181293769180886675">"Small"</item>
+    <item msgid="1484561228522634915">"Default"</item>
+    <item msgid="4014311587094503943">"Large"</item>
+    <item msgid="2904569270831156685">"Largest"</item>
+  </string-array>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rCA/arrays.xml b/packages/SettingsLib/res/values-en-rCA/arrays.xml
index 8a57232..698245a 100644
--- a/packages/SettingsLib/res/values-en-rCA/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rCA/arrays.xml
@@ -273,4 +273,10 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+  <string-array name="entries_font_size">
+    <item msgid="9181293769180886675">"Small"</item>
+    <item msgid="1484561228522634915">"Default"</item>
+    <item msgid="4014311587094503943">"Large"</item>
+    <item msgid="2904569270831156685">"Largest"</item>
+  </string-array>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rGB/arrays.xml b/packages/SettingsLib/res/values-en-rGB/arrays.xml
index 327e4e9..a75f05c 100644
--- a/packages/SettingsLib/res/values-en-rGB/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rGB/arrays.xml
@@ -273,4 +273,10 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+  <string-array name="entries_font_size">
+    <item msgid="9181293769180886675">"Small"</item>
+    <item msgid="1484561228522634915">"Default"</item>
+    <item msgid="4014311587094503943">"Large"</item>
+    <item msgid="2904569270831156685">"Largest"</item>
+  </string-array>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rIN/arrays.xml b/packages/SettingsLib/res/values-en-rIN/arrays.xml
index 327e4e9..a75f05c 100644
--- a/packages/SettingsLib/res/values-en-rIN/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rIN/arrays.xml
@@ -273,4 +273,10 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+  <string-array name="entries_font_size">
+    <item msgid="9181293769180886675">"Small"</item>
+    <item msgid="1484561228522634915">"Default"</item>
+    <item msgid="4014311587094503943">"Large"</item>
+    <item msgid="2904569270831156685">"Largest"</item>
+  </string-array>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rXC/arrays.xml b/packages/SettingsLib/res/values-en-rXC/arrays.xml
index 8af0a4a..7b98795 100644
--- a/packages/SettingsLib/res/values-en-rXC/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rXC/arrays.xml
@@ -273,4 +273,10 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+  <string-array name="entries_font_size">
+    <item msgid="9181293769180886675">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‎‏‎‎‎‎‎‏‎‎‎‎‎‏‎‎‏‏‏‎‏‏‎‏‎‏‎‎‎‏‎‎‎‏‎‏‏‎‏‏‎‏‎‎‏‎‎‏‏‎Small‎‏‎‎‏‎"</item>
+    <item msgid="1484561228522634915">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‏‎‎‏‎‎‏‏‎‏‎‎‎‏‏‏‎‎‎‏‎‎‏‎‎‏‎‎‏‏‎‎‎‎‏‎‏‏‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎Default‎‏‎‎‏‎"</item>
+    <item msgid="4014311587094503943">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‏‏‏‏‎‏‏‎‏‎‏‏‎‏‏‎‎‏‏‎‎‎‏‏‏‏‎‏‎‏‎‎‏‏‎‏‎‎‎‎‎‎‎‏‎‏‎‎‎‏‎‎‎‎‎‎‏‏‏‎Large‎‏‎‎‏‎"</item>
+    <item msgid="2904569270831156685">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‎‎‎‎‏‎‎‏‏‏‏‎‎‎‏‏‎‏‎‎‏‎‏‏‏‎‎‏‏‎‏‏‏‎‏‎‏‏‎‏‎‎‎‎‎‎‎‏‏‎‏‏‏‎‎‏‏‎‏‎Largest‎‏‎‎‏‎"</item>
+  </string-array>
 </resources>
diff --git a/packages/SettingsLib/res/values-es-rUS/arrays.xml b/packages/SettingsLib/res/values-es-rUS/arrays.xml
index 3813808..5769c8d 100644
--- a/packages/SettingsLib/res/values-es-rUS/arrays.xml
+++ b/packages/SettingsLib/res/values-es-rUS/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index b23f883..0792f7f 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -582,7 +582,7 @@
     <string name="user_add_user_type_title" msgid="551279664052914497">"Agregar"</string>
     <string name="user_new_user_name" msgid="60979820612818840">"Usuario nuevo"</string>
     <string name="user_new_profile_name" msgid="2405500423304678841">"Perfil nuevo"</string>
-    <string name="user_info_settings_title" msgid="6351390762733279907">"Datos de usuario"</string>
+    <string name="user_info_settings_title" msgid="6351390762733279907">"Datos del usuario"</string>
     <string name="profile_info_settings_title" msgid="105699672534365099">"Datos del perfil"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Para poder crear un perfil restringido, debes configurar un bloqueo de pantalla que proteja tus aplicaciones y datos personales."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Configurar bloqueo"</string>
diff --git a/packages/SettingsLib/res/values-es/arrays.xml b/packages/SettingsLib/res/values-es/arrays.xml
index 4924407..e2d9d33 100644
--- a/packages/SettingsLib/res/values-es/arrays.xml
+++ b/packages/SettingsLib/res/values-es/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index ff04270..fe97f34 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -587,7 +587,7 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Para poder crear un perfil restringido, debes configurar una pantalla de bloqueo que proteja tus aplicaciones y datos personales."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Establecer bloqueo"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Cambiar a <xliff:g id="USER_NAME">%s</xliff:g>"</string>
-    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creando usuario…"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creando usuario nuevo…"</string>
     <string name="creating_new_guest_dialog_message" msgid="1114905602181350690">"Creando nuevo invitado…"</string>
     <string name="add_user_failed" msgid="4809887794313944872">"No se ha podido crear el usuario"</string>
     <string name="add_guest_failed" msgid="8074548434469843443">"No se ha podido crear un nuevo invitado"</string>
diff --git a/packages/SettingsLib/res/values-et/arrays.xml b/packages/SettingsLib/res/values-et/arrays.xml
index 0402ac2..cd07db1 100644
--- a/packages/SettingsLib/res/values-et/arrays.xml
+++ b/packages/SettingsLib/res/values-et/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-eu/arrays.xml b/packages/SettingsLib/res/values-eu/arrays.xml
index d15712a..35424c0 100644
--- a/packages/SettingsLib/res/values-eu/arrays.xml
+++ b/packages/SettingsLib/res/values-eu/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-fa/arrays.xml b/packages/SettingsLib/res/values-fa/arrays.xml
index 41410cb..a3a6e48 100644
--- a/packages/SettingsLib/res/values-fa/arrays.xml
+++ b/packages/SettingsLib/res/values-fa/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 1bf56c0..9d398ad 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -193,7 +193,7 @@
     <string name="tts_lang_use_system" msgid="6312945299804012406">"ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ ŰČŰšŰ§Ù† ŰłÛŒŰłŰȘم"</string>
     <string name="tts_lang_not_selected" msgid="7927823081096056147">"ŰČŰšŰ§Ù† Ű§Ù†ŰȘ۟ۧۚ Ù†ŰŽŰŻÙ‡ ۧ۳ŰȘ"</string>
     <string name="tts_default_lang_summary" msgid="9042620014800063470">"Ű”ŰŻŰ§ÛŒ ۟ۧ۔ یک ŰČŰšŰ§Ù† ۱ۧ ŰšŰ±Ű§ÛŒ مŰȘن ÚŻÙŰȘŰ§Ű±ÛŒ ŰȘÙ†ŰžÛŒÙ… می‌Ú©Ù†ŰŻ"</string>
-    <string name="tts_play_example_title" msgid="1599468547216481684">"ŰšÙ‡ نمونه‌Ű§ÛŒ ÚŻÙˆŰŽ Ú©Ù†ÛŒŰŻ"</string>
+    <string name="tts_play_example_title" msgid="1599468547216481684">"ŰŽÙ†ÛŒŰŻÙ† نمونه"</string>
     <string name="tts_play_example_summary" msgid="634044730710636383">"Ù‚ŰłÙ…ŰȘ کوŰȘŰ§Ù‡ÛŒ ۧŰČ ŰȘŰ±Ú©ÛŒŰš ÚŻÙŰȘۧ۱ ÙŸŰźŰŽ ŰŽÙˆŰŻ"</string>
     <string name="tts_install_data_title" msgid="1829942496472751703">"Ù†Ű”Űš ŰŻŰ§ŰŻÙ‡‌Ù‡Ű§ÛŒ Ű”ÙˆŰȘی"</string>
     <string name="tts_install_data_summary" msgid="3608874324992243851">"Ù†Ű”Űš ŰŻŰ§ŰŻÙ‡‌Ù‡Ű§ÛŒ Ű”ÙˆŰȘی Ù…ÙˆŰ±ŰŻ Ù†ÛŒŰ§ŰČ ŰšŰ±Ű§ÛŒ ŰȘŰ±Ú©ÛŒŰš ÚŻÙŰȘۧ۱"</string>
diff --git a/packages/SettingsLib/res/values-fi/arrays.xml b/packages/SettingsLib/res/values-fi/arrays.xml
index 842fb8f..c7862e3 100644
--- a/packages/SettingsLib/res/values-fi/arrays.xml
+++ b/packages/SettingsLib/res/values-fi/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-fr-rCA/arrays.xml b/packages/SettingsLib/res/values-fr-rCA/arrays.xml
index dfa6db3..7048147 100644
--- a/packages/SettingsLib/res/values-fr-rCA/arrays.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-fr/arrays.xml b/packages/SettingsLib/res/values-fr/arrays.xml
index 92546da..43274bb 100644
--- a/packages/SettingsLib/res/values-fr/arrays.xml
+++ b/packages/SettingsLib/res/values-fr/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-gl/arrays.xml b/packages/SettingsLib/res/values-gl/arrays.xml
index fb8e5f2..e8c5463 100644
--- a/packages/SettingsLib/res/values-gl/arrays.xml
+++ b/packages/SettingsLib/res/values-gl/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index d69f0cb..09586be 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -190,7 +190,7 @@
     <string name="tts_default_pitch_title" msgid="6988592215554485479">"Ton"</string>
     <string name="tts_default_pitch_summary" msgid="9132719475281551884">"Afecta ao ton da voz sintetizada"</string>
     <string name="tts_default_lang_title" msgid="4698933575028098940">"Idioma"</string>
-    <string name="tts_lang_use_system" msgid="6312945299804012406">"Utiliza o idioma do sistema"</string>
+    <string name="tts_lang_use_system" msgid="6312945299804012406">"Utilizar o idioma do sistema"</string>
     <string name="tts_lang_not_selected" msgid="7927823081096056147">"Idioma non seleccionado"</string>
     <string name="tts_default_lang_summary" msgid="9042620014800063470">"Define a voz específica do idioma para o texto falado"</string>
     <string name="tts_play_example_title" msgid="1599468547216481684">"Escoitar un exemplo"</string>
diff --git a/packages/SettingsLib/res/values-gu/arrays.xml b/packages/SettingsLib/res/values-gu/arrays.xml
index e527d81..ac485e1 100644
--- a/packages/SettingsLib/res/values-gu/arrays.xml
+++ b/packages/SettingsLib/res/values-gu/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-hi/arrays.xml b/packages/SettingsLib/res/values-hi/arrays.xml
index 9b8d83e..e7f68d9 100644
--- a/packages/SettingsLib/res/values-hi/arrays.xml
+++ b/packages/SettingsLib/res/values-hi/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-hr/arrays.xml b/packages/SettingsLib/res/values-hr/arrays.xml
index 559383a..119ec6b 100644
--- a/packages/SettingsLib/res/values-hr/arrays.xml
+++ b/packages/SettingsLib/res/values-hr/arrays.xml
@@ -273,4 +273,10 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+  <string-array name="entries_font_size">
+    <item msgid="9181293769180886675">"Malo"</item>
+    <item msgid="1484561228522634915">"Zadano"</item>
+    <item msgid="4014311587094503943">"Veliko"</item>
+    <item msgid="2904569270831156685">"Najveće"</item>
+  </string-array>
 </resources>
diff --git a/packages/SettingsLib/res/values-hu/arrays.xml b/packages/SettingsLib/res/values-hu/arrays.xml
index 409d600..28ae1b6 100644
--- a/packages/SettingsLib/res/values-hu/arrays.xml
+++ b/packages/SettingsLib/res/values-hu/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-hy/arrays.xml b/packages/SettingsLib/res/values-hy/arrays.xml
index 009875d..5787215 100644
--- a/packages/SettingsLib/res/values-hy/arrays.xml
+++ b/packages/SettingsLib/res/values-hy/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-in/arrays.xml b/packages/SettingsLib/res/values-in/arrays.xml
index 9527417..6bb7e5d 100644
--- a/packages/SettingsLib/res/values-in/arrays.xml
+++ b/packages/SettingsLib/res/values-in/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index abdbf06..6dc9b40 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -345,7 +345,7 @@
     <string name="enable_terminal_summary" msgid="2481074834856064500">"Aktifkan aplikasi terminal yang menawarkan akses kerangka lokal"</string>
     <string name="hdcp_checking_title" msgid="3155692785074095986">"Pemeriksaan HDCP"</string>
     <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Setel perilaku pemeriksaan HDCP"</string>
-    <string name="debug_debugging_category" msgid="535341063709248842">"Debugging"</string>
+    <string name="debug_debugging_category" msgid="535341063709248842">"Proses debug"</string>
     <string name="debug_app" msgid="8903350241392391766">"Pilih aplikasi debug"</string>
     <string name="debug_app_not_set" msgid="1934083001283807188">"Tidak ada aplikasi debug yang disetel"</string>
     <string name="debug_app_set" msgid="6599535090477753651">"Aplikasi debug: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-is/arrays.xml b/packages/SettingsLib/res/values-is/arrays.xml
index 0b5b978..87433b7 100644
--- a/packages/SettingsLib/res/values-is/arrays.xml
+++ b/packages/SettingsLib/res/values-is/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-it/arrays.xml b/packages/SettingsLib/res/values-it/arrays.xml
index ae1e515..e68adb9 100644
--- a/packages/SettingsLib/res/values-it/arrays.xml
+++ b/packages/SettingsLib/res/values-it/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-iw/arrays.xml b/packages/SettingsLib/res/values-iw/arrays.xml
index 5d72aff..6dd1765 100644
--- a/packages/SettingsLib/res/values-iw/arrays.xml
+++ b/packages/SettingsLib/res/values-iw/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-ja/arrays.xml b/packages/SettingsLib/res/values-ja/arrays.xml
index 775e31c..385cfc9 100644
--- a/packages/SettingsLib/res/values-ja/arrays.xml
+++ b/packages/SettingsLib/res/values-ja/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-ka/arrays.xml b/packages/SettingsLib/res/values-ka/arrays.xml
index f3545b6..e800d45 100644
--- a/packages/SettingsLib/res/values-ka/arrays.xml
+++ b/packages/SettingsLib/res/values-ka/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-kk/arrays.xml b/packages/SettingsLib/res/values-kk/arrays.xml
index 3fd1b50..fa82852 100644
--- a/packages/SettingsLib/res/values-kk/arrays.xml
+++ b/packages/SettingsLib/res/values-kk/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-km/arrays.xml b/packages/SettingsLib/res/values-km/arrays.xml
index 2269df1..52d8207 100644
--- a/packages/SettingsLib/res/values-km/arrays.xml
+++ b/packages/SettingsLib/res/values-km/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-kn/arrays.xml b/packages/SettingsLib/res/values-kn/arrays.xml
index 975f60f..1ad0948 100644
--- a/packages/SettingsLib/res/values-kn/arrays.xml
+++ b/packages/SettingsLib/res/values-kn/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-ko/arrays.xml b/packages/SettingsLib/res/values-ko/arrays.xml
index 16b840b..6e00b1082 100644
--- a/packages/SettingsLib/res/values-ko/arrays.xml
+++ b/packages/SettingsLib/res/values-ko/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-ky/arrays.xml b/packages/SettingsLib/res/values-ky/arrays.xml
index 700aae1..ec37e64 100644
--- a/packages/SettingsLib/res/values-ky/arrays.xml
+++ b/packages/SettingsLib/res/values-ky/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-lo/arrays.xml b/packages/SettingsLib/res/values-lo/arrays.xml
index f116e6f..3fe8330 100644
--- a/packages/SettingsLib/res/values-lo/arrays.xml
+++ b/packages/SettingsLib/res/values-lo/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-lt/arrays.xml b/packages/SettingsLib/res/values-lt/arrays.xml
index c0aafdc..6b34af3 100644
--- a/packages/SettingsLib/res/values-lt/arrays.xml
+++ b/packages/SettingsLib/res/values-lt/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-lv/arrays.xml b/packages/SettingsLib/res/values-lv/arrays.xml
index 0f9ee52..16f1070 100644
--- a/packages/SettingsLib/res/values-lv/arrays.xml
+++ b/packages/SettingsLib/res/values-lv/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-mk/arrays.xml b/packages/SettingsLib/res/values-mk/arrays.xml
index 41427c1..d8b86be 100644
--- a/packages/SettingsLib/res/values-mk/arrays.xml
+++ b/packages/SettingsLib/res/values-mk/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-ml/arrays.xml b/packages/SettingsLib/res/values-ml/arrays.xml
index 98e3bd6..bf6c67a 100644
--- a/packages/SettingsLib/res/values-ml/arrays.xml
+++ b/packages/SettingsLib/res/values-ml/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-mn/arrays.xml b/packages/SettingsLib/res/values-mn/arrays.xml
index f3c10d7..776c5c3 100644
--- a/packages/SettingsLib/res/values-mn/arrays.xml
+++ b/packages/SettingsLib/res/values-mn/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-mr/arrays.xml b/packages/SettingsLib/res/values-mr/arrays.xml
index c37baaa2..8b36c73 100644
--- a/packages/SettingsLib/res/values-mr/arrays.xml
+++ b/packages/SettingsLib/res/values-mr/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-ms/arrays.xml b/packages/SettingsLib/res/values-ms/arrays.xml
index b19f038..2a1303b 100644
--- a/packages/SettingsLib/res/values-ms/arrays.xml
+++ b/packages/SettingsLib/res/values-ms/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-my/arrays.xml b/packages/SettingsLib/res/values-my/arrays.xml
index 3398c5b..332dba0 100644
--- a/packages/SettingsLib/res/values-my/arrays.xml
+++ b/packages/SettingsLib/res/values-my/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-nb/arrays.xml b/packages/SettingsLib/res/values-nb/arrays.xml
index 928ebc3..80916e3 100644
--- a/packages/SettingsLib/res/values-nb/arrays.xml
+++ b/packages/SettingsLib/res/values-nb/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index aede28a..145dc5d 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -574,7 +574,7 @@
     <string name="user_add_user_title" msgid="5457079143694924885">"Vil du legge til en ny bruker?"</string>
     <string name="user_add_user_message_long" msgid="1527434966294733380">"Du kan dele denne enheten med andre folk ved å opprette flere brukere. Hver bruker har sin egen plass de kan tilpasse med apper, bakgrunner og annet. Brukere kan også justere enhetsinnstillinger, for eksempel Wifi, som påvirker alle.\n\nNår du legger til en ny bruker, må vedkommende angi innstillinger for plassen sin.\n\nAlle brukere kan oppdatere apper for alle andre brukere. Innstillinger og tjenester for tilgjengelighet overføres kanskje ikke til den nye brukeren."</string>
     <string name="user_add_user_message_short" msgid="3295959985795716166">"Når du legger til en ny bruker, må hen konfigurere sitt eget område.\n\nAlle brukere kan oppdatere apper for alle andre brukere."</string>
-    <string name="user_setup_dialog_title" msgid="8037342066381939995">"Konfigurere brukeren nå?"</string>
+    <string name="user_setup_dialog_title" msgid="8037342066381939995">"Vil du konfigurere brukeren?"</string>
     <string name="user_setup_dialog_message" msgid="269931619868102841">"Sørg for at brukeren er tilgjengelig for å konfigurere området sitt på enheten"</string>
     <string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"Vil du konfigurere profilen nå?"</string>
     <string name="user_setup_button_setup_now" msgid="1708269547187760639">"Konfigurer nå"</string>
diff --git a/packages/SettingsLib/res/values-ne/arrays.xml b/packages/SettingsLib/res/values-ne/arrays.xml
index ac1f187..decf4f5 100644
--- a/packages/SettingsLib/res/values-ne/arrays.xml
+++ b/packages/SettingsLib/res/values-ne/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-nl/arrays.xml b/packages/SettingsLib/res/values-nl/arrays.xml
index 7c90eab..e827be1 100644
--- a/packages/SettingsLib/res/values-nl/arrays.xml
+++ b/packages/SettingsLib/res/values-nl/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 8ee274b..f29c7452 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -573,7 +573,7 @@
     <string name="user_add_profile_item_title" msgid="3111051717414643029">"Beperkt profiel"</string>
     <string name="user_add_user_title" msgid="5457079143694924885">"Nieuwe gebruiker toevoegen?"</string>
     <string name="user_add_user_message_long" msgid="1527434966294733380">"Je kunt dit apparaat met anderen delen door extra gebruikers te maken. Elke gebruiker heeft een eigen profiel met zelf gekozen apps, achtergrond, enzovoort. Gebruikers kunnen ook apparaatinstellingen aanpassen die van invloed zijn op alle gebruikers, zoals wifi.\n\nWanneer je een nieuwe gebruiker toevoegt, moet die persoon een eigen profiel instellen.\n\nElke gebruiker kan apps updaten voor alle andere gebruikers. Toegankelijkheidsinstellingen en -services worden mogelijk niet overgezet naar de nieuwe gebruiker."</string>
-    <string name="user_add_user_message_short" msgid="3295959985795716166">"Wanneer je een nieuwe gebruiker toevoegt, moet die persoon diens eigen profiel instellen.\n\nElke gebruiker kan apps updaten voor alle andere gebruikers."</string>
+    <string name="user_add_user_message_short" msgid="3295959985795716166">"Wanneer je een nieuwe gebruiker toevoegt, moet die persoon hun eigen profiel instellen.\n\nElke gebruiker kan apps updaten voor alle andere gebruikers."</string>
     <string name="user_setup_dialog_title" msgid="8037342066381939995">"Gebruiker nu instellen?"</string>
     <string name="user_setup_dialog_message" msgid="269931619868102841">"Zorg ervoor dat de persoon het apparaat kan overnemen om een profiel in te stellen"</string>
     <string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"Profiel nu instellen?"</string>
diff --git a/packages/SettingsLib/res/values-or/arrays.xml b/packages/SettingsLib/res/values-or/arrays.xml
index a6c40b0..fc783f0 100644
--- a/packages/SettingsLib/res/values-or/arrays.xml
+++ b/packages/SettingsLib/res/values-or/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-pa/arrays.xml b/packages/SettingsLib/res/values-pa/arrays.xml
index 0fd5c56..f0e8396 100644
--- a/packages/SettingsLib/res/values-pa/arrays.xml
+++ b/packages/SettingsLib/res/values-pa/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-pl/arrays.xml b/packages/SettingsLib/res/values-pl/arrays.xml
index 71ecd46..c2be21d 100644
--- a/packages/SettingsLib/res/values-pl/arrays.xml
+++ b/packages/SettingsLib/res/values-pl/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-pt-rBR/arrays.xml b/packages/SettingsLib/res/values-pt-rBR/arrays.xml
index f218fab..658b153 100644
--- a/packages/SettingsLib/res/values-pt-rBR/arrays.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-pt-rPT/arrays.xml b/packages/SettingsLib/res/values-pt-rPT/arrays.xml
index e323455..8b63f7f 100644
--- a/packages/SettingsLib/res/values-pt-rPT/arrays.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/arrays.xml
@@ -50,8 +50,8 @@
   </string-array>
   <string-array name="hdcp_checking_titles">
     <item msgid="2377230797542526134">"Nunca verificar"</item>
-    <item msgid="3919638466823112484">"Verificar apenas conteúdo DRM"</item>
-    <item msgid="9048424957228926377">"Verificar sempre"</item>
+    <item msgid="3919638466823112484">"Rever apenas conteúdo DRM"</item>
+    <item msgid="9048424957228926377">"Rever sempre"</item>
   </string-array>
   <string-array name="hdcp_checking_summaries">
     <item msgid="4045840870658484038">"Nunca utilizar a verificação HDCP"</item>
@@ -273,4 +273,10 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+  <string-array name="entries_font_size">
+    <item msgid="9181293769180886675">"Pequeno"</item>
+    <item msgid="1484561228522634915">"Predefinição"</item>
+    <item msgid="4014311587094503943">"Grande"</item>
+    <item msgid="2904569270831156685">"O maior"</item>
+  </string-array>
 </resources>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index d241f38..758bc32 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -335,8 +335,8 @@
     <string name="adb_keys_warning_message" msgid="2968555274488101220">"Revogar acesso à depuração USB de todos os computadores anteriormente autorizados?"</string>
     <string name="dev_settings_warning_title" msgid="8251234890169074553">"Permitir definições de programação?"</string>
     <string name="dev_settings_warning_message" msgid="37741686486073668">"Estas definições destinam-se apenas a programação. Podem fazer com que o seu aparelho e as aplicações nele existentes falhem ou funcionem mal."</string>
-    <string name="verify_apps_over_usb_title" msgid="6031809675604442636">"Verificar apps por USB"</string>
-    <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Verificar as aplicações instaladas via ADB/ADT para detetar comportamento perigoso"</string>
+    <string name="verify_apps_over_usb_title" msgid="6031809675604442636">"Rever apps por USB"</string>
+    <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Rever as aplicações instaladas via ADB/ADT para detetar comportamento perigoso"</string>
     <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"São apresentados os dispositivos Bluetooth sem nomes (apenas endereços MAC)"</string>
     <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Desativa a funcionalidade de volume absoluto do Bluetooth caso existam problemas de volume com dispositivos remotos, como um volume insuportavelmente alto ou a ausência de controlo"</string>
     <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Ativa a pilha de funcionalidades Bluetooth Gabeldorche."</string>
diff --git a/packages/SettingsLib/res/values-pt/arrays.xml b/packages/SettingsLib/res/values-pt/arrays.xml
index f218fab..658b153 100644
--- a/packages/SettingsLib/res/values-pt/arrays.xml
+++ b/packages/SettingsLib/res/values-pt/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-ro/arrays.xml b/packages/SettingsLib/res/values-ro/arrays.xml
index faa15c2..b839c12a 100644
--- a/packages/SettingsLib/res/values-ro/arrays.xml
+++ b/packages/SettingsLib/res/values-ro/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-ru/arrays.xml b/packages/SettingsLib/res/values-ru/arrays.xml
index 4b6e692..707a34a 100644
--- a/packages/SettingsLib/res/values-ru/arrays.xml
+++ b/packages/SettingsLib/res/values-ru/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-si/arrays.xml b/packages/SettingsLib/res/values-si/arrays.xml
index eaacfb8..7a8e9ee 100644
--- a/packages/SettingsLib/res/values-si/arrays.xml
+++ b/packages/SettingsLib/res/values-si/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-sk/arrays.xml b/packages/SettingsLib/res/values-sk/arrays.xml
index bbfe969..5ff15e7 100644
--- a/packages/SettingsLib/res/values-sk/arrays.xml
+++ b/packages/SettingsLib/res/values-sk/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-sl/arrays.xml b/packages/SettingsLib/res/values-sl/arrays.xml
index b2003e5..f1e59e7 100644
--- a/packages/SettingsLib/res/values-sl/arrays.xml
+++ b/packages/SettingsLib/res/values-sl/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-sq/arrays.xml b/packages/SettingsLib/res/values-sq/arrays.xml
index ed86380..7bceea7 100644
--- a/packages/SettingsLib/res/values-sq/arrays.xml
+++ b/packages/SettingsLib/res/values-sq/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-sr/arrays.xml b/packages/SettingsLib/res/values-sr/arrays.xml
index a95e47b..36882c2 100644
--- a/packages/SettingsLib/res/values-sr/arrays.xml
+++ b/packages/SettingsLib/res/values-sr/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-sv/arrays.xml b/packages/SettingsLib/res/values-sv/arrays.xml
index c63465c..8061e9c 100644
--- a/packages/SettingsLib/res/values-sv/arrays.xml
+++ b/packages/SettingsLib/res/values-sv/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-sw/arrays.xml b/packages/SettingsLib/res/values-sw/arrays.xml
index 6ed4d5a..862fe38 100644
--- a/packages/SettingsLib/res/values-sw/arrays.xml
+++ b/packages/SettingsLib/res/values-sw/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-ta/arrays.xml b/packages/SettingsLib/res/values-ta/arrays.xml
index 236f899..2f18e076 100644
--- a/packages/SettingsLib/res/values-ta/arrays.xml
+++ b/packages/SettingsLib/res/values-ta/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-te/arrays.xml b/packages/SettingsLib/res/values-te/arrays.xml
index fb4cff1..ed5a6a6 100644
--- a/packages/SettingsLib/res/values-te/arrays.xml
+++ b/packages/SettingsLib/res/values-te/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-th/arrays.xml b/packages/SettingsLib/res/values-th/arrays.xml
index 782e95e..e0046fc 100644
--- a/packages/SettingsLib/res/values-th/arrays.xml
+++ b/packages/SettingsLib/res/values-th/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-tl/arrays.xml b/packages/SettingsLib/res/values-tl/arrays.xml
index 19d3423..622587d 100644
--- a/packages/SettingsLib/res/values-tl/arrays.xml
+++ b/packages/SettingsLib/res/values-tl/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-tr/arrays.xml b/packages/SettingsLib/res/values-tr/arrays.xml
index 37891ae..99dca9e 100644
--- a/packages/SettingsLib/res/values-tr/arrays.xml
+++ b/packages/SettingsLib/res/values-tr/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-uk/arrays.xml b/packages/SettingsLib/res/values-uk/arrays.xml
index c32da85..976a487 100644
--- a/packages/SettingsLib/res/values-uk/arrays.xml
+++ b/packages/SettingsLib/res/values-uk/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-ur/arrays.xml b/packages/SettingsLib/res/values-ur/arrays.xml
index db9941e..39b7394 100644
--- a/packages/SettingsLib/res/values-ur/arrays.xml
+++ b/packages/SettingsLib/res/values-ur/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-uz/arrays.xml b/packages/SettingsLib/res/values-uz/arrays.xml
index edbd180..7605bdc 100644
--- a/packages/SettingsLib/res/values-uz/arrays.xml
+++ b/packages/SettingsLib/res/values-uz/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-vi/arrays.xml b/packages/SettingsLib/res/values-vi/arrays.xml
index ee599d6..5306574 100644
--- a/packages/SettingsLib/res/values-vi/arrays.xml
+++ b/packages/SettingsLib/res/values-vi/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 7f9d858..d548222 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -325,7 +325,7 @@
     <string name="select_usb_configuration_dialog_title" msgid="3579567144722589237">"Chọn cáș„u hình USB"</string>
     <string name="allow_mock_location" msgid="2102650981552527884">"Cho phép vị trí mô phỏng"</string>
     <string name="allow_mock_location_summary" msgid="179780881081354579">"Cho phép vị trí mô phỏng"</string>
-    <string name="debug_view_attributes" msgid="3539609843984208216">"Cho phép kiểm tra thuộc tính cá»§a cháșż độ xem"</string>
+    <string name="debug_view_attributes" msgid="3539609843984208216">"Cho phép kiểm tra thuộc tính khung hiển thị"</string>
     <string name="mobile_data_always_on_summary" msgid="1112156365594371019">"Luôn báș­t dữ liệu di động ngay cáșŁ khi Wi-Fi đang hoáșĄt động (để chuyển đổi máșĄng nhanh)."</string>
     <string name="tethering_hardware_offload_summary" msgid="7801345335142803029">"Sá»­ dỄng tính năng tăng tốc pháș§n cứng khi chia sáș» Internet náșżu có"</string>
     <string name="adb_warning_title" msgid="7708653449506485728">"Cho phép gụ lỗi qua USB?"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/arrays.xml b/packages/SettingsLib/res/values-zh-rCN/arrays.xml
index 2a85d31..ff3bac6 100644
--- a/packages/SettingsLib/res/values-zh-rCN/arrays.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-zh-rHK/arrays.xml b/packages/SettingsLib/res/values-zh-rHK/arrays.xml
index a84f0e2..baf6662 100644
--- a/packages/SettingsLib/res/values-zh-rHK/arrays.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-zh-rTW/arrays.xml b/packages/SettingsLib/res/values-zh-rTW/arrays.xml
index b69fd53..1669e80 100644
--- a/packages/SettingsLib/res/values-zh-rTW/arrays.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values-zu/arrays.xml b/packages/SettingsLib/res/values-zu/arrays.xml
index 0494f1c..d62e519 100644
--- a/packages/SettingsLib/res/values-zu/arrays.xml
+++ b/packages/SettingsLib/res/values-zu/arrays.xml
@@ -273,4 +273,12 @@
   </string-array>
   <string-array name="avatar_image_descriptions">
   </string-array>
+    <!-- no translation found for entries_font_size:0 (9181293769180886675) -->
+    <!-- no translation found for entries_font_size:0 (6490061470416867723) -->
+    <!-- no translation found for entries_font_size:1 (1484561228522634915) -->
+    <!-- no translation found for entries_font_size:1 (3579015730662088893) -->
+    <!-- no translation found for entries_font_size:2 (4014311587094503943) -->
+    <!-- no translation found for entries_font_size:2 (1678068858001018666) -->
+    <!-- no translation found for entries_font_size:3 (2904569270831156685) -->
+    <!-- no translation found for entries_font_size:3 (490158884605093126) -->
 </resources>
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index 663e8e4..ef664e9 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -660,4 +660,18 @@
     <!-- Content descriptions for each of the images in the avatar_images array. -->
     <string-array name="avatar_image_descriptions"/>
 
+    <string-array name="entries_font_size">
+        <item msgid="6490061470416867723">Small</item>
+        <item msgid="3579015730662088893">Default</item>
+        <item msgid="1678068858001018666">Large</item>
+        <item msgid="490158884605093126">Largest</item>
+    </string-array>
+
+    <string-array name="entryvalues_font_size" translatable="false">
+        <item>0.85</item>
+        <item>1.0</item>
+        <item>1.15</item>
+        <item>1.30</item>
+    </string-array>
+
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index d6586db..d9d7cc9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -185,6 +185,24 @@
     public abstract String getId();
 
     /**
+     * Get disabled reason of device
+     *
+     * @return disabled reason of device
+     */
+    public int getDisableReason() {
+        return -1;
+    }
+
+    /**
+     * Checks if device is has disabled reason
+     *
+     * @return true if device has disabled reason
+     */
+    public boolean hasDisabledReason() {
+        return false;
+    }
+
+    /**
      * Checks if device is suggested device from application
      *
      * @return true if device is suggested device
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
index 103512d..21e119a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
@@ -215,4 +215,20 @@
 
         assertThat(mOnBindListenerAnimationView).isNull();
     }
+
+    @Test
+    public void onBindViewHolder_default_shouldNotApplyDynamicColor() {
+        mPreference.onBindViewHolder(mViewHolder);
+
+        assertThat(mPreference.isApplyDynamicColor()).isFalse();
+    }
+
+    @Test
+    public void onBindViewHolder_applyDynamicColor_shouldReturnTrue() {
+        mPreference.applyDynamicColor();
+
+        mPreference.onBindViewHolder(mViewHolder);
+
+        assertThat(mPreference.isApplyDynamicColor()).isTrue();
+    }
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/LargeScreenSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/LargeScreenSettings.java
new file mode 100644
index 0000000..bdd4869
--- /dev/null
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/LargeScreenSettings.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.settings.backup;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.DisplayMetrics;
+import android.view.WindowManager;
+
+/** Settings that should not be restored when target device is a large screen
+ *  i.e. tablets and foldables in unfolded state
+ */
+public class LargeScreenSettings {
+    private static final float LARGE_SCREEN_MIN_DPS = 600;
+    private static final String LARGE_SCREEN_DO_NOT_RESTORE = "accelerometer_rotation";
+
+   /**
+    * Autorotation setting should not be restored when the target device is a large screen.
+    * (b/243489549)
+    */
+    public static boolean doNotRestoreIfLargeScreenSetting(String key, Context context) {
+        return isLargeScreen(context) && LARGE_SCREEN_DO_NOT_RESTORE.equals(key);
+    }
+
+    // copied from systemui/shared/...Utilities.java
+    // since we don't want to add compile time dependency on sys ui package
+    private static boolean isLargeScreen(Context context) {
+        final WindowManager windowManager = context.getSystemService(WindowManager.class);
+        final Rect bounds = windowManager.getCurrentWindowMetrics().getBounds();
+        float smallestWidth = dpiFromPx(Math.min(bounds.width(), bounds.height()),
+                context.getResources().getConfiguration().densityDpi);
+        return smallestWidth >= LARGE_SCREEN_MIN_DPS;
+    }
+
+    private static float dpiFromPx(float size, int densityDpi) {
+        float densityRatio = (float) densityDpi / DisplayMetrics.DENSITY_DEFAULT;
+        return (size / densityRatio);
+    }
+}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 211030a..b5eaa4b 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -130,6 +130,8 @@
         Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS,
         Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
         Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
+        Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
+        Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
         Settings.Secure.VR_DISPLAY_MODE,
         Settings.Secure.NOTIFICATION_BADGING,
         Settings.Secure.NOTIFICATION_DISMISS_RTL,
@@ -218,6 +220,7 @@
         Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED,
         Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO,
         Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE,
-        Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME
+        Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME,
+        Settings.Secure.LOCK_SCREEN_WEATHER_ENABLED
     };
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 0539f09..534e31a 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -189,6 +189,10 @@
         VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO, ANY_STRING_VALIDATOR);
         VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
                 ANY_STRING_VALIDATOR);
+        VALIDATORS.put(Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
+                ANY_STRING_VALIDATOR);
+        VALIDATORS.put(Secure.ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
+                ANY_STRING_VALIDATOR);
         VALIDATORS.put(Secure.ASSIST_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ASSIST_GESTURE_SILENCE_ALERTS_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ASSIST_GESTURE_WAKE_ENABLED, BOOLEAN_VALIDATOR);
@@ -350,5 +354,6 @@
         VALIDATORS.put(Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO, ANY_STRING_VALIDATOR);
         VALIDATORS.put(Secure.BLUETOOTH_LE_BROADCAST_CODE, ANY_STRING_VALIDATOR);
         VALIDATORS.put(Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME, ANY_STRING_VALIDATOR);
+        VALIDATORS.put(Secure.LOCK_SCREEN_WEATHER_ENABLED, BOOLEAN_VALIDATOR);
     }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index d3afccc..d0055d7 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -37,6 +37,7 @@
 import android.provider.Settings;
 import android.provider.settings.backup.DeviceSpecificSettings;
 import android.provider.settings.backup.GlobalSettings;
+import android.provider.settings.backup.LargeScreenSettings;
 import android.provider.settings.backup.SecureSettings;
 import android.provider.settings.backup.SystemSettings;
 import android.provider.settings.validators.GlobalSettingsValidators;
@@ -812,6 +813,12 @@
                 continue;
             }
 
+            if (LargeScreenSettings.doNotRestoreIfLargeScreenSetting(key, getBaseContext())) {
+                Log.i(TAG, "Skipping restore for setting " + key + " as the target device "
+                        + "is a large screen (i.e tablet or foldable in unfolded state)");
+                continue;
+            }
+
             String value = null;
             boolean hasValueToRestore = false;
             if (cachedEntries.indexOfKey(key) >= 0) {
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 3b862ff..3915012 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -356,6 +356,9 @@
     <!-- Permission needed to test wallpaper dimming -->
     <uses-permission android:name="android.permission.SET_WALLPAPER_DIM_AMOUNT" />
 
+    <!-- Permission needed to test wallpaper read methods -->
+    <uses-permission android:name="android.permission.READ_WALLPAPER_INTERNAL" />
+
     <!-- Permission required to test ContentResolver caching. -->
     <uses-permission android:name="android.permission.CACHE_CONTENT" />
 
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 2529157..ed62c5f 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -204,6 +204,45 @@
     path: "tests/utils/src",
 }
 
+filegroup {
+    name: "SystemUI-tests-robolectric-pilots",
+    srcs: [
+        // data
+        "tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt",
+        "tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt",
+        "tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt",
+        "tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt",
+        "tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt",
+        "tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt",
+        "tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt",
+        "tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt",
+        "tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt",
+        "tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt",
+        "tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt",
+        "tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt",
+        // domain
+        "tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt",
+        "tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt",
+        "tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt",
+        "tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt",
+        "tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt",
+        "tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt",
+        "tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt",
+        "tests/src/com/android/systemui/keyguard/domain/quickaffordance/FakeKeyguardQuickAffordanceRegistry.kt",
+        "tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt",
+        "tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt",
+        "tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt",
+        // ui
+        "tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt",
+        "tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModelTest.kt",
+        "tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt",
+        "tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt",
+        "tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt",
+        "tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt",
+    ],
+    path: "tests/src",
+}
+
 java_library {
     name: "SystemUI-tests-concurrency",
     srcs: [
@@ -315,8 +354,16 @@
     defaults: [
         "platform_app_defaults",
         "SystemUI_app_defaults",
+        "SystemUI_compose_defaults",
     ],
     manifest: "tests/AndroidManifest-base.xml",
+
+    srcs: [
+        "src/**/*.kt",
+        "src/**/*.java",
+        "src/**/I*.aidl",
+        ":ReleaseJavaFiles",
+    ],
     static_libs: [
         "SystemUI-tests-base",
     ],
@@ -330,6 +377,9 @@
     certificate: "platform",
     privileged: true,
     resource_dirs: [],
+    kotlincflags: ["-Xjvm-default=all"],
+
+    plugins: ["dagger2-compiler"],
 }
 
 android_robolectric_test {
@@ -337,6 +387,13 @@
     srcs: [
         "tests/robolectric/src/**/*.kt",
         "tests/robolectric/src/**/*.java",
+        ":SystemUI-tests-utils",
+        ":SystemUI-tests-robolectric-pilots",
+    ],
+    static_libs: [
+        "androidx.test.uiautomator_uiautomator",
+        "androidx.test.ext.junit",
+        "inline-mockito-robolectric-prebuilt",
     ],
     libs: [
         "android.test.runner",
@@ -344,7 +401,9 @@
         "android.test.mock",
         "truth-prebuilt",
     ],
-    kotlincflags: ["-Xjvm-default=enable"],
+
+    upstream: true,
+
     instrumentation_for: "SystemUIRobo-stub",
     java_resource_dirs: ["tests/robolectric/config"],
 }
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index b6e006f..71a82bf 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -68,6 +68,7 @@
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
     <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
+    <uses-permission android:name="android.permission.READ_PRECISE_PHONE_STATE" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
     <uses-permission android:name="android.permission.OVERRIDE_WIFI_CONFIG" />
@@ -674,6 +675,7 @@
             <intent-filter>
                 <action android:name="android.telecom.action.SHOW_SWITCH_TO_WORK_PROFILE_FOR_CALL_DIALOG" />
                 <category android:name="android.intent.category.DEFAULT" />
+                <data android:scheme="tel" />
             </intent-filter>
         </activity>
 
@@ -898,7 +900,7 @@
                   android:showWhenLocked="true"
                   android:showForAllUsers="true"
                   android:finishOnTaskLaunch="true"
-                  android:launchMode="singleInstance"
+                  android:lockTaskMode="if_whitelisted"
                   android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
                   android:visibleToInstantApps="true">
         </activity>
diff --git a/packages/SystemUI/animation/Android.bp b/packages/SystemUI/animation/Android.bp
index 8acc2f8..978ab5d 100644
--- a/packages/SystemUI/animation/Android.bp
+++ b/packages/SystemUI/animation/Android.bp
@@ -35,7 +35,6 @@
     ],
 
     static_libs: [
-        "PluginCoreLib",
         "androidx.core_core-animation-nodeps",
         "androidx.core_core-ktx",
         "androidx.annotation_annotation",
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index c729b09..17a94b86 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -41,6 +41,7 @@
 import androidx.annotation.UiThread
 import com.android.internal.annotations.VisibleForTesting
 import com.android.internal.policy.ScreenDecorationsUtils
+import java.lang.IllegalArgumentException
 import kotlin.math.roundToInt
 
 private const val TAG = "ActivityLaunchAnimator"
@@ -338,13 +339,24 @@
              * Return a [Controller] that will animate and expand [view] into the opening window.
              *
              * Important: The view must be attached to a [ViewGroup] when calling this function and
-             * during the animation. For safety, this method will return null when it is not.
+             * during the animation. For safety, this method will return null when it is not. The
+             * view must also implement [LaunchableView], otherwise this method will throw.
              *
              * Note: The background of [view] should be a (rounded) rectangle so that it can be
              * properly animated.
              */
             @JvmStatic
             fun fromView(view: View, cujType: Int? = null): Controller? {
+                // Make sure the View we launch from implements LaunchableView to avoid visibility
+                // issues.
+                if (view !is LaunchableView) {
+                    throw IllegalArgumentException(
+                        "An ActivityLaunchAnimator.Controller was created from a View that does " +
+                            "not implement LaunchableView. This can lead to subtle bugs where the" +
+                            " visibility of the View we are launching from is not what we expected."
+                    )
+                }
+
                 if (view.parent !is ViewGroup) {
                     Log.e(
                         TAG,
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index e91a671..42a8636 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -33,13 +33,10 @@
 import android.view.WindowManager
 import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
 import android.widget.FrameLayout
-import android.window.OnBackInvokedDispatcher
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.jank.InteractionJankMonitor.CujType
-import com.android.systemui.animation.back.BackAnimationSpec
-import com.android.systemui.animation.back.applyTo
-import com.android.systemui.animation.back.floatingSystemSurfacesForSysUi
-import com.android.systemui.animation.back.onBackAnimationCallbackFrom
+import com.android.systemui.util.registerAnimationOnBackInvoked
+import java.lang.IllegalArgumentException
 import kotlin.math.roundToInt
 
 private const val TAG = "DialogLaunchAnimator"
@@ -157,12 +154,23 @@
              * Create a [Controller] that can animate [source] to and from a dialog.
              *
              * Important: The view must be attached to a [ViewGroup] when calling this function and
-             * during the animation. For safety, this method will return null when it is not.
+             * during the animation. For safety, this method will return null when it is not. The
+             * view must also implement [LaunchableView], otherwise this method will throw.
              *
              * Note: The background of [view] should be a (rounded) rectangle so that it can be
              * properly animated.
              */
             fun fromView(source: View, cuj: DialogCuj? = null): Controller? {
+                // Make sure the View we launch from implements LaunchableView to avoid visibility
+                // issues.
+                if (source !is LaunchableView) {
+                    throw IllegalArgumentException(
+                        "A DialogLaunchAnimator.Controller was created from a View that does not " +
+                            "implement LaunchableView. This can lead to subtle bugs where the " +
+                            "visibility of the View we are launching from is not what we expected."
+                    )
+                }
+
                 if (source.parent !is ViewGroup) {
                     Log.e(
                         TAG,
@@ -249,23 +257,6 @@
             }
                 ?: controller
 
-        if (
-            animatedParent == null &&
-                controller is ViewDialogLaunchAnimatorController &&
-                controller.source !is LaunchableView
-        ) {
-            // Make sure the View we launch from implements LaunchableView to avoid visibility
-            // issues. Given that we don't own dialog decorViews so we can't enforce it for launches
-            // from a dialog.
-            // TODO(b/243636422): Throw instead of logging to enforce this.
-            Log.w(
-                TAG,
-                "A dialog was launched from a View that does not implement LaunchableView. This " +
-                    "can lead to subtle bugs where the visibility of the View we are " +
-                    "launching from is not what we expected."
-            )
-        }
-
         // Make sure we don't run the launch animation from the same source twice at the same time.
         if (openedDialogs.any { it.controller.sourceIdentity == controller.sourceIdentity }) {
             Log.e(
@@ -309,10 +300,16 @@
     ) {
         val view =
             openedDialogs.firstOrNull { it.dialog == animateFrom }?.dialogContentWithBackground
-                ?: throw IllegalStateException(
-                    "The animateFrom dialog was not animated using " +
-                        "DialogLaunchAnimator.showFrom(View|Dialog)"
-                )
+        if (view == null) {
+            Log.w(
+                TAG,
+                "Showing dialog $dialog normally as the dialog it is shown from was not shown " +
+                    "using DialogLaunchAnimator"
+            )
+            dialog.show()
+            return
+        }
+
         showFromView(
             dialog,
             view,
@@ -613,10 +610,16 @@
                 }
 
                 // Animate that view with the background. Throw if we didn't find one, because
-                // otherwise
-                // it's not clear what we should animate.
+                // otherwise it's not clear what we should animate.
+                if (viewGroupWithBackground == null) {
+                    error("Unable to find ViewGroup with background")
+                }
+
+                if (viewGroupWithBackground !is LaunchableView) {
+                    error("The animated ViewGroup with background must implement LaunchableView")
+                }
+
                 viewGroupWithBackground
-                    ?: throw IllegalStateException("Unable to find ViewGroup with background")
             } else {
                 // We will make the dialog window (and therefore its DecorView) fullscreen to make
                 // it possible to animate outside its bounds.
@@ -639,7 +642,7 @@
                     FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
                 )
 
-                val dialogContentWithBackground = FrameLayout(dialog.context)
+                val dialogContentWithBackground = LaunchableFrameLayout(dialog.context)
                 dialogContentWithBackground.background = decorView.background
 
                 // Make the window background transparent. Note that setting the window (or
@@ -720,7 +723,10 @@
 
         // Make the background view invisible until we start the animation. We use the transition
         // visibility like GhostView does so that we don't mess up with the accessibility tree (see
-        // b/204944038#comment17).
+        // b/204944038#comment17). Given that this background implements LaunchableView, we call
+        // setShouldBlockVisibilityChanges() early so that the current visibility (VISIBLE) is
+        // restored at the end of the animation.
+        dialogContentWithBackground.setShouldBlockVisibilityChanges(true)
         dialogContentWithBackground.setTransitionVisibility(View.INVISIBLE)
 
         // Make sure the dialog is visible instantly and does not do any window animation.
@@ -788,7 +794,7 @@
 
         if (featureFlags.isPredictiveBackQsDialogAnim) {
             // TODO(b/265923095) Improve animations for QS dialogs on configuration change
-            registerOnBackInvokedCallback(targetView = dialogContentWithBackground)
+            dialog.registerAnimationOnBackInvoked(targetView = dialogContentWithBackground)
         }
 
         // Show the dialog.
@@ -796,35 +802,6 @@
         moveSourceDrawingToDialog()
     }
 
-    private fun registerOnBackInvokedCallback(targetView: View) {
-        val metrics = targetView.resources.displayMetrics
-
-        val onBackAnimationCallback =
-            onBackAnimationCallbackFrom(
-                backAnimationSpec = BackAnimationSpec.floatingSystemSurfacesForSysUi(metrics),
-                displayMetrics = metrics, // TODO(b/265060720): We could remove this
-                onBackProgressed = { backTransformation -> backTransformation.applyTo(targetView) },
-                onBackInvoked = { dialog.dismiss() },
-            )
-
-        val dispatcher = dialog.onBackInvokedDispatcher
-        targetView.addOnAttachStateChangeListener(
-            object : View.OnAttachStateChangeListener {
-                override fun onViewAttachedToWindow(v: View) {
-                    dispatcher.registerOnBackInvokedCallback(
-                        OnBackInvokedDispatcher.PRIORITY_DEFAULT,
-                        onBackAnimationCallback
-                    )
-                }
-
-                override fun onViewDetachedFromWindow(v: View) {
-                    targetView.removeOnAttachStateChangeListener(this)
-                    dispatcher.unregisterOnBackInvokedCallback(onBackAnimationCallback)
-                }
-            }
-        )
-    }
-
     private fun moveSourceDrawingToDialog() {
         if (decorView.viewRootImpl == null) {
             // Make sure that we have access to the dialog view root to move the drawing to the
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
index 26aa0e8..23e3a01 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
@@ -34,6 +34,7 @@
 import android.view.ViewGroupOverlay
 import android.widget.FrameLayout
 import com.android.internal.jank.InteractionJankMonitor
+import java.lang.IllegalArgumentException
 import java.util.LinkedList
 import kotlin.math.min
 import kotlin.math.roundToInt
@@ -46,7 +47,8 @@
  * of the ghosted view.
  *
  * Important: [ghostedView] must be attached to a [ViewGroup] when calling this function and during
- * the animation.
+ * the animation. It must also implement [LaunchableView], otherwise an exception will be thrown
+ * during this controller instantiation.
  *
  * Note: Avoid instantiating this directly and call [ActivityLaunchAnimator.Controller.fromView]
  * whenever possible instead.
@@ -101,6 +103,15 @@
     private val background: Drawable?
 
     init {
+        // Make sure the View we launch from implements LaunchableView to avoid visibility issues.
+        if (ghostedView !is LaunchableView) {
+            throw IllegalArgumentException(
+                "A GhostedViewLaunchAnimatorController was created from a View that does not " +
+                    "implement LaunchableView. This can lead to subtle bugs where the visibility " +
+                    "of the View we are launching from is not what we expected."
+            )
+        }
+
         /** Find the first view with a background in [view] and its children. */
         fun findBackground(view: View): Drawable? {
             if (view.background != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableImageView.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableFrameLayout.kt
similarity index 71%
copy from packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableImageView.kt
copy to packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableFrameLayout.kt
index 7bbfec7..2eb503b 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableImageView.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableFrameLayout.kt
@@ -12,37 +12,35 @@
  * 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.ui.view
+package com.android.systemui.animation
 
 import android.content.Context
 import android.util.AttributeSet
-import android.widget.ImageView
-import com.android.systemui.animation.LaunchableView
-import com.android.systemui.animation.LaunchableViewDelegate
+import android.widget.FrameLayout
 
-class LaunchableImageView : ImageView, LaunchableView {
+/** A [FrameLayout] that also implements [LaunchableView]. */
+open class LaunchableFrameLayout : FrameLayout, LaunchableView {
     private val delegate =
         LaunchableViewDelegate(
             this,
             superSetVisibility = { super.setVisibility(it) },
         )
 
-    constructor(context: Context?) : super(context)
-    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
+    constructor(context: Context) : super(context)
+    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
     constructor(
-        context: Context?,
+        context: Context,
         attrs: AttributeSet?,
-        defStyleAttr: Int,
+        defStyleAttr: Int
     ) : super(context, attrs, defStyleAttr)
 
     constructor(
-        context: Context?,
+        context: Context,
         attrs: AttributeSet?,
         defStyleAttr: Int,
-        defStyleRes: Int,
+        defStyleRes: Int
     ) : super(context, attrs, defStyleAttr, defStyleRes)
 
     override fun setShouldBlockVisibilityChanges(block: Boolean) {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt
index 774255b..b98b9221 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt
@@ -35,8 +35,7 @@
      *
      * Note that calls to [View.setTransitionVisibility] shouldn't be blocked.
      *
-     * @param block whether we should block/postpone all calls to `setVisibility` and
-     * `setTransitionVisibility`.
+     * @param block whether we should block/postpone all calls to `setVisibility`.
      */
     fun setShouldBlockVisibilityChanges(block: Boolean)
 }
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
index fdab749..a08b598 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
@@ -23,6 +23,7 @@
 import android.graphics.Canvas
 import android.graphics.Typeface
 import android.graphics.fonts.Font
+import android.graphics.fonts.FontVariationAxis
 import android.text.Layout
 import android.util.SparseArray
 
@@ -215,13 +216,40 @@
             textInterpolator.targetPaint.textSize = textSize
         }
         if (weight >= 0) {
-            // Paint#setFontVariationSettings creates Typeface instance from scratch. To reduce the
-            // memory impact, cache the typeface result.
-            textInterpolator.targetPaint.typeface =
-                typefaceCache.getOrElse(weight) {
-                    textInterpolator.targetPaint.fontVariationSettings = "'$TAG_WGHT' $weight"
-                    textInterpolator.targetPaint.typeface
+            val fontVariationArray =
+                    FontVariationAxis.fromFontVariationSettings(
+                        textInterpolator.targetPaint.fontVariationSettings
+                    )
+            if (fontVariationArray.isNullOrEmpty()) {
+                textInterpolator.targetPaint.typeface =
+                    typefaceCache.getOrElse(weight) {
+                        textInterpolator.targetPaint.fontVariationSettings = "'$TAG_WGHT' $weight"
+                        textInterpolator.targetPaint.typeface
+                    }
+            } else {
+                val idx = fontVariationArray.indexOfFirst { it.tag == "$TAG_WGHT" }
+                if (idx == -1) {
+                    val updatedFontVariation =
+                        textInterpolator.targetPaint.fontVariationSettings + ",'$TAG_WGHT' $weight"
+                    textInterpolator.targetPaint.typeface =
+                        typefaceCache.getOrElse(weight) {
+                            textInterpolator.targetPaint.fontVariationSettings =
+                                    updatedFontVariation
+                            textInterpolator.targetPaint.typeface
+                        }
+                } else {
+                    fontVariationArray[idx] = FontVariationAxis(
+                            "$TAG_WGHT", weight.toFloat())
+                    val updatedFontVariation =
+                            FontVariationAxis.toFontVariationSettings(fontVariationArray)
+                    textInterpolator.targetPaint.typeface =
+                        typefaceCache.getOrElse(weight) {
+                            textInterpolator.targetPaint.fontVariationSettings =
+                                    updatedFontVariation
+                            textInterpolator.targetPaint.typeface
+                        }
                 }
+            }
         }
         if (color != null) {
             textInterpolator.targetPaint.color = color
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt
index 9257f99..46d5a5c 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt
@@ -25,7 +25,7 @@
 /** A [DialogLaunchAnimator.Controller] that can animate a [View] from/to a dialog. */
 class ViewDialogLaunchAnimatorController
 internal constructor(
-    internal val source: View,
+    private val source: View,
     override val cuj: DialogCuj?,
 ) : DialogLaunchAnimator.Controller {
     override val viewRoot: ViewRootImpl?
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtension.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtension.kt
index 33d14b1..8740d14 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtension.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtension.kt
@@ -16,15 +16,21 @@
 
 package com.android.systemui.animation.back
 
+import android.annotation.IntRange
 import android.util.DisplayMetrics
+import android.view.View
 import android.window.BackEvent
 import android.window.OnBackAnimationCallback
+import android.window.OnBackInvokedDispatcher
+import android.window.OnBackInvokedDispatcher.Priority
 
 /**
  * Generates an [OnBackAnimationCallback] given a [backAnimationSpec]. [onBackProgressed] will be
  * called on each update passing the current [BackTransformation].
  *
  * Optionally, you can specify [onBackStarted], [onBackInvoked], and [onBackCancelled] callbacks.
+ *
+ * @sample com.android.systemui.util.registerAnimationOnBackInvoked
  */
 fun onBackAnimationCallbackFrom(
     backAnimationSpec: BackAnimationSpec,
@@ -64,3 +70,34 @@
         }
     }
 }
+
+/**
+ * Register [OnBackAnimationCallback] when View is attached and unregister it when View is detached
+ *
+ * @sample com.android.systemui.util.registerAnimationOnBackInvoked
+ */
+fun View.registerOnBackInvokedCallbackOnViewAttached(
+    onBackInvokedDispatcher: OnBackInvokedDispatcher,
+    onBackAnimationCallback: OnBackAnimationCallback,
+    @Priority @IntRange(from = 0) priority: Int = OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+) {
+    addOnAttachStateChangeListener(
+        object : View.OnAttachStateChangeListener {
+            override fun onViewAttachedToWindow(v: View) {
+                onBackInvokedDispatcher.registerOnBackInvokedCallback(
+                    priority,
+                    onBackAnimationCallback
+                )
+            }
+
+            override fun onViewDetachedFromWindow(v: View) {
+                removeOnAttachStateChangeListener(this)
+                onBackInvokedDispatcher.unregisterOnBackInvokedCallback(onBackAnimationCallback)
+            }
+        }
+    )
+
+    if (isAttachedToWindow) {
+        onBackInvokedDispatcher.registerOnBackInvokedCallback(priority, onBackAnimationCallback)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableImageView.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableImageView.kt
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableImageView.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableImageView.kt
index 7bbfec7..a1d9d90 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableImageView.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableImageView.kt
@@ -15,7 +15,7 @@
  *
  */
 
-package com.android.systemui.common.ui.view
+package com.android.systemui.animation.view
 
 import android.content.Context
 import android.util.AttributeSet
@@ -23,6 +23,7 @@
 import com.android.systemui.animation.LaunchableView
 import com.android.systemui.animation.LaunchableViewDelegate
 
+/** An [ImageView] that also implements [LaunchableView]. */
 class LaunchableImageView : ImageView, LaunchableView {
     private val delegate =
         LaunchableViewDelegate(
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableLinearLayout.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableLinearLayout.kt
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableLinearLayout.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableLinearLayout.kt
index ddde628..bce2622 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableLinearLayout.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableLinearLayout.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.common.ui.view
+package com.android.systemui.animation.view
 
 import android.content.Context
 import android.util.AttributeSet
@@ -23,7 +23,7 @@
 import com.android.systemui.animation.LaunchableViewDelegate
 
 /** A [LinearLayout] that also implements [LaunchableView]. */
-class LaunchableLinearLayout : LinearLayout, LaunchableView {
+open class LaunchableLinearLayout : LinearLayout, LaunchableView {
     private val delegate =
         LaunchableViewDelegate(
             this,
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableImageView.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableTextView.kt
similarity index 78%
copy from packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableImageView.kt
copy to packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableTextView.kt
index 7bbfec7..286996d 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableImageView.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableTextView.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -15,15 +15,16 @@
  *
  */
 
-package com.android.systemui.common.ui.view
+package com.android.systemui.animation.view
 
 import android.content.Context
 import android.util.AttributeSet
-import android.widget.ImageView
+import android.widget.TextView
 import com.android.systemui.animation.LaunchableView
 import com.android.systemui.animation.LaunchableViewDelegate
 
-class LaunchableImageView : ImageView, LaunchableView {
+/** A [TextView] that also implements [LaunchableView]. */
+class LaunchableTextView : TextView, LaunchableView {
     private val delegate =
         LaunchableViewDelegate(
             this,
@@ -38,13 +39,6 @@
         defStyleAttr: Int,
     ) : super(context, attrs, defStyleAttr)
 
-    constructor(
-        context: Context?,
-        attrs: AttributeSet?,
-        defStyleAttr: Int,
-        defStyleRes: Int,
-    ) : super(context, attrs, defStyleAttr, defStyleRes)
-
     override fun setShouldBlockVisibilityChanges(block: Boolean) {
         delegate.setShouldBlockVisibilityChanges(block)
     }
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimation.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimation.kt
index 0e3d41c..442c6fa 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimation.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimation.kt
@@ -48,7 +48,7 @@
         animator.addUpdateListener { updateListener ->
             val now = updateListener.currentPlayTime
             val progress = updateListener.animatedValue as Float
-            rippleShader.progress = progress
+            rippleShader.rawProgress = progress
             rippleShader.distortionStrength = if (config.shouldDistort) 1 - progress else 0f
             rippleShader.time = now.toFloat()
         }
@@ -66,11 +66,28 @@
     fun isPlaying(): Boolean = animator.isRunning
 
     private fun applyConfigToShader() {
-        rippleShader.setCenter(config.centerX, config.centerY)
-        rippleShader.setMaxSize(config.maxWidth, config.maxHeight)
-        rippleShader.rippleFill = config.shouldFillRipple
-        rippleShader.pixelDensity = config.pixelDensity
-        rippleShader.color = ColorUtils.setAlphaComponent(config.color, config.opacity)
-        rippleShader.sparkleStrength = config.sparkleStrength
+        with(rippleShader) {
+            setCenter(config.centerX, config.centerY)
+            setMaxSize(config.maxWidth, config.maxHeight)
+            pixelDensity = config.pixelDensity
+            color = ColorUtils.setAlphaComponent(config.color, config.opacity)
+            sparkleStrength = config.sparkleStrength
+
+            assignFadeParams(baseRingFadeParams, config.baseRingFadeParams)
+            assignFadeParams(sparkleRingFadeParams, config.sparkleRingFadeParams)
+            assignFadeParams(centerFillFadeParams, config.centerFillFadeParams)
+        }
+    }
+
+    private fun assignFadeParams(
+        destFadeParams: RippleShader.FadeParams,
+        srcFadeParams: RippleShader.FadeParams?
+    ) {
+        srcFadeParams?.let {
+            destFadeParams.fadeInStart = it.fadeInStart
+            destFadeParams.fadeInEnd = it.fadeInEnd
+            destFadeParams.fadeOutStart = it.fadeOutStart
+            destFadeParams.fadeOutEnd = it.fadeOutEnd
+        }
     }
 }
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt
index 773ac55..1786d13 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt
@@ -20,8 +20,11 @@
     val pixelDensity: Float = 1f,
     var color: Int = Color.WHITE,
     val opacity: Int = RIPPLE_DEFAULT_ALPHA,
-    val shouldFillRipple: Boolean = false,
     val sparkleStrength: Float = RIPPLE_SPARKLE_STRENGTH,
+    // Null means it uses default fade parameter values.
+    val baseRingFadeParams: RippleShader.FadeParams? = null,
+    val sparkleRingFadeParams: RippleShader.FadeParams? = null,
+    val centerFillFadeParams: RippleShader.FadeParams? = null,
     val shouldDistort: Boolean = true
 ) {
     companion object {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt
index 9058510..61ca90a 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt
@@ -18,6 +18,7 @@
 import android.graphics.PointF
 import android.graphics.RuntimeShader
 import android.util.MathUtils
+import com.android.systemui.animation.Interpolators
 import com.android.systemui.surfaceeffects.shaderutil.SdfShaderLibrary
 import com.android.systemui.surfaceeffects.shaderutil.ShaderUtilLibrary
 
@@ -43,11 +44,24 @@
     }
     // language=AGSL
     companion object {
+        // Default fade in/ out values. The value range is [0,1].
+        const val DEFAULT_FADE_IN_START = 0f
+        const val DEFAULT_FADE_OUT_END = 1f
+
+        const val DEFAULT_BASE_RING_FADE_IN_END = 0.1f
+        const val DEFAULT_BASE_RING_FADE_OUT_START = 0.3f
+
+        const val DEFAULT_SPARKLE_RING_FADE_IN_END = 0.1f
+        const val DEFAULT_SPARKLE_RING_FADE_OUT_START = 0.4f
+
+        const val DEFAULT_CENTER_FILL_FADE_IN_END = 0f
+        const val DEFAULT_CENTER_FILL_FADE_OUT_START = 0f
+        const val DEFAULT_CENTER_FILL_FADE_OUT_END = 0.6f
+
         private const val SHADER_UNIFORMS =
             """
             uniform vec2 in_center;
             uniform vec2 in_size;
-            uniform float in_progress;
             uniform float in_cornerRadius;
             uniform float in_thickness;
             uniform float in_time;
@@ -68,7 +82,7 @@
                 vec2 p_distorted = distort(p, in_time, in_distort_radial, in_distort_xy);
                 float radius = in_size.x * 0.5;
                 float sparkleRing = soften(circleRing(p_distorted-in_center, radius), in_blur);
-                float inside = soften(sdCircle(p_distorted-in_center, radius * 1.2), in_blur);
+                float inside = soften(sdCircle(p_distorted-in_center, radius * 1.25), in_blur);
                 float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_time * 0.00175)
                     * (1.-sparkleRing) * in_fadeSparkle;
 
@@ -143,11 +157,26 @@
             }
 
         private fun subProgress(start: Float, end: Float, progress: Float): Float {
+            // Avoid division by 0.
+            if (start == end) {
+                // If start and end are the same and progress has exceeded the start/ end point,
+                // treat it as 1, otherwise 0.
+                return if (progress > start) 1f else 0f
+            }
+
             val min = Math.min(start, end)
             val max = Math.max(start, end)
             val sub = Math.min(Math.max(progress, min), max)
             return (sub - start) / (end - start)
         }
+
+        private fun getFade(fadeParams: FadeParams, rawProgress: Float): Float {
+            val fadeIn = subProgress(fadeParams.fadeInStart, fadeParams.fadeInEnd, rawProgress)
+            val fadeOut =
+                1f - subProgress(fadeParams.fadeOutStart, fadeParams.fadeOutEnd, rawProgress)
+
+            return Math.min(fadeIn, fadeOut)
+        }
     }
 
     /** Sets the center position of the ripple. */
@@ -162,33 +191,33 @@
         maxSize.y = height
     }
 
-    /** Progress of the ripple. Float value between [0, 1]. */
-    var progress: Float = 0.0f
+    /**
+     * Linear progress of the ripple. Float value between [0, 1].
+     *
+     * <p>Note that the progress here is expected to be linear without any curve applied.
+     */
+    var rawProgress: Float = 0.0f
         set(value) {
             field = value
-            setFloatUniform("in_progress", value)
-            val curvedProg = 1 - (1 - value) * (1 - value) * (1 - value)
+            progress = Interpolators.STANDARD.getInterpolation(value)
 
-            currentWidth = maxSize.x * curvedProg
-            currentHeight = maxSize.y * curvedProg
-            setFloatUniform("in_size", /* width= */ currentWidth, /* height= */ currentHeight)
-            setFloatUniform("in_thickness", maxSize.y * curvedProg * 0.5f)
+            setFloatUniform("in_fadeSparkle", getFade(sparkleRingFadeParams, value))
+            setFloatUniform("in_fadeRing", getFade(baseRingFadeParams, value))
+            setFloatUniform("in_fadeFill", getFade(centerFillFadeParams, value))
+        }
+
+    /** Progress with Standard easing curve applied. */
+    private var progress: Float = 0.0f
+        set(value) {
+            currentWidth = maxSize.x * value
+            currentHeight = maxSize.y * value
+            setFloatUniform("in_size", currentWidth, currentHeight)
+
+            setFloatUniform("in_thickness", maxSize.y * value * 0.5f)
             // radius should not exceed width and height values.
-            setFloatUniform("in_cornerRadius", Math.min(maxSize.x, maxSize.y) * curvedProg)
+            setFloatUniform("in_cornerRadius", Math.min(maxSize.x, maxSize.y) * value)
 
             setFloatUniform("in_blur", MathUtils.lerp(1.25f, 0.5f, value))
-
-            val fadeIn = subProgress(0f, 0.1f, value)
-            val fadeOutNoise = subProgress(0.4f, 1f, value)
-            var fadeOutRipple = 0f
-            var fadeFill = 0f
-            if (!rippleFill) {
-                fadeFill = subProgress(0f, 0.6f, value)
-                fadeOutRipple = subProgress(0.3f, 1f, value)
-            }
-            setFloatUniform("in_fadeSparkle", Math.min(fadeIn, 1 - fadeOutNoise))
-            setFloatUniform("in_fadeFill", 1 - fadeFill)
-            setFloatUniform("in_fadeRing", Math.min(fadeIn, 1 - fadeOutRipple))
         }
 
     /** Play time since the start of the effect. */
@@ -220,25 +249,97 @@
     var distortionStrength: Float = 0.0f
         set(value) {
             field = value
-            setFloatUniform("in_distort_radial", 75 * progress * value)
+            setFloatUniform("in_distort_radial", 75 * rawProgress * value)
             setFloatUniform("in_distort_xy", 75 * value)
         }
 
+    /**
+     * Pixel density of the screen that the effects are rendered to.
+     *
+     * <p>This value should come from [resources.displayMetrics.density].
+     */
     var pixelDensity: Float = 1.0f
         set(value) {
             field = value
             setFloatUniform("in_pixelDensity", value)
         }
 
-    /**
-     * True if the ripple should stayed filled in as it expands to give a filled-in circle effect.
-     * False for a ring effect.
-     */
-    var rippleFill: Boolean = false
-
     var currentWidth: Float = 0f
         private set
 
     var currentHeight: Float = 0f
         private set
+
+    /** Parameters that are used to fade in/ out of the sparkle ring. */
+    val sparkleRingFadeParams =
+        FadeParams(
+            DEFAULT_FADE_IN_START,
+            DEFAULT_SPARKLE_RING_FADE_IN_END,
+            DEFAULT_SPARKLE_RING_FADE_OUT_START,
+            DEFAULT_FADE_OUT_END
+        )
+
+    /**
+     * Parameters that are used to fade in/ out of the base ring.
+     *
+     * <p>Note that the shader draws the sparkle ring on top of the base ring.
+     */
+    val baseRingFadeParams =
+        FadeParams(
+            DEFAULT_FADE_IN_START,
+            DEFAULT_BASE_RING_FADE_IN_END,
+            DEFAULT_BASE_RING_FADE_OUT_START,
+            DEFAULT_FADE_OUT_END
+        )
+
+    /** Parameters that are used to fade in/ out of the center fill. */
+    val centerFillFadeParams =
+        FadeParams(
+            DEFAULT_FADE_IN_START,
+            DEFAULT_CENTER_FILL_FADE_IN_END,
+            DEFAULT_CENTER_FILL_FADE_OUT_START,
+            DEFAULT_CENTER_FILL_FADE_OUT_END
+        )
+
+    /**
+     * Parameters used for fade in and outs of the ripple.
+     *
+     * <p>Note that all the fade in/ outs are "linear" progression.
+     * ```
+     *          (opacity)
+     *          1
+     *          │
+     * maxAlpha ←       ――――――――――――
+     *          │      /            \
+     *          │     /              \
+     * minAlpha ←――――/                \―――― (alpha change)
+     *          │
+     *          │
+     *          0 ―――↑―――↑―――――――――↑―――↑――――1 (progress)
+     *               fadeIn        fadeOut
+     *               Start & End   Start & End
+     * ```
+     * <p>If no fade in/ out is needed, set [fadeInStart] and [fadeInEnd] to 0; [fadeOutStart] and
+     * [fadeOutEnd] to 1.
+     */
+    data class FadeParams(
+        /**
+         * The starting point of the fade out which ends at [fadeInEnd], given that the animation
+         * goes from 0 to 1.
+         */
+        var fadeInStart: Float = DEFAULT_FADE_IN_START,
+        /**
+         * The endpoint of the fade in when the fade in starts at [fadeInStart], given that the
+         * animation goes from 0 to 1.
+         */
+        var fadeInEnd: Float,
+        /**
+         * The starting point of the fade out which ends at 1, given that the animation goes from 0
+         * to 1.
+         */
+        var fadeOutStart: Float,
+
+        /** The endpoint of the fade out, given that the animation goes from 0 to 1. */
+        var fadeOutEnd: Float = DEFAULT_FADE_OUT_END,
+    )
 }
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt
index b37c734..3c9328c 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt
@@ -77,7 +77,7 @@
         rippleShader = RippleShader(rippleShape)
 
         rippleShader.color = RippleAnimationConfig.RIPPLE_DEFAULT_COLOR
-        rippleShader.progress = 0f
+        rippleShader.rawProgress = 0f
         rippleShader.sparkleStrength = RippleAnimationConfig.RIPPLE_SPARKLE_STRENGTH
         rippleShader.pixelDensity = resources.displayMetrics.density
 
@@ -93,7 +93,7 @@
         animator.addUpdateListener { updateListener ->
             val now = updateListener.currentPlayTime
             val progress = updateListener.animatedValue as Float
-            rippleShader.progress = progress
+            rippleShader.rawProgress = progress
             rippleShader.distortionStrength = 1 - progress
             rippleShader.time = now.toFloat()
             invalidate()
@@ -117,15 +117,6 @@
         rippleShader.color = ColorUtils.setAlphaComponent(color, alpha)
     }
 
-    /**
-     * Set whether the ripple should remain filled as the ripple expands.
-     *
-     * See [RippleShader.rippleFill].
-     */
-    fun setRippleFill(rippleFill: Boolean) {
-        rippleShader.rippleFill = rippleFill
-    }
-
     /** Set the intensity of the sparkles. */
     fun setSparkleStrength(strength: Float) {
         rippleShader.sparkleStrength = strength
@@ -142,25 +133,13 @@
         }
         // To reduce overdraw, we mask the effect to a circle or a rectangle that's bigger than the
         // active effect area. Values here should be kept in sync with the animation implementation
-        // in the ripple shader.
+        // in the ripple shader. (Twice bigger)
         if (rippleShape == RippleShape.CIRCLE) {
-            val maskRadius =
-                (1 -
-                    (1 - rippleShader.progress) *
-                        (1 - rippleShader.progress) *
-                        (1 - rippleShader.progress)) * maxWidth
+            val maskRadius = rippleShader.currentWidth
             canvas.drawCircle(centerX, centerY, maskRadius, ripplePaint)
         } else {
-            val maskWidth =
-                (1 -
-                    (1 - rippleShader.progress) *
-                        (1 - rippleShader.progress) *
-                        (1 - rippleShader.progress)) * maxWidth * 2
-            val maskHeight =
-                (1 -
-                    (1 - rippleShader.progress) *
-                        (1 - rippleShader.progress) *
-                        (1 - rippleShader.progress)) * maxHeight * 2
+            val maskWidth = rippleShader.currentWidth * 2
+            val maskHeight = rippleShader.currentHeight * 2
             canvas.drawRect(
                 /* left= */ centerX - maskWidth,
                 /* top= */ centerY - maskHeight,
diff --git a/packages/SystemUI/animation/src/com/android/systemui/util/Dialog.kt b/packages/SystemUI/animation/src/com/android/systemui/util/Dialog.kt
new file mode 100644
index 0000000..428856d
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/util/Dialog.kt
@@ -0,0 +1,51 @@
+/*
+ * 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
+
+import android.app.Dialog
+import android.view.View
+import android.window.OnBackInvokedDispatcher
+import com.android.systemui.animation.back.BackAnimationSpec
+import com.android.systemui.animation.back.BackTransformation
+import com.android.systemui.animation.back.applyTo
+import com.android.systemui.animation.back.floatingSystemSurfacesForSysUi
+import com.android.systemui.animation.back.onBackAnimationCallbackFrom
+import com.android.systemui.animation.back.registerOnBackInvokedCallbackOnViewAttached
+
+/**
+ * Register on the Dialog's [OnBackInvokedDispatcher] an animation using the [BackAnimationSpec].
+ * The [BackTransformation] will be applied on the [targetView].
+ */
+@JvmOverloads
+fun Dialog.registerAnimationOnBackInvoked(
+    targetView: View,
+    backAnimationSpec: BackAnimationSpec =
+        BackAnimationSpec.floatingSystemSurfacesForSysUi(
+            displayMetrics = targetView.resources.displayMetrics,
+        ),
+) {
+    targetView.registerOnBackInvokedCallbackOnViewAttached(
+        onBackInvokedDispatcher = onBackInvokedDispatcher,
+        onBackAnimationCallback =
+            onBackAnimationCallbackFrom(
+                backAnimationSpec = backAnimationSpec,
+                displayMetrics = targetView.resources.displayMetrics,
+                onBackProgressed = { backTransformation -> backTransformation.applyTo(targetView) },
+                onBackInvoked = { dismiss() },
+            ),
+    )
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
index 4e96dda..cfc38df 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
@@ -33,8 +33,8 @@
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.LocalContentColor
-import androidx.compose.material3.LocalMinimumTouchTargetEnforcement
 import androidx.compose.material3.contentColorFor
+import androidx.compose.material3.minimumInteractiveComponentSize
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.DisposableEffect
@@ -65,21 +65,17 @@
 import androidx.compose.ui.graphics.drawscope.scale
 import androidx.compose.ui.layout.boundsInRoot
 import androidx.compose.ui.layout.findRootCoordinates
-import androidx.compose.ui.layout.layout
 import androidx.compose.ui.layout.onGloballyPositioned
 import androidx.compose.ui.platform.ComposeView
 import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalViewConfiguration
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.ViewTreeLifecycleOwner
 import androidx.lifecycle.ViewTreeViewModelStoreOwner
-import com.android.compose.runtime.movableContentOf
 import com.android.systemui.animation.Expandable
 import com.android.systemui.animation.LaunchAnimator
 import kotlin.math.max
 import kotlin.math.min
-import kotlin.math.roundToInt
 
 /**
  * Create an expandable shape that can launch into an Activity or a Dialog.
@@ -220,21 +216,8 @@
     // If this expandable is expanded when it's being directly clicked on, let's ensure that it has
     // the minimum interactive size followed by all M3 components (48.dp).
     val minInteractiveSizeModifier =
-        if (onClick != null && LocalMinimumTouchTargetEnforcement.current) {
-            // TODO(b/242040009): Replace this by Modifier.minimumInteractiveComponentSize() once
-            // http://aosp/2305511 is available.
-            val minTouchSize = LocalViewConfiguration.current.minimumTouchTargetSize
-            Modifier.layout { measurable, constraints ->
-                // Copied from androidx.compose.material3.InteractiveComponentSize.kt
-                val placeable = measurable.measure(constraints)
-                val width = maxOf(placeable.width, minTouchSize.width.roundToPx())
-                val height = maxOf(placeable.height, minTouchSize.height.roundToPx())
-                layout(width, height) {
-                    val centerX = ((width - placeable.width) / 2f).roundToInt()
-                    val centerY = ((height - placeable.height) / 2f).roundToInt()
-                    placeable.place(centerX, centerY)
-                }
-            }
+        if (onClick != null) {
+            Modifier.minimumInteractiveComponentSize()
         } else {
             Modifier
         }
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index 59b4848..ab36d58 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -13,83 +13,179 @@
  */
 package com.android.systemui.shared.clocks
 
+import android.app.ActivityManager
+import android.app.UserSwitchObserver
 import android.content.Context
 import android.database.ContentObserver
 import android.graphics.drawable.Drawable
 import android.net.Uri
-import android.os.Handler
+import android.os.UserHandle
 import android.provider.Settings
 import android.util.Log
 import androidx.annotation.OpenForTesting
-import com.android.internal.annotations.Keep
 import com.android.systemui.plugins.ClockController
 import com.android.systemui.plugins.ClockId
 import com.android.systemui.plugins.ClockMetadata
 import com.android.systemui.plugins.ClockProvider
 import com.android.systemui.plugins.ClockProviderPlugin
+import com.android.systemui.plugins.ClockSettings
 import com.android.systemui.plugins.PluginListener
 import com.android.systemui.plugins.PluginManager
-import org.json.JSONObject
+import com.android.systemui.util.Assert
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
 
-private val TAG = ClockRegistry::class.simpleName
+private val TAG = ClockRegistry::class.simpleName!!
 private const val DEBUG = true
 
 /** ClockRegistry aggregates providers and plugins */
 open class ClockRegistry(
     val context: Context,
     val pluginManager: PluginManager,
-    val handler: Handler,
+    val scope: CoroutineScope,
+    val mainDispatcher: CoroutineDispatcher,
+    val bgDispatcher: CoroutineDispatcher,
     val isEnabled: Boolean,
-    userHandle: Int,
+    val handleAllUsers: Boolean,
     defaultClockProvider: ClockProvider,
     val fallbackClockId: ClockId = DEFAULT_CLOCK_ID,
 ) {
-    // Usually this would be a typealias, but a SAM provides better java interop
-    fun interface ClockChangeListener {
-        fun onClockChanged()
+    interface ClockChangeListener {
+        // Called when the active clock changes
+        fun onCurrentClockChanged() {}
+
+        // Called when the list of available clocks changes
+        fun onAvailableClocksChanged() {}
     }
 
     private val availableClocks = mutableMapOf<ClockId, ClockInfo>()
     private val clockChangeListeners = mutableListOf<ClockChangeListener>()
-    private val settingObserver = object : ContentObserver(handler) {
-        override fun onChange(selfChange: Boolean, uris: Collection<Uri>, flags: Int, userId: Int) =
-            clockChangeListeners.forEach { it.onClockChanged() }
-    }
-
-    private val pluginListener = object : PluginListener<ClockProviderPlugin> {
-        override fun onPluginConnected(plugin: ClockProviderPlugin, context: Context) =
-            connectClocks(plugin)
-
-        override fun onPluginDisconnected(plugin: ClockProviderPlugin) =
-            disconnectClocks(plugin)
-    }
-
-    open var currentClockId: ClockId
-        get() {
-            return try {
-                val json = Settings.Secure.getString(
-                    context.contentResolver,
-                    Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE
-                )
-                if (json == null || json.isEmpty()) {
-                    return fallbackClockId
-                }
-                ClockSetting.deserialize(json).clockId
-            } catch (ex: Exception) {
-                Log.e(TAG, "Failed to parse clock setting", ex)
-                fallbackClockId
+    private val settingObserver =
+        object : ContentObserver(null) {
+            override fun onChange(
+                selfChange: Boolean,
+                uris: Collection<Uri>,
+                flags: Int,
+                userId: Int
+            ) {
+                scope.launch(bgDispatcher) { querySettings() }
             }
         }
-        set(value) {
+
+    private val pluginListener =
+        object : PluginListener<ClockProviderPlugin> {
+            override fun onPluginConnected(plugin: ClockProviderPlugin, context: Context) =
+                connectClocks(plugin)
+
+            override fun onPluginDisconnected(plugin: ClockProviderPlugin) =
+                disconnectClocks(plugin)
+        }
+
+    private val userSwitchObserver =
+        object : UserSwitchObserver() {
+            override fun onUserSwitchComplete(newUserId: Int) {
+                scope.launch(bgDispatcher) { querySettings() }
+            }
+        }
+
+    // TODO(b/267372164): Migrate to flows
+    var settings: ClockSettings? = null
+        get() = field
+        protected set(value) {
+            if (field != value) {
+                field = value
+                scope.launch(mainDispatcher) { onClockChanged { it.onCurrentClockChanged() } }
+            }
+        }
+
+    var isRegistered: Boolean = false
+        private set
+
+    @OpenForTesting
+    open fun querySettings() {
+        assertNotMainThread()
+        val result =
             try {
-                val json = ClockSetting.serialize(ClockSetting(value, System.currentTimeMillis()))
+                val json =
+                    if (handleAllUsers) {
+                        Settings.Secure.getStringForUser(
+                            context.contentResolver,
+                            Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
+                            ActivityManager.getCurrentUser()
+                        )
+                    } else {
+                        Settings.Secure.getString(
+                            context.contentResolver,
+                            Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE
+                        )
+                    }
+
+                ClockSettings.deserialize(json)
+            } catch (ex: Exception) {
+                Log.e(TAG, "Failed to parse clock settings", ex)
+                null
+            }
+        settings = result
+    }
+
+    @OpenForTesting
+    open fun applySettings(value: ClockSettings?) {
+        assertNotMainThread()
+
+        try {
+            value?._applied_timestamp = System.currentTimeMillis()
+            val json = ClockSettings.serialize(value)
+
+            if (handleAllUsers) {
+                Settings.Secure.putStringForUser(
+                    context.contentResolver,
+                    Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
+                    json,
+                    ActivityManager.getCurrentUser()
+                )
+            } else {
                 Settings.Secure.putString(
                     context.contentResolver,
-                    Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE, json
+                    Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
+                    json
                 )
-            } catch (ex: Exception) {
-                Log.e(TAG, "Failed to set clock setting", ex)
             }
+        } catch (ex: Exception) {
+            Log.e(TAG, "Failed to set clock settings", ex)
+        }
+        settings = value
+    }
+
+    @OpenForTesting
+    protected open fun assertMainThread() {
+        Assert.isMainThread()
+    }
+
+    @OpenForTesting
+    protected open fun assertNotMainThread() {
+        Assert.isNotMainThread()
+    }
+
+    private fun onClockChanged(func: (ClockChangeListener) -> Unit) {
+        assertMainThread()
+        clockChangeListeners.forEach(func)
+    }
+
+    private fun mutateSetting(mutator: (ClockSettings) -> ClockSettings) {
+        scope.launch(bgDispatcher) { applySettings(mutator(settings ?: ClockSettings())) }
+    }
+
+    var currentClockId: ClockId
+        get() = settings?.clockId ?: fallbackClockId
+        set(value) {
+            mutateSetting { it.copy(clockId = value) }
+        }
+
+    var seedColor: Int?
+        get() = settings?.seedColor
+        set(value) {
+            mutateSetting { it.copy(seedColor = value) }
         }
 
     init {
@@ -99,23 +195,56 @@
                 "$defaultClockProvider did not register clock at $DEFAULT_CLOCK_ID"
             )
         }
+    }
 
-        if (isEnabled) {
-            pluginManager.addPluginListener(
-                pluginListener,
-                ClockProviderPlugin::class.java,
-                /*allowMultiple=*/ true
-            )
+    fun registerListeners() {
+        if (!isEnabled || isRegistered) {
+            return
+        }
+
+        isRegistered = true
+
+        pluginManager.addPluginListener(
+            pluginListener,
+            ClockProviderPlugin::class.java,
+            /*allowMultiple=*/ true
+        )
+
+        scope.launch(bgDispatcher) { querySettings() }
+        if (handleAllUsers) {
             context.contentResolver.registerContentObserver(
                 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE),
                 /*notifyForDescendants=*/ false,
                 settingObserver,
-                userHandle
+                UserHandle.USER_ALL
+            )
+
+            ActivityManager.getService().registerUserSwitchObserver(userSwitchObserver, TAG)
+        } else {
+            context.contentResolver.registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE),
+                /*notifyForDescendants=*/ false,
+                settingObserver
             )
         }
     }
 
+    fun unregisterListeners() {
+        if (!isRegistered) {
+            return
+        }
+
+        isRegistered = false
+
+        pluginManager.removePluginListener(pluginListener)
+        context.contentResolver.unregisterContentObserver(settingObserver)
+        if (handleAllUsers) {
+            ActivityManager.getService().unregisterUserSwitchObserver(userSwitchObserver)
+        }
+    }
+
     private fun connectClocks(provider: ClockProvider) {
+        var isAvailableChanged = false
         val currentId = currentClockId
         for (clock in provider.getClocks()) {
             val id = clock.clockId
@@ -126,10 +255,11 @@
                     "Clock Id conflict: $id is registered by both " +
                         "${provider::class.simpleName} and ${current.provider::class.simpleName}"
                 )
-                return
+                continue
             }
 
             availableClocks[id] = ClockInfo(clock, provider)
+            isAvailableChanged = true
             if (DEBUG) {
                 Log.i(TAG, "Added ${clock.clockId}")
             }
@@ -138,28 +268,38 @@
                 if (DEBUG) {
                     Log.i(TAG, "Current clock ($currentId) was connected")
                 }
-                clockChangeListeners.forEach { it.onClockChanged() }
+                onClockChanged { it.onCurrentClockChanged() }
             }
         }
+
+        if (isAvailableChanged) {
+            onClockChanged { it.onAvailableClocksChanged() }
+        }
     }
 
     private fun disconnectClocks(provider: ClockProvider) {
+        var isAvailableChanged = false
         val currentId = currentClockId
         for (clock in provider.getClocks()) {
             availableClocks.remove(clock.clockId)
+            isAvailableChanged = true
+
             if (DEBUG) {
                 Log.i(TAG, "Removed ${clock.clockId}")
             }
 
             if (currentId == clock.clockId) {
                 Log.w(TAG, "Current clock ($currentId) was disconnected")
-                clockChangeListeners.forEach { it.onClockChanged() }
+                onClockChanged { it.onCurrentClockChanged() }
             }
         }
+
+        if (isAvailableChanged) {
+            onClockChanged { it.onAvailableClocksChanged() }
+        }
     }
 
-    @OpenForTesting
-    open fun getClocks(): List<ClockMetadata> {
+    fun getClocks(): List<ClockMetadata> {
         if (!isEnabled) {
             return listOf(availableClocks[DEFAULT_CLOCK_ID]!!.metadata)
         }
@@ -194,36 +334,16 @@
         return createClock(DEFAULT_CLOCK_ID)!!
     }
 
-    private fun createClock(clockId: ClockId): ClockController? =
-        availableClocks[clockId]?.provider?.createClock(clockId)
+    private fun createClock(targetClockId: ClockId): ClockController? {
+        var settings = this.settings ?: ClockSettings()
+        if (targetClockId != settings.clockId) {
+            settings = settings.copy(clockId = targetClockId)
+        }
+        return availableClocks[targetClockId]?.provider?.createClock(settings)
+    }
 
     private data class ClockInfo(
         val metadata: ClockMetadata,
-        val provider: ClockProvider
+        val provider: ClockProvider,
     )
-
-    @Keep
-    data class ClockSetting(
-        val clockId: ClockId,
-        val _applied_timestamp: Long?
-    ) {
-        companion object {
-            private val KEY_CLOCK_ID = "clockId"
-            private val KEY_TIMESTAMP = "_applied_timestamp"
-
-            fun serialize(setting: ClockSetting): String {
-                return JSONObject()
-                    .put(KEY_CLOCK_ID, setting.clockId)
-                    .put(KEY_TIMESTAMP, setting._applied_timestamp)
-                    .toString()
-            }
-
-            fun deserialize(jsonStr: String): ClockSetting {
-                val json = JSONObject(jsonStr)
-                return ClockSetting(
-                    json.getString(KEY_CLOCK_ID),
-                    if (!json.isNull(KEY_TIMESTAMP)) json.getLong(KEY_TIMESTAMP) else null)
-            }
-        }
-    }
 }
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index 7645dec..2a40f5c 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.plugins.ClockEvents
 import com.android.systemui.plugins.ClockFaceController
 import com.android.systemui.plugins.ClockFaceEvents
+import com.android.systemui.plugins.ClockSettings
 import com.android.systemui.plugins.log.LogBuffer
 import java.io.PrintWriter
 import java.util.Locale
@@ -46,6 +47,7 @@
     ctx: Context,
     private val layoutInflater: LayoutInflater,
     private val resources: Resources,
+    private val settings: ClockSettings?,
 ) : ClockController {
     override val smallClock: DefaultClockFaceController
     override val largeClock: LargeClockFaceController
@@ -66,12 +68,14 @@
         smallClock =
             DefaultClockFaceController(
                 layoutInflater.inflate(R.layout.clock_default_small, parent, false)
-                    as AnimatableClockView
+                    as AnimatableClockView,
+                settings?.seedColor
             )
         largeClock =
             LargeClockFaceController(
                 layoutInflater.inflate(R.layout.clock_default_large, parent, false)
-                    as AnimatableClockView
+                    as AnimatableClockView,
+                settings?.seedColor
             )
         clocks = listOf(smallClock.view, largeClock.view)
 
@@ -85,11 +89,13 @@
         animations = DefaultClockAnimations(dozeFraction, foldFraction)
         events.onColorPaletteChanged(resources)
         events.onTimeZoneChanged(TimeZone.getDefault())
-        events.onTimeTick()
+        smallClock.events.onTimeTick()
+        largeClock.events.onTimeTick()
     }
 
     open inner class DefaultClockFaceController(
         override val view: AnimatableClockView,
+        val seedColor: Int?,
     ) : ClockFaceController {
 
         // MAGENTA is a placeholder, and will be assigned correctly in initialize
@@ -104,11 +110,16 @@
             }
 
         init {
+            if (seedColor != null) {
+                currentColor = seedColor
+            }
             view.setColors(currentColor, currentColor)
         }
 
         override val events =
             object : ClockFaceEvents {
+                override fun onTimeTick() = view.refreshTime()
+
                 override fun onRegionDarknessChanged(isRegionDark: Boolean) {
                     this@DefaultClockFaceController.isRegionDark = isRegionDark
                     updateColor()
@@ -129,7 +140,9 @@
 
         fun updateColor() {
             val color =
-                if (isRegionDark) {
+                if (seedColor != null) {
+                    seedColor
+                } else if (isRegionDark) {
                     resources.getColor(android.R.color.system_accent1_100)
                 } else {
                     resources.getColor(android.R.color.system_accent2_600)
@@ -149,7 +162,8 @@
 
     inner class LargeClockFaceController(
         view: AnimatableClockView,
-    ) : DefaultClockFaceController(view) {
+        seedColor: Int?,
+    ) : DefaultClockFaceController(view, seedColor) {
         override fun recomputePadding(targetRegion: Rect?) {
             // We center the view within the targetRegion instead of within the parent
             // view by computing the difference and adding that to the padding.
@@ -169,8 +183,6 @@
     }
 
     inner class DefaultClockEvents : ClockEvents {
-        override fun onTimeTick() = clocks.forEach { it.refreshTime() }
-
         override fun onTimeFormatChanged(is24Hr: Boolean) =
             clocks.forEach { it.refreshFormat(is24Hr) }
 
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
index 4c0504b..0fd1b49 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
@@ -22,6 +22,7 @@
 import com.android.systemui.plugins.ClockId
 import com.android.systemui.plugins.ClockMetadata
 import com.android.systemui.plugins.ClockProvider
+import com.android.systemui.plugins.ClockSettings
 
 private val TAG = DefaultClockProvider::class.simpleName
 const val DEFAULT_CLOCK_NAME = "Default Clock"
@@ -36,12 +37,12 @@
     override fun getClocks(): List<ClockMetadata> =
         listOf(ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME))
 
-    override fun createClock(id: ClockId): ClockController {
-        if (id != DEFAULT_CLOCK_ID) {
-            throw IllegalArgumentException("$id is unsupported by $TAG")
+    override fun createClock(settings: ClockSettings): ClockController {
+        if (settings.clockId != DEFAULT_CLOCK_ID) {
+            throw IllegalArgumentException("${settings.clockId} is unsupported by $TAG")
         }
 
-        return DefaultClockController(ctx, layoutInflater, resources)
+        return DefaultClockController(ctx, layoutInflater, resources, settings)
     }
 
     override fun getClockThumbnail(id: ClockId): Drawable? {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AnimationParams.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/shared/model/ClockPreviewConstants.kt
similarity index 60%
copy from packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AnimationParams.kt
copy to packages/SystemUI/customization/src/com/android/systemui/shared/clocks/shared/model/ClockPreviewConstants.kt
index 67733e9..6a77936 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AnimationParams.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/shared/model/ClockPreviewConstants.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -11,15 +11,12 @@
  * 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
+ * limitations under the License.
+ *
  */
-package com.android.systemui.keyguard.shared.model
 
-import kotlin.time.Duration
-import kotlin.time.Duration.Companion.milliseconds
+package com.android.systemui.shared.clocks.shared.model
 
-/** Animation parameters */
-data class AnimationParams(
-    val startTime: Duration = 0.milliseconds,
-    val duration: Duration,
-)
+object ClockPreviewConstants {
+    const val KEY_HIDE_CLOCK = "hide_clock"
+}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
index e4e9c46..c120876 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
@@ -181,6 +181,9 @@
         /** Flag denoting whether the Wallpaper preview should use the full screen UI. */
         const val FLAG_NAME_WALLPAPER_FULLSCREEN_PREVIEW = "wallpaper_fullscreen_preview"
 
+        /** Flag denoting whether the Monochromatic Theme is enabled. */
+        const val FLAG_NAME_MONOCHROMATIC_THEME = "is_monochromatic_theme_enabled"
+
         object Columns {
             /** String. Unique ID for the flag. */
             const val NAME = "name"
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardQuickAffordancePreviewConstants.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardQuickAffordancePreviewConstants.kt
index 18e8a96..bf922bc 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardQuickAffordancePreviewConstants.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardQuickAffordancePreviewConstants.kt
@@ -21,4 +21,5 @@
     const val MESSAGE_ID_SLOT_SELECTED = 1337
     const val KEY_SLOT_ID = "slot_id"
     const val KEY_INITIALLY_SELECTED_SLOT_ID = "initially_selected_slot_id"
+    const val KEY_HIGHLIGHT_QUICK_AFFORDANCES = "highlight_quick_affordances"
 }
diff --git a/packages/SystemUI/src/com/android/systemui/util/Assert.java b/packages/SystemUI/customization/src/com/android/systemui/util/Assert.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/util/Assert.java
rename to packages/SystemUI/customization/src/com/android/systemui/util/Assert.java
diff --git a/packages/SystemUI/docs/device-entry/quickaffordance.md b/packages/SystemUI/docs/device-entry/quickaffordance.md
index ccb35fa..d662649 100644
--- a/packages/SystemUI/docs/device-entry/quickaffordance.md
+++ b/packages/SystemUI/docs/device-entry/quickaffordance.md
@@ -52,6 +52,15 @@
 * Unselect an already-selected quick affordance from a slot
 * Unselect all already-selected quick affordances from a slot
 
+## Device Policy
+Returning `DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL` or
+`DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL` from
+`DevicePolicyManager#getKeyguardDisabledFeatures` will disable the keyguard quick affordance feature on the device.
+
+## Testing
+* Add a unit test for your implementation of `KeyguardQuickAffordanceConfig`
+* Manually verify that your implementation works in multi-user environments from both the main user and a secondary user
+
 ## Debugging
 To see the current state of the system, you can run `dumpsys`:
 
diff --git a/packages/SystemUI/docs/qs-tiles.md b/packages/SystemUI/docs/qs-tiles.md
index 4cb765d..488f8c7 100644
--- a/packages/SystemUI/docs/qs-tiles.md
+++ b/packages/SystemUI/docs/qs-tiles.md
@@ -301,9 +301,13 @@
     * Use only `handleUpdateState` to modify the values of the state to the new ones. This can be done by polling controllers or through the `arg` parameter.
     * If the controller is not a `CallbackController`, respond to `handleSetListening` by attaching/dettaching from controllers.
     * Implement `isAvailable` so the tile will not be created when it's not necessary.
-4. In `QSFactoryImpl`:
-    * Inject a `Provider` for the tile created before.
-    * Add a case to the `switch` with a unique String spec for the chosen tile.
+4. Either create a new feature module or find an existing related feature module and add the following binding method:
+    * ```kotlin
+      @Binds
+      @IntoMap
+      @StringKey(YourNewTile.TILE_SPEC) // A unique word that will map to YourNewTile
+      fun bindYourNewTile(yourNewTile: YourNewTile): QSTileImpl<*>
+      ```
 5. In [SystemUI/res/values/config.xml](/packages/SystemUI/res/values/config.xml), modify `quick_settings_tiles_stock` and add the spec defined in the previous step. If necessary, add it also to `quick_settings_tiles_default`. The first one contains a list of all the tiles that SystemUI knows how to create (to show to the user in the customization screen). The second one contains only the default tiles that the user will experience on a fresh boot or after they reset their tiles.
 6. In [SystemUI/res/values/tiles_states_strings.xml](/packages/SystemUI/res/values/tiles_states_strings.xml), add a new array for your tile. The name has to be `tile_states_<spec>`. Use a good description to help the translators.
 7. In [`SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt`](/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt), add a new element to the map in `SubtitleArrayMapping` corresponding to the resource created in the previous step.
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 1f00812..943d799 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -197,13 +197,10 @@
 -packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesManager.kt
 -packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
 -packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt
--packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLogger.kt
 -packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt
 -packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt
 -packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
 -packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
--packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt
 -packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLogger.kt
 -packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
 -packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -454,13 +451,6 @@
 -packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt
 -packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherFeatureController.kt
 -packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt
--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiActivityModel.kt
--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt
--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiConstants.kt
--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt
--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
 -packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt
 -packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsController.kt
 -packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt
@@ -611,7 +601,6 @@
 -packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/media/nearby/NearbyMediaDevicesManagerTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLoggerTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt
@@ -746,11 +735,6 @@
 -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLoggerTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/panelstate/ShadeExpansionStateManagerTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerOldImplTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLoggerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ClockTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
index bc6e5ec..e0d0184 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -50,16 +50,24 @@
     String TAG = "BcSmartspaceDataPlugin";
 
     /** Register a listener to get Smartspace data. */
-    void registerListener(SmartspaceTargetListener listener);
+    default void registerListener(SmartspaceTargetListener listener) {
+        throw new UnsupportedOperationException("Not implemented by " + getClass());
+    }
 
     /** Unregister a listener. */
-    void unregisterListener(SmartspaceTargetListener listener);
+    default void unregisterListener(SmartspaceTargetListener listener) {
+        throw new UnsupportedOperationException("Not implemented by " + getClass());
+    }
 
     /** Register a SmartspaceEventNotifier. */
-    default void registerSmartspaceEventNotifier(SmartspaceEventNotifier notifier) {}
+    default void registerSmartspaceEventNotifier(SmartspaceEventNotifier notifier) {
+        throw new UnsupportedOperationException("Not implemented by " + getClass());
+    }
 
     /** Push a SmartspaceTargetEvent to the SmartspaceEventNotifier. */
-    default void notifySmartspaceEvent(SmartspaceTargetEvent event) {}
+    default void notifySmartspaceEvent(SmartspaceTargetEvent event) {
+        throw new UnsupportedOperationException("Not implemented by " + getClass());
+    }
 
     /** Allows for notifying the SmartspaceSession of SmartspaceTargetEvents. */
     interface SmartspaceEventNotifier {
@@ -72,16 +80,20 @@
      * will be responsible for correctly setting the LayoutParams
      */
     default SmartspaceView getView(ViewGroup parent) {
-        return null;
+        throw new UnsupportedOperationException("Not implemented by " + getClass());
     }
 
     /**
      * As the smartspace view becomes available, allow listeners to receive an event.
      */
-    default void addOnAttachStateChangeListener(View.OnAttachStateChangeListener listener) { }
+    default void addOnAttachStateChangeListener(View.OnAttachStateChangeListener listener) {
+        throw new UnsupportedOperationException("Not implemented by " + getClass());
+    }
 
     /** Updates Smartspace data and propagates it to any listeners. */
-    void onTargetsAvailable(List<SmartspaceTarget> targets);
+    default void onTargetsAvailable(List<SmartspaceTarget> targets) {
+        throw new UnsupportedOperationException("Not implemented by " + getClass());
+    }
 
     /** Provides Smartspace data to registered listeners. */
     interface SmartspaceTargetListener {
@@ -96,7 +108,9 @@
         /**
          * Sets {@link BcSmartspaceConfigPlugin}.
          */
-        void registerConfigProvider(BcSmartspaceConfigPlugin configProvider);
+        default void registerConfigProvider(BcSmartspaceConfigPlugin configProvider) {
+            throw new UnsupportedOperationException("Not implemented by " + getClass());
+        }
 
         /**
          * Primary color for unprotected text
@@ -104,12 +118,6 @@
         void setPrimaryTextColor(int color);
 
         /**
-         * When the view is displayed on Dream, set the flag to true, immediately after the view is
-         * created.
-         */
-        void setIsDreaming(boolean isDreaming);
-
-        /**
          * Set the UI surface for the cards. Should be called immediately after the view is created.
          */
         void setUiSurface(String uiSurface);
@@ -138,28 +146,38 @@
         /**
          * Set or clear Do Not Disturb information.
          */
-        void setDnd(@Nullable Drawable image, @Nullable String description);
+        default void setDnd(@Nullable Drawable image, @Nullable String description) {
+            throw new UnsupportedOperationException("Not implemented by " + getClass());
+        }
 
         /**
          * Set or clear next alarm information
          */
-        void setNextAlarm(@Nullable Drawable image, @Nullable String description);
+        default void setNextAlarm(@Nullable Drawable image, @Nullable String description) {
+            throw new UnsupportedOperationException("Not implemented by " + getClass());
+        }
 
         /**
          * Set or clear device media playing
          */
-        void setMediaTarget(@Nullable SmartspaceTarget target);
+        default void setMediaTarget(@Nullable SmartspaceTarget target) {
+            throw new UnsupportedOperationException("Not implemented by " + getClass());
+        }
 
         /**
          * Get the index of the currently selected page.
          */
-        int getSelectedPage();
+        default int getSelectedPage() {
+            throw new UnsupportedOperationException("Not implemented by " + getClass());
+        }
 
         /**
          * Return the top padding value from the currently visible card, or 0 if there is no current
          * card.
          */
-        int getCurrentCardTopPadding();
+        default int getCurrentCardTopPadding() {
+            throw new UnsupportedOperationException("Not implemented by " + getClass());
+        }
     }
 
     /** Interface for launching Intents, which can differ on the lockscreen */
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
index a2a0709..1c2f38b 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
@@ -17,11 +17,13 @@
 import android.graphics.Rect
 import android.graphics.drawable.Drawable
 import android.view.View
+import com.android.internal.annotations.Keep
 import com.android.systemui.plugins.annotations.ProvidesInterface
 import com.android.systemui.plugins.log.LogBuffer
 import java.io.PrintWriter
 import java.util.Locale
 import java.util.TimeZone
+import org.json.JSONObject
 
 /** Identifies a clock design */
 typealias ClockId = String
@@ -41,7 +43,13 @@
     fun getClocks(): List<ClockMetadata>
 
     /** Initializes and returns the target clock design */
-    fun createClock(id: ClockId): ClockController
+    @Deprecated("Use overload with ClockSettings")
+    fun createClock(id: ClockId): ClockController {
+        return createClock(ClockSettings(id, null))
+    }
+
+    /** Initializes and returns the target clock design */
+    fun createClock(settings: ClockSettings): ClockController
 
     /** A static thumbnail for rendering in some examples */
     fun getClockThumbnail(id: ClockId): Drawable?
@@ -62,11 +70,16 @@
     val animations: ClockAnimations
 
     /** Initializes various rendering parameters. If never called, provides reasonable defaults. */
-    fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float) {
+    fun initialize(
+        resources: Resources,
+        dozeFraction: Float,
+        foldFraction: Float,
+    ) {
         events.onColorPaletteChanged(resources)
         animations.doze(dozeFraction)
         animations.fold(foldFraction)
-        events.onTimeTick()
+        smallClock.events.onTimeTick()
+        largeClock.events.onTimeTick()
     }
 
     /** Optional method for dumping debug information */
@@ -87,9 +100,6 @@
 
 /** Events that should call when various rendering parameters change */
 interface ClockEvents {
-    /** Call every time tick */
-    fun onTimeTick() {}
-
     /** Call whenever timezone changes */
     fun onTimeZoneChanged(timeZone: TimeZone) {}
 
@@ -101,6 +111,9 @@
 
     /** Call whenever the color palette should update */
     fun onColorPaletteChanged(resources: Resources) {}
+
+    /** Call whenever the weather data should update */
+    fun onWeatherDataChanged(data: Weather) {}
 }
 
 /** Methods which trigger various clock animations */
@@ -131,6 +144,13 @@
 
 /** Events that have specific data about the related face */
 interface ClockFaceEvents {
+    /** Call every time tick */
+    fun onTimeTick() {}
+
+    /** Expected interval between calls to onTimeTick. Can always reduce to PER_MINUTE in AOD. */
+    val tickRate: ClockTickRate
+        get() = ClockTickRate.PER_MINUTE
+
     /** Region Darkness specific to the clock face */
     fun onRegionDarknessChanged(isDark: Boolean) {}
 
@@ -150,8 +170,59 @@
     fun onTargetRegionChanged(targetRegion: Rect?) {}
 }
 
+/** Tick rates for clocks */
+enum class ClockTickRate(val value: Int) {
+    PER_MINUTE(2), // Update the clock once per minute.
+    PER_SECOND(1), // Update the clock once per second.
+    PER_FRAME(0), // Update the clock every second.
+}
+
 /** Some data about a clock design */
 data class ClockMetadata(
     val clockId: ClockId,
     val name: String,
 )
+
+/** Structure for keeping clock-specific settings */
+@Keep
+data class ClockSettings(
+    val clockId: ClockId? = null,
+    val seedColor: Int? = null,
+) {
+    var _applied_timestamp: Long? = null
+
+    companion object {
+        private val KEY_CLOCK_ID = "clockId"
+        private val KEY_SEED_COLOR = "seedColor"
+        private val KEY_TIMESTAMP = "_applied_timestamp"
+
+        fun serialize(setting: ClockSettings?): String {
+            if (setting == null) {
+                return ""
+            }
+
+            return JSONObject()
+                .put(KEY_CLOCK_ID, setting.clockId)
+                .put(KEY_SEED_COLOR, setting.seedColor)
+                .put(KEY_TIMESTAMP, setting._applied_timestamp)
+                .toString()
+        }
+
+        fun deserialize(jsonStr: String?): ClockSettings? {
+            if (jsonStr.isNullOrEmpty()) {
+                return null
+            }
+
+            val json = JSONObject(jsonStr)
+            val result =
+                ClockSettings(
+                    json.getString(KEY_CLOCK_ID),
+                    if (!json.isNull(KEY_SEED_COLOR)) json.getInt(KEY_SEED_COLOR) else null
+                )
+            if (!json.isNull(KEY_TIMESTAMP)) {
+                result._applied_timestamp = json.getLong(KEY_TIMESTAMP)
+            }
+            return result
+        }
+    }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/Weather.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/Weather.kt
new file mode 100644
index 0000000..302f175
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/Weather.kt
@@ -0,0 +1,85 @@
+package com.android.systemui.plugins
+
+import android.os.Bundle
+
+class Weather(val conditions: WeatherStateIcon, val temperature: Int, val isCelsius: Boolean) {
+    companion object {
+        private const val TAG = "Weather"
+        private const val WEATHER_STATE_ICON_KEY = "weather_state_icon_extra_key"
+        private const val TEMPERATURE_VALUE_KEY = "temperature_value_extra_key"
+        private const val TEMPERATURE_UNIT_KEY = "temperature_unit_extra_key"
+        private const val INVALID_TEMPERATURE = Int.MIN_VALUE
+
+        fun fromBundle(extras: Bundle): Weather? {
+            val icon =
+                WeatherStateIcon.fromInt(
+                    extras.getInt(WEATHER_STATE_ICON_KEY, WeatherStateIcon.UNKNOWN_ICON.id)
+                )
+            if (icon == null || icon == WeatherStateIcon.UNKNOWN_ICON) {
+                return null
+            }
+            val temperature = extras.getInt(TEMPERATURE_VALUE_KEY, INVALID_TEMPERATURE)
+            if (temperature == INVALID_TEMPERATURE) {
+                return null
+            }
+            return Weather(icon, temperature, extras.getBoolean(TEMPERATURE_UNIT_KEY))
+        }
+    }
+
+    enum class WeatherStateIcon(val id: Int) {
+        UNKNOWN_ICON(0),
+
+        // Clear, day & night.
+        SUNNY(1),
+        CLEAR_NIGHT(2),
+
+        // Mostly clear, day & night.
+        MOSTLY_SUNNY(3),
+        MOSTLY_CLEAR_NIGHT(4),
+
+        // Partly cloudy, day & night.
+        PARTLY_CLOUDY(5),
+        PARTLY_CLOUDY_NIGHT(6),
+
+        // Mostly cloudy, day & night.
+        MOSTLY_CLOUDY_DAY(7),
+        MOSTLY_CLOUDY_NIGHT(8),
+        CLOUDY(9),
+        HAZE_FOG_DUST_SMOKE(10),
+        DRIZZLE(11),
+        HEAVY_RAIN(12),
+        SHOWERS_RAIN(13),
+
+        // Scattered showers, day & night.
+        SCATTERED_SHOWERS_DAY(14),
+        SCATTERED_SHOWERS_NIGHT(15),
+
+        // Isolated scattered thunderstorms, day & night.
+        ISOLATED_SCATTERED_TSTORMS_DAY(16),
+        ISOLATED_SCATTERED_TSTORMS_NIGHT(17),
+        STRONG_TSTORMS(18),
+        BLIZZARD(19),
+        BLOWING_SNOW(20),
+        FLURRIES(21),
+        HEAVY_SNOW(22),
+
+        // Scattered snow showers, day & night.
+        SCATTERED_SNOW_SHOWERS_DAY(23),
+        SCATTERED_SNOW_SHOWERS_NIGHT(24),
+        SNOW_SHOWERS_SNOW(25),
+        MIXED_RAIN_HAIL_RAIN_SLEET(26),
+        SLEET_HAIL(27),
+        TORNADO(28),
+        TROPICAL_STORM_HURRICANE(29),
+        WINDY_BREEZY(30),
+        WINTRY_MIX_RAIN_SNOW(31);
+
+        companion object {
+            fun fromInt(value: Int) = values().firstOrNull { it.id == value }
+        }
+    }
+
+    override fun toString(): String {
+        return "$conditions $temperature${if (isCelsius) "C" else "F"}"
+    }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index 2b16999..1d28c63 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -16,9 +16,11 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.metrics.LogMaker;
 import android.service.quicksettings.Tile;
+import android.text.TextUtils;
 import android.view.View;
 
 import androidx.annotation.Nullable;
@@ -175,6 +177,24 @@
         public Drawable sideViewCustomDrawable;
         public String spec;
 
+        /** Get the state text. */
+        public String getStateText(int arrayResId, Resources resources) {
+            if (state == Tile.STATE_UNAVAILABLE || this instanceof QSTile.BooleanState) {
+                String[] array = resources.getStringArray(arrayResId);
+                return array[state];
+            } else {
+                return "";
+            }
+        }
+
+        /** Get the text for secondaryLabel. */
+        public String getSecondaryLabel(String stateText) {
+            if (TextUtils.isEmpty(secondaryLabel)) {
+                return stateText;
+            }
+            return secondaryLabel.toString();
+        }
+
         public boolean copyTo(State other) {
             if (other == null) throw new IllegalArgumentException();
             if (!other.getClass().equals(getClass())) throw new IllegalArgumentException();
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java
index 9ed3bac..70b5d73 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java
@@ -105,6 +105,11 @@
         default void onDozingChanged(boolean isDozing) {}
 
         /**
+         * Callback to be notified when Dreaming changes. Dreaming is stored separately from state.
+         */
+        default void onDreamingChanged(boolean isDreaming) {}
+
+        /**
          * Callback to be notified when the doze amount changes. Useful for animations.
          * Note: this will be called for each animation frame. Please be careful to avoid
          * performance regressions.
diff --git a/packages/SystemUI/res-keyguard/layout/footer_actions_text_button.xml b/packages/SystemUI/res-keyguard/layout/footer_actions_text_button.xml
index fc18132..6fe7d39 100644
--- a/packages/SystemUI/res-keyguard/layout/footer_actions_text_button.xml
+++ b/packages/SystemUI/res-keyguard/layout/footer_actions_text_button.xml
@@ -14,7 +14,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.systemui.common.ui.view.LaunchableLinearLayout
+<com.android.systemui.animation.view.LaunchableLinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="0dp"
     android:layout_height="@dimen/qs_security_footer_single_line_height"
@@ -63,4 +63,4 @@
         android:src="@*android:drawable/ic_chevron_end"
         android:autoMirrored="true"
         android:tint="?android:attr/textColorSecondary" />
-</com.android.systemui.common.ui.view.LaunchableLinearLayout>
\ No newline at end of file
+</com.android.systemui.animation.view.LaunchableLinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml
index 2cac9c7..90851e2 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml
@@ -45,7 +45,7 @@
           android:id="@+id/user_switcher_header"
           android:textDirection="locale"
           android:layout_width="@dimen/bouncer_user_switcher_width"
-          android:layout_height="wrap_content" />
+          android:layout_height="match_parent" />
     </com.android.keyguard.KeyguardUserSwitcherAnchor>
 
 </LinearLayout>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher_item.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher_item.xml
index c388f15..81f4c8c 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher_item.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher_item.xml
@@ -15,6 +15,7 @@
   -->
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/user_switcher_item"
     android:layout_width="match_parent"
     android:layout_height="wrap_content">
   <TextView
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
deleted file mode 100644
index 8497ff0..0000000
--- a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2012, 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.
-*/
--->
-
-<!-- This is the host view that generally contains two sub views: the widget view
-    and the security view. -->
-<com.android.keyguard.KeyguardHostView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/keyguard_host_view"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:clipChildren="false"
-    android:clipToPadding="false"
-    android:paddingTop="@dimen/keyguard_lock_padding"
-    android:importantForAccessibility="yes"> <!-- Needed because TYPE_WINDOW_STATE_CHANGED is sent
-                                                  from this view when bouncer is shown -->
-
-    <com.android.keyguard.KeyguardSecurityContainer
-        android:id="@+id/keyguard_security_container"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:clipChildren="false"
-        android:clipToPadding="false"
-        android:padding="0dp"
-        android:fitsSystemWindows="true"
-        android:layout_gravity="center">
-        <com.android.keyguard.KeyguardSecurityViewFlipper
-            android:id="@+id/view_flipper"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:clipChildren="false"
-            android:clipToPadding="false"
-            android:paddingTop="@dimen/keyguard_security_view_top_margin"
-            android:layout_gravity="center"
-            android:gravity="center">
-        </com.android.keyguard.KeyguardSecurityViewFlipper>
-    </com.android.keyguard.KeyguardSecurityContainer>
-
-</com.android.keyguard.KeyguardHostView>
-
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
index 64ece47..ca4028a 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -105,6 +105,7 @@
             android:id="@+id/key1"
             android:layout_width="0dp"
             android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/key2"
             androidprv:digit="1"
             androidprv:textView="@+id/pinEntry" />
 
@@ -112,6 +113,7 @@
             android:id="@+id/key2"
             android:layout_width="0dp"
             android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/key3"
             androidprv:digit="2"
             androidprv:textView="@+id/pinEntry" />
 
@@ -119,6 +121,7 @@
             android:id="@+id/key3"
             android:layout_width="0dp"
             android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/key4"
             androidprv:digit="3"
             androidprv:textView="@+id/pinEntry" />
 
@@ -126,6 +129,7 @@
             android:id="@+id/key4"
             android:layout_width="0dp"
             android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/key5"
             androidprv:digit="4"
             androidprv:textView="@+id/pinEntry" />
 
@@ -133,6 +137,7 @@
             android:id="@+id/key5"
             android:layout_width="0dp"
             android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/key6"
             androidprv:digit="5"
             androidprv:textView="@+id/pinEntry" />
 
@@ -140,6 +145,7 @@
             android:id="@+id/key6"
             android:layout_width="0dp"
             android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/key7"
             androidprv:digit="6"
             androidprv:textView="@+id/pinEntry" />
 
@@ -147,13 +153,16 @@
             android:id="@+id/key7"
             android:layout_width="0dp"
             android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/key8"
             androidprv:digit="7"
             androidprv:textView="@+id/pinEntry" />
 
+
         <com.android.keyguard.NumPadKey
             android:id="@+id/key8"
             android:layout_width="0dp"
             android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/key9"
             androidprv:digit="8"
             androidprv:textView="@+id/pinEntry" />
 
@@ -161,34 +170,33 @@
             android:id="@+id/key9"
             android:layout_width="0dp"
             android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/delete_button"
             androidprv:digit="9"
             androidprv:textView="@+id/pinEntry" />
 
-
         <com.android.keyguard.NumPadButton
             android:id="@+id/delete_button"
+            style="@style/NumPadKey.Delete"
             android:layout_width="0dp"
             android:layout_height="0dp"
-            style="@style/NumPadKey.Delete"
-            android:contentDescription="@string/keyboardview_keycode_delete"
-            />
+            android:accessibilityTraversalBefore="@id/key0"
+            android:contentDescription="@string/keyboardview_keycode_delete" />
 
         <com.android.keyguard.NumPadKey
             android:id="@+id/key0"
             android:layout_width="0dp"
             android:layout_height="0dp"
+            android:accessibilityTraversalBefore="@id/key_enter"
             androidprv:digit="0"
             androidprv:textView="@+id/pinEntry" />
 
         <com.android.keyguard.NumPadButton
             android:id="@+id/key_enter"
+            style="@style/NumPadKey.Enter"
             android:layout_width="0dp"
             android:layout_height="0dp"
-            style="@style/NumPadKey.Enter"
-            android:contentDescription="@string/keyboardview_keycode_enter"
-            />
-
-    </androidx.constraintlayout.widget.ConstraintLayout>
+            android:contentDescription="@string/keyboardview_keycode_enter" />
+</androidx.constraintlayout.widget.ConstraintLayout>
 
     <include layout="@layout/keyguard_eca"
              android:id="@+id/keyguard_selector_fade_container"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_security_container_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_security_container_view.xml
new file mode 100644
index 0000000..426cfaf
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_security_container_view.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+
+<com.android.keyguard.KeyguardSecurityContainer
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/keyguard_security_container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:clipChildren="false"
+    android:clipToPadding="false"
+    android:paddingTop="@dimen/keyguard_lock_padding"
+    android:importantForAccessibility="yes"> <!-- Needed because TYPE_WINDOW_STATE_CHANGED is sent
+                                                  from this view when bouncer is shown -->
+    <com.android.keyguard.KeyguardSecurityViewFlipper
+        android:id="@+id/view_flipper"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:clipChildren="false"
+        android:clipToPadding="false"
+        android:paddingTop="@dimen/keyguard_security_view_top_margin"
+        android:layout_gravity="center"
+        android:gravity="center">
+    </com.android.keyguard.KeyguardSecurityViewFlipper>
+</com.android.keyguard.KeyguardSecurityContainer>
+
diff --git a/packages/SystemUI/res-keyguard/values-ca/strings.xml b/packages/SystemUI/res-keyguard/values-ca/strings.xml
index fba2c8b1..7888e53 100644
--- a/packages/SystemUI/res-keyguard/values-ca/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ca/strings.xml
@@ -53,7 +53,7 @@
     <string name="kg_wrong_pattern" msgid="5907301342430102842">"Patró incorrecte"</string>
     <string name="kg_wrong_password" msgid="4143127991071670512">"Contrasenya incorrecta"</string>
     <string name="kg_wrong_pin" msgid="4160978845968732624">"El PIN no és correcte"</string>
-    <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Torna-ho a provar d\'aquí a # segon.}many{Try again in # seconds.}other{Torna-ho a provar d\'aquí a # segons.}}"</string>
+    <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Torna-ho a provar d\'aquí a # segon.}many{Torna-ho a provar d\'aquí a # segons.}other{Torna-ho a provar d\'aquí a # segons.}}"</string>
     <string name="kg_sim_pin_instructions" msgid="1942424305184242951">"Introdueix el PIN de la SIM."</string>
     <string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"Introdueix el PIN de la SIM de: <xliff:g id="CARRIER">%1$s</xliff:g>."</string>
     <string name="kg_sim_lock_esim_instructions" msgid="5577169988158738030">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Desactiva l\'eSIM per utilitzar el dispositiu sense servei mòbil."</string>
@@ -68,9 +68,9 @@
     <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Has escrit la contrasenya <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades de manera incorrecta. \n\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%2$d</xliff:g> segons."</string>
     <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Has dibuixat el patró de desbloqueig <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades de manera incorrecta. \n\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%2$d</xliff:g> segons."</string>
     <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"El codi PIN de la SIM no és correcte. Contacta amb l\'operador de telefonia mòbil per desbloquejar el dispositiu."</string>
-    <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{El codi PIN de la SIM no és correcte. Et queda # intent; si no l\'encertes, contacta amb l\'operador per desbloquejar el dispositiu.}many{Incorrect SIM PIN code, you have # remaining attempts. }other{El codi PIN de la SIM no és correcte. Et queden # intents. }}"</string>
+    <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{El codi PIN de la SIM no és correcte. Et queda # intent; si no l\'encertes, contacta amb l\'operador per desbloquejar el dispositiu.}many{El codi PIN de la SIM no és correcte. Et queden # intents. }other{El codi PIN de la SIM no és correcte. Et queden # intents. }}"</string>
     <string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"La SIM no es pot fer servir. Contacta amb l\'operador de telefonia mòbil."</string>
-    <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{El codi PUK de la SIM no és correcte. Et queda # intent; si no l\'encertes, la SIM no es podrà tornar a fer servir.}many{Incorrect SIM PUK code, you have # remaining attempts before SIM becomes permanently unusable.}other{El codi PUK de la SIM no és correcte. Et queden # intents; si no l\'encertes, la SIM no es podrà tornar a fer servir.}}"</string>
+    <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{El codi PUK de la SIM no és correcte. Et queda # intent; si no l\'encertes, la SIM no es podrà tornar a fer servir.}many{El codi PUK de la SIM no és correcte. Et queden # intents; si no l\'encertes, la SIM no es podrà tornar a fer servir.}other{El codi PUK de la SIM no és correcte. Et queden # intents; si no l\'encertes, la SIM no es podrà tornar a fer servir.}}"</string>
     <string name="kg_password_pin_failed" msgid="5136259126330604009">"Ha fallat l\'operació del PIN de la SIM"</string>
     <string name="kg_password_puk_failed" msgid="6778867411556937118">"No s\'ha pogut desbloquejar la SIM amb el codi PUK."</string>
     <string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Canvia el mètode d\'introducció"</string>
@@ -85,8 +85,8 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"El dispositiu s\'ha bloquejat manualment"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"No s\'ha reconegut"</string>
     <string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Desbloqueig facial necessita accés a la càmera"</string>
-    <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Introdueix el PIN de la SIM. Et queda # intent; si no l\'encertes, contacta amb l\'operador per desbloquejar el dispositiu.}many{Enter SIM PIN. You have # remaining attempts.}other{Introdueix el PIN de la SIM. Et queden # intents.}}"</string>
-    <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{La targeta SIM s\'ha desactivat. Introdueix el codi PUK per continuar. Et queda # intent; si no l\'encertes, la SIM no es podrà tornar a fer servir. Contacta amb l\'operador per obtenir informació.}many{SIM is now disabled. Enter PUK code to continue. You have # remaining attempts before SIM becomes permanently unusable. Contact carrier for details.}other{La targeta SIM s\'ha desactivat. Introdueix el codi PUK per continuar. Et queden # intents; si no l\'encertes, la SIM no es podrà tornar a fer servir. Contacta amb l\'operador per obtenir informació.}}"</string>
+    <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Introdueix el PIN de la SIM. Et queda # intent; si no l\'encertes, contacta amb l\'operador per desbloquejar el dispositiu.}many{Introdueix el PIN de la SIM. Et queden # intents.}other{Introdueix el PIN de la SIM. Et queden # intents.}}"</string>
+    <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{La targeta SIM s\'ha desactivat. Introdueix el codi PUK per continuar. Et queda # intent; si no l\'encertes, la SIM no es podrà tornar a fer servir. Contacta amb l\'operador per obtenir informació.}many{La targeta SIM s\'ha desactivat. Introdueix el codi PUK per continuar. Et queden # intents; si no l\'encertes, la SIM no es podrà tornar a fer servir. Contacta amb l\'operador per obtenir informació.}other{La targeta SIM s\'ha desactivat. Introdueix el codi PUK per continuar. Et queden # intents; si no l\'encertes, la SIM no es podrà tornar a fer servir. Contacta amb l\'operador per obtenir informació.}}"</string>
     <string name="clock_title_default" msgid="6342735240617459864">"Predeterminada"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Bombolla"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analògica"</string>
diff --git a/packages/SystemUI/res-keyguard/values-eu/strings.xml b/packages/SystemUI/res-keyguard/values-eu/strings.xml
index 3c794c6..d5dba42 100644
--- a/packages/SystemUI/res-keyguard/values-eu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-eu/strings.xml
@@ -84,7 +84,7 @@
     <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"Administratzaileak blokeatu egin du gailua"</string>
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Eskuz blokeatu da gailua"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Ez da ezagutu"</string>
-    <string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Aurpegi bidezko desblokeoak kamera atzitzeko baimena behar du"</string>
+    <string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Aurpegi bidezko desblokeoak kamera erabiltzeko baimena behar du"</string>
     <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Idatzi SIMaren PINa. # saiakera geratzen zaizu gailua desblokeatzeko operadorearekin harremanetan jarri behar izan aurretik.}other{Idatzi SIMaren PINa. # saiakera gelditzen zaizkizu.}}"</string>
     <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{Orain, SIMa desgaituta dago. Aurrera egiteko, idatzi PUK kodea. # saiakera geratzen zaizu SIMa betiko ez-erabilgarri geratu aurretik. Xehetasunak lortzeko, jarri operadorearekin harremanetan.}other{Orain, SIMa desgaituta dago. Aurrera egiteko, idatzi PUK kodea. # saiakera geratzen zaizkizu SIMa betiko ez-erabilgarri geratu aurretik. Xehetasunak lortzeko, jarri operadorearekin harremanetan.}}"</string>
     <string name="clock_title_default" msgid="6342735240617459864">"Lehenetsia"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml b/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml
index a1068c6..6c8db91 100644
--- a/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml
@@ -25,9 +25,6 @@
     <!-- Margin around the various security views -->
     <dimen name="keyguard_security_view_top_margin">12dp</dimen>
 
-    <!-- Padding for the lock icon on the keyguard -->
-    <dimen name="lock_icon_padding">16dp</dimen>
-
     <!-- Overload default clock widget parameters -->
     <dimen name="widget_big_font_size">100dp</dimen>
     <dimen name="widget_label_font_size">18sp</dimen>
diff --git a/packages/SystemUI/res/drawable/clipboard_minimized_background.xml b/packages/SystemUI/res/drawable/clipboard_minimized_background.xml
new file mode 100644
index 0000000..a179c14
--- /dev/null
+++ b/packages/SystemUI/res/drawable/clipboard_minimized_background.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:shape="rectangle">
+    <solid android:color="?androidprv:attr/colorAccentSecondary"/>
+    <corners android:radius="10dp"/>
+</shape>
diff --git a/packages/SystemUI/res/drawable/ic_content_paste.xml b/packages/SystemUI/res/drawable/ic_content_paste.xml
new file mode 100644
index 0000000..8c8b81e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_content_paste.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ 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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="48dp"
+        android:height="48dp"
+        android:viewportWidth="48"
+        android:viewportHeight="48"
+        android:tint="?attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M9,42Q7.7,42 6.85,41.15Q6,40.3 6,39V9Q6,7.7 6.85,6.85Q7.7,6 9,6H19.1Q19.45,4.25 20.825,3.125Q22.2,2 24,2Q25.8,2 27.175,3.125Q28.55,4.25 28.9,6H39Q40.3,6 41.15,6.85Q42,7.7 42,9V39Q42,40.3 41.15,41.15Q40.3,42 39,42ZM9,39H39Q39,39 39,39Q39,39 39,39V9Q39,9 39,9Q39,9 39,9H36V13.5H12V9H9Q9,9 9,9Q9,9 9,9V39Q9,39 9,39Q9,39 9,39ZM24,9Q24.85,9 25.425,8.425Q26,7.85 26,7Q26,6.15 25.425,5.575Q24.85,5 24,5Q23.15,5 22.575,5.575Q22,6.15 22,7Q22,7.85 22.575,8.425Q23.15,9 24,9Z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_progress_activity.xml b/packages/SystemUI/res/drawable/ic_progress_activity.xml
new file mode 100644
index 0000000..abf0625
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_progress_activity.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="48dp"
+    android:height="48dp"
+    android:viewportWidth="48"
+    android:viewportHeight="48"
+    android:tint="?attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M24,44Q19.8,44 16.15,42.45Q12.5,40.9 9.8,38.2Q7.1,35.5 5.55,31.85Q4,28.2 4,24Q4,19.8 5.55,16.15Q7.1,12.5 9.8,9.8Q12.5,7.1 16.15,5.55Q19.8,4 24,4Q24.6,4 25.05,4.45Q25.5,4.9 25.5,5.5Q25.5,6.1 25.05,6.55Q24.6,7 24,7Q16.95,7 11.975,11.975Q7,16.95 7,24Q7,31.05 11.975,36.025Q16.95,41 24,41Q31.05,41 36.025,36.025Q41,31.05 41,24Q41,23.4 41.45,22.95Q41.9,22.5 42.5,22.5Q43.1,22.5 43.55,22.95Q44,23.4 44,24Q44,28.2 42.45,31.85Q40.9,35.5 38.2,38.2Q35.5,40.9 31.85,42.45Q28.2,44 24,44Z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_font_scaling.xml b/packages/SystemUI/res/drawable/ic_qs_font_scaling.xml
new file mode 100644
index 0000000..d5b4c9e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_font_scaling.xml
@@ -0,0 +1,25 @@
+<!--
+   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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+         android:width="24dp"
+         android:height="24dp"
+         android:viewportWidth="24"
+         android:viewportHeight="24">
+<path
+    android:pathData="M7,20L7,7L2,7L2,4h13v3h-5v13ZM16,20v-8h-3L13,9h9v3h-3v8Z"
+    android:fillColor="#041E49"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_selected_border.xml b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_selected_border.xml
index acd2462..7d03b0d 100644
--- a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_selected_border.xml
+++ b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_selected_border.xml
@@ -21,7 +21,7 @@
   <item android:state_selected="true">
     <shape android:shape="oval">
       <stroke
-          android:color="@color/control_primary_text"
+          android:color="?android:attr/textColorPrimary"
           android:width="2dp"/>
       <size
           android:width="@dimen/keyguard_affordance_fixed_width"
diff --git a/packages/SystemUI/res/drawable/keyguard_settings_popup_menu_background.xml b/packages/SystemUI/res/drawable/keyguard_settings_popup_menu_background.xml
new file mode 100644
index 0000000..3807b92
--- /dev/null
+++ b/packages/SystemUI/res/drawable/keyguard_settings_popup_menu_background.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ 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.
+  ~
+  -->
+<ripple
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:color="?android:attr/colorControlHighlight">
+    <item android:id="@android:id/mask">
+        <shape android:shape="rectangle">
+            <solid android:color="@android:color/white"/>
+            <corners android:radius="28dp" />
+        </shape>
+    </item>
+    <item>
+        <shape android:shape="rectangle">
+            <solid android:color="?androidprv:attr/colorSurface" />
+            <corners android:radius="28dp" />
+        </shape>
+    </item>
+</ripple>
diff --git a/packages/SystemUI/res/layout/activity_rear_display_education.xml b/packages/SystemUI/res/layout/activity_rear_display_education.xml
index f5fc48c..094807e 100644
--- a/packages/SystemUI/res/layout/activity_rear_display_education.xml
+++ b/packages/SystemUI/res/layout/activity_rear_display_education.xml
@@ -41,9 +41,10 @@
     </androidx.cardview.widget.CardView>
 
     <TextView
+        android:id="@+id/rear_display_title_text_view"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:text="@string/rear_display_fold_bottom_sheet_title"
+        android:text="@string/rear_display_folded_bottom_sheet_title"
         android:textAppearance="@style/TextAppearance.Dialog.Title"
         android:lineSpacingExtra="2sp"
         android:paddingTop="@dimen/rear_display_title_top_padding"
@@ -54,7 +55,7 @@
     <TextView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:text="@string/rear_display_bottom_sheet_description"
+        android:text="@string/rear_display_folded_bottom_sheet_description"
         android:textAppearance="@style/TextAppearance.Dialog.Body"
         android:lineSpacingExtra="2sp"
         android:translationY="-1.24sp"
diff --git a/packages/SystemUI/res/layout/activity_rear_display_education_opened.xml b/packages/SystemUI/res/layout/activity_rear_display_education_opened.xml
index 6de06f7..e970bc5 100644
--- a/packages/SystemUI/res/layout/activity_rear_display_education_opened.xml
+++ b/packages/SystemUI/res/layout/activity_rear_display_education_opened.xml
@@ -42,9 +42,10 @@
     </androidx.cardview.widget.CardView>
 
     <TextView
+        android:id="@+id/rear_display_title_text_view"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:text="@string/rear_display_unfold_bottom_sheet_title"
+        android:text="@string/rear_display_unfolded_bottom_sheet_title"
         android:textAppearance="@style/TextAppearance.Dialog.Title"
         android:lineSpacingExtra="2sp"
         android:paddingTop="@dimen/rear_display_title_top_padding"
@@ -55,21 +56,11 @@
     <TextView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:text="@string/rear_display_bottom_sheet_description"
+        android:text="@string/rear_display_unfolded_bottom_sheet_description"
         android:textAppearance="@style/TextAppearance.Dialog.Body"
         android:lineSpacingExtra="2sp"
         android:translationY="-1.24sp"
         android:gravity="center_horizontal|top"
     />
 
-    <TextView
-        android:id="@+id/rear_display_warning_text_view"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="@string/rear_display_bottom_sheet_warning"
-        android:textAppearance="@style/TextAppearance.Dialog.Body"
-        android:lineSpacingExtra="2sp"
-        android:gravity="center_horizontal|top"
-    />
-
 </LinearLayout>
diff --git a/packages/SystemUI/res/layout/chipbar.xml b/packages/SystemUI/res/layout/chipbar.xml
index 8cf4f4d..0ff944c 100644
--- a/packages/SystemUI/res/layout/chipbar.xml
+++ b/packages/SystemUI/res/layout/chipbar.xml
@@ -60,14 +60,13 @@
             />
 
         <!-- At most one of [loading, failure_icon, undo] will be visible at a time. -->
-        <ProgressBar
+        <ImageView
             android:id="@+id/loading"
-            android:indeterminate="true"
             android:layout_width="@dimen/media_ttt_status_icon_size"
             android:layout_height="@dimen/media_ttt_status_icon_size"
             android:layout_marginStart="@dimen/media_ttt_last_item_start_margin"
-            android:indeterminateTint="?androidprv:attr/colorAccentPrimaryVariant"
-            style="?android:attr/progressBarStyleSmall"
+            android:src="@drawable/ic_progress_activity"
+            android:tint="?androidprv:attr/colorAccentPrimaryVariant"
             android:alpha="0.0"
             />
 
diff --git a/packages/SystemUI/res/layout/clipboard_overlay.xml b/packages/SystemUI/res/layout/clipboard_overlay.xml
index eec3b11..297cf2b 100644
--- a/packages/SystemUI/res/layout/clipboard_overlay.xml
+++ b/packages/SystemUI/res/layout/clipboard_overlay.xml
@@ -61,8 +61,6 @@
                      android:id="@+id/share_chip"/>
             <include layout="@layout/overlay_action_chip"
                      android:id="@+id/remote_copy_chip"/>
-            <include layout="@layout/overlay_action_chip"
-                     android:id="@+id/edit_chip"/>
         </LinearLayout>
     </HorizontalScrollView>
     <View
@@ -125,6 +123,45 @@
             android:layout_width="@dimen/clipboard_preview_size"
             android:layout_height="@dimen/clipboard_preview_size"/>
     </FrameLayout>
+    <LinearLayout
+        android:id="@+id/minimized_preview"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:visibility="gone"
+        android:elevation="7dp"
+        android:padding="8dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal"
+        android:layout_marginBottom="@dimen/overlay_action_container_margin_bottom"
+        android:background="@drawable/clipboard_minimized_background">
+        <ImageView
+            android:src="@drawable/ic_content_paste"
+            android:tint="?attr/overlayButtonTextColor"
+            android:layout_width="24dp"
+            android:layout_height="24dp"/>
+        <ImageView
+            android:src="@*android:drawable/ic_chevron_end"
+            android:tint="?attr/overlayButtonTextColor"
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            android:paddingEnd="-8dp"
+            android:paddingStart="-4dp"/>
+    </LinearLayout>
+    <androidx.constraintlayout.widget.Barrier
+        android:id="@+id/clipboard_content_top"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        app:barrierDirection="top"
+        app:constraint_referenced_ids="clipboard_preview,minimized_preview"/>
+    <androidx.constraintlayout.widget.Barrier
+        android:id="@+id/clipboard_content_end"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        app:barrierDirection="end"
+        app:constraint_referenced_ids="clipboard_preview,minimized_preview"/>
     <FrameLayout
         android:id="@+id/dismiss_button"
         android:layout_width="@dimen/overlay_dismiss_button_tappable_size"
@@ -132,10 +169,10 @@
         android:elevation="10dp"
         android:visibility="gone"
         android:alpha="0"
-        app:layout_constraintStart_toEndOf="@id/clipboard_preview"
-        app:layout_constraintEnd_toEndOf="@id/clipboard_preview"
-        app:layout_constraintTop_toTopOf="@id/clipboard_preview"
-        app:layout_constraintBottom_toTopOf="@id/clipboard_preview"
+        app:layout_constraintStart_toEndOf="@id/clipboard_content_end"
+        app:layout_constraintEnd_toEndOf="@id/clipboard_content_end"
+        app:layout_constraintTop_toTopOf="@id/clipboard_content_top"
+        app:layout_constraintBottom_toTopOf="@id/clipboard_content_top"
         android:contentDescription="@string/clipboard_dismiss_description">
         <ImageView
             android:id="@+id/dismiss_image"
diff --git a/packages/SystemUI/res/layout/clipboard_overlay_legacy.xml b/packages/SystemUI/res/layout/clipboard_overlay_legacy.xml
deleted file mode 100644
index 1a1fc75..0000000
--- a/packages/SystemUI/res/layout/clipboard_overlay_legacy.xml
+++ /dev/null
@@ -1,160 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<com.android.systemui.screenshot.DraggableConstraintLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/clipboard_ui"
-    android:theme="@style/FloatingOverlay"
-    android:alpha="0"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:contentDescription="@string/clipboard_overlay_window_name">
-    <ImageView
-        android:id="@+id/actions_container_background"
-        android:visibility="gone"
-        android:layout_height="0dp"
-        android:layout_width="0dp"
-        android:elevation="4dp"
-        android:background="@drawable/action_chip_container_background"
-        android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal"
-        app:layout_constraintBottom_toBottomOf="@+id/actions_container"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="@+id/actions_container"
-        app:layout_constraintEnd_toEndOf="@+id/actions_container"/>
-    <HorizontalScrollView
-        android:id="@+id/actions_container"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_marginEnd="@dimen/overlay_action_container_margin_horizontal"
-        android:paddingEnd="@dimen/overlay_action_container_padding_right"
-        android:paddingVertical="@dimen/overlay_action_container_padding_vertical"
-        android:elevation="4dp"
-        android:scrollbars="none"
-        android:layout_marginBottom="4dp"
-        app:layout_constraintHorizontal_bias="0"
-        app:layout_constraintWidth_percent="1.0"
-        app:layout_constraintWidth_max="wrap"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintStart_toEndOf="@+id/preview_border"
-        app:layout_constraintEnd_toEndOf="parent">
-        <LinearLayout
-            android:id="@+id/actions"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:animateLayoutChanges="true">
-            <include layout="@layout/overlay_action_chip"
-                     android:id="@+id/share_chip"/>
-            <include layout="@layout/overlay_action_chip"
-                     android:id="@+id/remote_copy_chip"/>
-            <include layout="@layout/overlay_action_chip"
-                     android:id="@+id/edit_chip"/>
-        </LinearLayout>
-    </HorizontalScrollView>
-    <View
-        android:id="@+id/preview_border"
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        android:layout_marginStart="@dimen/overlay_offset_x"
-        android:layout_marginBottom="12dp"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintBottom_toBottomOf="parent"
-        android:elevation="7dp"
-        app:layout_constraintEnd_toEndOf="@id/clipboard_preview_end"
-        app:layout_constraintTop_toTopOf="@id/clipboard_preview_top"
-        android:background="@drawable/overlay_border"/>
-    <androidx.constraintlayout.widget.Barrier
-        android:id="@+id/clipboard_preview_end"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        app:barrierMargin="@dimen/overlay_border_width"
-        app:barrierDirection="end"
-        app:constraint_referenced_ids="clipboard_preview"/>
-    <androidx.constraintlayout.widget.Barrier
-        android:id="@+id/clipboard_preview_top"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        app:barrierDirection="top"
-        app:barrierMargin="@dimen/overlay_border_width_neg"
-        app:constraint_referenced_ids="clipboard_preview"/>
-    <FrameLayout
-        android:id="@+id/clipboard_preview"
-        android:elevation="7dp"
-        android:background="@drawable/overlay_preview_background"
-        android:clipChildren="true"
-        android:clipToOutline="true"
-        android:clipToPadding="true"
-        android:layout_width="@dimen/clipboard_preview_size"
-        android:layout_margin="@dimen/overlay_border_width"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center"
-        app:layout_constraintBottom_toBottomOf="@id/preview_border"
-        app:layout_constraintStart_toStartOf="@id/preview_border"
-        app:layout_constraintEnd_toEndOf="@id/preview_border"
-        app:layout_constraintTop_toTopOf="@id/preview_border">
-        <TextView android:id="@+id/text_preview"
-                  android:textFontWeight="500"
-                  android:padding="8dp"
-                  android:gravity="center|start"
-                  android:ellipsize="end"
-                  android:autoSizeTextType="uniform"
-                  android:autoSizeMinTextSize="@dimen/clipboard_overlay_min_font"
-                  android:autoSizeMaxTextSize="@dimen/clipboard_overlay_max_font"
-                  android:textColor="?attr/overlayButtonTextColor"
-                  android:textColorLink="?attr/overlayButtonTextColor"
-                  android:background="?androidprv:attr/colorAccentSecondary"
-                  android:layout_width="@dimen/clipboard_preview_size"
-                  android:layout_height="@dimen/clipboard_preview_size"/>
-        <ImageView
-            android:id="@+id/image_preview"
-            android:scaleType="fitCenter"
-            android:adjustViewBounds="true"
-            android:contentDescription="@string/clipboard_image_preview"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"/>
-        <TextView
-            android:id="@+id/hidden_preview"
-            android:visibility="gone"
-            android:textFontWeight="500"
-            android:padding="8dp"
-            android:gravity="center"
-            android:textSize="14sp"
-            android:textColor="?attr/overlayButtonTextColor"
-            android:background="?androidprv:attr/colorAccentSecondary"
-            android:layout_width="@dimen/clipboard_preview_size"
-            android:layout_height="@dimen/clipboard_preview_size"/>
-    </FrameLayout>
-    <FrameLayout
-        android:id="@+id/dismiss_button"
-        android:layout_width="@dimen/overlay_dismiss_button_tappable_size"
-        android:layout_height="@dimen/overlay_dismiss_button_tappable_size"
-        android:elevation="10dp"
-        android:visibility="gone"
-        android:alpha="0"
-        app:layout_constraintStart_toEndOf="@id/clipboard_preview"
-        app:layout_constraintEnd_toEndOf="@id/clipboard_preview"
-        app:layout_constraintTop_toTopOf="@id/clipboard_preview"
-        app:layout_constraintBottom_toTopOf="@id/clipboard_preview"
-        android:contentDescription="@string/clipboard_dismiss_description">
-        <ImageView
-            android:id="@+id/dismiss_image"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:layout_margin="@dimen/overlay_dismiss_button_margin"
-            android:src="@drawable/overlay_cancel"/>
-    </FrameLayout>
-</com.android.systemui.screenshot.DraggableConstraintLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/controls_with_favorites.xml b/packages/SystemUI/res/layout/controls_with_favorites.xml
index ee3adba..aa211bf 100644
--- a/packages/SystemUI/res/layout/controls_with_favorites.xml
+++ b/packages/SystemUI/res/layout/controls_with_favorites.xml
@@ -20,7 +20,6 @@
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:orientation="horizontal"
-      android:layout_marginTop="@dimen/controls_top_margin"
       android:layout_marginBottom="@dimen/controls_header_bottom_margin">
 
     <!-- make sure the header stays centered in the layout by adding a spacer -->
@@ -78,6 +77,7 @@
         android:layout_weight="1"
         android:orientation="vertical"
         android:clipChildren="true"
+        android:paddingHorizontal="16dp"
         android:scrollbars="none">
     <include layout="@layout/global_actions_controls_list_view" />
 
@@ -88,10 +88,7 @@
       android:layout_width="match_parent"
       android:layout_height="0dp"
       android:layout_weight="1"
-      android:layout_marginLeft="@dimen/global_actions_side_margin"
-      android:layout_marginRight="@dimen/global_actions_side_margin"
       android:background="@drawable/controls_panel_background"
-      android:padding="@dimen/global_actions_side_margin"
       android:visibility="gone"
       />
 </merge>
diff --git a/packages/SystemUI/res/layout/dream_overlay_home_controls_chip.xml b/packages/SystemUI/res/layout/dream_overlay_home_controls_chip.xml
index de96e97..fb78b49 100644
--- a/packages/SystemUI/res/layout/dream_overlay_home_controls_chip.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_home_controls_chip.xml
@@ -20,7 +20,7 @@
     android:layout_width="wrap_content"
     android:paddingVertical="@dimen/dream_overlay_complication_home_controls_padding">
 
-    <ImageView
+    <com.android.systemui.animation.view.LaunchableImageView
         android:id="@+id/home_controls_chip"
         android:layout_height="@dimen/keyguard_affordance_fixed_height"
         android:layout_width="@dimen/keyguard_affordance_fixed_width"
diff --git a/packages/SystemUI/res/layout/font_scaling_dialog.xml b/packages/SystemUI/res/layout/font_scaling_dialog.xml
new file mode 100644
index 0000000..27c1e9d
--- /dev/null
+++ b/packages/SystemUI/res/layout/font_scaling_dialog.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<com.android.systemui.common.ui.view.SeekBarWithIconButtonsView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/font_scaling_slider"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center"
+    app:max="6"
+    app:progress="0"
+    app:iconStartContentDescription="@string/font_scaling_smaller"
+    app:iconEndContentDescription="@string/font_scaling_larger"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/global_actions_grid_lite.xml b/packages/SystemUI/res/layout/global_actions_grid_lite.xml
index 5588fd3..a64c9ae 100644
--- a/packages/SystemUI/res/layout/global_actions_grid_lite.xml
+++ b/packages/SystemUI/res/layout/global_actions_grid_lite.xml
@@ -33,7 +33,7 @@
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintEnd_toEndOf="parent"
       android:layout_weight="1">
-    <androidx.constraintlayout.widget.ConstraintLayout
+    <com.android.systemui.common.ui.view.LaunchableConstraintLayout
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:id="@android:id/list"
@@ -55,6 +55,6 @@
           app:flow_horizontalGap="@dimen/global_actions_lite_padding"
           app:flow_verticalGap="@dimen/global_actions_lite_padding"
           app:flow_horizontalStyle="packed"/>
-    </androidx.constraintlayout.widget.ConstraintLayout>
+    </com.android.systemui.common.ui.view.LaunchableConstraintLayout>
   </com.android.systemui.globalactions.GlobalActionsLayoutLite>
 </androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 3f95515..2871cdf 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -59,7 +59,7 @@
 
     </LinearLayout>
 
-    <com.android.systemui.common.ui.view.LaunchableImageView
+    <com.android.systemui.animation.view.LaunchableImageView
         android:id="@+id/start_button"
         android:layout_height="@dimen/keyguard_affordance_fixed_height"
         android:layout_width="@dimen/keyguard_affordance_fixed_width"
@@ -72,7 +72,7 @@
         android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset"
         android:visibility="gone" />
 
-    <com.android.systemui.common.ui.view.LaunchableImageView
+    <com.android.systemui.animation.view.LaunchableImageView
         android:id="@+id/end_button"
         android:layout_height="@dimen/keyguard_affordance_fixed_height"
         android:layout_width="@dimen/keyguard_affordance_fixed_width"
diff --git a/packages/SystemUI/res/layout/keyguard_qs_user_switch.xml b/packages/SystemUI/res/layout/keyguard_qs_user_switch.xml
index 6f33623..07c428b 100644
--- a/packages/SystemUI/res/layout/keyguard_qs_user_switch.xml
+++ b/packages/SystemUI/res/layout/keyguard_qs_user_switch.xml
@@ -24,7 +24,7 @@
     android:layout_gravity="end">
     <!-- We add a background behind the UserAvatarView with the same color and with a circular shape
          so that this view can be expanded into a Dialog or an Activity. -->
-    <FrameLayout
+    <com.android.systemui.animation.LaunchableFrameLayout
         android:id="@+id/kg_multi_user_avatar_with_background"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
@@ -42,5 +42,5 @@
             systemui:framePadding="0dp"
             systemui:frameWidth="0dp">
         </com.android.systemui.statusbar.phone.UserAvatarView>
-    </FrameLayout>
+    </com.android.systemui.animation.LaunchableFrameLayout>
 </FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyguard_settings_popup_menu.xml b/packages/SystemUI/res/layout/keyguard_settings_popup_menu.xml
new file mode 100644
index 0000000..89d88fe
--- /dev/null
+++ b/packages/SystemUI/res/layout/keyguard_settings_popup_menu.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ 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.
+  ~
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:minHeight="52dp"
+    android:orientation="horizontal"
+    android:gravity="center_vertical"
+    android:background="@drawable/keyguard_settings_popup_menu_background"
+    android:paddingStart="16dp"
+    android:paddingEnd="24dp"
+    android:paddingVertical="16dp">
+
+    <ImageView
+        android:id="@+id/icon"
+        android:layout_width="20dp"
+        android:layout_height="20dp"
+        android:layout_marginEnd="16dp"
+        android:tint="?android:attr/textColorPrimary"
+        android:importantForAccessibility="no"
+        tools:ignore="UseAppTint" />
+
+    <TextView
+        android:id="@+id/text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textColor="?android:attr/textColorPrimary"
+        android:textSize="14sp"
+        android:maxLines="1"
+        android:ellipsize="end" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
index aaa372a..e39f1a9 100644
--- a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
+++ b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
@@ -18,6 +18,7 @@
 
 <!-- LinearLayout -->
 <com.android.systemui.statusbar.policy.KeyguardUserDetailItemView
+        android:id="@+id/user_item"
         xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:systemui="http://schemas.android.com/apk/res-auto"
         android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/media_recommendation_view.xml b/packages/SystemUI/res/layout/media_recommendation_view.xml
new file mode 100644
index 0000000..c54c4e4
--- /dev/null
+++ b/packages/SystemUI/res/layout/media_recommendation_view.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<!-- Layout for media recommendation item inside QSPanel carousel -->
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Album cover -->
+    <ImageView
+        android:id="@+id/media_cover"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:translationZ="0dp"
+        android:scaleType="centerCrop"
+        android:adjustViewBounds="true"
+        android:clipToOutline="true"
+        android:background="@drawable/bg_smartspace_media_item"/>
+
+    <!-- App icon -->
+    <com.android.internal.widget.CachingIconView
+        android:id="@+id/media_rec_app_icon"
+        android:layout_width="@dimen/qs_media_rec_icon_top_margin"
+        android:layout_height="@dimen/qs_media_rec_icon_top_margin"
+        android:layout_marginStart="@dimen/qs_media_info_spacing"
+        android:layout_marginTop="@dimen/qs_media_info_spacing"/>
+
+    <!-- Artist name -->
+    <TextView
+        android:id="@+id/media_title"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/qs_media_info_spacing"
+        android:layout_marginEnd="@dimen/qs_media_info_spacing"
+        android:layout_marginBottom="@dimen/qs_media_rec_album_title_bottom_margin"
+        android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+        android:singleLine="true"
+        android:textSize="12sp"
+        android:gravity="top"
+        android:layout_gravity="bottom"
+        android:importantForAccessibility="no"/>
+
+    <!-- Album name -->
+    <TextView
+        android:id="@+id/media_subtitle"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/qs_media_rec_album_subtitle_height"
+        android:layout_marginEnd="@dimen/qs_media_info_spacing"
+        android:layout_marginStart="@dimen/qs_media_info_spacing"
+        android:layout_marginBottom="@dimen/qs_media_info_spacing"
+        android:fontFamily="@*android:string/config_headlineFontFamily"
+        android:singleLine="true"
+        android:textSize="11sp"
+        android:gravity="center_vertical"
+        android:layout_gravity="bottom"
+        android:importantForAccessibility="no"/>
+
+    <!-- Seek Bar -->
+    <SeekBar
+        android:id="@+id/media_progress_bar"
+        android:layout_width="match_parent"
+        android:layout_height="12dp"
+        android:layout_gravity="bottom"
+        android:maxHeight="@dimen/qs_media_enabled_seekbar_height"
+        android:thumb="@android:color/transparent"
+        android:splitTrack="false"
+        android:clickable="false"
+        android:progressTint="?android:attr/textColorPrimary"
+        android:progressBackgroundTint="?android:attr/textColorTertiary"
+        android:paddingTop="5dp"
+        android:paddingBottom="5dp"
+        android:paddingStart="0dp"
+        android:paddingEnd="0dp"
+        android:layout_marginEnd="@dimen/qs_media_info_spacing"
+        android:layout_marginStart="@dimen/qs_media_info_spacing"
+        android:layout_marginBottom="@dimen/qs_media_info_spacing"/>
+</merge>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_recommendations.xml b/packages/SystemUI/res/layout/media_recommendations.xml
new file mode 100644
index 0000000..65fc19c
--- /dev/null
+++ b/packages/SystemUI/res/layout/media_recommendations.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+
+<!-- Layout for media recommendations inside QSPanel carousel -->
+<com.android.systemui.util.animation.TransitionLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/media_recommendations_updated"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:clipChildren="false"
+    android:clipToPadding="false"
+    android:forceHasOverlappingRendering="false"
+    android:background="@drawable/qs_media_background"
+    android:theme="@style/MediaPlayer">
+
+    <!-- This view just ensures the full media player is a certain height. -->
+    <View
+        android:id="@+id/sizing_view"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/qs_media_session_height_expanded" />
+
+    <TextView
+        android:id="@+id/media_rec_title"
+        style="@style/MediaPlayer.Recommendation.Header"
+        android:text="@string/controls_media_smartspace_rec_header"/>
+
+    <FrameLayout
+        android:id="@+id/media_cover1_container"
+        style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated"
+        >
+
+        <include
+            layout="@layout/media_recommendation_view"/>
+
+    </FrameLayout>
+
+
+    <FrameLayout
+        android:id="@+id/media_cover2_container"
+        style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated"
+        >
+
+        <include
+            layout="@layout/media_recommendation_view"/>
+
+    </FrameLayout>
+
+    <FrameLayout
+        android:id="@+id/media_cover3_container"
+        style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated"
+        >
+
+        <include
+            layout="@layout/media_recommendation_view"/>
+
+    </FrameLayout>
+
+    <include
+        layout="@layout/media_long_press_menu" />
+
+</com.android.systemui.util.animation.TransitionLayout>
diff --git a/packages/SystemUI/res/layout/media_session_view.xml b/packages/SystemUI/res/layout/media_session_view.xml
index abc8337..9d91419 100644
--- a/packages/SystemUI/res/layout/media_session_view.xml
+++ b/packages/SystemUI/res/layout/media_session_view.xml
@@ -106,7 +106,7 @@
         app:layout_constrainedWidth="true"
         app:layout_constraintWidth_min="@dimen/min_clickable_item_size"
         app:layout_constraintHeight_min="@dimen/min_clickable_item_size">
-        <LinearLayout
+        <com.android.systemui.animation.view.LaunchableLinearLayout
             android:id="@+id/media_seamless_button"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
@@ -135,7 +135,7 @@
                 android:textDirection="locale"
                 android:textSize="12sp"
                 android:lineHeight="16sp" />
-        </LinearLayout>
+        </com.android.systemui.animation.view.LaunchableLinearLayout>
     </LinearLayout>
 
     <!-- Song name -->
diff --git a/packages/SystemUI/res/layout/media_ttt_chip_receiver.xml b/packages/SystemUI/res/layout/media_ttt_chip_receiver.xml
index 4483db8..02186fc 100644
--- a/packages/SystemUI/res/layout/media_ttt_chip_receiver.xml
+++ b/packages/SystemUI/res/layout/media_ttt_chip_receiver.xml
@@ -27,22 +27,28 @@
         android:layout_height="wrap_content"
         />
 
-    <com.android.systemui.media.taptotransfer.receiver.ReceiverChipRippleView
-        android:id="@+id/icon_glow_ripple"
+    <FrameLayout
+        android:id="@+id/icon_container_view"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        />
-
-    <!-- Add a bottom margin to avoid the glow of the icon ripple from being cropped by screen
-     bounds while animating with the icon -->
-    <com.android.internal.widget.CachingIconView
-        android:id="@+id/app_icon"
-        android:background="@drawable/media_ttt_chip_background_receiver"
-        android:layout_width="@dimen/media_ttt_icon_size_receiver"
-        android:layout_height="@dimen/media_ttt_icon_size_receiver"
-        android:layout_gravity="center|bottom"
         android:alpha="0.0"
-        android:layout_marginBottom="@dimen/media_ttt_receiver_icon_bottom_margin"
-        />
+        >
+        <com.android.systemui.media.taptotransfer.receiver.ReceiverChipRippleView
+            android:id="@+id/icon_glow_ripple"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            />
+
+        <!-- Add a bottom margin to avoid the glow of the icon ripple from being cropped by screen
+        bounds while animating with the icon -->
+        <com.android.internal.widget.CachingIconView
+            android:id="@+id/app_icon"
+            android:background="@drawable/media_ttt_chip_background_receiver"
+            android:layout_width="@dimen/media_ttt_icon_size_receiver"
+            android:layout_height="@dimen/media_ttt_icon_size_receiver"
+            android:layout_gravity="center|bottom"
+            android:layout_marginBottom="@dimen/media_ttt_receiver_icon_bottom_margin"
+            />
+    </FrameLayout>
 
 </FrameLayout>
diff --git a/packages/SystemUI/res/layout/ongoing_call_chip.xml b/packages/SystemUI/res/layout/ongoing_call_chip.xml
index c949ba0..238fc84 100644
--- a/packages/SystemUI/res/layout/ongoing_call_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_call_chip.xml
@@ -23,7 +23,7 @@
     android:layout_gravity="center_vertical|start"
     android:layout_marginStart="5dp"
 >
-    <LinearLayout
+    <com.android.systemui.animation.view.LaunchableLinearLayout
         android:id="@+id/ongoing_call_chip_background"
         android:layout_width="wrap_content"
         android:layout_height="@dimen/ongoing_appops_chip_height"
@@ -55,5 +55,5 @@
             android:textColor="?android:attr/colorPrimary"
         />
 
-    </LinearLayout>
+    </com.android.systemui.animation.view.LaunchableLinearLayout>
 </FrameLayout>
diff --git a/packages/SystemUI/res/layout/qs_user_detail_item.xml b/packages/SystemUI/res/layout/qs_user_detail_item.xml
index 7c86bc7..ad129e8 100644
--- a/packages/SystemUI/res/layout/qs_user_detail_item.xml
+++ b/packages/SystemUI/res/layout/qs_user_detail_item.xml
@@ -18,6 +18,7 @@
 
 <!-- LinearLayout -->
 <com.android.systemui.qs.tiles.UserDetailItemView
+        android:id="@+id/user_item"
         xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:systemui="http://schemas.android.com/apk/res-auto"
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml b/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml
index 2567176..130472d 100644
--- a/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml
+++ b/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml
@@ -23,7 +23,7 @@
     <TextView
         android:id="@+id/screen_recording_dialog_source_text"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
+        android:layout_height="wrap_content"
         android:layout_gravity="center_vertical"
         android:textAppearance="?android:attr/textAppearanceMedium"
         android:textSize="14sp"
diff --git a/packages/SystemUI/res/layout/screen_record_dialog_audio_source_selected.xml b/packages/SystemUI/res/layout/screen_record_dialog_audio_source_selected.xml
index e2b8d33..9d9f5c2 100644
--- a/packages/SystemUI/res/layout/screen_record_dialog_audio_source_selected.xml
+++ b/packages/SystemUI/res/layout/screen_record_dialog_audio_source_selected.xml
@@ -22,7 +22,7 @@
     android:layout_weight="1">
     <TextView
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
+        android:layout_height="wrap_content"
         android:text="@string/screenrecord_audio_label"
         android:textAppearance="?android:attr/textAppearanceMedium"
         android:fontFamily="@*android:string/config_headlineFontFamily"
@@ -30,7 +30,7 @@
     <TextView
         android:id="@+id/screen_recording_dialog_source_text"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
+        android:layout_height="wrap_content"
         android:textColor="?android:attr/textColorSecondary"
         android:textAppearance="?android:attr/textAppearanceSmall"/>
 </LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/screen_record_options.xml b/packages/SystemUI/res/layout/screen_record_options.xml
index 3f0eea9..6cc72dd 100644
--- a/packages/SystemUI/res/layout/screen_record_options.xml
+++ b/packages/SystemUI/res/layout/screen_record_options.xml
@@ -67,7 +67,7 @@
             android:importantForAccessibility="no"/>
         <TextView
             android:layout_width="0dp"
-            android:layout_height="match_parent"
+            android:layout_height="wrap_content"
             android:minHeight="48dp"
             android:layout_weight="1"
             android:gravity="center_vertical"
diff --git a/packages/SystemUI/res/layout/screenshot_detection_notice.xml b/packages/SystemUI/res/layout/screenshot_detection_notice.xml
new file mode 100644
index 0000000..fc936c0
--- /dev/null
+++ b/packages/SystemUI/res/layout/screenshot_detection_notice.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/screenshot_detection_notice"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:padding="12dp"
+    android:visibility="gone">
+
+    <TextView
+        android:id="@+id/screenshot_detection_notice_text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:lineHeight="24sp"
+        android:textSize="18sp" />
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/screenshot_static.xml b/packages/SystemUI/res/layout/screenshot_static.xml
index 496eb6e..7e9202c 100644
--- a/packages/SystemUI/res/layout/screenshot_static.xml
+++ b/packages/SystemUI/res/layout/screenshot_static.xml
@@ -31,7 +31,7 @@
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="@+id/actions_container"
         app:layout_constraintEnd_toEndOf="@+id/actions_container"
-        app:layout_constraintBottom_toTopOf="@id/screenshot_message_container"/>
+        app:layout_constraintBottom_toTopOf="@id/guideline"/>
     <HorizontalScrollView
         android:id="@+id/actions_container"
         android:layout_width="0dp"
@@ -127,57 +127,32 @@
         app:layout_constraintTop_toTopOf="@id/screenshot_preview"
         android:elevation="7dp"/>
 
-    <androidx.constraintlayout.widget.ConstraintLayout
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/guideline"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        app:layout_constraintGuide_end="0dp" />
+
+    <FrameLayout
         android:id="@+id/screenshot_message_container"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_marginHorizontal="@dimen/overlay_action_container_margin_horizontal"
-        android:layout_marginVertical="4dp"
+        android:layout_marginTop="4dp"
+        android:layout_marginBottom="@dimen/overlay_action_container_margin_bottom"
         android:paddingHorizontal="@dimen/overlay_action_container_padding_end"
         android:paddingVertical="@dimen/overlay_action_container_padding_vertical"
         android:elevation="4dp"
         android:background="@drawable/action_chip_container_background"
         android:visibility="gone"
+        app:layout_constraintTop_toBottomOf="@id/guideline"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintBottom_toBottomOf="parent">
-
-        <ImageView
-            android:id="@+id/screenshot_message_icon"
-            android:layout_width="48dp"
-            android:layout_height="48dp"
-            android:paddingEnd="4dp"
-            android:src="@drawable/ic_work_app_badge"
-            app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintEnd_toStartOf="@id/screenshot_message_content"
-            app:layout_constraintTop_toTopOf="parent"
-            app:layout_constraintBottom_toBottomOf="parent"/>
-
-        <TextView
-            android:id="@+id/screenshot_message_content"
-            android:layout_width="0dp"
-            android:layout_height="wrap_content"
-            android:layout_gravity="start"
-            app:layout_constraintTop_toTopOf="parent"
-            app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintStart_toEndOf="@id/screenshot_message_icon"
-            app:layout_constraintEnd_toStartOf="@id/message_dismiss_button"/>
-
-        <FrameLayout
-            android:id="@+id/message_dismiss_button"
-            android:layout_width="@dimen/overlay_dismiss_button_tappable_size"
-            android:layout_height="@dimen/overlay_dismiss_button_tappable_size"
-            app:layout_constraintStart_toEndOf="@id/screenshot_message_content"
-            app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintTop_toTopOf="parent"
-            app:layout_constraintBottom_toBottomOf="parent"
-            android:contentDescription="@string/screenshot_dismiss_work_profile">
-            <ImageView
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:layout_margin="@dimen/overlay_dismiss_button_margin"
-                android:src="@drawable/overlay_cancel"/>
-        </FrameLayout>
-
-    </androidx.constraintlayout.widget.ConstraintLayout>
+        app:layout_constraintWidth_max="450dp"
+        app:layout_constraintHorizontal_bias="0"
+        >
+        <include layout="@layout/screenshot_work_profile_first_run" />
+        <include layout="@layout/screenshot_detection_notice" />
+    </FrameLayout>
 </com.android.systemui.screenshot.DraggableConstraintLayout>
diff --git a/packages/SystemUI/res/layout/screenshot_work_profile_first_run.xml b/packages/SystemUI/res/layout/screenshot_work_profile_first_run.xml
new file mode 100644
index 0000000..392d845
--- /dev/null
+++ b/packages/SystemUI/res/layout/screenshot_work_profile_first_run.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/work_profile_first_run"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:paddingStart="16dp"
+    android:paddingEnd="4dp"
+    android:paddingVertical="16dp"
+    android:visibility="gone">
+    <ImageView
+        android:id="@+id/screenshot_message_icon"
+        android:layout_width="32dp"
+        android:layout_height="32dp"
+        android:layout_marginEnd="12dp"
+        android:layout_gravity="center_vertical"
+        android:src="@drawable/ic_work_app_badge"/>
+
+    <TextView
+        android:id="@+id/screenshot_message_content"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:layout_gravity="start|center_vertical"
+        android:textSize="18sp"
+        android:textColor="?android:attr/textColorPrimary"
+        android:lineHeight="24sp"
+        />
+
+    <FrameLayout
+        android:id="@+id/message_dismiss_button"
+        android:layout_width="@dimen/overlay_dismiss_button_tappable_size"
+        android:layout_height="@dimen/overlay_dismiss_button_tappable_size"
+        android:contentDescription="@string/screenshot_dismiss_work_profile">
+        <ImageView
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            android:layout_gravity="center"
+            android:src="@drawable/overlay_cancel"/>
+    </FrameLayout>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/seekbar_with_icon_buttons.xml b/packages/SystemUI/res/layout/seekbar_with_icon_buttons.xml
new file mode 100644
index 0000000..52d1d4f
--- /dev/null
+++ b/packages/SystemUI/res/layout/seekbar_with_icon_buttons.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   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.
+  -->
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+       xmlns:tools="http://schemas.android.com/tools"
+       android:id="@+id/seekbar_frame"
+       android:layout_width="match_parent"
+       android:layout_height="wrap_content"
+       android:clipChildren="false"
+       android:gravity="center_vertical"
+       android:orientation="horizontal"
+       tools:parentTag="android.widget.LinearLayout">
+
+    <FrameLayout
+        android:id="@+id/icon_start_frame"
+        android:layout_width="@dimen/min_clickable_item_size"
+        android:layout_height="@dimen/min_clickable_item_size"
+        android:clipChildren="false"
+        android:focusable="true" >
+        <ImageView
+            android:id="@+id/icon_start"
+            android:layout_width="@dimen/seekbar_icon_size"
+            android:layout_height="@dimen/seekbar_icon_size"
+            android:layout_gravity="center"
+            android:background="?android:attr/selectableItemBackgroundBorderless"
+            android:adjustViewBounds="true"
+            android:focusable="false"
+            android:src="@drawable/ic_remove"
+            android:tint="?android:attr/textColorPrimary"
+            android:tintMode="src_in" />
+    </FrameLayout>
+
+    <SeekBar
+        android:id="@+id/seekbar"
+        style="@android:style/Widget.Material.SeekBar.Discrete"
+        android:layout_width="0dp"
+        android:layout_height="48dp"
+        android:layout_gravity="center_vertical"
+        android:layout_weight="1" />
+
+    <FrameLayout
+        android:id="@+id/icon_end_frame"
+        android:layout_width="@dimen/min_clickable_item_size"
+        android:layout_height="@dimen/min_clickable_item_size"
+        android:clipChildren="false"
+        android:focusable="true" >
+        <ImageView
+            android:id="@+id/icon_end"
+            android:layout_width="@dimen/seekbar_icon_size"
+            android:layout_height="@dimen/seekbar_icon_size"
+            android:layout_gravity="center"
+            android:background="?android:attr/selectableItemBackgroundBorderless"
+            android:adjustViewBounds="true"
+            android:focusable="false"
+            android:src="@drawable/ic_add"
+            android:tint="?android:attr/textColorPrimary"
+            android:tintMode="src_in" />
+    </FrameLayout>
+
+</merge>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 159323a..3c860a9 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -26,6 +26,11 @@
     android:layout_height="match_parent"
     android:background="@android:color/transparent">
 
+    <com.android.systemui.common.ui.view.LongPressHandlingView
+        android:id="@+id/keyguard_long_press"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
     <ViewStub
         android:id="@+id/keyguard_qs_user_switch_stub"
         android:layout="@layout/keyguard_qs_user_switch"
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index 2c08f5d..356b36f 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -39,8 +39,11 @@
 
     <com.android.systemui.statusbar.notification.row.NotificationContentView
         android:id="@+id/expanded"
-       android:layout_width="match_parent"
-       android:layout_height="wrap_content" />
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="@dimen/notification_content_min_height"
+        android:gravity="center_vertical"
+        />
 
     <com.android.systemui.statusbar.notification.row.NotificationContentView
         android:id="@+id/expandedPublic"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 771f972..a2fa52c 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Ondergrens <xliff:g id="PERCENT">%1$d</xliff:g> persent"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Linkergrens <xliff:g id="PERCENT">%1$d</xliff:g> persent"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Regtergrens <xliff:g id="PERCENT">%1$d</xliff:g> persent"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Werkskermskote word in die <xliff:g id="APP">%1$s</xliff:g>-app gestoor"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Gestoor in <xliff:g id="APP">%1$s</xliff:g> in die werkprofiel"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Lêers"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> het hierdie skermskoot bespeur."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> en ander oop apps het hierdie skermskoot bespeur."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Skermopnemer"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Verwerk tans skermopname"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Deurlopende kennisgewing vir \'n skermopnamesessie"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Helderheid"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Kleuromkering"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Kleurregstelling"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Bestuur gebruikers"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Klaar"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Maak toe"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Outomaties"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Geen klank of vibrasie nie"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Geen klank of vibrasie nie en verskyn laer in gespreksafdeling"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Kan lui of vibreer op grond van toestelinstellings"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Kan lui of vibreer op grond van toestelinstellings. Gesprekke van <xliff:g id="APP_NAME">%1$s</xliff:g> af verskyn by verstek in ’n borrel."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Laat die stelsel bepaal of hierdie kennisgewing \'n klank moet maak of vibreer"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Status:&lt;/b&gt; Bevorder na Verstek"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Status:&lt;/b&gt; Gedegradeer na Stil"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"skermopname"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Titelloos"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Bystandmodus"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Vergrotingvenster"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Vergrotingvensterkontroles"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zoem in"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Kies program om kontroles by te voeg"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# kontrole bygevoeg.}other{# kontroles bygevoeg.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Verwyder"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Voeg <xliff:g id="APPNAME">%s</xliff:g> by?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Wanneer jy <xliff:g id="APPNAME">%s</xliff:g> byvoeg, kan dit kontroles en inhoud by hierdie paneel voeg. Jy kan in sommige apps kies watter kontroles hier verskyn."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"As gunsteling gemerk"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"As gunsteling gemerk; posisie <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"As gunsteling ontmerk"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Maak <xliff:g id="APP_LABEL">%1$s</xliff:g> oop"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Speel <xliff:g id="SONG_NAME">%1$s</xliff:g> deur <xliff:g id="ARTIST_NAME">%2$s</xliff:g> vanaf <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Speel <xliff:g id="SONG_NAME">%1$s</xliff:g> vanaf <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Vir jou"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Ontdoen"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Beweeg nader om op <xliff:g id="DEVICENAME">%1$s</xliff:g> te speel"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Beweeg nader aan <xliff:g id="DEVICENAME">%1$s</xliff:g> om hier te speel"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Iets is fout. Probeer weer."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Laai tans"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Saai jou media uit"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Saai tans <xliff:g id="APP_LABEL">%1$s</xliff:g> uit"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Onaktief, gaan program na"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Nie gekry nie"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Kontrole is nie beskikbaar nie"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Fout, probeer weer"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Voeg kontroles by"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Wysig kontroles"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Voeg app by"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Voeg uitvoere by"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Groep"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 toestel gekies"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Luidsprekers en skerms"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Voorgestelde toestelle"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Vereis premiumrekening"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Hoe uitsaai werk"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Saai uit"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Mense in jou omtrek met versoenbare Bluetooth-toestelle kan na die media luister wat jy uitsaai"</string>
@@ -894,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Kan nie uitsaai nie"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Kan nie stoor nie. Probeer weer."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Kan nie stoor nie."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Gebruik minstens 4 karakters"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Gebruik minder as 16 karakters"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Bounommer"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Bounommer is na knipbord gekopieer."</string>
     <string name="basic_status" msgid="2315371112182658176">"Maak gesprek oop"</string>
@@ -1013,24 +1030,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Minstens een toestel beskikbaar is"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Raak en hou kortpad"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Kanselleer"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Draai nou om"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Vou foon oop vir ’n beter selfie"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Draai om na voorste skerm vir ’n beter selfie?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Gebruik die agterste kamera vir ’n breër foto met ’n hoër resolusie."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Hierdie skerm sal afskakel"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Voubare toestel word ontvou"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Voubare toestel word omgekeer"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> batterykrag oor"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Koppel jou stilus aan ’n laaier"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Stilus se battery is amper pap"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Kan nie van hierdie profiel af bel nie"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Jou werkbeleid laat jou toe om slegs van die werkprofiel af foonoproepe te maak"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Skakel oor na werkprofiel"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Maak toe"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Sluitskerminstellings"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-af/tiles_states_strings.xml b/packages/SystemUI/res/values-af/tiles_states_strings.xml
index e60f233..da24a79 100644
--- a/packages/SystemUI/res/values-af/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-af/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Af"</item>
     <item msgid="5966994759929723339">"Aan"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 447f98d..7f2ccac 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"ዚሄራ á‰…áŒœá‰ á‰łá‹Š ገጜ áŠ„á‹­á‰łá‹Žá‰œ በ<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>
     <string name="screenrecord_name" msgid="2596401223859996572">"ዚማያ መቅጃ"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ዚማያ ገጜ ቀሚጻን á‰ áˆ›áˆ°áŠ“á‹łá‰” ላይ"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"ለአንዔ ዚማያ ገጜ ቀሚጻ ክፍለ-ጊዜ በመካሄዔ ያለ áˆ›áˆłá‹ˆá‰‚á‹«"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ቄሩህነቔ"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ተቃራኒ ቀለም"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ዹቀለም ማሔተካኚያ"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"ተጠቃሚዎቜን á‹«áˆ”á‰°á‹łá‹”áˆ©"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"ተኹናውኗል"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"ዝጋ"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"ራሔ-ሰር"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"ምንም ዔምፅ ወይም ንዝሚቔ ዹለም"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"ምንም ዔምፅ ወይም ንዝሚቔ ዹለም ኄና በውይይቔ ክፍል ላይ አይታይም"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"በመሣáˆȘያ á‰…áŠ•á‰„áˆźá‰œ መሰሚቔ ሊጼህ ወይም ሊነዝር ይቜላል"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"በሔልክ á‰…áŠ•á‰„áˆźá‰œ መሰሚቔ ሊጼህ ወይም ሊነዝር á‹­á‰œáˆ‹áˆáą ዹ<xliff:g id="APP_NAME">%1$s</xliff:g> ውይይቶቜ በነባáˆȘነቔ አሹፋ ይሆናሉ፱"</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ይህ áˆ›áˆłá‹ˆá‰‚á‹« ዔምፅ ወይም ንዝሚቔ መደሹግ ካለበቔ ሔርዓቱ ኄንá‹Čወሰን ያዔርጉቔ"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;ሁኔታ:&lt;/b&gt; ለነባáˆȘ ኹፍ ተዋውቋል፱"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;ሁኔታ:&lt;/b&gt; ወደ ዝምታ ዝቅ ተደርጓል"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"ማያን መቅሚጜ"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"ርዕሔ ዹለም"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"ተጠባባቂ"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"ዚማጉያ áˆ˜áˆ”áŠźá‰”"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"ዚማጉያ áˆ˜áˆ”áŠźá‰” መቆጣጠáˆȘያዎቜ"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"አጉላ"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"መቆጣጠáˆȘያዎቜን ለማኹል መተግበáˆȘያ ይምሹጡ"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# ቁጄጄር ታክሏል፱}one{# ቁጄጄር ታክሏል፱}other{# á‰áŒ„áŒ„áˆźá‰œ ታክለዋል፱}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"ተወግዷል"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g> ይታኹል?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"<xliff:g id="APPNAME">%s</xliff:g>ን áˆČያክሉ መቆጣጠáˆȘያዎቜን ኄና ይዘቔን ወደዚህ ፓነል ሊያክል á‹­á‰œáˆ‹áˆáą በአንዳንዔ መተግበáˆȘያዎቜ ውሔጄ ዚቔኛዎá‰č መቆጣጠáˆȘያዎቜ ኄዚህ ላይ áŠ„áŠ•á‹°áˆšá‰łá‹© መምሚጄ á‹­á‰œáˆ‹áˆ‰áą"</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"ተወዳጅ ዹተደሹገ"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"ተወዳጅ ተደርጓል፣ አቋም <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"ተወዳጅ አልተደሹገም"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> ክፈቔ"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="SONG_NAME">%1$s</xliff:g> በ<xliff:g id="ARTIST_NAME">%2$s</xliff:g> ኹ<xliff:g id="APP_LABEL">%3$s</xliff:g> ያጫውቱ"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> ኹ<xliff:g id="APP_LABEL">%2$s</xliff:g> ያጫውቱ"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"ለኄርሔዎ"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"ቀልቄሔ"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"በ<xliff:g id="DEVICENAME">%1$s</xliff:g> ላይ ለማጫወቔ ጠጋ ያዔርጉ"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"ኄዚህ ለማጫወቔ ወደ <xliff:g id="DEVICENAME">%1$s</xliff:g> ጠጋ ይበሉ"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"ዹሆነ ቜግር á‰°áˆáŒ„áˆŻáˆáą ኄንደገና á‹­áˆžáŠ­áˆ©áą"</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"በመጫን ላይ"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ጡባዊ"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"ዚኄርሔዎን ሚá‹Čያ cast በማዔሚግ ላይ"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"<xliff:g id="APP_LABEL">%1$s</xliff:g>ን Cast በማዔሚግ ላይ"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ንቁ á‹«áˆáŠŸáŠáŁ መተግበáˆȘያን ይፈቔáˆč"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"አልተገኘም"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"መቆጣጠáˆȘያ አይገኝም"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"áˆ”áˆ…á‰°á‰”áŁ ኄንደገና ይሞክሩ"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"መቆጣጠáˆȘያዎቜን አክል"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"መቆጣጠáˆȘያዎቜን ያርቔዑ"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"መተግበáˆȘያ አክል"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"ውጜዓቶቜን ያክሉ"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"ብዔን"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 መሣáˆȘያ ተመርጧል"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ዔምጜ ማውጫዎቜ ኄና áˆ›áˆłá‹«á‹Žá‰œ"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"ዹተጠቆሙ መሣáˆȘያዎቜ"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"ፕáˆȘሚዹም መለያ ይጠይቃል"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ማሰራጚቔ ኄንዎቔ ኄንደሚሠራ"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"ሔርጭቔ"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ተኳሃኝ ዚቄሉቱዝ መሣáˆȘያዎቜ ያላ቞ው á‰ áŠ á‰…áˆ«á‰ąá‹«á‹Ž ያሉ ሰዎቜ ኄርሔዎ ኄያሰራጩቔ ያሉቔን ሚá‹Čያ áˆ›á‹łáˆ˜áŒ„ ይቜላሉ"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"መሰራጚቔ አይቜልም"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"ማሔቀመጄ áŠ áˆá‰°á‰»áˆˆáˆáą ኄንደገና á‹­áˆžáŠ­áˆ©áą"</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"ማሔቀመጄ áŠ áˆá‰°á‰»áˆˆáˆáą"</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"ዚግንቄ ቁጄር"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"ዹገንባ ቁጄር ወደ ቅንጄቄ ሰሌዳ á‰°á‰€á‹”á‰·áˆáą"</string>
     <string name="basic_status" msgid="2315371112182658176">"ውይይቔ ይክፈቱ"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• á‰ąá‹«áŠ•áˆ” አንዔ መሣáˆȘያ ይገኛል"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"ዚይንኩ ኄና ይያዙ አቋራጭ"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"ይቅር"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"አሁን ገልበጄ"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"ለተሻለ ዚራሔ ፎቶ ሔልክን ይዘርጉ"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"ለተሻለ ዚራሔ ፎቶ ወደፊቔ áˆ›áˆłá‹« ይገልበጄ?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"ኹፍተኛ ጄራቔ ላለው ሰፊ ፎቶ ዹኋለኛውን ካሜራ ይጠቀሙ፱"</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ይህ ማያ ገጜ ይጠፋል"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"መታጠፍ ዚሚቜል መሣáˆȘያ ኄዚተዘሚጋ ነው"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"መታጠፍ ዚሚቜል መሣáˆȘያ ኄዚተገለበጠ ነው"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> ባቔáˆȘ ይቀራል"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"á‰„áˆźáˆ”áŒá‹ŽáŠ• ኹኃይል መሙያ ጋር ያገናኙ"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"á‹šá‰„áˆźáˆ”áŒ ባቔáˆȘ ዝቅተኛ ነው"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"ዹá‰Șዔዟ ካሜራ"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"ኹዚህ መገለጫ መደወል አይቻልም"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"ዚሄራ መመáˆȘያዎ ኄርሔዎ ኚሄራ መገለጫው ቄቻ ጄáˆȘ ኄንá‹Čያደርጉ á‹­áˆá‰…á‹”áˆá‹Žá‰łáˆ"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"ወደ ዚሄራ መገለጫ ቀይር"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"ዝጋ"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"ዚማያ ገጜ ቁልፍ á‰…áŠ•á‰„áˆźá‰œ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-am/tiles_states_strings.xml b/packages/SystemUI/res/values-am/tiles_states_strings.xml
index bbf2d23..26ef52d 100644
--- a/packages/SystemUI/res/values-am/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-am/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"ጠፍቷል"</item>
     <item msgid="5966994759929723339">"በርቷል"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 8099b23..50165d4 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"يŰȘم Ű­ÙŰž Ù„Ù‚Ű·Ű§ŰȘ Ű§Ù„ŰŽŰ§ŰŽŰ© Ű§Ù„ŰźŰ§Ű”Ű© ŰšŰ§Ù„Űčمل في ŰȘŰ·ŰšÙŠÙ‚ \"<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>
     <string name="screenrecord_name" msgid="2596401223859996572">"Ù…ŰłŰŹÙ‘Ù„ Ű§Ù„ŰŽŰ§ŰŽŰ©"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ŰŹŰ§Ű±Ù مŰčŰ§Ù„ŰŹŰ© ŰȘŰłŰŹÙŠÙ„ Ű§Ù„ŰŽŰ§ŰŽŰ©"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"ۄێŰčۧ۱ Ù…ŰłŰȘÙ…Ű± Ù„ŰŹÙ„ŰłŰ© ŰȘŰłŰŹÙŠÙ„ ێۧێ۩"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Ű§Ù„ŰłŰ·ÙˆŰč"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Ù‚Ù„Űš Ű§Ù„ŰŁÙ„ÙˆŰ§Ù†"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ŰȘŰ”Ű­ÙŠŰ­ Ű§Ù„ŰŁÙ„ÙˆŰ§Ù†"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"ۄۯۧ۱۩ Ű§Ù„Ù…ŰłŰȘŰźŰŻÙ…ÙŠÙ†"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"ŰȘم"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Ű„ŰșÙ„Ű§Ù‚"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"ŰȘÙ„Ù‚Ű§ŰŠÙŠ"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"ŰšŰŻÙˆÙ† Ű”ÙˆŰȘ ŰŁÙˆ Ű§Ù‡ŰȘŰČۧŰČ"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"ŰšŰŻÙˆÙ† Ű”ÙˆŰȘ ŰŁÙˆ Ű§Ù‡ŰȘŰČۧŰČ ÙˆŰȘŰžÙ‡Ű± في ŰŁŰłÙÙ„ Ù‚ŰłÙ… Ű§Ù„Ù…Ű­Ű§ŰŻŰ«Ű§ŰȘ"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"يمكن ۄ۔ۯۧ۱ Ű±Ù†ÙŠÙ† ŰŁÙˆ Ű§Ù‡ŰȘŰČۧŰČ ŰšÙ†Ű§ŰĄÙ‹ Űčلى Ű„ŰčۯۧۯۧŰȘ Ű§Ù„ŰŹÙ‡Ű§ŰČ"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"يمكن ۄ۔ۯۧ۱ Ű±Ù†ÙŠÙ† ŰŁÙˆ Ű§Ù‡ŰȘŰČۧŰČ ŰšÙ†Ű§ŰĄÙ‹ Űčلى Ű„ŰčۯۧۯۧŰȘ Ű§Ù„ŰŹÙ‡Ű§ŰČ. ŰȘŰžÙ‡Ű± Ű§Ù„Ù…Ű­Ű§ŰŻŰ«Ű§ŰȘ من \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" ÙƒÙÙ‚Ű§ŰčۧŰȘ ŰȘÙ„Ù‚Ű§ŰŠÙŠÙ‹Ű§."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Ű§Ù„ŰłÙ…Ű§Ű­ Ù„Ù„Ù†ŰžŰ§Ù… ŰšŰȘŰ­ŰŻÙŠŰŻ Ù…Ű§ ۄ۰ۧ ÙŠŰŹŰš Ű§Ù‡ŰȘŰČۧŰČ Ű§Ù„ŰŹÙ‡Ű§ŰČ ŰŁÙˆ ۄ۔ۯۧ۱ Ű±Ù†ÙŠÙ† ŰčÙ†ŰŻ ŰȘلقّي Ù‡Ű°Ű§ Ű§Ù„Ű„ŰŽŰčۧ۱"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"‏&lt;b&gt;Ű§Ù„Ű­Ű§Ù„Ű©:&lt;/b&gt; ŰȘمŰȘ Ű§Ù„ŰȘŰ±Ù‚ÙŠŰ© Ű„Ù„Ù‰ Ű§Ù„Ű„Űčۯۧۯ Ű§Ù„ŰȘÙ„Ù‚Ű§ŰŠÙŠ"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"‏&lt;b&gt;Ű§Ù„Ű­Ű§Ù„Ű©:&lt;/b&gt; ŰȘم ŰźÙŰ¶ Ű§Ù„ŰȘ۱ŰȘÙŠŰš Ű„Ù„Ù‰ Ű§Ù„ÙˆŰ¶Űč Ű”Ű§Ù…ŰȘ"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"ŰȘŰłŰŹÙŠÙ„ Ù…Ű­ŰȘوى Ű§Ù„ŰŽŰ§ŰŽŰ©"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"ŰšÙ„Ű§ ŰčÙ†ÙˆŰ§Ù†"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"ÙˆŰ¶Űč Ű§Ù„Ű§ŰłŰȘŰčۯۧۯ"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Ù†Ű§ÙŰ°Ű© Ű§Ù„ŰȘÙƒŰšÙŠŰ±"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"ŰčÙ†Ű§Ű”Ű± Ű§Ù„ŰȘŰ­ÙƒÙ… في Ù†Ű§ÙŰ°Ű© Ű§Ù„ŰȘÙƒŰšÙŠŰ±"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ŰȘÙƒŰšÙŠŰ±"</string>
@@ -791,17 +799,19 @@
     <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ŰȘŰšŰŻÙŠÙ„"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Ű§Ù†Ù‚Ű± لفŰȘŰ­ ميŰČۧŰȘ ŰȘŰłÙ‡ÙŠÙ„ Ű§Ù„Ű§ŰłŰȘŰźŰŻŰ§Ù…. يمكنك ŰȘŰźŰ”ÙŠŰ” Ù‡Ű°Ű§ Ű§Ù„ŰČ۱ ŰŁÙˆ ۧ۳ŰȘŰšŰŻŰ§Ù„Ù‡ من Ű§Ù„Ű„ŰčۯۧۯۧŰȘ.\n\n"<annotation id="link">"Űč۱۶ Ű§Ù„Ű„ŰčۯۧۯۧŰȘ"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"يمكنك نقل Ű§Ù„ŰČ۱ Ű„Ù„Ù‰ Ű§Ù„Ű­Ű§ÙŰ© Ù„Ű„ŰźÙŰ§ŰŠÙ‡ Ù…Ű€Ù‚ŰȘÙ‹Ű§."</string>
-    <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"نقل Ű„Ù„Ù‰ ŰŁŰčلى يمين Ű§Ù„ŰŽŰ§ŰŽŰ©"</string>
-    <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"نقل Ű„Ù„Ù‰ ŰŁŰčلى ÙŠŰłŰ§Ű± Ű§Ù„ŰŽŰ§ŰŽŰ©"</string>
-    <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"نقل Ű„Ù„Ù‰ ŰŁŰłÙÙ„ يمين Ű§Ù„ŰŽŰ§ŰŽŰ©"</string>
-    <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"نقل Ű„Ù„Ù‰ ŰŁŰłÙÙ„ ÙŠŰłŰ§Ű± Ű§Ù„ŰŽŰ§ŰŽŰ©"</string>
-    <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"نقله Ű„Ù„Ù‰ Ű§Ù„Ű­Ű§ÙŰ© ÙˆŰ„ŰźÙŰ§Ű€Ù‡"</string>
+    <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Ű§Ù„Ù†Ù‚Ù„ Ű„Ù„Ù‰ ŰŁŰčلى يمين Ű§Ù„ŰŽŰ§ŰŽŰ©"</string>
+    <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Ű§Ù„Ù†Ù‚Ù„ Ű„Ù„Ù‰ ŰŁŰčلى ÙŠŰłŰ§Ű± Ű§Ù„ŰŽŰ§ŰŽŰ©"</string>
+    <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Ű§Ù„Ù†Ù‚Ù„ Ű„Ù„Ù‰ ŰŁŰłÙÙ„ يمين Ű§Ù„ŰŽŰ§ŰŽŰ©"</string>
+    <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Ű§Ù„Ù†Ù‚Ù„ Ű„Ù„Ù‰ ŰŁŰłÙÙ„ ÙŠŰłŰ§Ű± Ű§Ù„ŰŽŰ§ŰŽŰ©"</string>
+    <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Ű§Ù„Ù†Ù‚Ù„ Ű„Ù„Ù‰ Ű§Ù„Ű­Ű§ÙŰ© ÙˆŰ§Ù„Ű„ŰźÙŰ§ŰĄ"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"نقله Ű„Ù„Ù‰ ۟ۧ۱ۏ Ű§Ù„Ű­Ű§ÙŰ© ÙˆŰ„ŰžÙ‡Ű§Ű±Ù‡"</string>
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"Ű„ÙŠÙ‚Ű§Ù/ŰȘفŰčيل"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Ű§Ù„ŰȘŰ­ÙƒÙ… ŰšŰ§Ù„ŰŹÙ‡Ű§ŰČ"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"ۧ۟ŰȘÙŠŰ§Ű± ŰȘŰ·ŰšÙŠÙ‚ Ù„Ű„Ű¶Ű§ÙŰ© ŰčÙ†Ű§Ű”Ű± Ű§Ù„ŰȘŰ­ÙƒÙ‘Ù…"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{ŰȘمŰȘ Ű„Ű¶Ű§ÙŰ© ŰčÙ†Ű”Ű± ŰȘŰ­ÙƒÙ‘Ù… ÙˆŰ§Ű­ŰŻ.}zero{ŰȘمŰȘ Ű„Ű¶Ű§ÙŰ© # ŰčÙ†Ű”Ű± ŰȘŰ­ÙƒÙ‘Ù….}two{ŰȘمŰȘ Ű„Ű¶Ű§ÙŰ© ŰčÙ†Ű”Ű±ÙŽÙŠ ŰȘŰ­ÙƒÙ‘Ù….}few{ŰȘمŰȘ Ű„Ű¶Ű§ÙŰ© # ŰčÙ†Ű§Ű”Ű± ŰȘŰ­ÙƒÙ‘Ù….}many{ŰȘمŰȘ Ű„Ű¶Ű§ÙŰ© # ŰčÙ†Ű”Ű± ŰȘŰ­ÙƒÙ‘Ù….}other{ŰȘمŰȘ Ű„Ű¶Ű§ÙŰ© # ŰčÙ†Ű”Ű± ŰȘŰ­ÙƒÙ‘Ù….}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"ŰȘمŰȘ Ű§Ù„Ű„ŰČŰ§Ù„Ű©"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"هل ŰȘŰ±ÙŠŰŻ Ű„Ű¶Ű§ÙŰ© \"<xliff:g id="APPNAME">%s</xliff:g>\"۟"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"ŰčÙ†ŰŻ Ű„Ű¶Ű§ÙŰ© ŰȘŰ·ŰšÙŠÙ‚ \"<xliff:g id="APPNAME">%s</xliff:g>\"ی يمكنه Ű„Ű¶Ű§ÙŰ© ŰčÙ†Ű§Ű”Ű± ŰȘŰ­ÙƒÙ‘Ù… ÙˆÙ…Ű­ŰȘوى Ű„Ù„Ù‰ Ù‡Ű°Ù‡ Ű§Ù„Ù„ÙˆŰ­Ű©. في ŰšŰč۶ Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚Ű§ŰȘی يمكنك ۧ۟ŰȘÙŠŰ§Ű± ŰčÙ†Ű§Ű”Ű± Ű§Ù„ŰȘŰ­ÙƒÙ‘Ù… Ű§Ù„ŰȘي ŰȘŰžÙ‡Ű± Ù‡Ù†Ű§."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"ŰȘمŰȘ Ű§Ù„Ű„Ű¶Ű§ÙŰ© Ű„Ù„Ù‰ Ű§Ù„Ù…ÙŰ¶Ù‘Ù„Ű©"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"ŰȘمŰȘ Ű§Ù„Ű„Ű¶Ű§ÙŰ© Ű„Ù„Ù‰ Ű§Ù„Ù…ÙŰ¶Ù‘Ù„Ű©ŰŒ Ű§Ù„Ù…ÙˆŰ¶Űč <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"ŰȘمŰȘ Ű§Ù„Ű„ŰČŰ§Ù„Ű© من Ű§Ù„Ù…ÙŰ¶Ù‘Ù„Ű©"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"فŰȘŰ­ <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"ŰȘŰŽŰșيل <xliff:g id="SONG_NAME">%1$s</xliff:g> Ù„Ù„ÙÙ†Ű§Ù† <xliff:g id="ARTIST_NAME">%2$s</xliff:g> من ŰȘŰ·ŰšÙŠÙ‚ <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"ŰȘŰŽŰșيل <xliff:g id="SONG_NAME">%1$s</xliff:g> من ŰȘŰ·ŰšÙŠÙ‚ <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Ű§Ù‚ŰȘ۱ۭۧۧŰȘ لك"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"ŰȘ۱ۧۏŰč"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Űčليك Ű§Ù„Ű§Ù‚ŰȘ۱ۧۚ لŰȘŰŽŰșيل Ű§Ù„ÙˆŰłŰ§ŰŠŰ· Űčلى <xliff:g id="DEVICENAME">%1$s</xliff:g>."</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"للŰȘŰŽŰșيل Ù‡Ù†Ű§ŰŒ Űčليك Ű§Ù„Ű§Ù‚ŰȘ۱ۧۚ من \"<xliff:g id="DEVICENAME">%1$s</xliff:g>\"."</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Ű­ŰŻŰ« ۟۷ۣ. ÙŠÙŰ±ŰŹÙ‰ Ű„Űčۧۯ۩ Ű§Ù„Ù…Ű­Ű§ÙˆÙ„Ű©."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"ŰŹŰ§Ű±Ù Ű§Ù„ŰȘŰ­Ù…ÙŠÙ„"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ŰŹÙ‡Ű§ŰČ Ù„ÙˆŰ­ÙŠ"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"ŰšŰ«Ù‘ Ű§Ù„ÙˆŰłŰ§ŰŠŰ·"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"ŰŹŰ§Ű±Ù ŰšŰ«Ù‘ \"<xliff:g id="APP_LABEL">%1$s</xliff:g>\""</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ŰșÙŠŰ± Ù†ŰŽŰ·ŰŒ ŰȘŰ­Ù‚Ù‘Ù‚ من Ű§Ù„ŰȘŰ·ŰšÙŠÙ‚."</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"لم يŰȘم Ű§Ù„ŰčŰ«ÙˆŰ± Űčليه."</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"ŰčÙ†Ű”Ű± Ű§Ù„ŰȘŰ­ÙƒÙ‘Ù… ŰșÙŠŰ± مŰȘÙˆÙÙ‘Ű±"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Ű­ŰŻŰ« ŰźŰ·ŰŁŰŒ ÙŠÙŰ±ŰŹÙ‰ Ű„Űčۧۯ۩ Ű§Ù„Ù…Ű­Ű§ÙˆÙ„Ű©."</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Ű„Ű¶Ű§ÙŰ© ŰčÙ†Ű§Ű”Ű± ŰȘŰ­ÙƒÙ‘Ù…"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"ŰȘŰčŰŻÙŠÙ„ ŰčÙ†Ű§Ű”Ű± Ű§Ù„ŰȘŰ­ÙƒÙ‘Ù…"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Ű„Ű¶Ű§ÙŰ© ŰȘŰ·ŰšÙŠÙ‚"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Ű„Ű¶Ű§ÙŰ© Ù…ŰźŰ±ŰŹŰ§ŰȘ"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Ù…ŰŹÙ…ÙˆŰčŰ©"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"ŰȘم ۧ۟ŰȘÙŠŰ§Ű± ŰŹÙ‡Ű§ŰČ ÙˆŰ§Ű­ŰŻ."</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"%%<xliff:g id="PERCENTAGE">%1$d</xliff:g>"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Ù…ÙƒŰšÙ‘Ű±Ű§ŰȘ Ű§Ù„Ű”ÙˆŰȘ ÙˆŰ§Ù„ŰŽŰ§ŰŽŰ§ŰȘ"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Ű§Ù„ŰŁŰŹÙ‡ŰČŰ© Ű§Ù„Ù…Ù‚ŰȘŰ±ÙŽŰ­Ű©"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"ÙŠŰŹŰš ۧ۳ŰȘŰźŰŻŰ§Ù… ۭ۳ۧۚ Ù…ŰŻÙÙˆŰč."</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ÙƒÙŠÙÙŠŰ© Űčمل Ű§Ù„ŰšŰ«"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Ű§Ù„ŰšŰ«"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"يمكن Ù„Ù„ŰŁŰŽŰźŰ§Ű” Ű§Ù„Ù‚Ű±ÙŠŰšÙŠÙ† منك Ű§Ù„Ű°ÙŠÙ† Ù„ŰŻÙŠÙ‡Ù… ŰŁŰŹÙ‡ŰČŰ© مŰȘÙˆŰ§ÙÙ‚Ű© ŰȘŰȘŰ¶Ù…Ù‘Ù† ŰšÙ„ÙˆŰȘÙˆŰ« Ű§Ù„Ű§ŰłŰȘÙ…Ű§Űč Ű„Ù„Ù‰ Ű§Ù„ÙˆŰłŰ§ŰŠŰ· Ű§Ù„ŰȘي ŰȘŰšŰ«Ù‡Ű§."</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"يŰȘŰčŰ°Ù‘Ű± Ű§Ù„ŰšŰ«"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Ù„Ű§ يمكن ۄۏ۱ۧۥ Ű§Ù„Ű­ÙŰž. ÙŠÙŰ±ŰŹÙ‰ Ű„Űčۧۯ۩ Ű§Ù„Ù…Ű­Ű§ÙˆÙ„Ű©."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Ù„Ű§ يمكن ۄۏ۱ۧۥ Ű§Ù„Ű­ÙŰž."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Ű±Ù‚Ù… Ű§Ù„Ű„Ű”ŰŻŰ§Ű±"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"ŰȘم Ù†ŰłŰź Ű±Ù‚Ù… Ű§Ù„Ű„Ű”ŰŻŰ§Ű± Ű„Ù„Ù‰ Ű§Ù„Ű­Ű§ÙŰžŰ©."</string>
     <string name="basic_status" msgid="2315371112182658176">"Ù…Ű­Ű§ŰŻŰ«Ű© مفŰȘÙˆŰ­Ű©"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• ŰȘÙˆÙÙÙ‘Ű± ŰŹÙ‡Ű§ŰČ ÙˆŰ§Ű­ŰŻ Űčلى Ű§Ù„ŰŁÙ‚Ù„"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Ű§Ù†Ù‚Ű± مŰč Ű§Ù„Ű§ŰłŰȘÙ…Ű±Ű§Ű± Űčلى Ű§Ù„Ű§ŰźŰȘ۔ۧ۱."</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Ű„Ù„Űșۧۥ"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Ù‚Ù„Űš Ű§Ù„ŰŹÙ‡Ű§ŰČ Ű§Ù„ŰąÙ†"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Űčليك فŰȘŰ­ Ű§Ù„Ù‡Ű§ŰȘف Ù„Ű§Ù„ŰȘÙ‚Ű§Ű· Ű”ÙˆŰ±Ű© ۰ۧŰȘÙŠŰ© ŰšŰŽÙƒÙ„ ŰŁÙŰ¶Ù„."</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"ŰŁŰȘŰ±ÙŠŰŻ ۧ۳ŰȘŰźŰŻŰ§Ù… Ű§Ù„ÙƒŰ§Ù…ÙŠŰ±Ű§ Ű§Ù„ŰŁÙ…Ű§Ù…ÙŠŰ© Ù„Ű”ÙˆŰ±Ű© ۰ۧŰȘÙŠŰ© ŰŁÙŰ¶Ù„ŰŸ"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"ۧ۳ŰȘŰźŰŻÙÙ… Ű§Ù„ÙƒŰ§Ù…ÙŠŰ±Ű§ Ű§Ù„ŰźÙ„ÙÙŠŰ© Ù„Ű§Ù„ŰȘÙ‚Ű§Ű· Ű”ÙˆŰ±Ű© ŰŁŰč۱۶ ÙˆŰšŰŻŰ±ŰŹŰ© ŰŻÙ‚Ű© ŰŁŰčلى."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"* ŰłÙŠŰȘم Ű„Ű·ÙŰ§ŰĄ Ù‡Ű°Ù‡ Ű§Ù„ŰŽŰ§ŰŽŰ©."</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ŰŹÙ‡Ű§ŰČ Ù‚Ű§ŰšÙ„ Ù„Ù„Ű·ÙŠ ÙŠŰŹŰ±ÙŠ فŰȘŰ­Ù‡"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ŰŹÙ‡Ű§ŰČ Ù‚Ű§ŰšÙ„ Ù„Ù„Ű·ÙŠ ÙŠŰŹŰ±ÙŠ Ù‚Ù„ŰšÙ‡"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Ű§Ù„Ù†ŰłŰšŰ© Ű§Ù„Ù…ŰŠÙˆÙŠŰ© Ű§Ù„Ù…ŰȘŰšÙ‚ÙŠŰ© من ŰŽŰ­Ù† Ű§Ù„ŰšŰ·Ű§Ű±ÙŠŰ©: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Űčليك ŰȘÙˆŰ”ÙŠÙ„ قلم Ű§Ù„ŰŽŰ§ŰŽŰ© ŰšŰŽŰ§Ű­Ù†."</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"ŰšŰ·Ű§Ű±ÙŠŰ© قلم Ű§Ù„ŰŽŰ§ŰŽŰ© Ù…Ù†ŰźÙŰ¶Ű©"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"ÙƒŰ§Ù…ÙŠŰ±Ű§ ÙÙŠŰŻÙŠÙˆ"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Ù„Ű§ يمكن Ű§Ù„Ű§ŰȘŰ”Ű§Ù„ ۚۧ۳ŰȘŰźŰŻŰ§Ù… Ù‡Ű°Ű§ Ű§Ù„Ù…Ù„Ù Ű§Ù„ŰŽŰźŰ”ÙŠ."</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"ŰȘŰłÙ…Ű­ لك ŰłÙŠŰ§ŰłŰ© Ű§Ù„Űčمل ۚۄۏ۱ۧۥ Ű§Ù„Ù…ÙƒŰ§Ù„Ù…Ű§ŰȘ Ű§Ù„Ù‡Ű§ŰȘÙÙŠŰ© من Ű§Ù„Ù…Ù„Ù Ű§Ù„ŰŽŰźŰ”ÙŠ للŰčمل ÙÙ‚Ű·."</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Ű§Ù„ŰȘŰšŰŻÙŠÙ„ Ű„Ù„Ù‰ Ű§Ù„Ù…Ù„Ù Ű§Ù„ŰŽŰźŰ”ÙŠ للŰčمل"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Ű„ŰșÙ„Ű§Ù‚"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Ű„ŰčۯۧۯۧŰȘ ێۧێ۩ Ű§Ù„Ù‚ÙÙ„"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ar/tiles_states_strings.xml b/packages/SystemUI/res/values-ar/tiles_states_strings.xml
index 44b58f9..b88d097 100644
--- a/packages/SystemUI/res/values-ar/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ar/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"ŰșÙŠŰ± مفŰčÙ‘Ù„Ű©"</item>
     <item msgid="5966994759929723339">"مفŰčÙŽÙ‘Ù„Ű©"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index fa04cb8..2f868ec 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"<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>
     <string name="screenrecord_name" msgid="2596401223859996572">"àŠžà§àŠ•à§à§°à§€àŠš à§°à§‡àŠ•à§°à§àŠĄàŠŸà§°"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"àŠžà§àŠ•à§àŠ°à§€àŠš à§°à§‡àŠ•à§°à§àŠĄàŠżàŠ™à§° àŠȘà§à§°àŠ•à§à§°àŠżàŠŻàŠŒàŠŸàŠ•à§°àŠŁ àŠčৈ àŠ†àŠ›à§‡"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"àŠžà§àŠ•à§àŠ°à§€àŠš à§°à§‡àŠ•à§°à§àŠĄàŠżàŠ‚ àŠ›à§‡àŠ¶à§àŠŹàŠš àŠšàŠČàŠż àŠ„àŠ•àŠŸ àŠžàŠźàŠŻàŠŒàŠ€ àŠȘà§‹à§±àŠŸ àŠœàŠŸàŠšàŠšà§€"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"àŠ‰àŠœà§àŠœà§àŠŹàŠČàŠ€àŠŸ"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"à§°àŠ‚ àŠŹàŠżàŠȘà§°à§€àŠ€àŠ•à§°àŠŁ"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"à§°àŠ‚ àŠ¶à§àŠ§à§°àŠŁà§€"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"àŠŹà§àŠŻà§±àŠčàŠŸà§°àŠ•àŠŸà§°à§€ àŠȘà§°àŠżàŠšàŠŸàŠČàŠšàŠŸ àŠ•à§°àŠ•"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"àŠžàŠźà§àŠȘàŠšà§àŠš àŠ•à§°àŠŸ àŠč’àŠČ"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"àŠŹàŠšà§àŠ§ àŠ•à§°àŠ•"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"àŠžà§àŠŹàŠŻàŠŒàŠ‚àŠ•à§à§°àŠżàŠŻàŠŒ"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"àŠ•à§‹àŠšà§‹ àŠ§à§àŠŹàŠšàŠż àŠ…àŠ„àŠŹàŠŸ àŠ•àŠźà§àŠȘàŠš àŠšàŠŸàŠ‡"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"àŠ•à§‹àŠšà§‹ àŠ§à§àŠŹàŠšàŠż àŠ…àŠ„àŠŹàŠŸ àŠ•àŠźà§àŠȘàŠš àŠšàŠŸàŠ‡ àŠ†à§°à§ àŠŹàŠŸà§°à§àŠ€àŠŸàŠČàŠŸàŠȘ àŠ¶àŠŸàŠ–àŠŸàŠŸà§‹à§° àŠ€àŠČà§° àŠ…àŠ‚àŠ¶àŠ€ àŠŠà§‡àŠ–àŠŸ àŠȘà§‹à§±àŠŸ àŠŻàŠŸàŠŻàŠŒ"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"àŠĄàŠżàŠ­àŠŸàŠ‡àŠšà§° àŠ›à§‡àŠŸàŠżàŠ™à§° àŠ“àŠȘà§°àŠ€ àŠšàŠżà§°à§àŠ­à§° àŠ•à§°àŠż à§°àŠżàŠ‚ àŠ•à§°àŠżàŠŹ àŠ…àŠ„àŠŹàŠŸ àŠ•àŠźà§àŠȘàŠš àŠč’àŠŹ àŠȘàŠŸà§°à§‡"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"àŠĄàŠżàŠ­àŠŸàŠ‡àŠšà§° àŠ›à§‡àŠŸàŠżàŠ™à§° àŠ“àŠȘà§°àŠ€ àŠšàŠżà§°à§àŠ­à§° àŠ•à§°àŠż à§°àŠżàŠ‚ àŠ•à§°àŠżàŠŹ àŠ…àŠ„àŠŹàŠŸ àŠ•àŠźà§àŠȘàŠš àŠč’àŠŹ àŠȘàŠŸà§°à§‡à„€ <xliff:g id="APP_NAME">%1$s</xliff:g>à§° àŠŹàŠŸà§°à§àŠ€àŠŸàŠČàŠŸàŠȘ àŠĄàŠżàŠ«’àŠČà§àŠŸàŠ­àŠŸà§±à§‡ àŠŹàŠŸàŠŹàŠČ àŠčàŠżàŠšàŠŸàŠȘে àŠȘà§à§°àŠŠà§°à§àŠ¶àŠżàŠ€ àŠčàŠŻàŠŒà„€"</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"àŠàŠ‡ àŠœàŠŸàŠšàŠšà§€àŠŸà§‹à§±à§‡ àŠ§à§àŠŹàŠšàŠż àŠšà§‡ àŠ•àŠźà§àŠȘàŠš àŠžà§ƒàŠ·à§àŠŸàŠż àŠ•à§°àŠżàŠŹ àŠžà§‡àŠŻàŠŒàŠŸ àŠ›àŠżàŠ·à§àŠŸà§‡àŠźàŠŸà§‹àŠ• àŠšàŠżà§°à§àŠ§àŠŸà§°àŠŁ àŠ•à§°àŠżàŠŹàŠČৈ àŠŠàŠżàŠŻàŠŒàŠ•"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;àŠžà§àŠ„àŠżàŠ€àŠż:&lt;/b&gt; àŠĄàŠżàŠ«’àŠČà§àŠŸàŠČৈ àŠŹà§ƒàŠŠà§àŠ§àŠż àŠ•à§°àŠŸ àŠčà§ˆàŠ›à§‡"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;àŠžà§àŠ„àŠżàŠ€àŠż:&lt;/b&gt; àŠšà§€à§°à§±àŠČৈ àŠčà§à§°àŠŸàŠž àŠ•à§°àŠŸ àŠčà§ˆàŠ›à§‡"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"àŠžà§àŠ•à§à§°à§€àŠš à§°à§‡àŠ•à§°à§àŠĄàŠżàŠ‚"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"àŠ•à§‹àŠšà§‹ àŠ¶àŠżà§°à§‹àŠšàŠŸàŠź àŠšàŠŸàŠ‡"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"àŠ·à§àŠŸà§‡àŠŁà§àŠĄàŠŹàŠŸàŠ‡"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"àŠŹàŠżàŠŹà§°à§àŠ§àŠš à§±àŠżàŠŁà§àŠĄ’"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"àŠŹàŠżàŠŹà§°à§àŠ§àŠš à§±àŠżàŠŁà§àŠĄ’à§° àŠšàŠżàŠŻàŠŒàŠšà§àŠ€à§à§°àŠŁàŠžàŠźà§‚àŠč"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"àŠœà§àŠź àŠ‡àŠš àŠ•à§°àŠ•"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"àŠšàŠżàŠŻàŠŒàŠšà§àŠ€à§à§°àŠŁàŠžàŠźà§‚àŠč àŠŻà§‹àŠ— àŠ•à§°àŠżàŠŹàŠČৈ àŠàŠȘ্‌ àŠŹàŠŸàŠ›àŠšàŠż àŠ•à§°àŠ•"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# àŠŸàŠŸ àŠšàŠżàŠŻàŠŒàŠšà§àŠ€à§à§°àŠŁ àŠŻà§‹àŠ— àŠŠàŠżàŠŻàŠŒàŠŸ àŠčà§ˆàŠ›à§‡à„€}one{# àŠŸàŠŸ àŠšàŠżàŠŻàŠŒàŠšà§àŠ€à§à§°àŠŁ àŠŻà§‹àŠ— àŠŠàŠżàŠŻàŠŒàŠŸ àŠčà§ˆàŠ›à§‡à„€}other{# àŠŸàŠŸ àŠšàŠżàŠŻàŠŒàŠšà§àŠ€à§à§°àŠŁ àŠŻà§‹àŠ— àŠŠàŠżàŠŻàŠŒàŠŸ àŠčà§ˆàŠ›à§‡à„€}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"àŠ†àŠàŠ€à§°à§‹à§±àŠŸ àŠč’àŠČ"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g> àŠŻà§‹àŠ— àŠŠàŠżàŠŹàŠšà§‡?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"àŠ†àŠȘà§àŠšàŠż <xliff:g id="APPNAME">%s</xliff:g> àŠŻà§‹àŠ— àŠŠàŠżàŠČে, àŠ‡ àŠàŠ‡ àŠȘà§‡àŠšà§‡àŠČàŠ€ àŠšàŠżàŠŻàŠŒàŠšà§àŠ€à§à§°àŠŁ àŠ†à§°à§ àŠžàŠźàŠČ àŠŻà§‹àŠ— àŠŠàŠżàŠŹ àŠȘàŠŸà§°à§‡à„€ àŠ•àŠżàŠ›à§àŠźàŠŸàŠš àŠàŠȘàŠ€ àŠ†àŠȘà§àŠšàŠż àŠ•à§‹àŠšàŠŹà§‹à§° àŠšàŠżàŠŻàŠŒàŠšà§àŠ€à§à§°àŠŁ àŠ‡àŠŻàŠŒàŠŸàŠ€ àŠŠà§‡àŠ–àŠŸ àŠȘà§‹à§±àŠŸ àŠŻàŠŸàŠŹ àŠžà§‡àŠŻàŠŒàŠŸ àŠŹàŠŸàŠ›àŠšàŠż àŠ•à§°àŠżàŠŹ àŠȘàŠŸà§°à§‡à„€"</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"àŠȘà§à§°àŠżàŠŻàŠŒ àŠčàŠżàŠšàŠŸàŠȘে àŠšàŠżàŠčà§àŠšàŠżàŠ€ àŠ•à§°àŠŸ àŠč’àŠČ"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"àŠȘà§à§°àŠżàŠŻàŠŒ àŠčàŠżàŠšàŠŸàŠȘে àŠšàŠżàŠčà§àŠšàŠżàŠ€ àŠ•à§°àŠŸ àŠč’àŠČ, àŠžà§àŠ„àŠŸàŠš <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"àŠ…àŠȘà§à§°àŠżàŠŻàŠŒ àŠčàŠżàŠšàŠŸàŠȘে àŠšàŠżàŠčà§àŠšàŠżàŠ€ àŠ•à§°àŠŸ àŠč’àŠČ"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> àŠ–à§‹àŠČàŠ•"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g>àŠ€ <xliff:g id="ARTIST_NAME">%2$s</xliff:g>à§° <xliff:g id="SONG_NAME">%1$s</xliff:g> àŠ—à§€àŠ€àŠŸà§‹ àŠȘ্àŠČে’ àŠ•à§°àŠ•"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g>àŠ€ <xliff:g id="SONG_NAME">%1$s</xliff:g> àŠ—à§€àŠ€àŠŸà§‹ àŠȘ্àŠČে’ àŠ•à§°àŠ•"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"àŠ†àŠȘà§‹àŠšàŠŸà§° àŠŹàŠŸàŠŹà§‡"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"àŠ†àŠšàŠĄà§ àŠ•à§°àŠ•"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g>àŠ€ àŠȘ্àŠČে’ àŠ•à§°àŠżàŠŹàŠČৈ àŠ“àŠšà§°àŠČৈ àŠŻàŠŸàŠ“àŠ•"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"àŠ‡àŠŻàŠŒàŠŸàŠ€ àŠȘ্àŠČে’ àŠ•à§°àŠżàŠŹàŠČৈ, <xliff:g id="DEVICENAME">%1$s</xliff:g>à§° àŠ“àŠšà§°àŠČৈ àŠŻàŠŸàŠ“àŠ•"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"àŠ•àŠżàŠŹàŠŸ àŠ­à§àŠČ àŠč’àŠČà„€ àŠȘà§àŠšà§° àŠšà§‡àŠ·à§àŠŸàŠŸ àŠ•à§°àŠ•à„€"</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"àŠČ’àŠĄ àŠčৈ àŠ†àŠ›à§‡"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"àŠŸà§‡àŠŹàŠČà§‡àŠŸ"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"àŠ†àŠȘà§‹àŠšàŠŸà§° àŠźàŠżàŠĄàŠżàŠŻàŠŒàŠŸ àŠ•àŠŸàŠ·à§àŠŸ àŠ•à§°àŠż àŠ„àŠ•àŠŸ àŠčà§ˆàŠ›à§‡"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"<xliff:g id="APP_LABEL">%1$s</xliff:g> àŠ•àŠŸàŠ·à§àŠŸ àŠ•à§°àŠż àŠ„àŠ•àŠŸ àŠčà§ˆàŠ›à§‡"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"àŠžàŠ•à§à§°àŠżàŠŻàŠŒ àŠšàŠčàŠŻàŠŒ, àŠàŠȘ্‌àŠŸà§‹ àŠȘà§°à§€àŠ•à§àŠ·àŠŸ àŠ•à§°àŠ•"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"àŠŹàŠżàŠšàŠŸà§°àŠż àŠȘà§‹à§±àŠŸ àŠšàŠ—’àŠČ"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"àŠšàŠżàŠŻàŠŒàŠšà§àŠ€à§à§°àŠŁàŠŸà§‹ àŠ‰àŠȘàŠČàŠŹà§àŠ§ àŠšàŠčàŠŻàŠŒ"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"àŠ†àŠžà§‹àŠà§±àŠŸàŠč àŠčà§ˆàŠ›à§‡, àŠ†àŠ•à§Œ àŠšà§‡àŠ·à§àŠŸàŠŸ àŠ•à§°àŠ•"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"àŠšàŠżàŠŻàŠŒàŠšà§àŠ€à§à§°àŠŁàŠžàŠźà§‚àŠč àŠŻà§‹àŠ— àŠŠàŠżàŠŻàŠŒàŠ•"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"àŠšàŠżàŠŻàŠŒàŠšà§àŠ€à§à§°àŠŁàŠžàŠźà§‚àŠč àŠžàŠźà§àŠȘàŠŸàŠŠàŠšàŠŸ àŠ•à§°àŠ•"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"àŠàŠȘ্‌ àŠŻà§‹àŠ— àŠŠàŠżàŠŻàŠŒàŠ•"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"àŠ†àŠ‰àŠŸàŠȘà§àŠŸàŠžàŠźà§‚àŠč àŠŻà§‹àŠ— àŠŠàŠżàŠŻàŠŒàŠ•"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"àŠ—à§‹àŠŸ"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"à§§ àŠŸàŠŸ àŠĄàŠżàŠ­àŠŸàŠ‡àŠš àŠŹàŠŸàŠ›àŠšàŠż àŠ•à§°àŠŸ àŠčà§ˆàŠ›à§‡"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"àŠžà§àŠȘà§€àŠ•àŠŸà§° àŠ†à§°à§ àŠĄàŠżàŠ›àŠȘ্àŠČে’"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"àŠȘà§°àŠŸàŠźà§°à§àŠ¶ àŠčàŠżàŠšàŠŸàŠȘে àŠȘà§‹à§±àŠŸ àŠĄàŠżàŠ­àŠŸàŠ‡àŠš"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Premium àŠàŠ•àŠŸàŠ‰àŠŁà§àŠŸà§° àŠ†à§±àŠ¶à§àŠŻàŠ•"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"àŠžàŠźà§àŠȘà§à§°àŠšàŠŸà§° àŠ•à§°àŠŸàŠŸà§‹à§±à§‡ àŠ•à§‡àŠšà§‡àŠ•à§ˆ àŠ•àŠŸàŠź àŠ•à§°à§‡"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"àŠžàŠźà§àŠȘà§à§°àŠšàŠŸà§°"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"àŠžàŠźàŠżàŠČ àŠŹà§àŠČà§àŠŸà§àŠ„ àŠĄàŠżàŠ­àŠŸàŠ‡àŠšà§° àŠžà§ˆàŠ€à§‡ àŠ†àŠȘà§‹àŠšàŠŸà§° àŠšàŠżàŠ•àŠŸà§±à§°à§àŠ€à§€ àŠžà§àŠ„àŠŸàŠšàŠ€ àŠ„àŠ•àŠŸ àŠČà§‹àŠ•àŠžàŠ•àŠČে àŠ†àŠȘà§àŠšàŠż àŠžàŠźà§àŠȘà§à§°àŠšàŠŸà§° àŠ•à§°àŠŸ àŠźàŠżàŠĄàŠżàŠŻàŠŒàŠŸàŠŸà§‹ àŠ¶à§àŠšàŠżàŠŹ àŠȘàŠŸà§°à§‡"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"àŠžàŠźà§àŠȘà§à§°àŠšàŠŸà§° àŠ•à§°àŠżàŠŹ àŠšà§‹à§±àŠŸà§°àŠż"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"àŠ›à§‡àŠ­ àŠ•à§°àŠżàŠŹ àŠšà§‹à§±àŠŸà§°àŠżà„€ àŠȘà§àŠšà§° àŠšà§‡àŠ·à§àŠŸàŠŸ àŠ•à§°àŠ•à„€"</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"àŠ›à§‡àŠ­ àŠ•à§°àŠżàŠŹ àŠšà§‹à§±àŠŸà§°àŠżà„€"</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"àŠŹàŠżàŠČà§àŠĄà§° àŠšàŠźà§àŠŹà§°"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"àŠ•à§àŠČàŠżàŠȘàŠŹ’à§°à§àŠĄàŠČৈ àŠŹàŠżàŠČà§àŠĄà§° àŠšàŠźà§àŠŹà§° àŠȘà§à§°àŠ€àŠżàŠČàŠżàŠȘàŠż àŠ•à§°àŠŸ àŠč’àŠČà„€"</string>
     <string name="basic_status" msgid="2315371112182658176">"àŠŹàŠŸà§°à§àŠ€àŠŸàŠČàŠŸàŠȘ àŠ–à§‹àŠČàŠ•"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• àŠ…àŠ€àŠż àŠ•àŠźà§‡àŠ“ àŠàŠŸàŠŸ àŠĄàŠżàŠ­àŠŸàŠ‡àŠš àŠ‰àŠȘàŠČàŠŹà§àŠ§"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"àŠ¶à§àŠŹà§°à§àŠŸàŠ•àŠŸàŠŸàŠŸà§‹àŠ€ àŠžà§àŠȘà§°à§àŠ¶ àŠ•à§°àŠż àŠ§à§°àŠż à§°àŠŸàŠ–àŠ•"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"àŠŹàŠŸàŠ€àŠżàŠČ àŠ•à§°àŠ•"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"àŠàŠ€àŠżàŠŻàŠŒàŠŸàŠ‡ àŠČà§àŠŸàŠżàŠŻàŠŒàŠŸàŠ‡ àŠŠàŠżàŠŻàŠŒàŠ•"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"àŠ‰àŠšà§àŠšàŠ€ àŠ›à§‡àŠČà§àŠ«àŠżà§° àŠŹàŠŸàŠŹà§‡ àŠ«’àŠšàŠŸà§‹ àŠ†àŠšàŠ«’àŠČà§àŠĄ àŠ•à§°àŠ•"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"àŠ‰àŠšà§àŠšàŠ€ àŠ›à§‡àŠČà§àŠ«àŠżà§° àŠŹàŠŸàŠŹà§‡ àŠžàŠšà§àŠźà§àŠ–à§° àŠĄàŠżàŠ›àŠȘ্àŠČে’ àŠČà§àŠŸàŠżàŠŻàŠŒàŠŸàŠ‡ àŠŠàŠżàŠŹàŠšà§‡?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"àŠ…àŠ§àŠżàŠ• à§°àŠżàŠœ’àŠČàŠżàŠ‰àŠ¶à§àŠŹàŠšà§° àŠŹàŠčàŠČ àŠ«àŠŸ’à§° àŠŹàŠŸàŠŹà§‡ àŠȘàŠżàŠ›àŠ«àŠŸàŠČে àŠ„àŠ•àŠŸ àŠ•à§‡àŠźà§‡à§°àŠŸàŠŸà§‹ àŠŹà§àŠŻà§±àŠčàŠŸà§° àŠ•à§°àŠ•à„€"</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ àŠ‡ àŠžà§àŠ•à§à§°à§€àŠšàŠ–àŠš àŠ…àŠ« àŠč’àŠŹ"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"àŠœàŠȘàŠŸàŠŹ àŠȘà§°àŠŸ àŠĄàŠżàŠ­àŠŸàŠ‡àŠšà§° àŠœàŠŸàŠȘ àŠ–à§àŠČàŠż àŠ„àŠ•àŠŸ àŠčà§ˆàŠ›à§‡"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"àŠœàŠȘàŠŸàŠŹ àŠȘà§°àŠŸ àŠĄàŠżàŠ­àŠŸàŠ‡àŠšà§° àŠ“àŠČà§‹àŠŸàŠŸàŠ‡ àŠ„àŠ•àŠŸ àŠčà§ˆàŠ›à§‡"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> àŠŹà§‡àŠŸàŠŸà§°à§€ àŠŹàŠŸàŠ•à§€ àŠ†àŠ›à§‡"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"àŠ†àŠȘà§‹àŠšàŠŸà§° àŠ·à§àŠŸàŠŸàŠ‡àŠČàŠŸàŠ› àŠàŠŸàŠŸ àŠšàŠŸà§°à§àŠœàŠŸà§°à§° àŠžà§ˆàŠ€à§‡ àŠžàŠ‚àŠŻà§‹àŠ— àŠ•à§°àŠ•"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"àŠ·à§àŠŸàŠŸàŠ‡àŠČàŠŸàŠ›à§° àŠŹà§‡àŠŸàŠŸà§°à§€ àŠ•àŠź àŠ†àŠ›à§‡"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"àŠ­àŠżàŠĄàŠżàŠ…’ àŠ•à§‡àŠźà§‡à§°àŠŸ"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"àŠàŠ‡ àŠȘ্ৰ’àŠ«àŠŸàŠ‡àŠČà§° àŠȘà§°àŠŸ àŠ•àŠČ àŠ•à§°àŠżàŠŹ àŠšà§‹à§±àŠŸà§°àŠż"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"àŠ†àŠȘà§‹àŠšàŠŸà§° àŠ•à§°à§àŠźàŠžà§àŠ„àŠŸàŠšà§° àŠšà§€àŠ€àŠżàŠŻàŠŒà§‡ àŠ†àŠȘà§‹àŠšàŠŸàŠ• àŠ•à§‡à§±àŠČ àŠ•à§°à§àŠźàŠžà§àŠ„àŠŸàŠšà§° àŠȘ্ৰ’àŠ«àŠŸàŠ‡àŠČà§° àŠȘà§°àŠŸ àŠ«’àŠš àŠ•àŠČ àŠ•à§°àŠżàŠŹàŠČৈ àŠŠàŠżàŠŻàŠŒà§‡"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"àŠ•à§°à§àŠźàŠžà§àŠ„àŠŸàŠšà§° àŠȘ্ৰ’àŠ«àŠŸàŠ‡àŠČàŠČৈ àŠžàŠČàŠšàŠż àŠ•à§°àŠ•"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"àŠŹàŠšà§àŠ§ àŠ•à§°àŠ•"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"àŠČàŠ• àŠžà§àŠ•à§à§°à§€àŠšà§° àŠ›à§‡àŠŸàŠżàŠ‚"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-as/tiles_states_strings.xml b/packages/SystemUI/res/values-as/tiles_states_strings.xml
index 3145341..e7dc9b4 100644
--- a/packages/SystemUI/res/values-as/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-as/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"àŠ…àŠ« àŠ†àŠ›à§‡"</item>
     <item msgid="5966994759929723339">"àŠ…àŠš àŠ†àŠ›à§‡"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index e6b27e0..db80125 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Aßağı sərhəd <xliff:g id="PERCENT">%1$d</xliff:g> faiz"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Sol sərhəd <xliff:g id="PERCENT">%1$d</xliff:g> faiz"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Sağ sərhəd <xliff:g id="PERCENT">%1$d</xliff:g> faiz"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"İß skrinßotları <xliff:g id="APP">%1$s</xliff:g> tətbiqində saxlanılır"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"İß profilində <xliff:g id="APP">%1$s</xliff:g> tətbiqində saxlanıb"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Fayllar"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> bu skrinßotu aßkarladı."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> və digər açıq tətbiqlər bu skrinßotu aßkarladı."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Ekran Yazıcısı"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekran çəkilißi emal edilir"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ekranın video çəkimi ərzində silinməyən bildiriß"</string>
@@ -178,7 +180,7 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> üzərindən qoßuldu."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> cihazına qoßulub."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Qoßulu deyil."</string>
-    <string name="data_connection_roaming" msgid="375650836665414797">"Rominq"</string>
+    <string name="data_connection_roaming" msgid="375650836665414797">"Rouminq"</string>
     <string name="cell_data_off" msgid="4886198950247099526">"Deaktiv"</string>
     <string name="accessibility_airplane_mode" msgid="1899529214045998505">"Uçuß rejimi"</string>
     <string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN aktivdir."</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Parlaqlıq"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Rəng inversiyası"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Rəng korreksiyası"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"İstifadəçiləri idarə edin"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Hazır"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Bağlayın"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Avtomatik"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Səs və ya vibrasiya yoxdur"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Söhbət siyahısının aßağısında səssiz və vibrasiyasız görünür"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Cihaz ayarlarına əsasən zəng çala və ya vibrasiya edə bilər"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Cihaz ayarlarına əsasən zəng çala və ya vibrasiya edə bilər. <xliff:g id="APP_NAME">%1$s</xliff:g> tətbiqindən söhbətlərdə defolt olaraq qabarcıq çıxır."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Bu bildirißin səs çıxarması və ya vibrasiya etməsi sistem tərəfindən təyin edilsin"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Status:&lt;/b&gt; Defolt ayara keçirilib"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Status:&lt;/b&gt; Səssiz rejimə keçirilib"</string>
@@ -636,7 +638,7 @@
     <item msgid="2681220472659720036">"Mübadilə buferi"</item>
     <item msgid="4795049793625565683">"Açar kodu"</item>
     <item msgid="80697951177515644">"Çevirin, klaviatura dəyißdirici"</item>
-    <item msgid="7626977989589303588">"Heç bir"</item>
+    <item msgid="7626977989589303588">"Heç biri"</item>
   </string-array>
   <string-array name="nav_bar_layouts">
     <item msgid="9156773083127904112">"Normal"</item>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"ekran çəkimi"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Baßlıq yoxdur"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Gözləmə rejimi"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Böyütmə Pəncərəsi"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Böyütmə Pəncərəsi Kontrolları"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Yaxınlaßdırın"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Kontrol əlavə etmək üçün tətbiq seçin"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# nizamlayıcı əlavə edilib.}other{# nizamlayıcı əlavə edilib.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Silinib"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g> əlavə edilsin?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"<xliff:g id="APPNAME">%s</xliff:g> əlavə etdiyiniz zaman, o, bu panelə nizamlayıcılar və məzmun əlavə edə bilər. Bəzi tətbiqlərdə burada hansı nizamlayıcıların göstərilməsini seçə bilərsiniz."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Sevimlilərə əlavə edilib"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Sevimlilərə əlavə edilib, sıra: <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Sevimlilərdən silinib"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> tətbiqini açın"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> tərəfindən <xliff:g id="SONG_NAME">%1$s</xliff:g> mahnısını <xliff:g id="APP_LABEL">%3$s</xliff:g> tətbiqindən oxudun"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> mahnısını <xliff:g id="APP_LABEL">%2$s</xliff:g> tətbiqindən oxudun"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Sizin üçün"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Geri qaytarın"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> cihazında oxutmaq üçün yaxınlaßın"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Burada oxutmaq üçün <xliff:g id="DEVICENAME">%1$s</xliff:g> cihazına yaxınlaßdırın"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Xəta oldu. Yenə cəhd edin."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Yüklənir"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"planßet"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Medianız yayımlanır"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"<xliff:g id="APP_LABEL">%1$s</xliff:g> yayımlanır"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Aktiv deyil, tətbiqi yoxlayın"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Tapılmadı"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Nəzarət əlçatan deyil"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Xəta, yenidən cəhd edin"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Vidcet əlavə edin"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Vidcetlərə düzəliß edin"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Tətbiq əlavə edin"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Nəticələri əlavə edin"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Qrup"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 cihaz seçilib"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Dinamiklər &amp; Displeylər"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Təklif olunan Cihazlar"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Premium hesab tələb edilir"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Yayım necə ißləyir"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Yayım"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Uyğun Bluetooth cihazları olan yaxınlığınızdakı insanlar yayımladığınız medianı dinləyə bilər"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Yayımlamaq mümkün deyil"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Yadda saxlamaq mümkün deyil. Yenə cəhd edin."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Yadda saxlamaq mümkün deyil."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Montaj nömrəsi"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Versiya nömrəsi mübadilə buferinə kopyalandı."</string>
     <string name="basic_status" msgid="2315371112182658176">"Açıq söhbət"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Ən azı bir cihaz əlçatandır"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Qısayola toxunub saxlayın"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Ləğv edin"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"İndi fırladın"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Daha yaxßı selfi üçün telefonu açın"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Daha yaxßı selfi üçün ön displeyə çevrilsin?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Daha yüksək ayırdetmə dəqiqliyi ilə daha geniß Ɵəkil üçün arxaya baxan kameradan istifadə edin."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Bu ekran deaktiv ediləcək"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Qatlana bilən cihaz açılır"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Qatlana bilən cihaz fırladılır"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> enerji qalıb"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Qələmi adapterə qoƟun"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Qələm enerjisi azdır"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Bu profildən zəng etmək mümkün deyil"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"İß siyasətiniz yalnız iß profilindən telefon zəngləri etməyə imkan verir"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"İß profilinə keçin"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Bağlayın"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Kilid ekranı ayarları"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-az/tiles_states_strings.xml b/packages/SystemUI/res/values-az/tiles_states_strings.xml
index fb745b25..e0fcf89 100644
--- a/packages/SystemUI/res/values-az/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-az/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Deaktiv"</item>
     <item msgid="5966994759929723339">"Aktiv"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index fb95a91..494bcbe 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Donja ivica <xliff:g id="PERCENT">%1$d</xliff:g> posto"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Leva ivica <xliff:g id="PERCENT">%1$d</xliff:g> posto"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Desna ivica <xliff:g id="PERCENT">%1$d</xliff:g> posto"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Snimci ekrana za posao se čuvaju u aplikaciji <xliff:g id="APP">%1$s</xliff:g>"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Sačuvano je u aplikaciji <xliff:g id="APP">%1$s</xliff:g> na poslovnom profilu"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Fajlovi"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"Aplikacija <xliff:g id="APPNAME">%1$s</xliff:g> je otkrila ovaj snimak ekrana."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> i druge otvorene aplikacije su otkrile ovaj snimak ekrana."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Snimač ekrana"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Obrađujemo video snimka ekrana"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Obaveštenje o sesiji snimanja ekrana je aktivno"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Osvetljenost"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inverzija boja"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Korekcija boja"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Upravljajte korisnicima"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Gotovo"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Zatvori"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatska"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Bez zvuka i vibriranja"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Bez zvuka i vibriranja i prikazuje se u nastavku odeljka za konverzacije"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"MoĆŸe da zvoni ili vibrira u zavisnosti od podešavanja uređaja"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"MoĆŸe da zvoni ili vibrira u zavisnosti od podešavanja uređaja. Konverzacije iz aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g> podrazumevano se prikazuju u oblačićima."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Neka sistem utvrdi da li ovo obaveštenje treba da emituje zvuk ili da vibrira"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Status:&lt;/b&gt; Unapređeno u Podrazumevano"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Status:&lt;/b&gt; Degradirano u Nečujno"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"snimanje ekrana"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Bez naslova"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Stanje pripravnosti"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Prozor za uvećanje"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kontrole prozora za uvećanje"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Uvećajte"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Odaberite aplikaciju za dodavanje kontrola"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# kontrola je dodata.}one{# kontrola je dodata.}few{# kontrole su dodate.}other{# kontrola je dodato.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Uklonjeno"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Ćœelite li da dodate <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Kada dodate aplikaciju <xliff:g id="APPNAME">%s</xliff:g>, ona moĆŸe da dodaje kontrole i sadrĆŸaj u ovo okno. U nekim aplikacijama moĆŸete da izaberete koje će se kontrole ovde prikazivati."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Označeno je kao omiljeno"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Označeno je kao omiljeno, <xliff:g id="NUMBER">%d</xliff:g>. pozicija"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Uklonjeno je iz omiljenih"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Otvorite <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Pustite <xliff:g id="SONG_NAME">%1$s</xliff:g> izvođača <xliff:g id="ARTIST_NAME">%2$s</xliff:g> iz aplikacije <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Pustite <xliff:g id="SONG_NAME">%1$s</xliff:g> iz aplikacije <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Za vas"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Opozovi"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"PribliĆŸite da biste puštali muziku na: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Da biste puštali sadrĆŸaj ovde, pribliĆŸite uređaju <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Došlo je do greške. Probajte ponovo."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Učitava se"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Prebacivanje medija"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Prebacuje se <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Neaktivno. Vidite aplikaciju"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Nije pronađeno"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Kontrola nije dostupna"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Greška. Probajte ponovo"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Dodaj kontrole"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Izmeni kontrole"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Dodaj aplikaciju"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Dodajte izlaze"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupa"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Izabran je 1 uređaj"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Zvučnici i ekrani"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"PredloĆŸeni uređaji"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Zahteva premijum nalog"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kako funkcioniše emitovanje"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Emitovanje"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Ljudi u blizini sa kompatibilnim Bluetooth uređajima mogu da slušaju medijski sadrĆŸaj koji emitujete"</string>
@@ -894,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Emitovanje nije uspelo"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Čuvanje nije uspelo. Probajte ponovo."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Čuvanje nije uspelo."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Koristite bar 4 znaka"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Koristite manje od 16 znakova"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Broj verzije"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Broj verzije je kopiran u privremenu memoriju."</string>
     <string name="basic_status" msgid="2315371112182658176">"Otvorite konverzaciju"</string>
@@ -1013,24 +1030,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• da je dostupan barem jedan uređaj"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Dodirnite i zadrĆŸite prečicu"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"OtkaĆŸi"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Obrnite"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Otvorite telefon za bolji selfi"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Ćœelite da obrnete na prednji ekran za bolji selfi?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Koristite zadnju kameru da biste snimili širu sliku sa višom rezolucijom."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Ovaj ekran će se isključiti"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Uređaj na preklop se otvara"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Uređaj na preklop se obrće"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Preostalo je još<xliff:g id="PERCENTAGE">%s</xliff:g> baterije"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"PoveĆŸite pisaljku sa punjačem"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Nizak nivo baterije pisaljke"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Video kamera"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Ne moĆŸete da upućujete pozive sa ovog profila"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Smernice za posao vam omogućavaju da telefonirate samo sa poslovnog profila"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Pređi na poslovni profil"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Zatvori"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Podešavanja zaključanog ekrana"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
index b69b064..fd604b5 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Isključeno"</item>
     <item msgid="5966994759929723339">"Uključeno"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index f1d174d..62faafb 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"ĐŸŃ€Đ°Ń†ĐŸŃžĐœŃ‹Ń Đ·ĐŽŃ‹ĐŒĐșі эĐșŃ€Đ°ĐœĐ° захаĐČĐ°ĐœŃ‹ ў ĐżŃ€Đ°ĐłŃ€Đ°ĐŒĐ” \"<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>
     <string name="screenrecord_name" msgid="2596401223859996572">"Запіс эĐșŃ€Đ°ĐœĐ°"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ĐĐżŃ€Đ°Ń†ĐŸŃžĐČаДцца запіс эĐșŃ€Đ°ĐœĐ°"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Đ‘ŃĐłŃƒŃ‡Đ°Đ” апаĐČŃŃˆŃ‡ŃĐœĐœĐ” ĐŽĐ»Ń ŃĐ”Đ°ĐœŃĐ° Đ·Đ°ĐżŃ–ŃŃƒ эĐșŃ€Đ°ĐœĐ°"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ЯрĐșасць"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Đ†ĐœĐČĐ”Ń€ŃŃ–Ń ĐșĐŸĐ»Đ”Ń€Đ°Ńž"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"КарэĐșцыя ĐșĐŸĐ»Đ”Ń€Đ°Ńž"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"КіраĐČаць ĐșĐ°Ń€Ń‹ŃŃ‚Đ°Đ»ŃŒĐœŃ–ĐșĐ°ĐŒŃ–"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Đ“Đ°Ń‚ĐŸĐČа"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"ЗаĐșрыць"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"ĐŃžŃ‚Đ°ĐŒĐ°Ń‚Ń‹Ń‡ĐœĐ°"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"БДз гуĐșу ці ĐČібрацыі"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"ПаĐșазĐČаДцца бДз гуĐșу ці ĐČібрацыі ў разЎзДлД Ń€Đ°Đ·ĐŒĐŸŃž"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"ĐŁ Đ·Đ°Đ»Đ”Đ¶ĐœĐ°ŃŃ†Ń– аЎ ĐœĐ°Đ»Đ°ĐŽ прылаЎы ĐŒĐ°ĐłŃ‡Ń‹ĐŒŃ‹ Đ·ĐČĐ°ĐœĐŸĐș Đ°Đ±ĐŸ ĐČŃ–Đ±Ń€Đ°Ń†Ń‹Ń"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"ĐŁ Đ·Đ°Đ»Đ”Đ¶ĐœĐ°ŃŃ†Ń– аЎ ĐœĐ°Đ»Đ°ĐŽ прылаЎы ĐŒĐ°ĐłŃ‡Ń‹ĐŒŃ‹ Đ·ĐČĐ°ĐœĐŸĐș Đ°Đ±ĐŸ ĐČŃ–Đ±Ń€Đ°Ń†Ń‹Ń. Đ Đ°Đ·ĐŒĐŸĐČы ў ĐżŃ€Đ°ĐłŃ€Đ°ĐŒĐ” \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" ŃŃ‚Đ°ĐœĐŽĐ°Ń€Ń‚ĐœĐ° ĐżĐ°ŃŃžĐ»ŃŃŽŃ†Ń†Đ° ў ĐČŃ‹ĐłĐ»ŃĐŽĐ·Đ” ўсплыĐČĐ°Đ»ŃŒĐœŃ‹Ń… апаĐČŃŃˆŃ‡ŃĐœĐœŃŃž."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ĐĄŃ–ŃŃ‚ŃĐŒĐ° ŃĐ°ĐŒĐ° буЎзД ĐČŃ‹Đ·ĐœĐ°Ń‡Đ°Ń†ŃŒ, ці Ń‚Ń€ŃĐ±Đ° ĐŽĐ»Ń гэтага апаĐČŃŃˆŃ‡ŃĐœĐœŃ ўĐșĐ»ŃŽŃ‡Đ°Ń†ŃŒ гуĐș Đ°Đ±ĐŸ ĐČібрацыю"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;ĐĄŃ‚Đ°Đœ:&lt;/b&gt; ĐŸĐ°Đ·ĐœĐ°Ń‡Đ°ĐœĐ° яĐș ŃŃ‚Đ°ĐœĐŽĐ°Ń€Ń‚ĐœĐ°Đ”"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;ĐĄŃ‚Đ°Đœ:&lt;/b&gt; ĐŸĐ”Ń€Đ°ĐČĐ”ĐŽĐ·Đ”ĐœĐ° ў Ń€ŃĐ¶Ń‹ĐŒ \"БДз гуĐșу\""</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"запіс эĐșŃ€Đ°ĐœĐ°"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"БДз ĐœĐ°Đ·ĐČы"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Đ ŃĐ¶Ń‹ĐŒ чаĐșĐ°ĐœĐœŃ"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"АĐșĐœĐŸ паĐČĐ”Đ»Ń–Ń‡ŃĐœĐœŃ"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"ĐĐ°Đ»Đ°ĐŽŃ‹ аĐșĐœĐ° паĐČĐ”Đ»Ń–Ń‡ŃĐœĐœŃ"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ПаĐČŃĐ»Ń–Ń‡Ń‹Ń†ŃŒ ĐŒĐ°ŃˆŃ‚Đ°Đ±"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"ВыбДрыцД ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃƒ ĐŽĐ»Ń ЎаЎаĐČĐ°ĐœĐœŃ ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Đ°Ńž ĐșіраĐČĐ°ĐœĐœŃ"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Đ”Đ°ĐŽĐ°ĐŽĐ·Đ”ĐœŃ‹ # ŃĐ»Đ”ĐŒĐ”ĐœŃ‚ ĐșіраĐČĐ°ĐœĐœŃ.}one{Đ”Đ°ĐŽĐ°ĐŽĐ·Đ”ĐœĐ° # ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Đ° ĐșіраĐČĐ°ĐœĐœŃ.}few{Đ”Đ°ĐŽĐ°ĐŽĐ·Đ”ĐœĐ° # ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Ń‹ ĐșіраĐČĐ°ĐœĐœŃ.}many{Đ”Đ°ĐŽĐ°ĐŽĐ·Đ”ĐœĐ° # ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Đ°Ńž ĐșіраĐČĐ°ĐœĐœŃ.}other{Đ”Đ°ĐŽĐ°ĐŽĐ·Đ”ĐœĐ° # ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Đ° ĐșіраĐČĐ°ĐœĐœŃ.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Đ’Ń‹ĐŽĐ°Đ»Đ”ĐœĐ°"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"ДаЮаць ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃƒ \"<xliff:g id="APPNAME">%s</xliff:g>\"?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Калі ĐČы ЎаЎасцД ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃƒ \"<xliff:g id="APPNAME">%s</xliff:g>\", ŃĐœĐ° Đ·ĐŒĐŸĐ¶Đ° ЎаЎаĐČаць ĐœĐ° гэту ĐżĐ°ĐœŃĐ»ŃŒ ĐœĐ°Đ»Đ°ĐŽŃ‹ і Đ·ĐŒĐ”ŃŃ†Ń–ĐČа. Đ”Đ»Ń ĐœĐ”ĐșĐ°Ń‚ĐŸŃ€Ń‹Ń… ĐżŃ€Đ°ĐłŃ€Đ°ĐŒ ĐČы Đ·ĐŒĐŸĐ¶Đ°Ń†Đ” ĐČŃ‹Đ±Ń€Đ°Ń†ŃŒ, яĐșія ĐœĐ°Đ»Đ°ĐŽŃ‹ Đ±ŃƒĐŽŃƒŃ†ŃŒ тут паĐșазĐČацца."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Đ”Đ°ĐŽĐ°ĐŽĐ·Đ”ĐœĐ° ў Đ°Đ±Ń€Đ°ĐœĐ°Đ”"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Đ”Đ°ĐŽĐ°ĐŽĐ·Đ”ĐœĐ° ў Đ°Đ±Ń€Đ°ĐœĐ°Đ”, ĐżĐ°Đ·Ń–Ń†Ń‹Ń <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Đ’Ń‹ĐŽĐ°Đ»Đ”ĐœĐ° Đ· Đ°Đ±Ń€Đ°ĐœĐ°ĐłĐ°"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"АЎĐșрыĐčцД ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃƒ \"<xliff:g id="APP_LABEL">%1$s</xliff:g>\""</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"ПраĐčграĐčцД ĐșĐ°ĐŒĐżĐ°Đ·Ń–Ń†Ń‹ŃŽ \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" (ĐČыĐșĐ°ĐœĐ°ŃžŃ†Đ° – <xliff:g id="ARTIST_NAME">%2$s</xliff:g>) Đ· ĐŽĐ°ĐżĐ°ĐŒĐŸĐłĐ°Đč ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃ‹ \"<xliff:g id="APP_LABEL">%3$s</xliff:g>\""</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"ПраĐčграĐčцД ĐșĐ°ĐŒĐżĐ°Đ·Ń–Ń†Ń‹ŃŽ \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" Đ· ĐŽĐ°ĐżĐ°ĐŒĐŸĐłĐ°Đč ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃ‹ \"<xliff:g id="APP_LABEL">%2$s</xliff:g>\""</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Đ”Đ»Ń ĐČас"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"ĐĐŽŃ€Đ°Đ±Ń–Ń†ŃŒ"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Каб праĐčграць ĐŒŃƒĐ»ŃŒŃ‚Ń‹ĐŒĐ”ĐŽŃ‹Ń ĐœĐ° прылаЎзД \"<xliff:g id="DEVICENAME">%1$s</xliff:g>\", ĐœĐ°Đ±Đ»Ń–Đ·ŃŒŃ†Đ”ŃŃ Ўа ŃĐ”"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Каб праĐčграць тут, паЎыЎзіцД Đ±Đ»Ń–Đ¶ŃĐč Ўа прылаЎы \"<xliff:g id="DEVICENAME">%1$s</xliff:g>\""</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"ĐĐ”ŃˆŃ‚Đ° паĐčŃˆĐ»ĐŸ ĐœĐ” таĐș. ĐŸĐ°ŃžŃ‚Đ°Ń€Ń‹Ń†Đ” ŃĐżŃ€ĐŸĐ±Ńƒ."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"ІЎзД Đ·Đ°ĐłŃ€ŃƒĐ·Đșа"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ĐżĐ»Đ°ĐœŃˆŃŃ‚"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"ĐąŃ€Đ°ĐœŃĐ»ŃŃ†Ń‹Ń ĐŒŃƒĐ»ŃŒŃ‚Ń‹ĐŒĐ”ĐŽŃ‹ĐčĐœĐ°ĐłĐ° Đ·ĐŒĐ”ŃŃ†Ń–ĐČа"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"ĐąŃ€Đ°ĐœŃĐ»ŃŃ†Ń‹Ń ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃ‹ \"<xliff:g id="APP_LABEL">%1$s</xliff:g>\""</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ĐĐ”Đ°ĐșŃ‚Ń‹ŃžĐœĐ°, праĐČДрцД ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃƒ"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"ĐĐ” Đ·ĐœĐŸĐčĐŽĐ·Đ”ĐœĐ°"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"КіраĐČĐ°ĐœĐœĐ” ĐœĐ”ĐŽĐ°ŃŃ‚ŃƒĐżĐœĐ°Đ”"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"ĐŸĐ°ĐŒŃ‹Đ»Đșа, паўтарыцД ŃĐżŃ€ĐŸĐ±Ńƒ"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"ДаЮаць ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Ń‹ ĐșіраĐČĐ°ĐœĐœŃ"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Đ—ĐŒŃĐœŃ–Ń†ŃŒ ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Ń‹ ĐșіраĐČĐ°ĐœĐœŃ"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"ДаЮаць ĐżŃ€Đ°ĐłŃ€Đ°ĐŒŃƒ"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"ДаЮаĐčцД прылаЎы ĐČыĐČаЎу"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Група"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Đ’Ń‹Đ±Ń€Đ°ĐœĐ° 1 ĐżŃ€Ń‹Đ»Đ°ĐŽĐ°"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Đ”Ń‹ĐœĐ°ĐŒŃ–Đșі і ĐŽŃ‹ŃĐżĐ»ŃŃ–"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"ĐŸŃ€Ń‹Đ»Đ°ĐŽŃ‹, яĐșія ĐżĐ°ĐŽŃ‚Ń€Ń‹ĐŒĐ»Ń–ĐČаюцца"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"ĐŸĐ°Ń‚Ń€Đ°Đ±ŃƒĐ”Ń†Ń†Đ° ĐżĐ»Đ°Ń‚ĐœŃ‹ ўліĐșĐŸĐČы запіс"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ĐŻĐș аЎбыĐČаДцца Ń‚Ń€Đ°ĐœŃĐ»ŃŃ†Ń‹Ń"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"ĐąŃ€Đ°ĐœŃĐ»ŃŃ†Ń‹Ń"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ЛюЎзі ĐżĐ°Đ±Đ»Ń–Đ·Ńƒ, у яĐșіх ёсць прылаЎы Đ· Bluetooth, Đ·ĐŒĐŸĐłŃƒŃ†ŃŒ ĐżŃ€Đ°ŃĐ»ŃƒŃ…ĐČаць ĐŒŃƒĐ»ŃŒŃ‚Ń‹ĐŒĐ”ĐŽŃ‹ĐčĐœĐ°Đ” Đ·ĐŒĐ”ŃŃ†Ń–ĐČа, яĐșĐŸĐ” ĐČы Ń‚Ń€Đ°ĐœŃĐ»Ń–Ń€ŃƒĐ”Ń†Đ”"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"ĐĐ” ŃžĐŽĐ°Đ»ĐŸŃŃ Đ·Đ°ĐżŃƒŃŃ†Ń–Ń†ŃŒ Ń‚Ń€Đ°ĐœŃĐ»ŃŃ†Ń‹ŃŽ"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"ĐĐ” ŃžĐŽĐ°Đ»ĐŸŃŃ захаĐČаць. ĐŸĐ°ŃžŃ‚Đ°Ń€Ń‹Ń†Đ” ŃĐżŃ€ĐŸĐ±Ńƒ."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"ĐĐ” ŃžĐŽĐ°Đ»ĐŸŃŃ захаĐČаць."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"ĐŃƒĐŒĐ°Ń€ Đ·Đ±ĐŸŃ€Đșі"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"ĐŃƒĐŒĐ°Ń€ Đ·Đ±ĐŸŃ€Đșі сĐșапіраĐČĐ°ĐœŃ‹ ў Đ±ŃƒŃ„Đ”Ń€ Đ°Đ±ĐŒĐ”ĐœŃƒ."</string>
     <string name="basic_status" msgid="2315371112182658176">"АЎĐșрытая Ń€Đ°Đ·ĐŒĐŸĐČа"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Đ”Đ°ŃŃ‚ŃƒĐżĐœĐ°Ń хаця б Đ°ĐŽĐœĐ° прылаЎа."</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"ДаĐșŃ€Đ°ĐœŃ–Ń†Đ”ŃŃ і ŃžŃ‚Ń€Ń‹ĐŒĐ»Ń–ĐČаĐčцД ŃŃ€Đ»Ń‹Đș"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"ĐĄĐșасаĐČаць"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"ĐŸĐ”Ń€Đ°ĐșĐ»ŃŽŃ‡Ń‹Ń†ŃŒ"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Каб Đ°Ń‚Ń€Ń‹ĐŒĐ°Ń†ŃŒ лДпшаД ŃŃĐ»Ń„Ń–, расĐșрыĐčцД Ń‚ŃĐ»Đ”Ń„ĐŸĐœ"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"ĐŸĐ”Ń€Đ°ĐșĐ»ŃŽŃ‡Ń‹Ń†ŃŒ ĐœĐ° ĐżŃŃ€ŃĐŽĐœŃŽŃŽ ĐșĐ°ĐŒĐ”Ń€Ńƒ ĐŽĐ»Ń лДпшага ŃŃĐ»Ń„Ń–?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Каб Đ·Ń€Đ°Đ±Ń–Ń†ŃŒ шырэĐčшаД Ń„ĐŸŃ‚Đ° Đ· Đ±ĐŸĐ»ŃŒŃˆ ĐČŃ‹ŃĐŸĐșаĐč Ń€Đ°Đ·ĐŽĐ·ŃĐ»ŃĐ»ŃŒĐœĐ°ŃŃ†ŃŽ, сĐșарыстаĐčцД Đ·Đ°ĐŽĐœŃŽŃŽ ĐșĐ°ĐŒĐ”Ń€Ńƒ."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Гэты эĐșŃ€Đ°Đœ буЎзД ĐČыĐșĐ»ŃŽŃ‡Đ°ĐœŃ‹"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ĐĄĐșĐ»Đ°ĐŽĐœĐ°Ń прылаЎа ў расĐșĐ»Đ°ĐŽĐ·Đ”ĐœŃ‹ĐŒ ĐČŃ‹ĐłĐ»ŃĐŽĐ·Đ”"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ĐŸĐ”Ń€Đ°ĐČĐ”Ń€ĐœŃƒŃ‚Đ°Ń сĐșĐ»Đ°ĐŽĐœĐ°Ń прылаЎа"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Đ—Đ°ŃŃ‚Đ°Đ»ĐŸŃŃ Đ·Đ°Ń€Đ°ĐŽŃƒ: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ПаЮĐșлючыцД ĐżŃŃ€ĐŸ Ўа Đ·Đ°Ń€Đ°ĐŽĐœĐ°Đč прылаЎы"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"ĐŃ–Đ·Đșі ŃžĐ·Ń€ĐŸĐČĐ”ĐœŃŒ Đ·Đ°Ń€Đ°ĐŽŃƒ пяра"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"ВіЮэаĐșĐ°ĐŒĐ”Ń€Đ°"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"ĐĐ” ŃžĐŽĐ°Đ»ĐŸŃŃ Đ·Ń€Đ°Đ±Ń–Ń†ŃŒ ĐČыĐșліĐș Đ· гэтага ĐżŃ€ĐŸŃ„Ń–Đ»ŃŽ"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Đ—ĐłĐŸĐŽĐœĐ° Đ· палітыĐșаĐč ĐČашаĐč Đ°Ń€ĐłĐ°ĐœŃ–Đ·Đ°Ń†Ń‹Ń–, Ń€Đ°Đ±Ń–Ń†ŃŒ Ń‚ŃĐ»Đ”Ń„ĐŸĐœĐœŃ‹Ń ĐČыĐșліĐșі ЎазĐČĐŸĐ»Đ”ĐœĐ° Ń‚ĐŸĐ»ŃŒĐșі Đ· ĐżŃ€Đ°Ń†ĐŸŃžĐœĐ°ĐłĐ° ĐżŃ€ĐŸŃ„Ń–Đ»ŃŽ"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"ĐŸĐ”Ń€Đ°Đșлючыцца ĐœĐ° ĐżŃ€Đ°Ń†ĐŸŃžĐœŃ‹ ĐżŃ€ĐŸŃ„Ń–Đ»ŃŒ"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"ЗаĐșрыць"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"ĐĐ°Đ»Đ°ĐŽŃ‹ эĐșŃ€Đ°ĐœĐ° блаĐșŃ–Ń€ĐŸŃžĐșі"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-be/tiles_states_strings.xml b/packages/SystemUI/res/values-be/tiles_states_strings.xml
index 8fb2da2..4050129 100644
--- a/packages/SystemUI/res/values-be/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-be/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"ВыĐșĐ»ŃŽŃ‡Đ°ĐœĐ°"</item>
     <item msgid="5966994759929723339">"ĐŁĐșĐ»ŃŽŃ‡Đ°ĐœĐ°"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index ce4b9ed..ad34919 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"ЕĐșŃ€Đ°ĐœĐœĐžŃ‚Đ” ŃĐœĐžĐŒĐșĐž, ĐœĐ°ĐżŃ€Đ°ĐČĐ”ĐœĐž ĐČ ŃĐ»ŃƒĐ¶Đ”Đ±ĐœĐžŃ ĐżĐŸŃ‚Ń€Đ”Đ±ĐžŃ‚Đ”Đ»ŃĐșĐž ĐżŃ€ĐŸŃ„ĐžĐ», сД запазĐČат ĐČ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ”Ń‚ĐŸ „<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">"Files"</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>
     <string name="screenrecord_name" msgid="2596401223859996572">"Запос ĐœĐ° Đ”ĐșŃ€Đ°ĐœĐ°"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Запосът ĐœĐ° Đ”ĐșŃ€Đ°ĐœĐ° сД ĐŸĐ±Ń€Đ°Đ±ĐŸŃ‚ĐČа"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"йДĐșŃƒŃ‰ĐŸ ОзĐČДстОД за ŃĐ”ŃĐžŃ за запОсĐČĐ°ĐœĐ” ĐœĐ° Đ”ĐșŃ€Đ°ĐœĐ°"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ЯрĐșĐŸŃŃ‚"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ĐŠĐČĐ”Ń‚ĐŸĐČĐ”: ĐžĐœĐČДрт."</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ĐšĐŸŃ€Đ”Đșцоя ĐœĐ° цĐČĐ”Ń‚ĐŸĐČĐ”"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"УпраĐČĐ»Đ”ĐœĐžĐ” ĐœĐ° ĐżĐŸŃ‚Ń€Đ”Đ±ĐžŃ‚Đ”Đ»ĐžŃ‚Đ”"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Đ“ĐŸŃ‚ĐŸĐČĐŸ"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"ЗатĐČĐ°Ń€ŃĐœĐ”"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"АĐČŃ‚ĐŸĐŒĐ°Ń‚ĐžŃ‡ĐœĐŸ"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"БДз Đ·ĐČуĐș ОлО ĐČĐžĐ±Ń€ĐžŃ€Đ°ĐœĐ”"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"БДз Đ·ĐČуĐș ОлО ĐČĐžĐ±Ń€ĐžŃ€Đ°ĐœĐ” Đž сД ĐżĐŸĐșазĐČа ĐżĐŸ-ĐŽĐŸĐ»Ńƒ ĐČ ŃĐ”Đșцоята с Ń€Đ°Đ·ĐłĐŸĐČĐŸŃ€Đž"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"ĐœĐŸĐ¶Đ” Ўа Đ·ĐČŃŠĐœĐž ОлО Ўа ĐČОбрОра ĐČŃŠĐ· ĐŸŃĐœĐŸĐČа ĐœĐ° ĐœĐ°ŃŃ‚Ń€ĐŸĐčĐșОтД ĐœĐ° ŃƒŃŃ‚Ń€ĐŸĐčстĐČĐŸŃ‚ĐŸ"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"ĐœĐŸĐ¶Đ” Ўа Đ·ĐČŃŠĐœĐž ОлО Ўа ĐČОбрОра ĐČŃŠĐ· ĐŸŃĐœĐŸĐČа ĐœĐ° ĐœĐ°ŃŃ‚Ń€ĐŸĐčĐșОтД ĐœĐ° ŃƒŃŃ‚Ń€ĐŸĐčстĐČĐŸŃ‚ĐŸ. Đ Đ°Đ·ĐłĐŸĐČĐŸŃ€ĐžŃ‚Đ” ĐŸŃ‚ <xliff:g id="APP_NAME">%1$s</xliff:g> сД ĐżĐŸĐșазĐČат ĐșĐ°Ń‚ĐŸ Đ±Đ°Đ»ĐŸĐœŃ‡Đ”Ń‚Đ° ĐżĐŸ ĐżĐŸĐŽŃ€Đ°Đ·Đ±ĐžŃ€Đ°ĐœĐ”."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ĐĐ”Đșа ŃĐžŃŃ‚Đ”ĐŒĐ°Ń‚Đ° Ўа ĐŸĐżŃ€Đ”ĐŽĐ”Đ»Ń ЎалО ĐŽĐ°ĐŽĐ”ĐœĐŸ ОзĐČДстОД Ўа сД ĐżŃ€ĐžĐŽŃ€ŃƒĐ¶Đ°ĐČа ĐŸŃ‚ Đ·ĐČуĐș, ОлО ĐČĐžĐ±Ń€ĐžŃ€Đ°ĐœĐ”"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;ĐĄŃŠŃŃ‚ĐŸŃĐœĐžĐ”:&lt;/b&gt; ĐŸĐŸĐČĐžŃˆĐ”ĐœĐŸ ĐŽĐŸ ĐŸŃĐœĐŸĐČĐœĐŸ"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;ĐĄŃŠŃŃ‚ĐŸŃĐœĐžĐ”:&lt;/b&gt; ĐŸĐŸĐœĐžĐ¶Đ”ĐœĐŸ ĐŽĐŸ бДззĐČŃƒŃ‡ĐœĐŸ"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"запОсĐČĐ°ĐœĐ” ĐœĐ° Đ”ĐșŃ€Đ°ĐœĐ°"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"ĐŃĐŒĐ° заглаĐČОД"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Đ Đ”Đ¶ĐžĐŒ ĐœĐ° ĐłĐŸŃ‚ĐŸĐČĐœĐŸŃŃ‚"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"ĐŸŃ€ĐŸĐ·ĐŸŃ€Đ”Ń† за уĐČĐ”Đ»ĐžŃ‡Đ”ĐœĐžĐ”"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"ĐšĐŸĐœŃ‚Ń€ĐŸĐ»Đž за ĐżŃ€ĐŸĐ·ĐŸŃ€Đ”Ń†Đ° за уĐČĐ”Đ»ĐžŃ‡Đ”ĐœĐžĐ”"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ĐŁĐČДлОчаĐČĐ°ĐœĐ” ĐœĐ° ĐŒĐ°Ń‰Đ°Đ±Đ°"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Đ˜Đ·Đ±Đ”Ń€Đ”Ń‚Đ” ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ”, за Ўа ĐŽĐŸĐ±Đ°ĐČОтД ĐșĐŸĐœŃ‚Ń€ĐŸĐ»Đž"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Đ”ĐŸĐ±Đ°ĐČĐ”ĐœĐ° Đ” # ĐșĐŸĐœŃ‚Ń€ĐŸĐ»Đ°.}other{Đ”ĐŸĐ±Đ°ĐČĐ”ĐœĐž са # ĐșĐŸĐœŃ‚Ń€ĐŸĐ»Đž.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"ĐŸŃ€Đ”ĐŒĐ°Ń…ĐœĐ°Ń‚ĐŸ"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Да сД ĐŽĐŸĐ±Đ°ĐČĐž лО <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"ĐšĐŸĐłĐ°Ń‚ĐŸ ĐŽĐŸĐ±Đ°ĐČОтД <xliff:g id="APPNAME">%s</xliff:g>, ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ”Ń‚ĐŸ ĐŒĐŸĐ¶Đ” Ўа ĐŽĐŸĐ±Đ°ĐČĐž ĐșĐŸĐœŃ‚Ń€ĐŸĐ»Đž Đž ŃŃŠĐŽŃŠŃ€Đ¶Đ°ĐœĐžĐ” ĐșŃŠĐŒ Ń‚ĐŸĐ·Đž ĐżĐ°ĐœĐ”Đ». НяĐșĐŸĐž ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ ĐČĐž ЎаĐČат ĐČŃŠĐ·ĐŒĐŸĐ¶ĐœĐŸŃŃ‚ Ўа ОзбОратД ĐșĐŸĐž ĐșĐŸĐœŃ‚Ń€ĐŸĐ»Đž Ўа сД ĐżĐŸĐșазĐČат туĐș."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"ĐžĐ·ĐœĐ°Ń‡Đ”ĐœĐŸ ĐșĐ°Ń‚ĐŸ Đ»ŃŽĐ±ĐžĐŒĐŸ"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"ĐžĐ·ĐœĐ°Ń‡Đ”ĐœĐŸ ĐșĐ°Ń‚ĐŸ Đ»ŃŽĐ±ĐžĐŒĐŸ – ĐżĐŸĐ·ĐžŃ†ĐžŃ <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"ĐĐ” Đ” ĐŸĐ·ĐœĐ°Ń‡Đ”ĐœĐŸ ĐșĐ°Ń‚ĐŸ Đ»ŃŽĐ±ĐžĐŒĐŸ"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"ОтĐČĐ°Ń€ŃĐœĐ” ĐœĐ° <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"ПусĐșĐ°ĐœĐ” ĐœĐ° <xliff:g id="SONG_NAME">%1$s</xliff:g> ĐœĐ° <xliff:g id="ARTIST_NAME">%2$s</xliff:g> ĐŸŃ‚ <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"ПусĐșĐ°ĐœĐ” ĐœĐ° <xliff:g id="SONG_NAME">%1$s</xliff:g> ĐŸŃ‚ <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"За ĐČас"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"ĐžŃ‚ĐŒŃĐœĐ°"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"ĐŸŃ€Đ”ĐŒĐ”ŃŃ‚Đ”Ń‚Đ” сД ĐżĐŸ-Đ±Đ»ĐžĐ·ĐŸ, за Ўа сД ĐČŃŠĐ·ĐżŃ€ĐŸĐžĐ·ĐČДЎД ĐœĐ° <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"За ĐČŃŠĐ·ĐżŃ€ĐŸĐžĐ·ĐČĐ”Đ¶ĐŽĐ°ĐœĐ” туĐș сД прОблОжДтД ĐŽĐŸ <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"ĐĐ”Ń‰ĐŸ сД ĐŸĐ±ŃŠŃ€Đșа. ОпотаĐčтД ĐŸŃ‚ĐœĐŸĐČĐŸ."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"ЗарДжЎа сД"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"таблДт"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"ĐœŃƒĐ»Ń‚ĐžĐŒĐ”ĐŽĐžŃŃ‚Đ° сД прДЎаĐČа"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"<xliff:g id="APP_LABEL">%1$s</xliff:g> сД прДЎаĐČа"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ĐĐ”Đ°ĐșтоĐČĐœĐŸ, ĐżŃ€ĐŸĐČДрДтД ĐżŃ€ĐžĐ»ĐŸĐ¶."</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"ĐĐ” Đ” ĐœĐ°ĐŒĐ”Ń€Đ”ĐœĐŸ"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"ĐšĐŸĐœŃ‚Ń€ĐŸĐ»Đ°Ń‚Đ° ĐœĐ” Đ” ĐœĐ°Đ»ĐžŃ†Đ”"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Đ“Ń€Đ”ŃˆĐșа. ОпотаĐčтД ĐŸŃ‚ĐœĐŸĐČĐŸ"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Đ”ĐŸĐ±Đ°ĐČŃĐœĐ” ĐœĐ° ĐșĐŸĐœŃ‚Ń€ĐŸĐ»Đž"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"РДЎаĐșŃ‚ĐžŃ€Đ°ĐœĐ” ĐœĐ° ĐșĐŸĐœŃ‚Ń€ĐŸĐ»ĐžŃ‚Đ”"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Đ”ĐŸĐ±Đ°ĐČŃĐœĐ” ĐœĐ° ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ”"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Đ”ĐŸĐ±Đ°ĐČŃĐœĐ” ĐœĐ° ĐžĐ·Ń…ĐŸĐŽŃŃ‰Đž ŃƒŃŃ‚Ń€ĐŸĐčстĐČа"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Група"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 ĐžĐ·Đ±Ń€Đ°ĐœĐŸ ŃƒŃŃ‚Ń€ĐŸĐčстĐČĐŸ"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Đ’ĐžŃĐŸĐșĐŸĐłĐŸĐČĐŸŃ€ĐžŃ‚Đ”Đ»Đž Đž Đ”ĐșŃ€Đ°ĐœĐž"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"ĐŸŃ€Đ”ĐŽĐ»ĐŸĐ¶Đ”ĐœĐž ŃƒŃŃ‚Ń€ĐŸĐčстĐČа"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"ĐĐ”ĐŸĐ±Ń…ĐŸĐŽĐžĐŒ Đ” ĐżĐ»Đ°Ń‚Đ”Đœ ĐżŃ€ĐŸŃ„ĐžĐ»"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"КаĐș Ń€Đ°Đ±ĐŸŃ‚Đž прДЎаĐČĐ°ĐœĐ”Ń‚ĐŸ"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"ĐŸŃ€Đ”ĐŽĐ°ĐČĐ°ĐœĐ”"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Đ„ĐŸŃ€Đ°Ń‚Đ° ĐČ Đ±Đ»ĐžĐ·ĐŸŃŃ‚ със съĐČĐŒĐ”ŃŃ‚ĐžĐŒĐž ŃƒŃŃ‚Ń€ĐŸĐčстĐČа с Bluetooth ĐŒĐŸĐłĐ°Ń‚ Ўа ŃĐ»ŃƒŃˆĐ°Ń‚ ĐŒŃƒĐ»Ń‚ĐžĐŒĐ”ĐŽĐžŃŃ‚Đ°, ĐșĐŸŃŃ‚ĐŸ прДЎаĐČатД"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"ĐŸŃ€Đ”ĐŽĐ°ĐČĐ°ĐœĐ”Ń‚ĐŸ ĐœĐ” Đ” ĐČŃŠĐ·ĐŒĐŸĐ¶ĐœĐŸ"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"ĐĐ” ĐŒĐŸĐ¶Đ” Ўа сД запазО. ОпотаĐčтД ĐŸŃ‚ĐœĐŸĐČĐŸ."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"ĐĐ” ĐŒĐŸĐ¶Đ” Ўа сД запазО."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"ĐĐŸĐŒĐ”Ń€ ĐœĐ° ĐșĐŸĐŒĐżĐžĐ»Đ°Ń†ĐžŃŃ‚Đ°"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"ĐĐŸĐŒĐ”Ń€ŃŠŃ‚ ĐœĐ° ĐșĐŸĐŒĐżĐžĐ»Đ°Ń†ĐžŃŃ‚Đ° Đ” ĐșĐŸĐżĐžŃ€Đ°Đœ ĐČ Đ±ŃƒŃ„Đ”Ń€ĐœĐ°Ń‚Đ° ĐżĐ°ĐŒĐ”Ń‚."</string>
     <string name="basic_status" msgid="2315371112182658176">"ОтĐČĐŸŃ€Đ”Đœ Ń€Đ°Đ·ĐłĐŸĐČĐŸŃ€"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• ĐĐ°Đ»ĐžŃ†Đ” Đ” ĐżĐŸĐœĐ” Đ”ĐŽĐœĐŸ ŃƒŃŃ‚Ń€ĐŸĐčстĐČĐŸ."</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Đ”ĐŸĐșĐŸŃĐœĐ”Ń‚Đ” Đž заЎръжтД прДĐșоя път"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"ОтĐșаз"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"ĐžĐ±Ń€ŃŠŃ‰Đ°ĐœĐ” сДга"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"ОтĐČĐŸŃ€Đ”Ń‚Đ” Ń‚Đ”Đ»Đ”Ń„ĐŸĐœĐ° за ĐżĐŸ-ĐŽĐŸĐ±Ń€ĐŸ сДлфО"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Да сД ĐżĐŸĐ»Đ·ĐČа лО ĐżŃ€Đ”ĐŽĐœĐ°Ń‚Đ° ĐșĐ°ĐŒĐ”Ń€Đ° за ĐżĐŸ-ĐŽĐŸĐ±Ń€ĐŸ сДлфО?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Đ˜Đ·ĐżĐŸĐ»Đ·ĐČаĐčтД Đ·Đ°ĐŽĐœĐ°Ń‚Đ° ĐșĐ°ĐŒĐ”Ń€Đ° за ĐżĐŸ-ŃˆĐžŃ€ĐŸĐșа ŃĐœĐžĐŒĐșа с ĐżĐŸ-ĐČĐžŃĐŸĐșа Ń€Đ°Đ·ĐŽĐ”Đ»ĐžŃ‚Đ”Đ»ĐœĐ° ŃĐżĐŸŃĐŸĐ±ĐœĐŸŃŃ‚."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ĐąĐŸĐ·Đž Đ”ĐșŃ€Đ°Đœ щД сД ОзĐșлючО"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"РазгъĐČĐ°ĐœĐ” ĐœĐ° сгъĐČĐ°Đ”ĐŒĐŸ ŃƒŃŃ‚Ń€ĐŸĐčстĐČĐŸ"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ĐžĐ±Ń€ŃŠŃ‰Đ°ĐœĐ” ĐœĐ° сгъĐČĐ°Đ”ĐŒĐŸ ŃƒŃŃ‚Ń€ĐŸĐčстĐČĐŸ"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"ОстаĐČаща Đ±Đ°Ń‚Đ”Ń€ĐžŃ: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ĐĄĐČържДтД пОсалĐșата ĐșŃŠĐŒ Đ·Đ°Ń€ŃĐŽĐœĐŸ ŃƒŃŃ‚Ń€ĐŸĐčстĐČĐŸ"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Đ‘Đ°Ń‚Đ”Ń€ĐžŃŃ‚Đ° ĐœĐ° пОсалĐșата Đ” ĐžĐ·Ń‚ĐŸŃ‰Đ”ĐœĐ°"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Đ’ĐžĐŽĐ”ĐŸĐșĐ°ĐŒĐ”Ń€Đ°"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"ĐĐ” ĐŒĐŸĐ¶Đ” Ўа сД ОзĐČършо ĐŸĐ±Đ°Đ¶ĐŽĐ°ĐœĐ” ĐŸŃ‚ Ń‚ĐŸĐ·Đž ĐżĐŸŃ‚Ń€Đ”Đ±ĐžŃ‚Đ”Đ»ŃĐșĐž ĐżŃ€ĐŸŃ„ĐžĐ»"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"ĐĄĐ»ŃƒĐ¶Đ”Đ±ĐœĐžŃ‚Đ” праĐČОла ĐČĐž ЎаĐČат ĐČŃŠĐ·ĐŒĐŸĐ¶ĐœĐŸŃŃ‚ Ўа ОзĐČършĐČатД Ń‚Đ”Đ»Đ”Ń„ĐŸĐœĐœĐž ĐŸĐ±Đ°Đ¶ĐŽĐ°ĐœĐžŃ ŃĐ°ĐŒĐŸ ĐŸŃ‚ ŃĐ»ŃƒĐ¶Đ”Đ±ĐœĐžŃ ĐżĐŸŃ‚Ń€Đ”Đ±ĐžŃ‚Đ”Đ»ŃĐșĐž ĐżŃ€ĐŸŃ„ĐžĐ»"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"ĐŸŃ€Đ”ĐČĐșлючĐČĐ°ĐœĐ” ĐșŃŠĐŒ ŃĐ»ŃƒĐ¶Đ”Đ±ĐœĐžŃ ĐżĐŸŃ‚Ń€Đ”Đ±ĐžŃ‚Đ”Đ»ŃĐșĐž ĐżŃ€ĐŸŃ„ĐžĐ»"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"ЗатĐČĐ°Ń€ŃĐœĐ”"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"ĐĐ°ŃŃ‚Ń€ĐŸĐčĐșĐž за заĐșĐ»ŃŽŃ‡Đ”ĐœĐžŃ Đ”ĐșŃ€Đ°Đœ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bg/tiles_states_strings.xml b/packages/SystemUI/res/values-bg/tiles_states_strings.xml
index b85133b..011c624 100644
--- a/packages/SystemUI/res/values-bg/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bg/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"ИзĐșĐ»."</item>
     <item msgid="5966994759929723339">"ВĐșĐ»."</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 33e8eef..3196842 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -91,8 +91,10 @@
     <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> percent"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"àŠ…àŠ«àŠżàŠžà§‡àŠ° àŠžà§àŠ•à§àŠ°àŠżàŠšàŠ¶àŠŸ <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>
     <string name="screenrecord_name" msgid="2596401223859996572">"àŠžà§àŠ•à§àŠ°àŠżàŠš àŠ°à§‡àŠ•àŠ°à§àŠĄàŠŸàŠ°"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"àŠžà§àŠ•à§àŠ°àŠżàŠš àŠ°à§‡àŠ•àŠ°à§àŠĄàŠżàŠ‚ àŠȘà§àŠ°àŠžà§‡àŠž àŠčàŠšà§àŠ›à§‡"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"àŠžà§àŠ•à§àŠ°àŠżàŠš àŠ°à§‡àŠ•àŠ°à§àŠĄàŠżàŠ‚ àŠžà§‡àŠ¶àŠš àŠšàŠČàŠŸàŠ° àŠŹàŠżàŠœà§àŠžàŠȘà§àŠ€àŠż"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"àŠ‰àŠœà§àŠœà§àŠŹàŠČàŠ€àŠŸ"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"àŠ•àŠŸàŠČàŠŸàŠ° àŠ‡àŠšàŠ­àŠŸàŠ°à§àŠžàŠš"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"àŠ°àŠ™ àŠžàŠ‚àŠ¶à§‹àŠ§àŠš"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"àŠŹà§àŠŻàŠŹàŠčàŠŸàŠ°àŠ•àŠŸàŠ°à§€àŠŠà§‡àŠ° àŠźà§àŠŻàŠŸàŠšà§‡àŠœ àŠ•àŠ°à§àŠš"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"àŠžàŠźà§àŠȘàŠšà§àŠš àŠčàŠŻàŠŒà§‡àŠ›à§‡"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"àŠŹàŠšà§àŠ§ àŠ•àŠ°à§àŠš"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"àŠ…àŠŸà§‹àŠźà§‡àŠŸàŠżàŠ•"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"àŠ†àŠ“àŠŻàŠŒàŠŸàŠœ àŠ•àŠ°àŠŹà§‡ àŠšàŠŸ àŠŹàŠŸ àŠ­àŠŸàŠ‡àŠŹà§àŠ°à§‡àŠŸ àŠčàŠŹà§‡ àŠšàŠŸ"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"àŠ†àŠ“àŠŻàŠŒàŠŸàŠœ àŠ•àŠ°àŠŹà§‡ àŠšàŠŸ àŠŹàŠŸ àŠ­àŠŸàŠ‡àŠŹà§àŠ°à§‡àŠŸ àŠčàŠŹà§‡ àŠšàŠŸ àŠàŠŹàŠ‚ àŠ•àŠ„à§‹àŠȘàŠ•àŠ„àŠš àŠŹàŠżàŠ­àŠŸàŠ—à§‡àŠ° àŠšàŠżàŠšà§‡àŠ° àŠŠàŠżàŠ•à§‡ àŠŠà§‡àŠ–àŠŸ àŠŻàŠŸàŠŹà§‡"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"àŠĄàŠżàŠ­àŠŸàŠ‡àŠžà§‡àŠ° àŠžà§‡àŠŸàŠżàŠ‚àŠž àŠ…àŠšà§àŠŻàŠŸàŠŻàŠŒà§€ àŠ°àŠżàŠ‚ àŠŹàŠŸ àŠ­àŠŸàŠ‡àŠŹà§àŠ°à§‡àŠŸ àŠčàŠ€à§‡ àŠȘàŠŸàŠ°à§‡"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"àŠĄàŠżàŠ­àŠŸàŠ‡àŠžà§‡àŠ° àŠžà§‡àŠŸàŠżàŠ‚àŠž àŠ…àŠšà§àŠŻàŠŸàŠŻàŠŒà§€ àŠ°àŠżàŠ‚ àŠŹàŠŸ àŠ­àŠŸàŠ‡àŠŹà§àŠ°à§‡àŠŸ àŠčàŠ€à§‡ àŠȘàŠŸàŠ°à§‡à„€ <xliff:g id="APP_NAME">%1$s</xliff:g>-àŠàŠ° àŠ•àŠ„à§‹àŠȘàŠ•àŠ„àŠš àŠžàŠŸàŠ§àŠŸàŠ°àŠŁàŠ€ àŠŹàŠŸàŠŹàŠČà§‡àŠ° àŠźàŠ€à§‹ àŠŠà§‡àŠ–àŠŸàŠŹà§‡à„€"</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"àŠàŠ‡ àŠŹàŠżàŠœà§àŠžàŠȘà§àŠ€àŠż àŠàŠČে àŠĄàŠżàŠ­àŠŸàŠ‡àŠž àŠ†àŠ“àŠŻàŠŒàŠŸàŠœ àŠ•àŠ°àŠŹà§‡ àŠšàŠŸ àŠ­àŠŸàŠ‡àŠŹà§àŠ°à§‡àŠŸ àŠ•àŠ°àŠŹà§‡ àŠ€àŠŸ àŠžàŠżàŠžà§àŠŸà§‡àŠźàŠ•à§‡ àŠžà§‡àŠŸ àŠ•àŠ°àŠ€à§‡ àŠŠàŠżàŠš"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;àŠžà§àŠŸà§àŠŻàŠŸàŠŸàŠŸàŠž:&lt;/b&gt; àŠČà§‡àŠ­à§‡àŠČ àŠŹàŠŸàŠĄàŠŒàŠżàŠŻàŠŒà§‡ àŠĄàŠżàŠ«àŠČà§àŠŸ àŠ•àŠ°àŠŸ àŠčàŠŻàŠŒà§‡àŠ›à§‡"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;àŠžà§àŠŸà§àŠŻàŠŸàŠŸàŠŸàŠž:&lt;/b&gt; àŠČà§‡àŠ­à§‡àŠČ àŠ•àŠźàŠżàŠŻàŠŒà§‡ àŠžàŠŸàŠ‡àŠČà§‡àŠšà§ àŠ•àŠ°àŠŸ àŠčàŠŻàŠŒà§‡àŠ›à§‡"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"àŠžà§àŠ•à§àŠ°àŠżàŠš àŠ°à§‡àŠ•àŠ°à§àŠĄàŠżàŠ‚"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"àŠ•à§‹àŠšàŠ“ àŠ¶à§€àŠ°à§àŠ·àŠ• àŠšà§‡àŠ‡"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"àŠžà§àŠŸà§àŠŻàŠŸàŠšà§àŠĄàŠŹàŠŸàŠ‡"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"àŠ‰àŠ‡àŠšà§àŠĄà§‹ àŠŹàŠĄàŠŒ àŠ•àŠ°à§‡ àŠŠà§‡àŠ–àŠŸ"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"àŠ‰àŠ‡àŠšà§àŠĄà§‹ àŠ•àŠšà§àŠŸà§àŠ°à§‹àŠČ àŠŹàŠĄàŠŒ àŠ•àŠ°à§‡ àŠŠà§‡àŠ–àŠŸ"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"àŠŹàŠĄàŠŒ àŠ•àŠ°à§àŠš"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"àŠ•àŠšà§àŠŸà§àŠ°à§‹àŠČ àŠŻà§‹àŠ— àŠ•àŠ°àŠ€à§‡ àŠ…à§àŠŻàŠŸàŠȘ àŠŹà§‡àŠ›à§‡ àŠšàŠżàŠš"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{#àŠŸàŠż àŠ•àŠšà§àŠŸà§àŠ°à§‹àŠČ àŠŻà§‹àŠ— àŠ•àŠ°àŠŸ àŠčàŠŻàŠŒà§‡àŠ›à§‡à„€}one{#àŠŸàŠż àŠ•àŠšà§àŠŸà§àŠ°à§‹àŠČ àŠŻà§‹àŠ— àŠ•àŠ°àŠŸ àŠčàŠŻàŠŒà§‡àŠ›à§‡à„€}other{#àŠŸàŠż àŠ•àŠšà§àŠŸà§àŠ°à§‹àŠČ àŠŻà§‹àŠ— àŠ•àŠ°àŠŸ àŠčàŠŻàŠŒà§‡àŠ›à§‡à„€}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"àŠžàŠ°àŠŸàŠšà§‹ àŠčàŠŻàŠŒà§‡àŠ›à§‡"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g> àŠŻà§‹àŠ— àŠ•àŠ°àŠŹà§‡àŠš?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"àŠ†àŠȘàŠšàŠż <xliff:g id="APPNAME">%s</xliff:g> àŠŻà§‹àŠ— àŠ•àŠ°àŠČে, àŠàŠ‡ àŠȘà§àŠŻàŠŸàŠšà§‡àŠČে àŠàŠŸàŠż àŠ•àŠšà§àŠŸà§àŠ°à§‹àŠČ àŠ“ àŠ•àŠšà§àŠŸà§‡àŠšà§àŠŸ àŠŻà§‹àŠ— àŠ•àŠ°àŠ€à§‡ àŠȘàŠŸàŠ°àŠŹà§‡à„€ àŠ•àŠżàŠ›à§ àŠ…à§àŠŻàŠŸàŠȘà§‡àŠ° àŠ•à§àŠ·à§‡àŠ€à§àŠ°à§‡, àŠàŠ–àŠŸàŠšà§‡ àŠ•à§‹àŠš àŠ•à§‹àŠš àŠ•àŠšà§àŠŸà§àŠ°à§‹àŠČ àŠŠà§‡àŠ–àŠŸ àŠŻàŠŸàŠŹà§‡ àŠ†àŠȘàŠšàŠż àŠ€àŠŸ àŠšàŠżàŠŻàŠŒàŠšà§àŠ€à§àŠ°àŠŁ àŠ•àŠ°àŠ€à§‡ àŠȘàŠŸàŠ°àŠŹà§‡àŠšà„€"</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"àŠȘàŠ›àŠšà§àŠŠàŠžàŠ‡ àŠčàŠżàŠžà§‡àŠŹà§‡ àŠšàŠżàŠčà§àŠšàŠżàŠ€ àŠ•àŠ°à§‡àŠ›à§‡àŠš"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"àŠȘàŠ›àŠšà§àŠŠàŠžàŠ‡ àŠčàŠżàŠžà§‡àŠŹà§‡ àŠšàŠżàŠčà§àŠšàŠżàŠ€ àŠ•àŠ°à§‡àŠ›à§‡àŠš, àŠ…àŠŹàŠžà§àŠ„àŠŸàŠš <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"àŠȘàŠ›àŠšà§àŠŠàŠžàŠ‡ àŠ„à§‡àŠ•à§‡ àŠžàŠ°àŠżàŠŻàŠŒà§‡ àŠŠàŠżàŠŻàŠŒà§‡àŠ›à§‡àŠš"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> àŠ…à§àŠŻàŠŸàŠȘ àŠ–à§àŠČà§àŠš"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g>-àŠàŠ° <xliff:g id="SONG_NAME">%1$s</xliff:g> àŠ—àŠŸàŠšàŠŸàŠż <xliff:g id="APP_LABEL">%3$s</xliff:g> àŠ…à§àŠŻàŠŸàŠȘে àŠšàŠŸàŠČàŠŸàŠš"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> àŠ—àŠŸàŠšàŠŸàŠż <xliff:g id="APP_LABEL">%2$s</xliff:g> àŠ…à§àŠŻàŠŸàŠȘে àŠšàŠŸàŠČàŠŸàŠš"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"àŠ†àŠȘàŠšàŠŸàŠ° àŠœàŠšà§àŠŻ"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"àŠ†àŠ—à§‡àŠ° àŠ…àŠŹàŠžà§àŠ„àŠŸàŠŻàŠŒ àŠ«àŠżàŠ°à§àŠš"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g>-àŠ àŠšàŠŸàŠČàŠŸàŠ€à§‡ àŠ†àŠ°àŠ“ àŠ•àŠŸàŠ›à§‡ àŠ†àŠšà§àŠš"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"àŠàŠ–àŠŸàŠšà§‡ àŠšàŠŸàŠČàŠŸàŠšà§‹àŠ° àŠœàŠšà§àŠŻ àŠ†àŠȘàŠšàŠŸàŠ° àŠĄàŠżàŠ­àŠŸàŠ‡àŠž <xliff:g id="DEVICENAME">%1$s</xliff:g>-àŠàŠ° àŠ•àŠŸàŠ›à§‡ àŠšàŠżàŠŻàŠŒà§‡ àŠŻàŠŸàŠš"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"àŠ•à§‹àŠšàŠ“ àŠžàŠźàŠžà§àŠŻàŠŸ àŠčàŠŻàŠŒà§‡àŠ›à§‡à„€ àŠ†àŠŹàŠŸàŠ° àŠšà§‡àŠ·à§àŠŸàŠŸ àŠ•àŠ°à§àŠšà„€"</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"àŠČà§‹àŠĄ àŠ•àŠ°àŠŸ àŠčàŠšà§àŠ›à§‡"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"àŠŸà§àŠŻàŠŸàŠŹàŠČà§‡àŠŸ"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"àŠ†àŠȘàŠšàŠŸàŠ° àŠźàŠżàŠĄàŠżàŠŻàŠŒàŠŸ àŠ•àŠŸàŠžà§àŠŸ àŠ•àŠ°àŠŸ"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"<xliff:g id="APP_LABEL">%1$s</xliff:g> àŠ•àŠŸàŠžà§àŠŸ àŠ•àŠ°àŠŸ àŠčàŠšà§àŠ›à§‡"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"àŠŹàŠšà§àŠ§ àŠ†àŠ›à§‡, àŠ…à§àŠŻàŠŸàŠȘ àŠšà§‡àŠ• àŠ•àŠ°à§àŠš"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"àŠ–à§àŠàŠœà§‡ àŠȘàŠŸàŠ“àŠŻàŠŒàŠŸ àŠŻàŠŸàŠŻàŠŒàŠšàŠż"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"àŠ•àŠšà§àŠŸà§àŠ°à§‹àŠČ àŠ‰àŠȘàŠČàŠ­à§àŠŻ àŠšà§‡àŠ‡"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"àŠžàŠźàŠžà§àŠŻàŠŸ àŠčàŠŻàŠŒà§‡àŠ›à§‡, àŠ†àŠŹàŠŸàŠ° àŠšà§‡àŠ·à§àŠŸàŠŸ àŠ•àŠ°à§àŠš"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"àŠ•àŠšà§àŠŸà§àŠ°à§‹àŠČ àŠŻà§‹àŠ— àŠ•àŠ°à§àŠš"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"àŠ•àŠšà§àŠŸà§àŠ°à§‹àŠČ àŠàŠĄàŠżàŠŸ àŠ•àŠ°à§àŠš"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"àŠ…à§àŠŻàŠŸàŠȘ àŠŻà§‹àŠ— àŠ•àŠ°à§àŠš"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"àŠ†àŠ‰àŠŸàŠȘà§àŠŸ àŠŻà§‹àŠ— àŠ•àŠ°à§àŠš"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"àŠ—à§àŠ°à§àŠȘ"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"à§§àŠŸàŠż àŠĄàŠżàŠ­àŠŸàŠ‡àŠž àŠŹà§‡àŠ›à§‡ àŠšà§‡àŠ“àŠŻàŠŒàŠŸ àŠčàŠŻàŠŒà§‡àŠ›à§‡"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"àŠžà§àŠȘàŠżàŠ•àŠŸàŠ° &amp; àŠĄàŠżàŠžàŠȘ্àŠČে"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"àŠžàŠŸàŠœà§‡àŠžà§àŠŸ àŠ•àŠ°àŠŸ àŠĄàŠżàŠ­àŠŸàŠ‡àŠž"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"àŠȘà§àŠ°àŠżàŠźàŠżàŠŻàŠŒàŠŸàŠź àŠ…à§àŠŻàŠŸàŠ•àŠŸàŠ‰àŠšà§àŠŸ àŠȘà§àŠ°àŠŻàŠŒà§‹àŠœàŠš"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"àŠŹà§àŠ°àŠĄàŠ•àŠŸàŠžà§àŠŸ àŠ•à§€àŠ­àŠŸàŠŹà§‡ àŠ•àŠŸàŠœ àŠ•àŠ°à§‡"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"àŠžàŠźà§àŠȘà§àŠ°àŠšàŠŸàŠ° àŠ•àŠ°à§àŠš"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"àŠ†àŠ¶àŠȘàŠŸàŠ¶à§‡ àŠČà§‹àŠ•àŠœàŠš àŠŻàŠŸàŠŠà§‡àŠ° àŠźàŠŸàŠšàŠŸàŠšàŠžàŠ‡ àŠŹà§àŠČà§àŠŸà§àŠ„ àŠĄàŠżàŠ­àŠŸàŠ‡àŠž àŠ†àŠ›à§‡, àŠ€àŠŸàŠ°àŠŸ àŠ†àŠȘàŠšàŠŸàŠ° àŠŹà§àŠ°àŠĄàŠ•àŠŸàŠžà§àŠŸ àŠ•àŠ°àŠŸ àŠźàŠżàŠĄàŠżàŠŻàŠŒàŠŸ àŠ¶à§àŠšàŠ€à§‡ àŠȘàŠŸàŠ°àŠŹà§‡àŠš"</string>
@@ -894,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"àŠžàŠźà§àŠȘà§àŠ°àŠšàŠŸàŠ° àŠ•àŠ°àŠŸ àŠŻàŠŸàŠšà§àŠ›à§‡ àŠšàŠŸ"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"àŠžà§‡àŠ­ àŠ•àŠ°àŠŸ àŠŻàŠŸàŠšà§àŠ›à§‡ àŠšàŠŸà„€ àŠ†àŠŹàŠŸàŠ° àŠšà§‡àŠ·à§àŠŸàŠŸ àŠ•àŠ°à§àŠšà„€"</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"àŠžà§‡àŠ­ àŠ•àŠ°àŠŸ àŠŻàŠŸàŠšà§àŠ›à§‡ àŠšàŠŸà„€"</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"àŠ•àŠźàŠȘàŠ•à§àŠ·à§‡ à§ȘàŠŸàŠż àŠ…àŠ•à§àŠ·àŠ° àŠŹà§àŠŻàŠŹàŠčàŠŸàŠ° àŠ•àŠ°à§àŠš"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"à§§à§ŹàŠŸàŠżàŠ° àŠšà§‡àŠŻàŠŒà§‡ àŠ•àŠź àŠ…àŠ•à§àŠ·àŠ° àŠŹà§àŠŻàŠŹàŠčàŠŸàŠ° àŠ•àŠ°à§àŠš"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"àŠŹàŠżàŠČà§àŠĄ àŠšàŠźà§àŠŹàŠ°"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"àŠŹàŠżàŠČà§àŠĄ àŠšàŠźà§àŠŹàŠ° àŠ•à§àŠČàŠżàŠȘàŠŹà§‹àŠ°à§àŠĄà§‡ àŠ•àŠȘàŠż àŠ•àŠ°àŠŸ àŠčàŠŻàŠŒà§‡àŠ›à§‡à„€"</string>
     <string name="basic_status" msgid="2315371112182658176">"àŠ–à§‹àŠČàŠŸ àŠ•àŠ„à§‹àŠȘàŠ•àŠ„àŠš"</string>
@@ -1013,24 +1030,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• àŠ…àŠšà§àŠ€àŠ€ àŠàŠ•àŠŸàŠż àŠĄàŠżàŠ­àŠŸàŠ‡àŠž àŠ‰àŠȘàŠČàŠ­à§àŠŻ"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"àŠ¶àŠ°à§àŠŸàŠ•àŠŸàŠŸ àŠŸàŠŸàŠš àŠ•àŠ°à§‡ àŠ§àŠ°à§‡ àŠ°àŠŸàŠ–à§àŠš"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"àŠŹàŠŸàŠ€àŠżàŠČ àŠ•àŠ°à§àŠš"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"àŠàŠ–àŠšàŠ‡ àŠ‰àŠČà§àŠŸàŠŸàŠš"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"àŠ†àŠ°àŠ“ àŠ­àŠŸàŠČ àŠžà§‡àŠČàŠ«àŠżàŠ° àŠœàŠšà§àŠŻ àŠ«à§‹àŠš àŠ†àŠšàŠ«à§‹àŠČà§àŠĄ àŠ•àŠ°àŠŸ"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"àŠ†àŠ°àŠ“ àŠ­àŠŸàŠČ àŠžà§‡àŠČàŠ«àŠżàŠ° àŠœàŠšà§àŠŻ àŠžàŠŸàŠźàŠšà§‡àŠ° àŠ•à§àŠŻàŠŸàŠźà§‡àŠ°àŠŸàŠŻàŠŒ àŠȘàŠŸàŠČà§àŠŸàŠŸàŠ€à§‡ àŠšàŠŸàŠš?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"àŠ†àŠ°àŠ“ àŠ­àŠŸàŠČ àŠ°à§‡àŠœà§‹àŠČàŠżàŠ‰àŠ¶àŠš àŠžàŠč àŠ†àŠ°àŠ“ àŠŹà§‡àŠ¶àŠż àŠ“àŠŻàŠŒàŠŸàŠ‡àŠĄ àŠ›àŠŹàŠżàŠ° àŠœàŠšà§àŠŻ àŠŹà§àŠŻàŠŸàŠ•-àŠ•à§àŠŻàŠŸàŠźà§‡àŠ°àŠŸ àŠŹà§àŠŻàŠŹàŠčàŠŸàŠ° àŠ•àŠ°à§àŠšà„€"</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ àŠàŠ‡ àŠžà§àŠ•à§àŠ°àŠżàŠš àŠŹàŠšà§àŠ§ àŠčàŠŻàŠŒà§‡ àŠŻàŠŸàŠŹà§‡"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"àŠ«à§‹àŠČà§àŠĄ àŠ•àŠ°àŠŸ àŠŻàŠŸàŠŻàŠŒ àŠàŠźàŠš àŠĄàŠżàŠ­àŠŸàŠ‡àŠž àŠ–à§‹àŠČàŠŸ àŠčàŠšà§àŠ›à§‡"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"àŠ«à§‹àŠČà§àŠĄ àŠ•àŠ°àŠŸ àŠŻàŠŸàŠŻàŠŒ àŠàŠźàŠš àŠĄàŠżàŠ­àŠŸàŠ‡àŠž àŠ‰àŠČà§àŠŸàŠŸàŠšà§‹ àŠčàŠšà§àŠ›à§‡"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> àŠŹà§àŠŻàŠŸàŠŸàŠŸàŠ°àŠżàŠ° àŠšàŠŸàŠ°à§àŠœ àŠŹàŠŸàŠ•àŠż àŠ†àŠ›à§‡"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"àŠ•à§‹àŠšàŠ“ àŠšàŠŸàŠ°à§àŠœàŠŸàŠ°à§‡àŠ° àŠžàŠŸàŠ„à§‡ àŠ†àŠȘàŠšàŠŸàŠ° àŠžà§àŠŸàŠŸàŠ‡àŠČàŠŸàŠž àŠ•àŠŸàŠšà§‡àŠ•à§àŠŸ àŠ•àŠ°à§àŠš"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"àŠžà§àŠŸàŠŸàŠ‡àŠČàŠŸàŠž àŠŹà§àŠŻàŠŸàŠŸàŠŸàŠ°àŠżàŠ€à§‡ àŠšàŠŸàŠ°à§àŠœ àŠ•àŠź àŠ†àŠ›à§‡"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"àŠ­àŠżàŠĄàŠżàŠ“ àŠ•à§àŠŻàŠŸàŠźà§‡àŠ°àŠŸ"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"àŠàŠ‡ àŠȘà§àŠ°à§‹àŠ«àŠŸàŠ‡àŠČ àŠ„à§‡àŠ•à§‡ àŠ•àŠČ àŠ•àŠ°àŠŸ àŠŻàŠŸàŠšà§àŠ›à§‡ àŠšàŠŸ"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"àŠ•àŠŸàŠœ àŠžàŠ‚àŠ•à§àŠ°àŠŸàŠšà§àŠ€ àŠšà§€àŠ€àŠż, àŠ†àŠȘàŠšàŠŸàŠ•à§‡ àŠ¶à§àŠ§à§àŠźàŠŸàŠ€à§àŠ° àŠ…àŠ«àŠżàŠž àŠȘà§àŠ°à§‹àŠ«àŠŸàŠ‡àŠČ àŠ„à§‡àŠ•à§‡ àŠ•àŠČ àŠ•àŠ°àŠŸàŠ° àŠ…àŠšà§àŠźàŠ€àŠż àŠŠà§‡àŠŻàŠŒ"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"àŠ…àŠ«àŠżàŠž àŠȘà§àŠ°à§‹àŠ«àŠŸàŠ‡àŠČে àŠȘàŠŸàŠČà§àŠŸà§‡ àŠšàŠżàŠš"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"àŠŹàŠšà§àŠ§ àŠ•àŠ°à§àŠš"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"àŠČàŠ• àŠžà§àŠ•à§àŠ°àŠżàŠš àŠžà§‡àŠŸàŠżàŠ‚àŠž"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bn/tiles_states_strings.xml b/packages/SystemUI/res/values-bn/tiles_states_strings.xml
index d70afc0..00ce04e 100644
--- a/packages/SystemUI/res/values-bn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bn/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"àŠŹàŠšà§àŠ§ àŠ†àŠ›à§‡"</item>
     <item msgid="5966994759929723339">"àŠšàŠŸàŠČু àŠ†àŠ›à§‡"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index c0c609d..8134ec9 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Donja granica <xliff:g id="PERCENT">%1$d</xliff:g> posto"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Lijeva granica <xliff:g id="PERCENT">%1$d</xliff:g> posto"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Desna granica <xliff:g id="PERCENT">%1$d</xliff:g> posto"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Poslovni snimci ekrana se pohranjuju u aplikaciji <xliff:g id="APP">%1$s</xliff:g>"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Sačuvano je u aplikaciji <xliff:g id="APP">%1$s</xliff:g> na radnom profilu"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Fajlovi"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"Aplikacija <xliff:g id="APPNAME">%1$s</xliff:g> je otkrila ovaj snimak ekrana."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"Aplikacija <xliff:g id="APPNAME">%1$s</xliff:g> i druge otvorene aplikacije su otkrile ovaj snimak ekrana."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Snimač ekrana"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Obrađivanje snimka ekrana"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Obavještenje za sesiju snimanja ekrana je u toku"</string>
@@ -255,6 +257,7 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Osvjetljenje"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inverzija boja"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Ispravka boja"</string>
+    <string name="quick_settings_font_scaling_label" msgid="5289001009876936768">"Veličina fonta"</string>
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Upravljajte korisnicima"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Gotovo"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Zatvori"</string>
@@ -532,8 +535,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatski"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Bez zvuka ili vibracije"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Bez zvuka ili vibracije i pojavljuje se pri dnu odjeljka razgovora"</string>
-    <string name="notification_channel_summary_default" msgid="777294388712200605">"MoĆŸda će zvoniti ili vibrirati, ovisno o postavkama uređaja"</string>
-    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"MoĆŸda će zvoniti ili vibrirati, ovisno o postavkama uređaja. Razgovori iz aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g> prikazuju se u oblačiću prema zadanim postavkama."</string>
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"MoĆŸe zvoniti ili vibrirati na osnovu postavki uređaja"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"MoĆŸe zvoniti ili vibrirati na osnovu postavki uređaja. Razgovori iz aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g> prikazuju se u oblačićima prema zadanim postavkama."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Neka sistem odluči treba li se ovo obavještenje oglasiti zvukom ili vibracijom"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Status:&lt;/b&gt; je unaprijeđen u Zadano"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Status:&lt;/b&gt; je unazađen u Nečujno"</string>
@@ -775,6 +778,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"snimanje ekrana"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Bez naslova"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Stanje mirovanja"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Prozor za uvećavanje"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kontrole prozora za uvećavanje"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Uvećavanje"</string>
@@ -800,6 +809,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Odaberite aplikaciju da dodate kontrole"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Dodana je # kontrola.}one{Dodana je # kontrola.}few{Dodane su # kontrole.}other{Dodano je # kontrola.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Uklonjeno"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Dodati aplikaciju <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Kada dodate aplikaciju <xliff:g id="APPNAME">%s</xliff:g>, ona moĆŸe dodavati kontrole i sadrĆŸaj na ovu ploču. U nekim aplikacijama moĆŸete odabrati koje kontrole se prikazuju ovdje."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Dodano u omiljeno"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Dodano u omiljeno, pozicija <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Uklonjeno iz omiljenog"</string>
@@ -850,6 +861,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Otvorite aplikaciju <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Reproducirajte pjesmu <xliff:g id="SONG_NAME">%1$s</xliff:g> izvođača <xliff:g id="ARTIST_NAME">%2$s</xliff:g> pomoću aplikacije <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Reproducirajte pjesmu <xliff:g id="SONG_NAME">%1$s</xliff:g> pomoću aplikacije <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Za vas"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Poništi"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"PribliĆŸite da reproducirate na uređaju <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Da reproducirate ovdje, pribliĆŸite se uređaju <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -857,6 +869,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Nešto nije uredu. Pokušajte ponovo."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Učitavanje"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Emitiranje medija"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Emitiranje aplikacije <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Neaktivno, vidite aplikaciju"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Nije pronađeno"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Kontrola nije dostupna"</string>
@@ -866,6 +880,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Greška, pokušajte ponovo"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Dodaj kontrole"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Uredi kontrole"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Dodaj aplikaciju"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Dodajte izlaze"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupa"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Odabran je 1 uređaj"</string>
@@ -881,6 +896,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Zvučnici i ekrani"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"PredloĆŸeni uređaji"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Zahtijeva premijum račun"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kako funkcionira emitiranje"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Emitirajte"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Osobe u vašoj blizini s kompatibilnim Bluetooth uređajima mogu slušati medijske sadrĆŸaje koje emitirate"</string>
@@ -892,6 +908,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Nije moguće emitirati"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Nije moguće sačuvati. Pokušajte ponovo."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Nije moguće sačuvati."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Koristite najmanje 4 znaka"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Koristite manje od 16 znakova"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Broj verzije"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Broj verzije je kopiran u međumemoriju."</string>
     <string name="basic_status" msgid="2315371112182658176">"Otvoreni razgovor"</string>
@@ -1011,19 +1029,20 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Dostupan je najmanje jedan uređaj"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Dodirnite i zadrĆŸite prečicu"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"OtkaĆŸi"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Obrni sada"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Raširite telefon za bolji selfi"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Obrnuti na prednji ekran radi boljeg selfija?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Koristite straĆŸnju kameru za širu fotografiju veće rezolucije."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Ekran će se isključiti"</b></string>
+    <string name="rear_display_bottom_sheet_confirm" msgid="1507591562761552899">"Promijenite zaslon odmah"</string>
+    <string name="rear_display_folded_bottom_sheet_title" msgid="3930008746560711990">"Otklopite telefon"</string>
+    <string name="rear_display_unfolded_bottom_sheet_title" msgid="6291111173057304055">"Ćœelite li promijeniti zaslon?"</string>
+    <string name="rear_display_folded_bottom_sheet_description" msgid="6842767125783222695">"Za višu razlučivost upotrijebite straĆŸnju kameru"</string>
+    <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Za višu razlučivost okrenite telefon"</string>
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Sklopivi uređaj se rasklapa"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Sklopivi uređaj se obrće"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Preostalo baterije: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Priključite pisaljku na punjač"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Baterija pisaljke je slaba"</string>
-    <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
-    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Nije moguće uspostavljati pozive s ovog profila"</string>
-    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Vaša pravila za poslovne uređaje omogućuju vam upućivanje poziva samo s poslovnog profila"</string>
-    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Prijeđite na poslovni profil"</string>
+    <string name="video_camera" msgid="7654002575156149298">"Video kamera"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Nije moguće pozvati s ovog profila"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Radna pravila vam dozvoljavaju upućivanje telefonskih poziva samo s radnog profila"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Pređite na radni profil"</string>
     <string name="call_from_work_profile_close" msgid="7927067108901068098">"Zatvori"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Postavke zaključavanja ekrana"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bs/tiles_states_strings.xml b/packages/SystemUI/res/values-bs/tiles_states_strings.xml
index b69b064..32051ef 100644
--- a/packages/SystemUI/res/values-bs/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bs/tiles_states_strings.xml
@@ -176,4 +176,9 @@
     <item msgid="8014986104355098744">"Isključeno"</item>
     <item msgid="5966994759929723339">"Uključeno"</item>
   </string-array>
+  <string-array name="tile_states_font_scaling">
+    <item msgid="3173069902082305985">"Nedostupno"</item>
+    <item msgid="2478289035899842865">"Isključeno"</item>
+    <item msgid="5137565285664080143">"Uključeno"</item>
+  </string-array>
 </resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 9c10046..ca8a4c3 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Marge inferior <xliff:g id="PERCENT">%1$d</xliff:g> per cent"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Marge esquerre <xliff:g id="PERCENT">%1$d</xliff:g> per cent"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Marge dret <xliff:g id="PERCENT">%1$d</xliff:g> per cent"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Les captures de pantalla de treball es desen a l\'aplicació <xliff:g id="APP">%1$s</xliff:g>"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"S\'ha desat al perfil de treball de <xliff:g id="APP">%1$s</xliff:g>"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Fitxers"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> ha detectat aquesta captura de pantalla."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> i altres aplicacions obertes han detectat aquesta captura de pantalla."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Gravació de pantalla"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processant gravació de pantalla"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificació en curs d\'una sessió de gravació de la pantalla"</string>
@@ -216,7 +218,7 @@
     <string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Sensors desactivats"</string>
     <string name="accessibility_clear_all" msgid="970525598287244592">"Esborra totes les notificacions."</string>
     <string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
-    <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# notificació més a l\'interior.}many{# more notifications inside.}other{# notificacions més a l\'interior.}}"</string>
+    <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# notificació més a l\'interior.}many{# notificacions més a l\'interior.}other{# notificacions més a l\'interior.}}"</string>
     <string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"La pantalla està bloquejada en orientació horitzontal."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"La pantalla està bloquejada en orientació vertical."</string>
     <string name="dessert_case" msgid="9104973640704357717">"Capsa de postres"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillantor"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversió de colors"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correcció de color"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Gestiona els usuaris"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Fet"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Tanca"</string>
@@ -264,7 +268,7 @@
     <string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Punt d\'accés Wi-Fi"</string>
     <string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"S\'està activant…"</string>
     <string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Estalvi dades activat"</string>
-    <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# dispositiu}many{# devices}other{# dispositius}}"</string>
+    <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# dispositiu}many{# dispositius}other{# dispositius}}"</string>
     <string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Llanterna"</string>
     <string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Càmera en ús"</string>
     <string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Dades mòbils"</string>
@@ -365,7 +369,7 @@
     <string name="guest_notification_session_active" msgid="5567273684713471450">"Estàs en mode de convidat"</string>
     <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"En afegir un usuari nou, se sortirà del mode de convidat i se suprimiran totes les aplicacions i dades de la sessió de convidat actual."</string>
     <string name="user_limit_reached_title" msgid="2429229448830346057">"S\'ha assolit el límit d\'usuaris"</string>
-    <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Només es pot crear 1 usuari.}many{You can add up to # users.}other{Pots afegir fins a # usuaris.}}"</string>
+    <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Només es pot crear 1 usuari.}many{Pots afegir fins a # usuaris.}other{Pots afegir fins a # usuaris.}}"</string>
     <string name="user_remove_user_title" msgid="9124124694835811874">"Vols suprimir l\'usuari?"</string>
     <string name="user_remove_user_message" msgid="6702834122128031833">"Totes les aplicacions i les dades d\'aquest usuari se suprimiran."</string>
     <string name="user_remove_user_remove" msgid="8387386066949061256">"Suprimeix"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automàtic"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Sense so ni vibració"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Sense so ni vibració i es mostra més avall a la secció de converses"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Pot sonar o vibrar en funció de la configuració del dispositiu"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Pot sonar o vibrar en funció de la configuració del dispositiu. Les converses de l\'aplicació <xliff:g id="APP_NAME">%1$s</xliff:g> es mostren com a bombolles de manera predeterminada."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Fes que el sistema determini si aquesta notificació ha d\'emetre un so o una vibració"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Estat&lt;/b&gt;: s\'ha augmentat a Predeterminat"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Estat&lt;/b&gt;: s\'ha disminuït a Silenci"</string>
@@ -570,8 +572,8 @@
     <string name="notification_menu_snooze_action" msgid="5415729610393475019">"Recorda-m\'ho"</string>
     <string name="snooze_undo" msgid="2738844148845992103">"Desfés"</string>
     <string name="snoozed_for_time" msgid="7586689374860469469">"S\'ha posposat <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
-    <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# hora}=2{# hores}many{# hours}other{# hores}}"</string>
-    <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minut}many{# minutes}other{# minuts}}"</string>
+    <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# hora}=2{# hores}many{# hores}other{# hores}}"</string>
+    <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minut}many{# minuts}other{# minuts}}"</string>
     <string name="battery_detail_switch_title" msgid="6940976502957380405">"Estalvi de bateria"</string>
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Botó <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Inici"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"gravació de pantalla"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Sense títol"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"En espera"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Finestra d\'ampliació"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Finestra de controls d\'ampliació"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Amplia"</string>
@@ -800,8 +808,10 @@
     <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"commuta"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Controls de dispositius"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"Selecciona l\'aplicació per afegir controls"</string>
-    <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{S\'ha afegit # control.}many{# controls added.}other{S\'han afegit # controls.}}"</string>
+    <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{S\'ha afegit # control.}many{S\'han afegit # controls.}other{S\'han afegit # controls.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Suprimit"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Vols afegir <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"En afegir <xliff:g id="APPNAME">%s</xliff:g>, podrà afegir controls i contingut en aquest tauler. En algunes aplicacions, pots triar quins controls es mostren aquí."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Afegit als preferits"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Afegit als preferits, posició <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Suprimit dels preferits"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Obre <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Reprodueix <xliff:g id="SONG_NAME">%1$s</xliff:g> (<xliff:g id="ARTIST_NAME">%2$s</xliff:g>) des de l\'aplicació <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Reprodueix <xliff:g id="SONG_NAME">%1$s</xliff:g> des de l\'aplicació <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Per a tu"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Desfés"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Mou més a prop per reproduir a <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Per reproduir contingut aquí, apropa\'l a <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"S\'ha produït un error. Torna-ho a provar."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"S\'està carregant"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tauleta"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"S\'està emetent el contingut multimèdia"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"S\'està emetent <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inactiu; comprova l\'aplicació"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"No s\'ha trobat"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"El control no està disponible"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Error; torna-ho a provar"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Afegeix controls"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Edita els controls"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Afegeix una aplicació"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Afegeix sortides"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Grup"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 dispositiu seleccionat"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Altaveus i pantalles"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositius suggerits"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Requereix un compte prèmium"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Com funciona l\'emissió"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Emet"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Les persones properes amb dispositius Bluetooth compatibles poden escoltar el contingut multimèdia que emets"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"No es pot emetre"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"No es pot desar. Torna-ho a provar."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"No es pot desar."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número de compilació"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"El número de compilació s\'ha copiat al porta-retalls."</string>
     <string name="basic_status" msgid="2315371112182658176">"Conversa oberta"</string>
@@ -964,7 +983,7 @@
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Afegeix la icona"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"No afegeixis la icona"</string>
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Selecciona un usuari"</string>
-    <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# aplicació està activa}many{# apps are active}other{# aplicacions estan actives}}"</string>
+    <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# aplicació està activa}many{# aplicacions estan actives}other{# aplicacions estan actives}}"</string>
     <string name="fgs_dot_content_description" msgid="2865071539464777240">"Informació nova"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aplicacions actives"</string>
     <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Aquestes aplicacions estan actives i executant-se, fins i tot quan no les utilitzes. Això en millora la funcionalitat, però també pot afectar la durada de la bateria."</string>
@@ -994,7 +1013,7 @@
     <string name="dream_overlay_status_bar_camera_off" msgid="5273073778969890823">"La càmera està desactivada"</string>
     <string name="dream_overlay_status_bar_mic_off" msgid="8366534415013819396">"El micròfon està desactivat"</string>
     <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Càmera i micròfon desactivats"</string>
-    <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificació}many{# notifications}other{# notificacions}}"</string>
+    <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificació}many{# notificacions}other{# notificacions}}"</string>
     <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
     <string name="note_task_button_label" msgid="8718616095800343136">"Presa de notes"</string>
     <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"S\'està emetent"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Almenys un dispositiu està disponible."</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Mantén premuda la drecera"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Cancel·la"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Gira ara"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Desplega el telèfon per fer una millor selfie"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Girar a pantalla frontal per fer millors selfies?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Utilitza la càmera posterior per obtenir una foto més àmplia amb una resolució més alta."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Aquesta pantalla s\'apagarà"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositiu plegable desplegant-se"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositiu plegable girant"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Queda un <xliff:g id="PERCENTAGE">%s</xliff:g> de bateria"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connecta el llapis òptic a un carregador"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria del llapis òptic baixa"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Càmera de vídeo"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"No es pot trucar des d\'aquest perfil"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"La teva política de treball et permet fer trucades només des del perfil de treball"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Canvia al perfil de treball"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Tanca"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Configuració pantalla de bloqueig"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ca/tiles_states_strings.xml b/packages/SystemUI/res/values-ca/tiles_states_strings.xml
index aaf19c7..067b970 100644
--- a/packages/SystemUI/res/values-ca/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ca/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Desactivat"</item>
     <item msgid="5966994759929723339">"Activat"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index db60cfd..0e6643e 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Dolní okraj <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Levý okraj <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Pravý okraj <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Pracovní snímky obrazovky se ukládají do aplikace <xliff:g id="APP">%1$s</xliff:g>"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"UloĆŸeno v aplikaci <xliff:g id="APP">%1$s</xliff:g> v pracovním profilu"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Soubory"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"Aplikace <xliff:g id="APPNAME">%1$s</xliff:g> objevila tento snímek obrazovky."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> a ostatní otevƙené aplikace objevily tento snímek obrazovky."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Rekordér obrazovky"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Záznam obrazovky se zpracovává"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Trvalé oznámení o relaci nahrávání"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Jas"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Pƙevrácení barev"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Korekce barev"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Správa uĆŸivatelĆŻ"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Hotovo"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Zavƙít"</string>
@@ -775,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"nahrávání obrazovky"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Bez názvu"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Pohotovostní reĆŸim"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Zvětšovací okno"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Ovládací prvky zvětšovacího okna"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"PƙiblíĆŸit"</string>
@@ -800,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Vyberte aplikaci, pro kterou chcete pƙidat ovládací prvky"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Byl pƙidán # ovládací prvek.}few{Byly pƙidány # ovládací prvky.}many{Bylo pƙidáno # ovládacího prvku.}other{Bylo pƙidáno # ovládacích prvkĆŻ.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Odstraněno"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Pƙidat aplikaci <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"KdyĆŸ pƙidáte aplikaci <xliff:g id="APPNAME">%s</xliff:g>, mĆŻĆŸe do tohoto panelu pƙidat ovládací prvky a obsah. V některých aplikacích si mĆŻĆŸete vybrat, které ovládací prvky se zde zobrazí."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Pƙidáno do oblíbených"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Pƙidáno do oblíbených na pozici <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Odebráno z oblíbených"</string>
@@ -850,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Otevƙít aplikaci <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Pƙehrát skladbu <xliff:g id="SONG_NAME">%1$s</xliff:g> od interpreta <xliff:g id="ARTIST_NAME">%2$s</xliff:g> z aplikace <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Pƙehrát skladbu <xliff:g id="SONG_NAME">%1$s</xliff:g> z aplikace <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Pro vás"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Vrátit zpět"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Pokud chcete pƙehrávat na zaƙízení <xliff:g id="DEVICENAME">%1$s</xliff:g>, pƙibliĆŸte se k němu"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Pokud obsah chcete pƙehrát na tomto zaƙízení, pƙesuƈte ho blíĆŸe k zaƙízení <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -857,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Došlo k chybě. Zkuste to znovu."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Načítání"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Odesílání médií"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Odesílání aplikace <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Neaktivní, zkontrolujte aplikaci"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Nenalezeno"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Ovládání není k dispozici"</string>
@@ -866,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Chyba, zkuste to znovu"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Pƙidat ovládací prvky"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Upravit ovládací prvky"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Pƙidat aplikaci"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Pƙidání výstupĆŻ"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Skupina"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Je vybráno 1 zaƙízení"</string>
@@ -881,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Reproduktory a displeje"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Navrhovaná zaƙízení"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"VyĆŸaduje prémiový účet"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Jak vysílání funguje"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Vysílání"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Lidé ve vašem okolí s kompatibilními zaƙízeními Bluetooth mohou poslouchat média, která vysíláte"</string>
@@ -892,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Vysílání se nezdaƙilo"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"UloĆŸení se nezdaƙilo. Zkuste to znovu."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"UloĆŸení se nezdaƙilo."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Heslo musí mít alespoƈ 4 znaky"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"PouĆŸijte méně neĆŸ 16 znakĆŻ"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Číslo sestavení"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Číslo sestavení bylo zkopírováno do schránky."</string>
     <string name="basic_status" msgid="2315371112182658176">"Otevƙít konverzaci"</string>
@@ -1011,11 +1030,16 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Je k dispozici alespoƈ jedno zaƙízení"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"PodrĆŸte zkratku"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Zrušit"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Otočit"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"RozloĆŸte telefon, selfie bude lepší"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Otočit na pƙední fotoaparát pro lepší selfie?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Pomocí zadního fotoaparátu poƙiďte širší fotku s vyšším rozlišením."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Tato obrazovka se vypne"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Rozkládání rozkládacího zaƙízení"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Otáčení rozkládacího zaƙízení"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Zbývá <xliff:g id="PERCENTAGE">%s</xliff:g> baterie"</string>
@@ -1026,4 +1050,5 @@
     <string name="call_from_work_profile_text" msgid="3458704745640229638">"Vaše pracovní zásady vám umoĆŸĆˆují telefonovat pouze z pracovního profilu"</string>
     <string name="call_from_work_profile_action" msgid="2937701298133010724">"Pƙepnout na pracovní profil"</string>
     <string name="call_from_work_profile_close" msgid="7927067108901068098">"Zavƙít"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Nastavení obrazovky uzamčení"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-cs/tiles_states_strings.xml b/packages/SystemUI/res/values-cs/tiles_states_strings.xml
index 64e83e0..df3d403 100644
--- a/packages/SystemUI/res/values-cs/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-cs/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Vypnuto"</item>
     <item msgid="5966994759929723339">"Zapnuto"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 4ad66fa..0fc3897 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Nederste kant: <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Venstre kant: <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Højre kant: <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Screenshots, der tages via arbejdsprofilen, gemmer i appen <xliff:g id="APP">%1$s</xliff:g>"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Gemt i <xliff:g id="APP">%1$s</xliff:g> på arbejdsprofilen"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Filer"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> har registreret dette screenshot."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> og andre åbne apps har registreret dette screenshot."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Skærmoptagelse"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Behandler skærmoptagelse"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Konstant notifikation om skærmoptagelse"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Lysstyrke"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Ombytning af farver"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Farvekorrigering"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Administrer brugere"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Udfør"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Luk"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatisk"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Ingen lyd eller vibration"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Ingen lyd eller vibration, og den vises længere nede i samtalesektionen"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Kan ringe eller vibrere baseret på enhedens indstillinger"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Kan ringe eller vibrere baseret på enhedens indstillinger. Samtaler fra <xliff:g id="APP_NAME">%1$s</xliff:g> vises som standard i bobler."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Få systemet til at afgøre, om denne notifikation skal vibrere eller afspille en lyd"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Status:&lt;/b&gt; Angivet som Standard"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Status:&lt;/b&gt; Angivet som Lydløs"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"skærmoptagelse"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Ingen titel"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Standby"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Vindue med forstørrelse"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Vindue med forstørrelsesstyring"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zoom ind"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Vælg en app for at tilføje styring"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# styringselement er tilføjet.}one{# styringselement er tilføjet.}other{# styringselementer er tilføjet.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Fjernet"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Vil du tilføje <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Når du tilføjer <xliff:g id="APPNAME">%s</xliff:g>, kan den føje styringselementer og indhold til dette panel. I nogle apps kan du vælge, hvilke styringselementer der vises her."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Angivet som favorit"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Angivet som favorit. Position <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Fjernet fra favoritter"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Åbn <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Afspil <xliff:g id="SONG_NAME">%1$s</xliff:g> af <xliff:g id="ARTIST_NAME">%2$s</xliff:g> via <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Afspil <xliff:g id="SONG_NAME">%1$s</xliff:g> via <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Til dig"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Fortryd"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Ryk tættere på for at afspille på <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"For at afspille her skal enheden tættere på <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Noget gik galt. Prøv igen."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Indlæser"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Caster medie"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Caster <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv. Tjek appen"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Ikke fundet"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Styringselement ikke tilgængeligt"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Der opstod en fejl. Prøv igen"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Tilføj styring"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Rediger styring"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Tilføj app"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Tilføj medieudgange"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Gruppe"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Der er valgt 1 enhed"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Højttalere og skærme"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Foreslåede enheder"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Kræver Premium-konto"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Sådan fungerer udsendelser"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Udsendelse"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Personer i nærheden, som har kompatible Bluetooth-enheder, kan lytte til det medie, du udsender"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Der kan ikke udsendes en fællesbesked"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Der kan ikke gemmes. Prøv igen."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Der kan ikke gemmes."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Buildnummer"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Buildnummeret blev kopieret til udklipsholderen."</string>
     <string name="basic_status" msgid="2315371112182658176">"Åben samtale"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Mindst én enhed er tilgængelig"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Hold fingeren på genvejen"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Annuller"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Vend nu"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Fold telefonen ud for at tage en bedre selfie"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Vil du bruge frontkameraet for at få bedre selfie?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Brug bagsidekameraet for at få et bredere billede med højere opløsning."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ *Denne skærm slukkes"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Foldbar enhed foldes ud"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Foldbar enhed vendes om"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> batteri tilbage"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Slut din styluspen til en oplader"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Lavt batteriniveau på styluspen"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Du kan ikke ringe fra denne profil"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Din arbejdspolitik tillader kun, at du kan foretage telefonopkald fra arbejdsprofilen"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Skift til arbejdsprofil"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Luk"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Indstillinger for låseskærm"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-da/tiles_states_strings.xml b/packages/SystemUI/res/values-da/tiles_states_strings.xml
index f0132dc..7e2f87d 100644
--- a/packages/SystemUI/res/values-da/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-da/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Fra"</item>
     <item msgid="5966994759929723339">"Til"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 86d5d76..d6568e7 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Unterer Rand <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Linker Rand <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Rechter Rand <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Mit einem Arbeitsprofil aufgenommene Screenshots werden in der App „<xliff:g id="APP">%1$s</xliff:g>“ gespeichert"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"In <xliff:g id="APP">%1$s</xliff:g> im Arbeitsprofil gespeichert"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Dateien"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> hat diesen Screenshot erkannt."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> und andere geöffnete Apps haben diesen Screenshot erkannt."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Bildschirmaufzeichnung"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Bildschirmaufzeichnung…"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Fortlaufende Benachrichtigung für eine Bildschirmaufzeichnung"</string>
@@ -183,9 +185,9 @@
     <string name="accessibility_airplane_mode" msgid="1899529214045998505">"Flugmodus"</string>
     <string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN an."</string>
     <string name="accessibility_battery_level" msgid="5143715405241138822">"Akku bei <xliff:g id="NUMBER">%d</xliff:g> Prozent."</string>
-    <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Akku bei <xliff:g id="PERCENTAGE">%1$d</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Akku bei <xliff:g id="PERCENTAGE">%1$d</xliff:g> Prozent, <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Akku wird aufgeladen, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> Prozent."</string>
-    <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Akku bei <xliff:g id="PERCENTAGE">%d</xliff:g>. Zum Schutz des Akkus wurde das Laden pausiert."</string>
+    <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Akku bei <xliff:g id="PERCENTAGE">%d</xliff:g> Prozent. Zum Schutz des Akkus wurde das Laden pausiert."</string>
     <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Akku bei <xliff:g id="PERCENTAGE">%1$d</xliff:g>. <xliff:g id="TIME">%2$s</xliff:g>. Zum Schutz des Akkus wurde das Laden pausiert."</string>
     <string name="accessibility_overflow_action" msgid="8555835828182509104">"Alle Benachrichtigungen ansehen"</string>
     <string name="accessibility_tty_enabled" msgid="1123180388823381118">"Schreibtelefonie aktiviert"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Helligkeit"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Farbumkehr"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Farbkorrektur"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Nutzer verwalten"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Fertig"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Schließen"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatisch"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Kein Ton und keine Vibration"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Kein Ton und keine Vibration, erscheint weiter unten im Bereich „Unterhaltungen“"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Kann je nach Geräteeinstellungen klingeln oder vibrieren"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Kann je nach Geräteeinstellungen klingeln oder vibrieren. Unterhaltungen von <xliff:g id="APP_NAME">%1$s</xliff:g> werden standardmäßig als Bubble angezeigt."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Das System entscheiden lassen, ob bei dieser Benachrichtigung ein Ton oder eine Vibration ausgegeben wird"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Status&lt;/b&gt;: auf „Standard“ hochgestuft"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Status&lt;/b&gt;: auf „Lautlos“ herabgestuft"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"Bildschirmaufzeichnung"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Kein Titel"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Standby"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Vergrößerungsfenster"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Einstellungen für Vergrößerungsfenster"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Heranzoomen"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"App zum Hinzufügen von Steuerelementen auswählen"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# Steuerelement hinzugefügt.}other{# Steuerelemente hinzugefügt.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Entfernt"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g> hinzufügen?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Wenn du <xliff:g id="APPNAME">%s</xliff:g> hinzufügst, kann diese App Einstellungen und Inhalte zu diesem Bereich hinzufügen. In einigen Apps kannst du festlegen, welche Einstellungen hier angezeigt werden sollen."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Zu Favoriten hinzugefügt"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Zu Favoriten hinzugefügt, Position <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Aus Favoriten entfernt"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> öffnen"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="SONG_NAME">%1$s</xliff:g> von <xliff:g id="ARTIST_NAME">%2$s</xliff:g> über <xliff:g id="APP_LABEL">%3$s</xliff:g> wiedergeben"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> über <xliff:g id="APP_LABEL">%2$s</xliff:g> wiedergeben"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Für mich"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Rückgängig machen"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Gehe für die Wiedergabe näher an „<xliff:g id="DEVICENAME">%1$s</xliff:g>“ heran"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Für eine Wiedergabe auf diesem Gerät muss es näher bei <xliff:g id="DEVICENAME">%1$s</xliff:g> sein"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Ein Fehler ist aufgetreten. Versuch es noch einmal."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Wird geladen"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"Tablet"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Medien werden gestreamt"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"<xliff:g id="APP_LABEL">%1$s</xliff:g> wird gestreamt"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv – sieh in der App nach"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Nicht gefunden"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Steuerelement nicht verfügbar"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Fehler – versuch es noch mal"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Steuerelemente hinzufügen"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Steuerelemente bearbeiten"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"App hinzufügen"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Ausgabegeräte hinzufügen"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Gruppe"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Ein Gerät ausgewählt"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Lautsprecher &amp; Displays"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Vorgeschlagene Geräte"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Premium-Konto erforderlich"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Funktionsweise von Nachrichten an alle"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Nachricht an alle"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Personen, die in der Nähe sind und kompatible Bluetooth-Geräten haben, können sich die Medien anhören, die du per Nachricht an alle sendest"</string>
@@ -894,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Übertragung nicht möglich"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Speichern nicht möglich. Versuche es noch einmal."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Speichern nicht möglich."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Gib mindestens vier Zeichen ein"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Maximal 16 Zeichen möglich"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build-Nummer"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Build-Nummer in Zwischenablage kopiert."</string>
     <string name="basic_status" msgid="2315371112182658176">"Offene Unterhaltung"</string>
@@ -1013,24 +1030,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Mindestens ein Gerät ist verfügbar"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Verknüpfung berühren &amp; halten"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Abbrechen"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Jetzt umdrehen"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Für ein besseres Selfie Smartphone öffnen"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Für ein besseres Selfie Frontbildschirm verwenden?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Verwende die Rückkamera, um Fotos mit einem weiteren Blickwinkel und höherer Auflösung aufzunehmen."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Dieses Display wird dann ausgeschaltet"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Faltbares Gerät wird geöffnet"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Faltbares Gerät wird umgeklappt"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Akku bei <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Schließe deinen Eingabestift an ein Ladegerät an"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Stylus-Akkustand niedrig"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Keine Anrufe über dieses Profil möglich"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Gemäß den Arbeitsrichtlinien darfst du nur über dein Arbeitsprofil telefonieren"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Zum Arbeitsprofil wechseln"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Schließen"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Sperrbildschirm-Einstellungen"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-de/tiles_states_strings.xml b/packages/SystemUI/res/values-de/tiles_states_strings.xml
index bc50e16..bd73a05 100644
--- a/packages/SystemUI/res/values-de/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-de/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Aus"</item>
     <item msgid="5966994759929723339">"An"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index ad66104..7b413ed 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"Τα στιγμιότυπα οθόνης εργασÎŻας αποθηκεύονται στην εφαρμογÎź <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>
     <string name="screenrecord_name" msgid="2596401223859996572">"ΕγγραφÎź οθόνης"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ΕπεξεργασÎŻα εγγραφÎźς οθόνης"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"ΕιδοποÎŻηση σε εξέλιξη για μια περÎŻοδο λειτουργÎŻας εγγραφÎźς οθόνης"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Φωτεινότητα"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ΑντιστροφÎź χρωμÎŹτων"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Διόρθωση χρωμÎŹτων"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"ΔιαχεÎŻριση χρηστών"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Τέλος"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"ΚλεÎŻσιμο"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Αυτόματο"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"ΧωρÎŻς Îźχο Îź δόνηση"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"ΧωρÎŻς Îźχο Îź δόνηση και εμφανÎŻζεται χαμηλÎŹ στην ενότητα συζητÎźσεων"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Ενδέχεται να κουδουνÎŻζει Îź να δονεÎŻται βÎŹσει των ρυθμÎŻσεων συσκευÎźς"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Ενδέχεται να κουδουνÎŻζει Îź να δονεÎŻται βÎŹσει των ρυθμÎŻσεων συσκευÎźς. Οι συζητÎźσεις από την εφαρμογÎź <xliff:g id="APP_NAME">%1$s</xliff:g> εμφανÎŻζονται σε συννεφÎŹκι από προεπιλογÎź."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Επιτρέψτε στο σύστημα να αποφασÎŻσει αν αυτÎź η ειδοποÎŻηση θα αναπαρÎŹγει έναν Îźχο Îź θα ενεργοποιÎźσει τη δόνηση της συσκευÎźς"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;ΚατÎŹσταση:&lt;/b&gt; ΠροÎŹχθηκε σε ΠροεπιλογÎź"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;ΚατÎŹσταση:&lt;/b&gt; ΥποβιβÎŹστηκε σε Αθόρυβη"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"εγγραφÎź οθόνης"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"ΧωρÎŻς τÎŻτλο"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"ΚατÎŹσταση αναμονÎźς"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"ΠαρÎŹθυρο μεγέθυνσης"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"ΣτοιχεÎŻα ελέγχου παραθύρου μεγέθυνσης"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Μεγέθυνση"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"ΕπιλογÎź εφαρμογÎźς για προσθÎźκη στοιχεÎŻων ελέγχου"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Προστέθηκε # στοιχεÎŻο ελέγχου.}other{Προστέθηκαν # στοιχεÎŻα ελέγχου.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"ΚαταργÎźθηκε"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"ΠροσθÎźκη <xliff:g id="APPNAME">%s</xliff:g>;"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Όταν προσθέσετε την εφαρμογÎź <xliff:g id="APPNAME">%s</xliff:g>, μπορεÎŻ να προσθέσει στοιχεÎŻα ελέγχου και περιεχόμενο σε αυτό το πλαÎŻσιο. Σε ορισμένες εφαρμογές, μπορεÎŻτε να επιλέξετε ποια στοιχεÎŻα ελέγχου θα εμφανÎŻζονται εδώ."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Προστέθηκε στα αγαπημένα"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Προστέθηκε στα αγαπημένα, στη θέση <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Αφαιρέθηκε από τα αγαπημένα"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Άνοιγμα της εφαρμογÎźς <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"ΑναπαραγωγÎź του <xliff:g id="SONG_NAME">%1$s</xliff:g> από <xliff:g id="ARTIST_NAME">%2$s</xliff:g> στην εφαρμογÎź <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"ΑναπαραγωγÎź του <xliff:g id="SONG_NAME">%1$s</xliff:g> στην εφαρμογÎź <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Για εσÎŹς"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"ΑναÎŻρεση"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"ΠλησιÎŹστε για αναπαραγωγÎź στη συσκευÎź <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Για να γÎŻνει αναπαραγωγÎź εδώ, μετακινηθεÎŻτε πιο κοντÎŹ στη συσκευÎź <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"ΠαρουσιÎŹστηκε κÎŹποιο πρόβλημα. ΔοκιμÎŹστε ξανÎŹ."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Φόρτωση"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"ΜετÎŹδοση των μέσων σας"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"ΜετÎŹδοση <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Ανενεργό, έλεγχος εφαρμογÎźς"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Δεν βρέθηκε."</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Μη διαθέσιμο στοιχεÎŻο ελέγχου"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"ΣφÎŹλμα, προσπαθÎźστε ξανÎŹ."</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"ΠροσθÎźκη στοιχεÎŻων ελέγχου"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"ΕπεξεργασÎŻα στοιχεÎŻων ελέγχου"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"ΠροσθÎźκη εφαρμογÎźς"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"ΠροσθÎźκη εξόδων"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"ΟμÎŹδα"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Επιλέχτηκε 1 συσκευÎź"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ΗχεÎŻα και οθόνες"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Προτεινόμενες συσκευές"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"ΑπαιτεÎŻ λογαριασμό premium"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Πώς λειτουργεÎŻ η μετÎŹδοση"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"ΜετÎŹδοση"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Οι ÎŹνθρωποι με συμβατές συσκευές Bluetooth που βρÎŻσκονται κοντÎŹ σας μπορούν να ακούσουν το μέσο που μεταδÎŻδετε."</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Δεν εÎŻναι δυνατÎź η μετÎŹδοση"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Δεν εÎŻναι δυνατÎź η αποθÎźκευση. ΔοκιμÎŹστε ξανÎŹ."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Δεν εÎŻναι δυνατÎź η αποθÎźκευση."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Αριθμός έκδοσης"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Ο αριθμός έκδοσης αντιγρÎŹφηκε στο πρόχειρο."</string>
     <string name="basic_status" msgid="2315371112182658176">"Άνοιγμα συνομιλÎŻας"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• ΕÎŻναι διαθέσιμη τουλÎŹχιστον μÎŻα συσκευÎź"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Παρατεταμένο ÎŹγγιγμα συντόμευσης"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Ακύρωση"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"ΑναστροφÎź τώρα"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Ξεδιπλώστε το τηλέφωνο για καλύτερη selfie"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"ΑναστροφÎź στην μπροστ. οθόνη για καλύτερη selfie;"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"ΧρησιμοποιÎźστε την πÎŻσω κÎŹμερα για να βγÎŹλετε μια φωτογραφÎŻα με μεγαλύτερο εύρος και υψηλότερη ανÎŹλυση."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"* ΑυτÎź η οθόνη θα απενεργοποιηθεÎŻ"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Αναδιπλούμενη συσκευÎź που ξεδιπλώνει"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Αναδιπλούμενη συσκευÎź που διπλώνει"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Απομένει το <xliff:g id="PERCENTAGE">%s</xliff:g> της μπαταρÎŻας"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Συνδέστε τη γραφÎŻδα σε έναν φορτιστÎź"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"ΧαμηλÎź στÎŹθμη μπαταρÎŻας γραφÎŻδας"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"ΒιντεοκÎŹμερα"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Δεν εÎŻναι δυνατÎź η κλÎźση από αυτό το προφÎŻλ"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Η πολιτικÎź εργασÎŻας σÎŹς επιτρέπει να πραγματοποιεÎŻτε τηλεφωνικές κλÎźσεις μόνο από το προφÎŻλ εργασÎŻας σας."</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"ΕναλλαγÎź σε προφÎŻλ εργασÎŻας"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"ΚλεÎŻσιμο"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"ΡυθμÎŻσεις κλειδώματος οθόνης"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-el/tiles_states_strings.xml b/packages/SystemUI/res/values-el/tiles_states_strings.xml
index 352af39..5c7c738 100644
--- a/packages/SystemUI/res/values-el/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-el/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Ανενεργό"</item>
     <item msgid="5966994759929723339">"Ενεργό"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 5a82f79..5b6c106 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Bottom boundary <xliff:g id="PERCENT">%1$d</xliff:g> per cent"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Left boundary <xliff:g id="PERCENT">%1$d</xliff:g> per cent"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Right boundary <xliff:g id="PERCENT">%1$d</xliff:g> per cent"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Work screenshots are saved in the <xliff:g id="APP">%1$s</xliff:g> app"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Saved in <xliff:g id="APP">%1$s</xliff:g> in the work profile"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Files"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> detected this screenshot."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> and other open apps detected this screenshot."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
@@ -255,6 +257,7 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brightness"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Colour inversion"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Colour correction"</string>
+    <string name="quick_settings_font_scaling_label" msgid="5289001009876936768">"Font size"</string>
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Manage users"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Done"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Close"</string>
@@ -775,6 +778,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"screen recording"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"No title"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Standby"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Magnification window"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Magnification window controls"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zoom in"</string>
@@ -800,6 +809,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Choose app to add controls"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# control added.}other{# controls added.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Removed"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Add <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"When you add <xliff:g id="APPNAME">%s</xliff:g>, it can add controls and content to this panel. In some apps, you can choose which controls show up here."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Favourited"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Favourited, position <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Unfavourited"</string>
@@ -850,6 +861,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Open <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> by <xliff:g id="ARTIST_NAME">%2$s</xliff:g> from <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> from <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"For you"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Undo"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Move closer to play on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"To play here, move closer to <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -857,6 +869,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Something went wrong. Try again."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Loading"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Casting your media"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Casting <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inactive, check app"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Not found"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Control is unavailable"</string>
@@ -866,6 +880,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Error, try again"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Add controls"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Edit controls"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Add app"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Add outputs"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Group"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"One device selected"</string>
@@ -881,6 +896,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers &amp; displays"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Suggested devices"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Requires premium account"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"How broadcasting works"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Broadcast"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"People near you with compatible Bluetooth devices can listen to the media that you\'re broadcasting"</string>
@@ -892,6 +908,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Can’t broadcast"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Can’t save. Try again."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Can’t save."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Use at least four characters"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Use fewer than 16 characters"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build number"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Build number copied to clipboard."</string>
     <string name="basic_status" msgid="2315371112182658176">"Open conversation"</string>
@@ -1011,11 +1029,11 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• At least one device is available"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Touch &amp; hold shortcut"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Cancel"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Flip now"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Unfold phone for a better selfie"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Flip to front display for a better selfie?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Use the rear-facing camera for a wider photo with higher resolution."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ This screen will turn off"</b></string>
+    <string name="rear_display_bottom_sheet_confirm" msgid="1507591562761552899">"Switch screens now"</string>
+    <string name="rear_display_folded_bottom_sheet_title" msgid="3930008746560711990">"Unfold phone"</string>
+    <string name="rear_display_unfolded_bottom_sheet_title" msgid="6291111173057304055">"Switch screens?"</string>
+    <string name="rear_display_folded_bottom_sheet_description" msgid="6842767125783222695">"For higher resolution, use the rear camera"</string>
+    <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"For higher resolution, flip the phone"</string>
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Foldable device being unfolded"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Foldable device being flipped around"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> battery remaining"</string>
@@ -1026,4 +1044,5 @@
     <string name="call_from_work_profile_text" msgid="3458704745640229638">"Your work policy allows you to make phone calls only from the work profile"</string>
     <string name="call_from_work_profile_action" msgid="2937701298133010724">"Switch to work profile"</string>
     <string name="call_from_work_profile_close" msgid="7927067108901068098">"Close"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Lock screen settings"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml
index 56cdbef..0cf2868 100644
--- a/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml
@@ -176,4 +176,9 @@
     <item msgid="8014986104355098744">"Off"</item>
     <item msgid="5966994759929723339">"On"</item>
   </string-array>
+  <string-array name="tile_states_font_scaling">
+    <item msgid="3173069902082305985">"Unavailable"</item>
+    <item msgid="2478289035899842865">"Off"</item>
+    <item msgid="5137565285664080143">"On"</item>
+  </string-array>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 3bcb98e..50134c8 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Bottom boundary <xliff:g id="PERCENT">%1$d</xliff:g> percent"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Left boundary <xliff:g id="PERCENT">%1$d</xliff:g> percent"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Right boundary <xliff:g id="PERCENT">%1$d</xliff:g> percent"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Work screenshots are saved in the <xliff:g id="APP">%1$s</xliff:g> app"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Saved in <xliff:g id="APP">%1$s</xliff:g> in the work profile"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Files"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> detected this screenshot."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> and other open apps detected this screenshot."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
@@ -255,6 +257,7 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brightness"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Color inversion"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Color correction"</string>
+    <string name="quick_settings_font_scaling_label" msgid="5289001009876936768">"Font size"</string>
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Manage users"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Done"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Close"</string>
@@ -775,6 +778,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"screen recording"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"No title"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Standby"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Magnification Window"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Magnification Window Controls"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zoom in"</string>
@@ -800,6 +809,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Choose app to add controls"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# control added.}other{# controls added.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Removed"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Add <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"When you add <xliff:g id="APPNAME">%s</xliff:g>, it can add controls and content to this panel. In some apps, you can choose which controls show up here."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Favorited"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Favorited, position <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Unfavorited"</string>
@@ -850,6 +861,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Open <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> by <xliff:g id="ARTIST_NAME">%2$s</xliff:g> from <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> from <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"For You"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Undo"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Move closer to play on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"To play here, move closer to <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -857,6 +869,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Something went wrong. Try again."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Loading"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Casting your media"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Casting <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inactive, check app"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Not found"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Control is unavailable"</string>
@@ -866,6 +880,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Error, try again"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Add controls"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Edit controls"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Add app"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Add outputs"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Group"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 device selected"</string>
@@ -881,6 +896,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers &amp; Displays"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Suggested Devices"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Requires premium account"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"How broadcasting works"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Broadcast"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"People near you with compatible Bluetooth devices can listen to the media you\'re broadcasting"</string>
@@ -892,6 +908,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Can’t broadcast"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Can’t save. Try again."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Can’t save."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Use at least 4 characters"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Use fewer than 16 characters"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build number"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Build number copied to clipboard."</string>
     <string name="basic_status" msgid="2315371112182658176">"Open conversation"</string>
@@ -1011,11 +1029,11 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• At least one device is available"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Touch &amp; hold shortcut"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Cancel"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Flip now"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Unfold phone for a better selfie"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Flip to front display for a better selfie?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Use the rear-facing camera for a wider photo with higher resolution."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ This screen will turn off"</b></string>
+    <string name="rear_display_bottom_sheet_confirm" msgid="1507591562761552899">"Switch screens now"</string>
+    <string name="rear_display_folded_bottom_sheet_title" msgid="3930008746560711990">"Unfold phone"</string>
+    <string name="rear_display_unfolded_bottom_sheet_title" msgid="6291111173057304055">"Switch screens?"</string>
+    <string name="rear_display_folded_bottom_sheet_description" msgid="6842767125783222695">"For higher resolution, use the rear camera"</string>
+    <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"For higher resolution, flip the phone"</string>
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Foldable device being unfolded"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Foldable device being flipped around"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> battery remaining"</string>
@@ -1026,4 +1044,5 @@
     <string name="call_from_work_profile_text" msgid="3458704745640229638">"Your work policy allows you to make phone calls only from the work profile"</string>
     <string name="call_from_work_profile_action" msgid="2937701298133010724">"Switch to work profile"</string>
     <string name="call_from_work_profile_close" msgid="7927067108901068098">"Close"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Lock screen settings"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml
index 56cdbef..0cf2868 100644
--- a/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml
@@ -176,4 +176,9 @@
     <item msgid="8014986104355098744">"Off"</item>
     <item msgid="5966994759929723339">"On"</item>
   </string-array>
+  <string-array name="tile_states_font_scaling">
+    <item msgid="3173069902082305985">"Unavailable"</item>
+    <item msgid="2478289035899842865">"Off"</item>
+    <item msgid="5137565285664080143">"On"</item>
+  </string-array>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 5a82f79..5b6c106 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Bottom boundary <xliff:g id="PERCENT">%1$d</xliff:g> per cent"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Left boundary <xliff:g id="PERCENT">%1$d</xliff:g> per cent"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Right boundary <xliff:g id="PERCENT">%1$d</xliff:g> per cent"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Work screenshots are saved in the <xliff:g id="APP">%1$s</xliff:g> app"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Saved in <xliff:g id="APP">%1$s</xliff:g> in the work profile"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Files"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> detected this screenshot."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> and other open apps detected this screenshot."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
@@ -255,6 +257,7 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brightness"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Colour inversion"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Colour correction"</string>
+    <string name="quick_settings_font_scaling_label" msgid="5289001009876936768">"Font size"</string>
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Manage users"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Done"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Close"</string>
@@ -775,6 +778,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"screen recording"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"No title"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Standby"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Magnification window"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Magnification window controls"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zoom in"</string>
@@ -800,6 +809,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Choose app to add controls"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# control added.}other{# controls added.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Removed"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Add <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"When you add <xliff:g id="APPNAME">%s</xliff:g>, it can add controls and content to this panel. In some apps, you can choose which controls show up here."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Favourited"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Favourited, position <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Unfavourited"</string>
@@ -850,6 +861,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Open <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> by <xliff:g id="ARTIST_NAME">%2$s</xliff:g> from <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> from <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"For you"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Undo"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Move closer to play on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"To play here, move closer to <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -857,6 +869,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Something went wrong. Try again."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Loading"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Casting your media"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Casting <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inactive, check app"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Not found"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Control is unavailable"</string>
@@ -866,6 +880,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Error, try again"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Add controls"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Edit controls"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Add app"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Add outputs"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Group"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"One device selected"</string>
@@ -881,6 +896,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers &amp; displays"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Suggested devices"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Requires premium account"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"How broadcasting works"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Broadcast"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"People near you with compatible Bluetooth devices can listen to the media that you\'re broadcasting"</string>
@@ -892,6 +908,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Can’t broadcast"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Can’t save. Try again."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Can’t save."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Use at least four characters"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Use fewer than 16 characters"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build number"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Build number copied to clipboard."</string>
     <string name="basic_status" msgid="2315371112182658176">"Open conversation"</string>
@@ -1011,11 +1029,11 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• At least one device is available"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Touch &amp; hold shortcut"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Cancel"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Flip now"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Unfold phone for a better selfie"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Flip to front display for a better selfie?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Use the rear-facing camera for a wider photo with higher resolution."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ This screen will turn off"</b></string>
+    <string name="rear_display_bottom_sheet_confirm" msgid="1507591562761552899">"Switch screens now"</string>
+    <string name="rear_display_folded_bottom_sheet_title" msgid="3930008746560711990">"Unfold phone"</string>
+    <string name="rear_display_unfolded_bottom_sheet_title" msgid="6291111173057304055">"Switch screens?"</string>
+    <string name="rear_display_folded_bottom_sheet_description" msgid="6842767125783222695">"For higher resolution, use the rear camera"</string>
+    <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"For higher resolution, flip the phone"</string>
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Foldable device being unfolded"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Foldable device being flipped around"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> battery remaining"</string>
@@ -1026,4 +1044,5 @@
     <string name="call_from_work_profile_text" msgid="3458704745640229638">"Your work policy allows you to make phone calls only from the work profile"</string>
     <string name="call_from_work_profile_action" msgid="2937701298133010724">"Switch to work profile"</string>
     <string name="call_from_work_profile_close" msgid="7927067108901068098">"Close"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Lock screen settings"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml
index 56cdbef..0cf2868 100644
--- a/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml
@@ -176,4 +176,9 @@
     <item msgid="8014986104355098744">"Off"</item>
     <item msgid="5966994759929723339">"On"</item>
   </string-array>
+  <string-array name="tile_states_font_scaling">
+    <item msgid="3173069902082305985">"Unavailable"</item>
+    <item msgid="2478289035899842865">"Off"</item>
+    <item msgid="5137565285664080143">"On"</item>
+  </string-array>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 5a82f79..5b6c106 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Bottom boundary <xliff:g id="PERCENT">%1$d</xliff:g> per cent"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Left boundary <xliff:g id="PERCENT">%1$d</xliff:g> per cent"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Right boundary <xliff:g id="PERCENT">%1$d</xliff:g> per cent"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Work screenshots are saved in the <xliff:g id="APP">%1$s</xliff:g> app"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Saved in <xliff:g id="APP">%1$s</xliff:g> in the work profile"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Files"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> detected this screenshot."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> and other open apps detected this screenshot."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
@@ -255,6 +257,7 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brightness"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Colour inversion"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Colour correction"</string>
+    <string name="quick_settings_font_scaling_label" msgid="5289001009876936768">"Font size"</string>
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Manage users"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Done"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Close"</string>
@@ -775,6 +778,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"screen recording"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"No title"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Standby"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Magnification window"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Magnification window controls"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zoom in"</string>
@@ -800,6 +809,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Choose app to add controls"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# control added.}other{# controls added.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Removed"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Add <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"When you add <xliff:g id="APPNAME">%s</xliff:g>, it can add controls and content to this panel. In some apps, you can choose which controls show up here."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Favourited"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Favourited, position <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Unfavourited"</string>
@@ -850,6 +861,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Open <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> by <xliff:g id="ARTIST_NAME">%2$s</xliff:g> from <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> from <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"For you"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Undo"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Move closer to play on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"To play here, move closer to <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -857,6 +869,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Something went wrong. Try again."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Loading"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Casting your media"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Casting <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inactive, check app"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Not found"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Control is unavailable"</string>
@@ -866,6 +880,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Error, try again"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Add controls"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Edit controls"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Add app"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Add outputs"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Group"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"One device selected"</string>
@@ -881,6 +896,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers &amp; displays"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Suggested devices"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Requires premium account"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"How broadcasting works"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Broadcast"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"People near you with compatible Bluetooth devices can listen to the media that you\'re broadcasting"</string>
@@ -892,6 +908,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Can’t broadcast"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Can’t save. Try again."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Can’t save."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Use at least four characters"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Use fewer than 16 characters"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build number"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Build number copied to clipboard."</string>
     <string name="basic_status" msgid="2315371112182658176">"Open conversation"</string>
@@ -1011,11 +1029,11 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• At least one device is available"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Touch &amp; hold shortcut"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Cancel"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Flip now"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Unfold phone for a better selfie"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Flip to front display for a better selfie?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Use the rear-facing camera for a wider photo with higher resolution."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ This screen will turn off"</b></string>
+    <string name="rear_display_bottom_sheet_confirm" msgid="1507591562761552899">"Switch screens now"</string>
+    <string name="rear_display_folded_bottom_sheet_title" msgid="3930008746560711990">"Unfold phone"</string>
+    <string name="rear_display_unfolded_bottom_sheet_title" msgid="6291111173057304055">"Switch screens?"</string>
+    <string name="rear_display_folded_bottom_sheet_description" msgid="6842767125783222695">"For higher resolution, use the rear camera"</string>
+    <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"For higher resolution, flip the phone"</string>
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Foldable device being unfolded"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Foldable device being flipped around"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> battery remaining"</string>
@@ -1026,4 +1044,5 @@
     <string name="call_from_work_profile_text" msgid="3458704745640229638">"Your work policy allows you to make phone calls only from the work profile"</string>
     <string name="call_from_work_profile_action" msgid="2937701298133010724">"Switch to work profile"</string>
     <string name="call_from_work_profile_close" msgid="7927067108901068098">"Close"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Lock screen settings"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml
index 56cdbef..0cf2868 100644
--- a/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml
@@ -176,4 +176,9 @@
     <item msgid="8014986104355098744">"Off"</item>
     <item msgid="5966994759929723339">"On"</item>
   </string-array>
+  <string-array name="tile_states_font_scaling">
+    <item msgid="3173069902082305985">"Unavailable"</item>
+    <item msgid="2478289035899842865">"Off"</item>
+    <item msgid="5137565285664080143">"On"</item>
+  </string-array>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 8641491..f8f7759 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‏‏‎‏‏‎‏‏‏‎‎‏‎‏‎‎‏‏‏‎‎‎‏‎‏‏‏‎‏‎‏‎‎‎‎‏‎‏‏‏‎‏‏‏‎‏‎‏‎‏‏‏‎‎Bottom boundary ‎‏‎‎‏‏‎<xliff:g id="PERCENT">%1$d</xliff:g>‎‏‎‎‏‏‏‎ percent‎‏‎‎‏‎"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‏‎‎‎‏‏‏‎‏‎‎‎‎‏‏‏‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‏‎‏‏‏‎‏‏‎‏‎Left boundary ‎‏‎‎‏‏‎<xliff:g id="PERCENT">%1$d</xliff:g>‎‏‎‎‏‏‏‎ percent‎‏‎‎‏‎"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‏‎‏‎‏‎‏‏‎‏‎‏‏‎‎‎‎‎‏‏‎‏‎‏‏‎‏‎‎‎‏‏‎‏‎‏‏‏‏‎‏‏‎‎‏‏‎‏‎‏‏‏‏‎‎‏‎Right boundary ‎‏‎‎‏‏‎<xliff:g id="PERCENT">%1$d</xliff:g>‎‏‎‎‏‏‏‎ percent‎‏‎‎‏‎"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎‎‎‎‏‏‏‏‎‏‏‎‏‏‏‎‎‏‎‎‎‎‏‎‏‎‎‎‏‏‏‎‏‏‎‎‎‏‏‎‎‏‏‏‏‎‏‎‎‏‎‏‎‎‏‎Work screenshots are saved in the ‎‏‎‎‏‏‎<xliff:g id="APP">%1$s</xliff:g>‎‏‎‎‏‏‏‎ app‎‏‎‎‏‎"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‏‏‎‏‎‎‎‏‎‏‎‏‏‎‎‏‎‏‎‏‏‏‏‏‎‏‎‎‏‏‎‎‏‏‎‎‎‎‏‎‏‎‎‏‏‏‎‎‏‏‎‎‎‏‎‏‎Saved in ‎‏‎‎‏‏‎<xliff:g id="APP">%1$s</xliff:g>‎‏‎‎‏‏‏‎ in the work profile‎‏‎‎‏‎"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‏‎‎‏‎‏‎‎‎‏‏‎‎‏‏‎‏‎‏‎‏‏‏‏‎‏‎‏‎‏‎‎‎‏‏‎‎‎‎‎‏‎‏‎‎‎‏‏‏‎‎‎‎Files‎‏‎‎‏‎"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‏‎‎‎‏‏‏‏‎‎‎‏‎‎‏‏‏‏‏‎‎‏‎‏‎‎‎‎‎‎‎‏‎‏‎‎‎‎‏‏‎‏‎‎‎‎‏‏‎‏‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="APPNAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ detected this screenshot.‎‏‎‎‏‎"</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‎‏‏‏‏‏‏‎‎‎‏‎‎‏‎‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‎‎‏‎‎‏‎‎‏‏‎‎‎‏‎‏‏‎‏‎‎‏‎‎‏‎‎‏‏‎<xliff:g id="APPNAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ and other open apps detected this screenshot.‎‏‎‎‏‎"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‎‎‎‎‏‎‎‎‎‏‎‎‎‏‎‏‎‎‏‎‏‎‎‎‎‎‏‎‎‎‏‎‏‎‎‏‎‎‎‎‏‎‎‎‎‏‏‏‏‎‎‏‏‏‎‎‎Screen Recorder‎‏‎‎‏‎"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‏‎‎‏‎‎‎‎‏‎‏‎‏‎‎‎‏‏‎‏‏‏‏‎‎‎‏‏‏‏‎‎‎‏‏‎‎‏‎‎Processing screen recording‎‏‎‎‏‎"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‎‏‏‎‏‎‏‏‎‎‎‎‎‏‎‏‎‏‏‎‏‎‎‎‏‏‎‏‏‏‎‎‏‎‎‎‎‎‎‏‏‎‎‎‏‏‏‏‎‏‏‏‏‎Ongoing notification for a screen record session‎‏‎‎‏‎"</string>
@@ -255,6 +257,7 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‎‎‏‏‏‏‎‏‏‏‎‎‏‎‎‏‏‏‏‎‎‏‎‎‎‏‎‏‏‎‏‏‎‎‏‏‎‏‎‏‏‎‎‏‏‎‏‎‎‎‏‎‏‎‎‎Brightness‎‏‎‎‏‎"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‏‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‏‏‏‏‏‎‏‎‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‎‎‎‏‎‏‏‎‏‎‎‎‎Color inversion‎‏‎‎‏‎"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‏‏‏‎‎‏‎‏‎‎‏‎‏‎‎‎‏‎‎‎‎‎‏‏‎‏‏‎‎‎‎‏‏‎‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‎‏‎‎‎‎Color correction‎‏‎‎‏‎"</string>
+    <string name="quick_settings_font_scaling_label" msgid="5289001009876936768">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‏‏‎‎‏‏‎‎‏‎‎‏‏‏‎‎‏‏‎‎‏‎‏‎‏‏‎‏‎‏‎‎‏‏‎‎‏‎‏‏‎‏‎‏‎‎‎‎‏‎‎‎‎‎‎‎Font size‎‏‎‎‏‎"</string>
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‏‏‎‎‏‎‏‏‏‎‏‏‏‏‏‎‎‎‏‏‏‎‎‎‎‏‎‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‎Manage users‎‏‎‎‏‎"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‎‎‎‎‏‏‎‏‏‎‎‏‏‎‎‎‏‎‎‏‏‎‎‏‏‏‎‎‏‏‎‎‎‏‎‎‏‎‎‏‏‎‏‎‎‏‎‏‏‏‏‎‎‎‏‎Done‎‏‎‎‏‎"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‎‏‏‎‏‏‎‎‎‏‏‎‏‏‏‏‏‎‎‏‏‎‏‎‏‏‏‎‏‏‎‎‏‏‎‎‏‎‎‎‏‎‎‎‏‏‎‎‎‎‏‎‎‎‏‎Close‎‏‎‎‏‎"</string>
@@ -775,6 +778,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‏‎‏‏‎‏‏‏‎‎‏‏‎‎‏‏‎‏‏‎‎‏‎‎‏‎‏‏‎‏‎‏‎‏‎‏‎‏‏‎‏‏‏‏‏‎‎‏‏‎‏‏‎screen recording‎‏‎‎‏‎"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‏‎‎‏‎‎‏‎‏‏‏‏‏‎‎‎‎‎‎‎‏‏‏‎‎‏‎‎‏‏‎‎‎‏‏‏‎‎‏‏‎‏‏‏‎‎‏‏‏‏‎‏‎‎No title‎‏‎‎‏‎"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‎‎‎‎‎‎‏‏‏‎‏‏‎‏‏‏‎‏‏‎‎‎‎‏‏‎‎‏‎‏‎‎‏‎‏‎‎‎‏‎‏‎‎‏‏‎‏‏‏‎‎‏‎‏‎Standby‎‏‎‎‏‎"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‎‎‎‎‎‎‎‎‎‏‏‎‎‎‎‏‎‏‎‏‏‏‎‏‎‏‎‎‎‎‎‎‏‏‏‎‎‎‏‏‎‏‎‎‏‎‏‏‎‏‏‏‎‏‎Magnification Window‎‏‎‎‏‎"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‏‎‏‏‏‎‎‎‏‏‏‏‏‎‏‎‏‏‏‏‎‎‎‎‏‎‎‏‎‎‏‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎Magnification Window Controls‎‏‎‎‏‎"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‎‎‎‎‏‎‎‏‎‎‏‎‎‏‏‎‏‏‏‏‎‏‎‏‏‎‎‎‏‎‏‏‎‎‏‎‏‏‏‎‎‎‎‏‎‏‎‎‎‏‎‎‏‎Zoom in‎‏‎‎‏‎"</string>
@@ -800,6 +809,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‏‏‏‏‏‎‏‏‏‎‎‎‏‎‏‎‎‎‎‏‎‎‏‏‏‎‏‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‎‎Choose app to add controls‎‏‎‎‏‎"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‏‎‎‏‎‏‎‎‏‎‎‏‏‏‎‎‎‎‏‏‏‎‏‏‎‏‎‎‏‎‎‏‏‎‎‏‎‎‏‎‎‏‎‏‎‎‏‎‎‎‎‏‏‎# control added.‎‏‎‎‏‎}other{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‏‎‎‏‎‏‎‎‏‎‎‏‏‏‎‎‎‎‏‏‏‎‏‏‎‏‎‎‏‎‎‏‏‎‎‏‎‎‏‎‎‏‎‏‎‎‏‎‎‎‎‏‏‎# controls added.‎‏‎‎‏‎}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‎‎‏‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‏‏‎‎‏‎‎‏‏‎‏‎‏‏‏‎‏‎‎‏‎‏‏‏‎‏‏‏‏‏‏‏‏‎Removed‎‏‎‎‏‎"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‏‎‏‏‎‏‏‎‎‎‎‏‏‎‎‏‏‎‎‏‎‎‎‏‎‎‎‏‎‏‎‎‏‏‎‏‏‎‎‏‎‏‎‏‏‏‎‎‏‎‎‎‏‎‏‎‎Add ‎‏‎‎‏‏‎<xliff:g id="APPNAME">%s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‎‎‎‎‎‏‎‏‏‏‏‏‎‎‏‏‎‏‏‎‏‎‎‏‏‎‏‎‎‎‎‏‎‏‏‏‎‎‏‏‎‏‏‏‎‏‎‏‎‏‎‏‏‏‎When you add ‎‏‎‎‏‏‎<xliff:g id="APPNAME">%s</xliff:g>‎‏‎‎‏‏‏‎, it can add controls and content to this panel. In some apps, you can choose which controls show up here.‎‏‎‎‏‎"</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‎‎‎‏‎‎‏‎‏‎‎‏‏‎‎‏‏‎‏‏‎‏‏‏‏‎‎‎‎‏‏‎‏‎‏‎‎‎‏‏‎‏‏‎‎‎‎‎‎‎‏‎Favorited‎‏‎‎‏‎"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‎‏‏‎‎‎‎‎‎‏‎‏‎‎‎‎‏‎‎‎‎‏‎‎‏‏‎‎‏‎‏‎‎‏‎‏‎‎‎‏‎‏‏‏‎‏‏‎‏‏‏‎‎‎‏‎‏‎Favorited, position ‎‏‎‎‏‏‎<xliff:g id="NUMBER">%d</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‏‏‏‏‎‏‏‎‏‎‏‎‎‎‎‎‏‎‎‎‏‏‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‎‏‎‏‏‏‎‏‏‎‎‎‎‏‏‏‎Unfavorited‎‏‎‎‏‎"</string>
@@ -850,6 +861,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‎‏‏‎‎‏‏‎‏‏‏‎‎‎‏‎‎‏‏‏‏‎‏‏‎‏‎‏‎‎‎‎‎‏‏‎‎‏‎‏‏‎‎‏‏‏‎‏‎‏‎‎‏‏‎‎‎Open ‎‏‎‎‏‏‎<xliff:g id="APP_LABEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‎‎‎‎‏‏‏‎‏‏‎‏‏‎‎‎‏‏‎‎‎‏‎‏‏‎‏‏‎‏‏‎‎‎‏‎‏‏‎‎‏‎‎‎‏‎‏‏‎‎‎‏‏‎Play ‎‏‎‎‏‏‎<xliff:g id="SONG_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ by ‎‏‎‎‏‏‎<xliff:g id="ARTIST_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎ from ‎‏‎‎‏‏‎<xliff:g id="APP_LABEL">%3$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‎‎‏‎‏‏‏‎‎‏‏‏‎‎‎‏‎‏‏‏‎‏‎‏‏‏‎‏‎‎‏‎‎‎‏‎‎‏‎‎‏‏‏‏‏‏‎‎‏‎‏‎Play ‎‏‎‎‏‏‎<xliff:g id="SONG_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ from ‎‏‎‎‏‏‎<xliff:g id="APP_LABEL">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‎‏‎‎‎‎‏‏‎‎‎‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‏‎‎‏‎‎‏‏‎‎‎‏‏‎‎‏‎‏‎‎‎‎‎‎‎‎‎‏‎‎For You‎‏‎‎‏‎"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‎‏‎‎‏‏‏‎‏‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‎‎‎‎‎‎‏‏‎‎‎‎‏‎‎‏‏‏‏‏‎‏‎‎‎‎‎‎Undo‎‏‎‎‏‎"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‎‎‎‏‏‎‎‎‏‏‎‎‎‏‏‎‏‎‎‏‎‎‎‏‏‎‎‏‎‏‏‏‏‏‏‎‎‎‎‎‎‏‏‎‎‏‏‏‏‎‏‏‏‎‎‎‎Move closer to play on ‎‏‎‎‏‏‎<xliff:g id="DEVICENAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‎‏‎‏‏‏‏‏‏‎‎‎‏‏‏‏‏‎‏‎‎‎‏‎‏‎‎‎‏‏‎‏‏‏‏‏‎‏‎‏‏‏‎‏‎‏‏‎‎‏‎‎‏‎‎To play here, move closer to ‎‏‎‎‏‏‎<xliff:g id="DEVICENAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
@@ -857,6 +869,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‏‏‏‎‎‎‏‏‎‎‏‎‎‏‏‎‎‎‎‏‏‏‎‎‏‏‏‏‏‎‎‎‎‎‏‎‏‎‏‏‎‏‎‏‏‎‎‏‎‏‏‎Something went wrong. Try again.‎‏‎‎‏‎"</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‏‏‏‎‎‎‎‎‏‎‎‏‏‏‎‎‎‏‏‎‏‎‎‎‎‎‏‏‏‎‏‎‎‏‎‎‎‏‎‏‎‎‏‎‏‏‎‎‎‏‎‎‏‏‎‎Loading‎‏‎‎‏‎"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‏‏‏‎‎‏‎‏‏‏‏‎‏‏‏‎‏‎‎‎‎‏‏‏‎‏‎‎‏‎‎‏‏‎‏‎‏‎‎‏‎‎‏‏‎‎‎‏‏‏‎‎‏‎tablet‎‏‎‎‏‎"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‏‏‎‎‎‏‎‏‎‏‎‎‏‎‎‎‎‎‏‎‎‏‎‏‏‏‎‏‏‏‎‏‎‎‎‏‏‎‏‏‎‎‎‏‎‏‎‏‏‏‎‎‏‏‎Casting your media‎‏‎‎‏‎"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‏‏‏‏‎‏‏‏‎‎‏‎‎‏‎‏‎‏‎‏‏‏‏‏‎‏‎‏‎‎‎‎‎‏‏‏‏‎‏‎Casting ‎‏‎‎‏‏‎<xliff:g id="APP_LABEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‎‎‎‎‏‎‏‏‎‎‎‏‏‏‎‎‎‏‏‏‎‏‎‎‎‏‎‏‎‏‎‎‏‏‏‏‎‏‏‎‏‎‎‎‎‏‎‎‏‏‏‏‏‏‎‎Inactive, check app‎‏‎‎‏‎"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‏‎‎‏‎‎‏‎‏‎‎‎‎‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎‏‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‎‎‏‏‏‏‎‎Not found‎‏‎‎‏‎"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‎‎‏‎‏‏‏‏‎‎‏‏‎‎‎‏‎‎‏‎‏‎‎‏‎‏‎‎‎‎‎‏‎‏‎‎‏‎‎‎‎‏‎‎‏‏‎‎‏‎‏‎‎Control is unavailable‎‏‎‎‏‎"</string>
@@ -866,6 +880,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‎‏‎‏‎‎‏‏‎‏‏‎‏‎‏‎‏‏‎‏‏‏‎‎‎‎‎‏‏‏‏‎‏‎‎‏‎‏‏‎‎‎‏‏‏‎‎‏‎‎‏‏‏‏‎‏‎Error, try again‎‏‎‎‏‎"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‏‏‎‏‏‏‏‏‎‎‏‎‏‎‏‏‎‎‏‎‎‎‎‏‏‎‏‏‎‏‎‏‏‎‏‏‏‏‎‎‎‏‎‎‏‏‎‎‏‏‎‎‏‎‎Add controls‎‏‎‎‏‎"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‎‏‎‏‏‏‎‎‎‎‏‎‎‎‎‏‏‏‎‎‏‏‎‎‎‎‏‎‎‎‏‎‏‎‏‎‏‏‎‏‎‏‎‏‏‎‏‏‎‏‏‏‎‏‏‎‎Edit controls‎‏‎‎‏‎"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‎‎‏‎‏‎‏‎‏‎‏‎‎‏‎‏‎‎‏‎‎‏‏‏‏‏‎‏‎‎‏‎‏‎‎‏‎‏‎‏‎‏‏‏‏‎‎‏‎‎‎‏‎Add app‎‏‎‎‏‎"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎‏‏‏‎‏‏‏‎‏‎‎‎‏‎‏‏‎‎‏‏‎‎‎‎‏‏‏‎‎‎‏‏‏‎‏‎‏‎‏‏‏‎‎‎‎‏‏‎‏‏‏‎‎Add outputs‎‏‎‎‏‎"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‎‏‎‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‏‎‏‎‎‏‎‎‎‏‎‏‎‎‏‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‏‎‎‎Group‎‏‎‎‏‎"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‎‎‎‎‏‏‏‏‎‎‏‏‎‏‏‎‏‏‎‏‏‎‏‎‏‎‎‏‎‎‎‎‏‏‏‎‎‏‏‎‎‏‏‎‏‎‏‎‎‎‎‏‎‎‏‎‎1 device selected‎‏‎‎‏‎"</string>
@@ -881,6 +896,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‎‏‏‎‎‏‏‎‎‎‎‎‎‏‏‎‏‎‏‏‏‎‎‏‏‎‎‏‎‏‎‎‎‏‎‎‎‏‎‎‎‎‏‎‎‏‎‎‏‏‏‏‎‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%1$d</xliff:g>‎‏‎‎‏‏‏‎%%‎‏‎‎‏‎"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‎‎‎‏‎‎‏‎‏‎‎‎‎‏‏‎‎‏‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‎‎‎Speakers &amp; Displays‎‏‎‎‏‎"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‏‎‎‎‏‎‏‎‎‏‎‏‎‏‏‎‏‏‎‏‏‎‎‎‏‎‏‎‎‎‏‎‎‎‎‏‎‏‏‏‏‎‎‏‏‏‏‎‏‎‎‏‎‎Suggested Devices‎‏‎‎‏‎"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‏‏‏‎‏‏‎‎‏‏‎‏‎‏‎‎‎‏‏‏‏‏‎‏‎‎‏‎‎‎‎‏‏‏‎‏‏‎‏‎‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎Requires premium account‎‏‎‎‏‎"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‏‎‏‎‎‏‎‏‎‎‎‎‏‎‎‏‏‏‎‏‏‏‎‏‎‏‎‎‎‏‎‏‏‏‏‎‎‎‎‎‏‏‎‏‏‏‎‎‏‏‏‏‏‏‎‎How broadcasting works‎‏‎‎‏‎"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‎‏‎‏‎‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‎‎‎‏‎‎‏‏‎‏‎‏‏‎‏‏‏‎‎‎‏‏‏‏‎‏‏‏‎Broadcast‎‏‎‎‏‎"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‏‏‎‏‎‏‏‎‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‎‎‎‏‎‎‎‎‏‎‏‏‎‎‏‏‏‏‎‏‎‎‏‏‏‏‎‎People near you with compatible Bluetooth devices can listen to the media you\'re broadcasting‎‏‎‎‏‎"</string>
@@ -892,6 +908,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‏‏‏‎‎‎‏‎‏‏‎‏‏‎‏‏‏‎‏‏‎‎‎‎‏‎‎‎‎‎‏‎‎‎‏‏‎‎‎‎‎‏‎‎‏‎‎‏‏‏‎‏‏‏‏‎Can’t broadcast‎‏‎‎‏‎"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‏‎‏‏‎‏‏‏‏‏‏‏‎‎‎‎‎‎‏‎‎‎‎‏‏‏‏‏‎‏‎‎‎‎‏‎‎‎‎‏‏‏‎‎‏‎‎‎‎‏‎‏‏‎‎‏‎Can’t save. Try again.‎‏‎‎‏‎"</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‎‎‏‏‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‎‎‏‎‏‏‏‏‏‏‎‎‎‏‎‏‎‎‎‎‎‎‎‏‏‎Can’t save.‎‏‎‎‏‎"</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‎‏‏‏‎‎‏‎‏‎‎‎‏‏‎‎‎‏‏‎‏‏‏‎‏‎‏‎‏‏‎‏‎‎‎‎‏‏‏‎‎‎‎‏‏‎‏‎‎‏‏‏‎‎‏‎Use at least 4 characters‎‏‎‎‏‎"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‏‎‎‎‏‏‎‏‏‎‎‎‏‎‏‎‎‎‏‎‏‎‏‎‎‏‎‎‏‎‏‏‎‎‏‏‏‏‎‎‎‎‎‏‎‏‏‎‎Use fewer than 16 characters‎‏‎‎‏‎"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‎‎‏‎‎‎‎‎‎‏‏‏‎‎‎‎‎‎‏‎‎‏‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‎‎‏‎‎‏‎‎Build number‎‏‎‎‏‎"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‎‎‏‎‏‏‏‎‎‏‎‎‏‎‏‎‏‎‎‎‎‏‏‏‎‎‏‎‎‎‎‎‎‎‏‏‏‎‎‏‎‏‏‏‏‎‎‎‎‏‎‎‎‏‏‎‎Build number copied to clipboard.‎‏‎‎‏‎"</string>
     <string name="basic_status" msgid="2315371112182658176">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‎‏‎‎‎‎‏‏‏‎‏‏‎‎‏‏‏‎‎‎‎‏‎‎‏‎‏‏‎‎‎‏‏‎‏‏‎‎‏‎‎‏‏‎‏‎‎‏‎‎‎‎‎‎‎‎Open conversation‎‏‎‎‏‎"</string>
@@ -1011,11 +1029,11 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‎‏‏‎‏‏‏‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‎‏‎‎‏‎‎‏‏‏‎‏‎‏‏‏‏‏‏‏‎‏‏‎‏‎‏‎‎‎• At least one device is available‎‏‎‎‏‎"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‎‏‎‏‎‎‏‏‎‎‏‏‏‏‏‏‏‎‏‎‎‎‏‎‏‎‏‎‎‎‎‏‎‏‏‏‎‏‏‎‏‎‎‎‏‎‏‎‏‎‎‎‎‎Touch &amp; hold shortcut‎‏‎‎‏‎"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‎‎‎‎‏‎‎‏‏‎‎‏‏‎‏‏‏‎‎‏‎‏‎‏‏‎‎‏‏‏‎‎‏‏‎‎‎‎‏‎‎‏‏‎‏‏‏‎‏‏‎‎‎‎‎‎‎Cancel‎‏‎‎‏‎"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‏‏‎‏‎‏‎‎‏‏‎‎‏‏‏‏‏‎‎‎‏‏‎‏‏‎‏‎‏‎‎‎‎‏‏‎‏‏‎‏‎‏‏‏‏‏‎‎‏‎‎‏‎‏‏‎‎Flip now‎‏‎‎‏‎"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‎‏‎‏‏‏‏‏‏‎‏‎‎‏‏‏‏‎‏‎‏‏‏‎‏‎‎‎‎‎‏‏‏‎‎‎‏‏‏‎‏‏‎‎‎‎‎‎‏‏‎‎‎Unfold phone for a better selfie‎‏‎‎‏‎"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‏‎‏‎‏‎‎‏‏‎‎‏‎‏‎‏‎‏‏‎‏‏‏‏‎‎‏‏‎‎‎‏‎‎‎‎‎‏‏‎‎‎‎‎‎‎‎‎‎‏‎‎‎‏‎‏‎Flip to front display for a better selfie?‎‏‎‎‏‎"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‏‎‏‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‎‏‏‎‎‎‎‏‏‎‏‎‎‏‏‏‏‏‏‎‎‎‏‎‏‎‏‏‏‎‎‎‎‎Use the rear-facing camera for a wider photo with higher resolution.‎‏‎‎‏‎"</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‎‎‏‏‏‎‏‏‎‏‏‎‏‎‏‏‎‎‎‏‎‏‏‏‎‎‎‎‎‏‏‏‏‎‎‏‏‏‎‎‏‏‎‎‎‏‎‏‏‎‏‎‎‏‎‎‎‏‎‎‏‏‎"<b>"‎‏‎‎‏‏‏‎✱ This screen will turn off‎‏‎‎‏‏‎"</b>"‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <string name="rear_display_bottom_sheet_confirm" msgid="1507591562761552899">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‏‏‏‎‏‏‎‎‎‎‎‎‏‎‏‎‏‎‎‎‏‎‎‏‏‏‏‎‏‏‎‎‏‏‎‎‎‏‏‏‎‏‎‎‎‏‎‎‎‎‎‎‎‎‏‏‎Switch screens now‎‏‎‎‏‎"</string>
+    <string name="rear_display_folded_bottom_sheet_title" msgid="3930008746560711990">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‎‎‎‏‎‏‎‎‎‏‏‎‎‏‎‎‎‏‎‎‎‏‎‎‎‎‎‎‏‏‎‏‏‏‎‏‏‎‏‏‏‏‎‏‏‎‏‎‎‏‏‎‏‏‎‎Unfold phone‎‏‎‎‏‎"</string>
+    <string name="rear_display_unfolded_bottom_sheet_title" msgid="6291111173057304055">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‏‎‎‏‏‏‎‏‎‎‎‎‏‎‎‎‏‎‎‎‏‏‏‏‎‏‏‏‎‎‎‏‎‎‏‎‎‏‎‎‏‎‏‎‏‎‏‏‏‏‏‎‏‏‏‎Switch screens?‎‏‎‎‏‎"</string>
+    <string name="rear_display_folded_bottom_sheet_description" msgid="6842767125783222695">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‎‏‏‎‎‏‏‎‎‏‎‎‎‏‏‏‎‎‏‎‎‏‎‎‎‏‎‏‎‎‎‎‎‏‏‏‏‎‎‏‏‎‎‏‏‎‏‎‎‏‏‏‎For higher resolution, use the rear camera‎‏‎‎‏‎"</string>
+    <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‎‏‎‏‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‏‏‏‎‎‎‎‏‏‎‏‎‎‏‎‎‎‎‎‏‎‎‏‏‏‎‎‎‎‎‏‎‎‏‎For higher resolution, flip the phone‎‏‎‎‏‎"</string>
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‎‏‎‏‏‎‎‎‏‎‎‎‎‎‎‏‎‏‏‏‏‏‎‏‏‎‏‏‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎Foldable device being unfolded‎‏‎‎‏‎"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‎‎‎‎‎‎‏‎‎‎‏‎‎‎‎‎‎‏‏‏‎‏‏‎‎‏‏‏‏‏‎‏‎‏‎‏‎‎‏‎‏‏‏‎‎‎‎‏‏‎‎‏‎‎‎‎‎Foldable device being flipped around‎‏‎‎‏‎"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‎‏‏‏‏‎‏‏‏‎‏‎‎‎‏‏‎‏‎‏‏‏‎‏‎‏‎‏‏‏‎‎‏‎‎‏‎‎‎‏‎‏‎‏‏‏‎‎‎‏‏‏‎‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%s</xliff:g>‎‏‎‎‏‏‏‎ battery remaining‎‏‎‎‏‎"</string>
@@ -1026,4 +1044,5 @@
     <string name="call_from_work_profile_text" msgid="3458704745640229638">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎‏‎‎‏‎‎‎‎‏‎‎‎‏‎‏‏‎‏‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‏‏‎‎Your work policy allows you to make phone calls only from the work profile‎‏‎‎‏‎"</string>
     <string name="call_from_work_profile_action" msgid="2937701298133010724">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‎‎‏‎‎‏‏‎‎‏‏‏‏‏‏‎‎‎‏‎‎‎‎‎‏‏‎‏‏‎‏‏‎‏‏‎‎‎‎‎‎‏‎‎‏‎‎‏‎‎‏‎‎‎Switch to work profile‎‏‎‎‏‎"</string>
     <string name="call_from_work_profile_close" msgid="7927067108901068098">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‎‎‎‎‏‎‏‎‎‏‏‎‎‏‏‎‎‎‏‎‎‏‎‏‎‎‎‎‎‎‏‎‏‏‏‎‏‏‎‎‎‏‏‏‎‏‎‏‎‎‎‎‏‎‎Close‎‏‎‎‏‎"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‏‎‏‏‏‎‎‏‎‏‎‏‏‏‎‎‎‎‎‏‏‏‎‎‎‏‏‎‏‎‏‎‎‎‎‏‏‎‎‏‏‎‎‎‏‏‎Lock screen settings‎‏‎‎‏‎"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml
index 3a8e34c..b9c8e5f 100644
--- a/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml
@@ -176,4 +176,9 @@
     <item msgid="8014986104355098744">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‎‏‏‏‏‎‎‏‏‎‏‏‎‎‏‎‏‏‎‎‎‏‏‎‏‏‏‎‎‎‏‏‏‎‎‎‏‎‎‎‎‎‏‏‏‏‎‎‎‎Off‎‏‎‎‏‎"</item>
     <item msgid="5966994759929723339">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‎‎‏‏‏‏‎‎‎‎‎‏‏‎‎‎‎‏‏‏‏‎‏‎‏‎‎‎‏‎‎‎‏‏‏‎‏‎‎‏‏‏‏‎‎‏‏‏‎‎‏‎‏‏‎On‎‏‎‎‏‎"</item>
   </string-array>
+  <string-array name="tile_states_font_scaling">
+    <item msgid="3173069902082305985">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‎‏‎‏‎‎‏‎‎‎‏‏‎‎‏‎‏‏‎‏‏‏‏‏‏‎‎‎‎‎‏‎Unavailable‎‏‎‎‏‎"</item>
+    <item msgid="2478289035899842865">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‎‏‏‎‎‏‎‎‏‎‏‎‎‏‏‎‏‎‏‏‏‏‏‏‎‏‏‎‎‎‏‏‏‎‎‎‏‏‏‎‎‎‏‏‎‎‎‏‎‎‏‏‎‎‎‏‎Off‎‏‎‎‏‎"</item>
+    <item msgid="5137565285664080143">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‎‎‏‏‎‎‎‏‎‎‏‏‎‎‎‏‏‎‏‎‎‎‎‏‎‎‎‏‏‎‎‏‏‎‎‎‏‎‏‏‎‎‎‎‎‏‎‎‎‎‏‏‏‏‎On‎‏‎‎‏‎"</item>
+  </string-array>
 </resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 2d7954d..4b49a20 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Límite inferior: <xliff:g id="PERCENT">%1$d</xliff:g> por ciento"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Límite izquierdo: <xliff:g id="PERCENT">%1$d</xliff:g> por ciento"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Límite derecho: <xliff:g id="PERCENT">%1$d</xliff:g> por ciento"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Las capturas de pantalla de trabajo se guardan en la app de <xliff:g id="APP">%1$s</xliff:g>"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Se guardó en <xliff:g id="APP">%1$s</xliff:g> en el perfil de trabajo"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Files"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> detectó que tomaste una captura de pantalla."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> y otras apps en ejecución detectaron que tomaste una captura de pantalla."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Grabadora de pantalla"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Procesando grabación pantalla"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificación constante para una sesión de grabación de pantalla"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillo"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Invertir colores"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Corregir colores"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Administrar usuarios"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Listo"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Cerrar"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Sin sonido ni vibración"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"No suena ni vibra, y aparece en la parte inferior de la sección de conversaciones."</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Puede sonar o vibrar según la configuración del dispositivo"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Puede sonar o vibrar según la configuración del dispositivo. Conversaciones de la burbuja de <xliff:g id="APP_NAME">%1$s</xliff:g> de forma predeterminada."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Dejar que el sistema determine si esta notificación debe emitir un sonido o una vibración"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Estado:&lt;/b&gt; Se promovió a Predeterminada"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Estado:&lt;/b&gt; Descendió de nivel a Silenciada"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"Grabación de pant."</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Sin título"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"En espera"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Ventana de ampliación"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Controles de ampliación de la ventana"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Acercar"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Elige la app para agregar los controles"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Se agregó # control.}many{Se agregaron # controles.}other{Se agregaron # controles.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Quitados"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"¿Quieres agregar <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Si agregas la app de <xliff:g id="APPNAME">%s</xliff:g>, se incluirán controles y contenido en este panel. Algunas apps te permiten elegir qué controles mostrar aquí."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Está en favoritos"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Está en favoritos en la posición <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"No está en favoritos"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Abre <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Reproduce <xliff:g id="SONG_NAME">%1$s</xliff:g>, de <xliff:g id="ARTIST_NAME">%2$s</xliff:g>, en <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Reproducir <xliff:g id="SONG_NAME">%1$s</xliff:g> en <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Para ti"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Deshacer"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Acércate para reproducir en <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Para reproducir aquí, acércate a <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Se produjo un error. Vuelve a intentarlo."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Cargando"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Transmitiendo tu contenido multimedia"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Transmitiendo <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inactivo. Verifica la app"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"No se encontró"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"El control no está disponible"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Error. Vuelve a intentarlo."</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Agregar controles"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Editar controles"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Agregar app"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Agregar salidas"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupo"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Se seleccionó 1 dispositivo"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Bocinas y pantallas"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositivos sugeridos"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Requiere una cuenta premium"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cómo funciona la transmisión"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Transmisión"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Las personas cercanas con dispositivos Bluetooth compatibles pueden escuchar el contenido multimedia que transmites"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Error al iniciar transmisión"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"No se puede guardar. Vuelve a intentarlo."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"No se puede guardar."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número de compilación"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Se copió el número de compilación en el portapapeles."</string>
     <string name="basic_status" msgid="2315371112182658176">"Conversación abierta"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Hay al menos un dispositivo disponible."</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Mantener presionado atajo"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Cancelar"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Girar ahora"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Despliega el teléfono para tomar una selfie mejor"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"¿Cambiar a pantalla frontal para mejores selfies?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Usa la cámara trasera para tomar una foto más amplia y con mejor resolución."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Esta pantalla se apagará"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositivo plegable siendo desplegado"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositivo plegable siendo girado"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> de batería restante"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecta tu pluma stylus a un cargador"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"La pluma stylus tiene poca batería"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Videocámara"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"No se puede llamar desde este perfil"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Tu política del trabajo te permite hacer llamadas telefónicas solo desde el perfil de trabajo"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Cambiar al perfil de trabajo"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Cerrar"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Config. de pantalla de bloqueo"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
index 89ee62d..e156100 100644
--- a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"No"</item>
     <item msgid="5966994759929723339">"Sí"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 9bd4b45..a180e2c 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"<xliff:g id="PERCENT">%1$d</xliff:g> por ciento del límite inferior"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"<xliff:g id="PERCENT">%1$d</xliff:g> por ciento del límite izquierdo"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"<xliff:g id="PERCENT">%1$d</xliff:g> por ciento del límite derecho"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Las capturas de pantalla de trabajo se guardan en la aplicación <xliff:g id="APP">%1$s</xliff:g>"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Se ha guardado en el perfil de trabajo de <xliff:g id="APP">%1$s</xliff:g>"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Archivos"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> ha detectado esta captura de pantalla."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> y otras aplicaciones abiertas han detectado esta captura de pantalla."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Grabación de pantalla"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Procesando grabación de pantalla"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificación continua de una sesión de grabación de la pantalla"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillo"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Invertir colores"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Corrección de color"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Gestionar usuarios"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Hecho"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Cerrar"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Sin sonido ni vibración"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Sin sonido ni vibración, y se muestra más abajo en la sección de conversaciones"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Puede sonar o vibrar según los ajustes del dispositivo"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Puede sonar o vibrar según los ajustes del dispositivo. Las conversaciones de <xliff:g id="APP_NAME">%1$s</xliff:g> aparecen como burbujas de forma predeterminada."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Haz que el sistema determine si con esta notificación el dispositivo debe sonar o vibrar"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Estado:&lt;/b&gt; cambio a Predeterminado"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Estado:&lt;/b&gt; cambio a Silencio"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"grabación de pantalla"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Sin título"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"En espera"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Ventana de ampliación"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Ventana de controles de ampliación"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Ampliar"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Elige una aplicación para añadir controles"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# control añadido.}many{# controles añadidos.}other{# controles añadidos.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Quitado"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"¿Añadir <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Si añades <xliff:g id="APPNAME">%s</xliff:g>, podrá añadir controles y contenido a este panel. En algunas aplicaciones, puedes elegir qué controles aparecen aquí."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Añadido a favoritos"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Añadido a favoritos (posición <xliff:g id="NUMBER">%d</xliff:g>)"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Quitado de favoritos"</string>
@@ -852,13 +862,16 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Abrir <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Poner <xliff:g id="SONG_NAME">%1$s</xliff:g> de <xliff:g id="ARTIST_NAME">%2$s</xliff:g> en <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Poner <xliff:g id="SONG_NAME">%1$s</xliff:g> en <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Para ti"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Deshacer"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Acércate a <xliff:g id="DEVICENAME">%1$s</xliff:g> para reproducir contenido ahí"</string>
-    <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Para reproducirlo, acércate al dispositivo (<xliff:g id="DEVICENAME">%1$s</xliff:g>)"</string>
+    <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Para reproducirlo aquí, acércate al dispositivo (<xliff:g id="DEVICENAME">%1$s</xliff:g>)"</string>
     <string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Reproduciendo en <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_transfer_failed" msgid="7955354964610603723">"Se ha producido un error. Inténtalo de nuevo."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Cargando"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Enviando tu contenido multimedia"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Enviando <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inactivo, comprobar aplicación"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"No se ha encontrado"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Control no disponible"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Error: Vuelve a intentarlo"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Añadir controles"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Editar controles"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Añadir aplicación"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Añadir dispositivos de salida"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupo"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 dispositivo seleccionado"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Altavoces y pantallas"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Sugerencias de dispositivos"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Requiere una cuenta premium"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cómo funciona la emisión"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Emisión"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Las personas cercanas con dispositivos Bluetooth compatibles pueden escuchar el contenido multimedia que emites"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"No se puede emitir"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"No se puede guardar. Inténtalo de nuevo."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"No se puede guardar."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número de compilación"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Número de compilación copiado en el portapapeles."</string>
     <string name="basic_status" msgid="2315371112182658176">"Conversación abierta"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Al menos un dispositivo debe estar disponible"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Mantén pulsado el acceso directo"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Cancelar"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Girar ahora"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Despliega el teléfono para hacer un selfie mejor"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"¿Usar pantalla frontal para hacer mejores selfies?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Usa la cámara trasera para hacer una foto más amplia y con mayor resolución."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Esta pantalla se apagará"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositivo plegable desplegándose"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositivo plegable mostrado desde varios ángulos"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Batería restante: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecta tu lápiz óptico a un cargador"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Batería del lápiz óptico baja"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Videocámara"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"No se puede llamar desde este perfil"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Tu política del trabajo solo te permite hacer llamadas telefónicas desde el perfil de trabajo"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Cambiar al perfil de trabajo"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Cerrar"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Ajustes de pantalla de bloqueo"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-es/tiles_states_strings.xml b/packages/SystemUI/res/values-es/tiles_states_strings.xml
index fe4cbed..cee8371 100644
--- a/packages/SystemUI/res/values-es/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-es/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Desactivado"</item>
     <item msgid="5966994759929723339">"Activado"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index baf8520..b9d753e 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Alapiir: <xliff:g id="PERCENT">%1$d</xliff:g> protsenti"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Vasak piir: <xliff:g id="PERCENT">%1$d</xliff:g> protsenti"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Parem piir: <xliff:g id="PERCENT">%1$d</xliff:g> protsenti"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Töö ekraanipildid salvestatakse rakendusse <xliff:g id="APP">%1$s</xliff:g>"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Salvestati tööprofiilil rakendusse <xliff:g id="APP">%1$s</xliff:g>"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Files"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> tuvastas selle ekraanipildi."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> ja muud avatud rakendused tuvastasid selle ekraanipildi."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Ekraanisalvesti"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekraanisalvestuse töötlemine"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Pooleli märguanne ekraanikuva salvestamise seansi puhul"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Heledus"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Värvide ümberpööramine"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Värviparandus"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Kasutajate haldamine"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Valmis"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Sule"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automaatne"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Ilma heli ja vibreerimiseta"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Ilma heli ja vibreerimiseta, kuvatakse vestluste jaotises allpool"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Võib seadme seadete põhjal heliseda või vibreerida"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Võib seadme seadete põhjal heliseda või vibreerida. Rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> vestlused kuvatakse vaikimisi mullis."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Laske süsteemil määrata, kas selle märguande puhul peaks esitama heli või vibreerima"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Olek:&lt;/b&gt; määrati prioriteet Vaikimisi"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Olek:&lt;/b&gt; määrati prioriteet Vaikne"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"ekraanikuva salvest."</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Pealkiri puudub"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"OotereĆŸiim"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Suurendamisaken"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Suurendamisakna juhtelemendid"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Suumi sisse"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Valige juhtelementide lisamiseks rakendus"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Lisati # juhtnupp.}other{Lisati # juhtnuppu.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Eemaldatud"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Kas lisada <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Kui lisate rakenduse <xliff:g id="APPNAME">%s</xliff:g>, saab see sellele paneelile lisada juhtelemendid ja sisu. Mõnes rakenduses saate valida, millised juhtelemendid siin kuvatakse."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Lisatud lemmikuks"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Lisatud lemmikuks, positsioon <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Eemaldatud lemmikute hulgast"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Rakenduse <xliff:g id="APP_LABEL">%1$s</xliff:g> avamine"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Esita lugu <xliff:g id="SONG_NAME">%1$s</xliff:g> esitajalt <xliff:g id="ARTIST_NAME">%2$s</xliff:g> rakenduses <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Esita lugu <xliff:g id="SONG_NAME">%1$s</xliff:g> rakenduses <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Teile"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Võta tagasi"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Liikuge lähemale, et seadmes <xliff:g id="DEVICENAME">%1$s</xliff:g> esitada"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Siin esitamiseks minge seadmele <xliff:g id="DEVICENAME">%1$s</xliff:g> lähemale"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Midagi läks valesti. Proovige uuesti."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Laadimine"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tahvelarvuti"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Teie meedia ülekandmine"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Rakenduse <xliff:g id="APP_LABEL">%1$s</xliff:g> ülekandmine"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Passiivne, vaadake rakendust"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Ei leitud"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Juhtelement pole saadaval"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Ilmnes viga, proovige uuesti"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Lisa juhtelemente"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Muuda juhtelemente"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Rakenduse lisamine"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Väljundite lisamine"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupp"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 seade on valitud"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Kõlarid ja ekraanid"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Soovitatud seadmed"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Vajalik on tasuline konto"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kuidas ülekandmine toimib?"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Ülekanne"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Teie läheduses olevad inimesed, kellel on ühilduvad Bluetooth-seadmed, saavad kuulata teie ülekantavat meediat"</string>
@@ -894,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Ei saa üle kanda"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Ei saa salvestada. Proovige uuesti."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Ei saa salvestada."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Kasutage vähemalt 4 tähemärki"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Kasutage vähem kui 16 tähemärki"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Järgunumber"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Järgunumber kopeeriti lõikelauale."</string>
     <string name="basic_status" msgid="2315371112182658176">"Avage vestlus"</string>
@@ -1013,24 +1030,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Vähemalt üks seade on saadaval"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Pikalt puudutamise otsetee"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Tühista"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Pööra kohe ümber"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Voltige telefon parema selfi jaoks lahti"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Kas kasutada parema selfi jaoks esikaamerat?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Kasutage tagakülje kaamerat, et jäädvustada suurema eraldusvõimega laiem foto."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ See ekraan lülitatakse välja"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Volditava seadme lahtivoltimine"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Volditava seadme ümberpööramine"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Akutase on <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Ühendage elektronpliiats laadijaga"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Elektronpliiatsi akutase on madal"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Videokaamera"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Sellelt profiililt ei saa helistada"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Teie töökoha eeskirjad lubavad teil helistada ainult tööprofiililt"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Lülitu tööprofiilile"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Sule"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Lukustuskuva seaded"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-et/tiles_states_strings.xml b/packages/SystemUI/res/values-et/tiles_states_strings.xml
index 07eddef..7bf520f5 100644
--- a/packages/SystemUI/res/values-et/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-et/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Väljas"</item>
     <item msgid="5966994759929723339">"Sees"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index b9018f5..722bd78 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -32,13 +32,13 @@
     <string name="battery_saver_start_action" msgid="8353766979886287140">"Aktibatu"</string>
     <string name="battery_saver_dismiss_action" msgid="7199342621040014738">"Ez, eskerrik asko"</string>
     <string name="status_bar_settings_auto_rotation" msgid="8329080442278431708">"Biratu pantaila automatikoki"</string>
-    <string name="usb_device_permission_prompt" msgid="4414719028369181772">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> atzitzeko baimena eman nahi diozu <xliff:g id="APPLICATION">%1$s</xliff:g> aplikazioari?"</string>
+    <string name="usb_device_permission_prompt" msgid="4414719028369181772">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> erabiltzeko baimena eman nahi diozu <xliff:g id="APPLICATION">%1$s</xliff:g> aplikazioari?"</string>
     <string name="usb_device_permission_prompt_warn" msgid="2309129784984063656">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> erabiltzeko baimena eman nahi diozu <xliff:g id="APPLICATION">%1$s</xliff:g> aplikazioari?\nAplikazioak ez du grabatzeko baimenik, baina baliteke USB bidezko gailu horren bidez audioa grabatzea."</string>
-    <string name="usb_audio_device_permission_prompt_title" msgid="4221351137250093451">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> atzitzeko baimena eman nahi diozu <xliff:g id="APPLICATION">%1$s</xliff:g> aplikazioari?"</string>
+    <string name="usb_audio_device_permission_prompt_title" msgid="4221351137250093451">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> erabiltzeko baimena eman nahi diozu <xliff:g id="APPLICATION">%1$s</xliff:g> aplikazioari?"</string>
     <string name="usb_audio_device_confirm_prompt_title" msgid="8828406516732985696">"<xliff:g id="APPLICATION">%1$s</xliff:g> ireki nahi duzu <xliff:g id="USB_DEVICE">%2$s</xliff:g> kudeatzeko?"</string>
     <string name="usb_audio_device_prompt_warn" msgid="2504972133361130335">"Aplikazioak ez du grabatzeko baimenik, baina baliteke USB bidezko gailu honen bidez audioa grabatzea. <xliff:g id="APPLICATION">%1$s</xliff:g> gailu honekin erabiliz gero, baliteke deiak, jakinarazpenak eta alarmak ez entzutea."</string>
     <string name="usb_audio_device_prompt" msgid="7944987408206252949">"<xliff:g id="APPLICATION">%1$s</xliff:g> gailu honekin erabiliz gero, baliteke deiak, jakinarazpenak eta alarmak ez entzutea."</string>
-    <string name="usb_accessory_permission_prompt" msgid="717963550388312123">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> atzitzeko baimena eman nahi diozu <xliff:g id="APPLICATION">%1$s</xliff:g> aplikazioari?"</string>
+    <string name="usb_accessory_permission_prompt" msgid="717963550388312123">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> erabiltzeko baimena eman nahi diozu <xliff:g id="APPLICATION">%1$s</xliff:g> aplikazioari?"</string>
     <string name="usb_device_confirm_prompt" msgid="4091711472439910809">"<xliff:g id="APPLICATION">%1$s</xliff:g> ireki nahi duzu <xliff:g id="USB_DEVICE">%2$s</xliff:g> kudeatzeko?"</string>
     <string name="usb_device_confirm_prompt_warn" msgid="990208659736311769">"<xliff:g id="APPLICATION">%1$s</xliff:g> ireki nahi duzu <xliff:g id="USB_DEVICE">%2$s</xliff:g> erabiltzeko?\nAplikazioak ez du grabatzeko baimenik, baina baliteke audioa grabatzea USB bidezko gailu horren bidez."</string>
     <string name="usb_accessory_confirm_prompt" msgid="5728408382798643421">"<xliff:g id="APPLICATION">%1$s</xliff:g> ireki nahi duzu <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> kudeatzeko?"</string>
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Beheko ertza: ehuneko <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Ezkerreko ertza: ehuneko <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Eskuineko ertza: ehuneko <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Laneko pantaila-argazkiak <xliff:g id="APP">%1$s</xliff:g> aplikazioan gordetzen dira"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Laneko profilaren <xliff:g id="APP">%1$s</xliff:g> aplikazioan gorde da"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Fitxategiak"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> aplikazioak pantaila-argazkia hauteman du."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> aplikazioak eta irekitako beste aplikazio batzuek pantaila-argazkia hauteman dute."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Pantaila-grabagailua"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Pantaila-grabaketa prozesatzen"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Pantailaren grabaketa-saioaren jakinarazpen jarraitua"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Distira"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Kolore-alderantzikatzea"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Koloreen zuzenketa"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Kudeatu erabiltzaileak"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Eginda"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Itxi"</string>
@@ -298,9 +302,9 @@
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Gailuaren mikrofonoa desblokeatu nahi duzu?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Gailuaren kamera desblokeatu nahi duzu?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Gailuaren kamera eta mikrofonoa desblokeatu nahi dituzu?"</string>
-    <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Mikrofonoa atzitzeko baimena duten aplikazio eta zerbitzu guztiek erabili ahalko dute."</string>
-    <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Kamera atzitzeko baimena duten aplikazio eta zerbitzu guztiek erabili ahalko dute."</string>
-    <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Kamera edo mikrofonoa atzitzeko baimena duten aplikazio eta zerbitzu guztiek erabili ahalko dituzte."</string>
+    <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Mikrofonoa erabiltzeko baimena duten aplikazio eta zerbitzu guztiek erabili ahalko dute."</string>
+    <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Kamera erabiltzeko baimena duten aplikazio eta zerbitzu guztiek erabili ahalko dute."</string>
+    <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Kamera edo mikrofonoa erabiltzeko baimena duten aplikazio eta zerbitzu guztiek erabili ahalko dituzte."</string>
     <string name="sensor_privacy_start_use_mic_blocked_dialog_title" msgid="2640140287496469689">"Blokeatuta dago mikrofonoa"</string>
     <string name="sensor_privacy_start_use_camera_blocked_dialog_title" msgid="7398084286822440384">"Blokeatuta dago kamera"</string>
     <string name="sensor_privacy_start_use_mic_camera_blocked_dialog_title" msgid="195236134743281973">"Blokeatuta daude mikrofonoa eta kamera"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatikoa"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Ez du tonurik jotzen edo dar-dar egiten"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Ez du tonurik jotzen edo dar-dar egiten, eta elkarrizketen atalaren behealdean agertzen da"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Baliteke tonua jotzea edo dardara egitea, gailuaren ezarpenen arabera"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Baliteke tonua jotzea edo dardara egitea, gailuaren ezarpenen arabera. Modu lehenetsian, <xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioko elkarrizketak burbuila gisa agertzen dira."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Ezarri sistemak zehaztu dezala jakinarazpen honek soinua edo dardara egin behar duen ala ez"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"Lehenetsi gisa ezarri da &lt;b&gt;egoera:&lt;/b&gt;"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"Soinurik gabeko modura aldatu da &lt;b&gt;egoera:&lt;/b&gt;"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"pantaila-grabaketa"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Ez du izenik"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Egonean"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Lupa-leihoa"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Lupa-leihoaren aukerak"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Handitu"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Aukeratu aplikazio bat kontrolatzeko aukerak gehitzeko"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Kontrolatzeko # aukera gehitu da.}other{Kontrolatzeko # aukera gehitu dira.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Kenduta"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g> gehitu nahi duzu?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"<xliff:g id="APPNAME">%s</xliff:g> gehitzen duzunean, kontrolatzeko aukerak eta edukia gehi ditzake panelean. Aplikazio batzuetan, hemen zein kontrolatzeko aukera agertzen diren aukera dezakezu."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Gogokoetan dago"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"<xliff:g id="NUMBER">%d</xliff:g>. gogokoa da"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Ez dago gogokoetan"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Ireki <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Erreproduzitu <xliff:g id="SONG_NAME">%1$s</xliff:g> (<xliff:g id="ARTIST_NAME">%2$s</xliff:g>) <xliff:g id="APP_LABEL">%3$s</xliff:g> bidez"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Erreproduzitu <xliff:g id="SONG_NAME">%1$s</xliff:g> <xliff:g id="APP_LABEL">%2$s</xliff:g> bidez"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Zuretzat"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Desegin"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Gertura ezazu <xliff:g id="DEVICENAME">%1$s</xliff:g> gailuan erreproduzitzeko"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Hemen erreproduzitzeko, hurbildu <xliff:g id="DEVICENAME">%1$s</xliff:g> gailura"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Arazoren bat izan da. Saiatu berriro."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Kargatzen"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tableta"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Multimedia-edukia igortzen"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"<xliff:g id="APP_LABEL">%1$s</xliff:g> aplikazioa igortzen"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inaktibo; egiaztatu aplikazioa"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Ez da aurkitu"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Ez dago erabilgarri kontrolatzeko aukera"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Errorea. Saiatu berriro."</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Gehitu aukerak"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Editatu aukerak"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Gehitu aplikazio bat"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Gehitu irteerak"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Taldea"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 gailu hautatu da"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"%% <xliff:g id="PERCENTAGE">%1$d</xliff:g>"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Bozgorailuak eta pantailak"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Iradokitako gailuak"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Premium kontu bat behar da"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Nola funtzionatzen dute iragarpenek?"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Iragarri"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Bluetooth bidezko gailu bateragarriak dituzten inguruko pertsonek iragartzen ari zaren multimedia-edukia entzun dezakete"</string>
@@ -894,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Ezin da iragarri"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Ezin da gorde. Saiatu berriro."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Ezin da gorde."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Erabili lau karaktere gutxienez"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Erabili 16 karaktere baino gutxiago"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Konpilazio-zenbakia"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Kopiatu da konpilazio-zenbakia arbelean."</string>
     <string name="basic_status" msgid="2315371112182658176">"Elkarrizketa irekia"</string>
@@ -1013,24 +1030,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Gutxienez gailu bat erabilgarri dago."</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Eduki sakatuta lasterbidea"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Utzi"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Irauli"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Ireki telefonoa autoargazki hobeak ateratzeko"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Telefonoa irauli nahi duzu autoargazki hobeak ateratzeko?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Erabili atzeko kamera kalitate handiagoko argazki zabalago bat ateratzeko."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Pantaila itzali egingo da"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Gailu tolesgarria zabaltzen"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Gailu tolesgarria biratzen"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Bateriaren <xliff:g id="PERCENTAGE">%s</xliff:g> geratzen da"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Konektatu arkatza kargagailu batera"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Arkatzak bateria gutxi du"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Bideokamera"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Ezin duzu deitu profil honetatik"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Deiak laneko profiletik soilik egiteko baimena ematen dizute laneko gidalerroek"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Aldatu laneko profilera"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Itxi"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Pantaila blokeatuaren ezarpenak"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-eu/tiles_states_strings.xml b/packages/SystemUI/res/values-eu/tiles_states_strings.xml
index 3bf49c8..333ede1 100644
--- a/packages/SystemUI/res/values-eu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-eu/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Desaktibatuta"</item>
     <item msgid="5966994759929723339">"Aktibatuta"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 1acbd5a3..fa7c5c6 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"Ù†Ù…Ű§ÚŻŰ±ÙŰȘ‌Ù‡Ű§ÛŒ Ù†Ù…Ű§ÛŒÙ‡ Ú©Ű§Ű±ÛŒ ۯ۱ ŰšŰ±Ù†Ű§Ù…Ù‡ <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">"Files"</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>
     <string name="screenrecord_name" msgid="2596401223859996572">"۶ۚ۷‌Ú©Ù†Ù†ŰŻÙ‡ Ű”ÙŰ­Ù‡‌Ù†Ù…Ű§ÛŒŰŽ"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ŰŻŰ±Ű­Ű§Ù„ ÙŸŰ±ŰŻŰ§ŰČŰŽ ۶ۚ۷ Ű”ÙŰ­Ù‡‌Ù†Ù…Ű§ÛŒŰŽ"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"ۧŰčÙ„Ű§Ù† ŰŻŰ±Ű­Ű§Ù„ Ű§Ù†ŰŹŰ§Ù… ŰšŰ±Ű§ÛŒ ŰŹÙ„ŰłÙ‡ ۶ۚ۷ Ű”ÙŰ­Ù‡‌Ù†Ù…Ű§ÛŒŰŽ"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Ű±ÙˆŰŽÙ†Ű§ÛŒÛŒ"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ÙˆŰ§Ű±ÙˆÙ†ÚŻÛŒ Ű±Ù†ÚŻ"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ŰȘŰ”Ű­ÛŒŰ­ Ű±Ù†ÚŻ"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Ù…ŰŻÛŒŰ±ÛŒŰȘ Ú©Ű§Ű±ŰšŰ±Ű§Ù†"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"ŰȘÙ…Ű§Ù…"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"ۚ۳ŰȘن"</string>
@@ -775,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"۶ۚ۷ Ű”ÙŰ­Ù‡‌Ù†Ù…Ű§ÛŒŰŽ"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"ŰšŰŻÙˆÙ† ŰčÙ†ÙˆŰ§Ù†"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"ŰąÙ…Ű§ŰŻÙ‡‌ŰšÙ‡‌کۧ۱"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"ÙŸÙ†ŰŹŰ±Ù‡ ۯ۱ێŰȘ‌Ù†Ù…Ű§ÛŒÛŒ"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"کنŰȘŰ±Ù„‌Ù‡Ű§ÛŒ ÙŸÙ†ŰŹŰ±Ù‡ ۯ۱ێŰȘ‌Ù†Ù…Ű§ÛŒÛŒ"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ŰČوم‌ÙŸÛŒŰŽ Ú©Ű±ŰŻÙ†"</string>
@@ -800,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Ű§Ù†ŰȘ۟ۧۚ ŰšŰ±Ù†Ű§Ù…Ù‡ ŰšŰ±Ű§ÛŒ Ű§ÙŰČÙˆŰŻÙ† کنŰȘŰ±Ù„‌Ù‡Ű§"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# کنŰȘŰ±Ù„ Ű§Ű¶Ű§ÙÙ‡ ŰŽŰŻ.}one{# کنŰȘŰ±Ù„ Ű§Ű¶Ű§ÙÙ‡ ŰŽŰŻ.}other{# کنŰȘŰ±Ù„ Ű§Ű¶Ű§ÙÙ‡ ŰŽŰŻ.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Ű­Ű°Ù ŰŽŰŻ"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g> Ű§ÙŰČÙˆŰŻÙ‡ ŰŽÙˆŰŻŰŸ"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"وقŰȘی <xliff:g id="APPNAME">%s</xliff:g> ۱ۧ Ű§Ű¶Ű§ÙÙ‡ می‌Ú©Ù†ÛŒŰŻŰŒ می‌ŰȘÙˆŰ§Ù†ŰŻ کنŰȘŰ±Ù„‌Ù‡Ű§ و Ù…Ű­ŰȘÙˆŰ§ ۱ۧ ŰšÙ‡ Ű§ÛŒÙ† ÙŸŰ§Ù†Ù„ Ű§Ű¶Ű§ÙÙ‡ Ú©Ù†ŰŻ. ۯ۱ ŰšŰ±ŰźÛŒ‌ۧŰČ ŰšŰ±Ù†Ű§Ù…Ù‡‌Ù‡Ű§ می‌ŰȘÙˆŰ§Ù†ÛŒŰŻ Ű§Ù†ŰȘ۟ۧۚ Ú©Ù†ÛŒŰŻ چه کنŰȘŰ±Ù„‌Ù‡Ű§ÛŒÛŒ ۯ۱ Ű§ÛŒÙ†ŰŹŰ§ Ù†ŰŽŰ§Ù† ŰŻŰ§ŰŻÙ‡ ŰŽÙˆŰŻ."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"ŰšÙ‡ Ù…ÙˆŰ§Ű±ŰŻ ŰŻÙ„ŰźÙˆŰ§Ù‡ Ű§Ű¶Ű§ÙÙ‡ ŰŽŰŻ"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Ű§Ű¶Ű§ÙÙ‡‌ŰŽŰŻÙ‡ ŰšÙ‡ Ù…ÙˆŰ§Ű±ŰŻ ŰŻÙ„ŰźÙˆŰ§Ù‡ŰŒ ŰŹŰ§ÛŒÚŻŰ§Ù‡ <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Ű­Ű°Ù‌ŰŽŰŻÙ‡ ۧŰČ Ù…ÙˆŰ§Ű±ŰŻ ŰŻÙ„ŰźÙˆŰ§Ù‡"</string>
@@ -850,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"ۚۧŰČ Ú©Ű±ŰŻÙ† <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="SONG_NAME">%1$s</xliff:g> ۧŰČ <xliff:g id="ARTIST_NAME">%2$s</xliff:g> ۱ۧ ۧŰČŰ·Ű±ÛŒÙ‚ <xliff:g id="APP_LABEL">%3$s</xliff:g> ÙŸŰźŰŽ Ú©Ù†ÛŒŰŻ"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> ۱ۧ ۧŰČŰ·Ű±ÛŒÙ‚ <xliff:g id="APP_LABEL">%2$s</xliff:g> ÙŸŰźŰŽ Ú©Ù†ÛŒŰŻ"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"ŰšŰ±Ű§ÛŒ ŰŽÙ…Ű§"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"ÙˆŰ§ÚŻŰ±ŰŻ"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"ŰšŰ±Ű§ÛŒ ÙŸŰźŰŽ ۯ۱ <xliff:g id="DEVICENAME">%1$s</xliff:g> ŰšÙ‡ ŰŻŰłŰȘÚŻŰ§Ù‡ نŰČŰŻÛŒÚ©‌ŰȘ۱ ŰŽÙˆÛŒŰŻ"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"ŰšŰ±Ű§ÛŒ ÙŸŰźŰŽ ۯ۱ Ű§ÛŒÙ†ŰŹŰ§ŰŒ ŰšÙ‡ <xliff:g id="DEVICENAME">%1$s</xliff:g> نŰČŰŻÛŒÚ©‌ŰȘ۱ ŰŽÙˆÛŒŰŻ"</string>
@@ -857,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Ù…ŰŽÚ©Ù„ÛŒ ÙŸÛŒŰŽ ŰąÙ…ŰŻ. ŰŻÙˆŰšŰ§Ű±Ù‡ Ű§Ù…ŰȘŰ­Ű§Ù† Ú©Ù†ÛŒŰŻ."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"ŰŻŰ±Ű­Ű§Ù„ ۚۧ۱ Ú©Ű±ŰŻÙ†"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"Ű±Ű§ÛŒŰ§Ù†Ù‡ Ù„ÙˆŰ­ÛŒ"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"ÙŸŰźŰŽ Ù…Ű­ŰȘÙˆŰ§ÛŒ Ű±ŰłŰ§Ù†Ù‡‌Ù‡Ű§"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"ÙŸŰźŰŽ Ù…Ű­ŰȘÙˆŰ§ÛŒ <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ŰșÛŒŰ±ÙŰčŰ§Ù„ŰŒ ŰšŰ±Ù†Ű§Ù…Ù‡ ۱ۧ ŰšŰ±Ű±ŰłÛŒ Ú©Ù†ÛŒŰŻ"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"ÙŸÛŒŰŻŰ§ Ù†ŰŽŰŻ"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"کنŰȘŰ±Ù„ ۯ۱ۯ۳ŰȘ۱۳ Ù†ÛŒŰłŰȘ"</string>
@@ -866,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"ŰźŰ·Ű§ŰŒ ŰŻÙˆŰšŰ§Ű±Ù‡ Ű§Ù…ŰȘŰ­Ű§Ù† Ú©Ù†ÛŒŰŻ"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Ű§ÙŰČÙˆŰŻÙ† کنŰȘŰ±Ù„‌Ù‡Ű§"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"ÙˆÛŒŰ±Ű§ÛŒŰŽ کنŰȘŰ±Ù„‌Ù‡Ű§"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Ű§ÙŰČÙˆŰŻÙ† ŰšŰ±Ù†Ű§Ù…Ù‡"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Ű§ÙŰČÙˆŰŻÙ† ŰźŰ±ÙˆŰŹÛŒ"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"ÚŻŰ±ÙˆÙ‡"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Û± ŰŻŰłŰȘÚŻŰ§Ù‡ Ű§Ù†ŰȘ۟ۧۚ ŰŽŰŻ"</string>
@@ -881,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ŰšÙ„Ù†ŰŻÚŻÙˆÙ‡Ű§ و Ù†Ù…Ű§ÛŒŰŽÚŻŰ±Ù‡Ű§"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"ŰŻŰłŰȘÚŻŰ§Ù‡‌Ù‡Ű§ÛŒ ÙŸÛŒŰŽÙ†Ù‡Ű§ŰŻÛŒ"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"ۭ۳ۧۚ ممŰȘۧŰČ Ù„Ű§ŰČم ۧ۳ŰȘ"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"همه‌ÙŰ±ŰȘŰłŰȘی Ú†Ű·ÙˆŰ± کۧ۱ می‌Ú©Ù†ŰŻ"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"همه‌ÙŰ±ŰłŰȘی"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"‏Ű§ÙŰ±Ű§ŰŻÛŒ که ۯ۱ Ű§Ű·Ű±Ű§ÙŰȘŰ§Ù† ŰŻŰłŰȘÚŻŰ§Ù‡‌Ù‡Ű§ÛŒ Bluetooth ۳ۧŰČگۧ۱ ŰŻŰ§Ű±Ù†ŰŻ می‌ŰȘÙˆŰ§Ù†Ù†ŰŻ ŰšÙ‡ Ű±ŰłŰ§Ù†Ù‡‌Ű§ÛŒ که همه‌ÙŰ±ŰłŰȘی می‌Ú©Ù†ÛŒŰŻ ÚŻÙˆŰŽ Ú©Ù†Ù†ŰŻ"</string>
@@ -892,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"همه‌ÙŰ±ŰłŰȘی Ű§Ù†ŰŹŰ§Ù… Ù†ŰŽŰŻ"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Ű°ŰźÛŒŰ±Ù‡ Ù†ŰŽŰŻ. ŰŻÙˆŰšŰ§Ű±Ù‡ Ű§Ù…ŰȘŰ­Ű§Ù† Ú©Ù†ÛŒŰŻ."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Ű°ŰźÛŒŰ±Ù‡ Ù†ŰŽŰŻ."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Ű­ŰŻŰ§Ù‚Ù„ ۧŰČ ÛŽ Ù†ÙˆÛŒŰłÙ‡ ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ú©Ù†ÛŒŰŻ"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"ۧŰČ Ú©Ù…ŰȘ۱ ۧŰČ Û±Û¶ Ù†ÙˆÛŒŰłÙ‡ ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ú©Ù†ÛŒŰŻ"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"ŰŽÙ…Ű§Ű±Ù‡ ۳ۧ۟ŰȘ"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"ŰŽÙ…Ű§Ű±Ù‡ ۳ۧ۟ŰȘ ۯ۱ ŰšŰ±ÛŒŰŻÙ‡‌ŰŻŰ§Ù† Ú©ÙŸÛŒ ŰŽŰŻ."</string>
     <string name="basic_status" msgid="2315371112182658176">"ۚۧŰČ Ú©Ű±ŰŻÙ† Ù…Ú©Ű§Ù„Ù…Ù‡"</string>
@@ -945,7 +964,7 @@
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"موقŰȘŰ§Ù‹ مŰȘŰ”Ù„ ۧ۳ŰȘ"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"ۧŰȘŰ”Ű§Ù„ ۶Űčیف"</string>
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"ŰŻŰ§ŰŻÙ‡ ŰȘلفن Ù‡Ù…Ű±Ű§Ù‡ ŰšÙ‡‌Ű·ÙˆŰ± ŰźÙˆŰŻÚ©Ű§Ű± مŰȘŰ”Ù„ Ù†ŰźÙˆŰ§Ù‡ŰŻ ŰŽŰŻ"</string>
-    <string name="mobile_data_no_connection" msgid="1713872434869947377">"ۧŰȘŰ”Ű§Ù„ ŰšŰ±Ù‚Ű±Ű§Ű± Ù†ÛŒŰłŰȘ"</string>
+    <string name="mobile_data_no_connection" msgid="1713872434869947377">"ۧŰȘŰ”Ű§Ù„ Ű§ÛŒÙ†ŰȘŰ±Ù†ŰȘ Ù…ÙˆŰŹÙˆŰŻ Ù†ÛŒŰłŰȘ"</string>
     <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ŰŽŰšÚ©Ù‡ ŰŻÛŒÚŻŰ±ÛŒ ÙˆŰŹÙˆŰŻ Ù†ŰŻŰ§Ű±ŰŻ"</string>
     <string name="all_network_unavailable" msgid="4112774339909373349">"ŰŽŰšÚ©Ù‡‌Ű§ÛŒ ۯ۱ ŰŻŰłŰȘ۱۳ Ù†ÛŒŰłŰȘ"</string>
     <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
@@ -1011,11 +1030,16 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Ű­ŰŻŰ§Ù‚Ù„ یک ŰŻŰłŰȘÚŻŰ§Ù‡ ۯ۱ۯ۳ŰȘ۱۳ ۚۧێۯ"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Ù…ÛŒŰ§Ù†‌ۚ۱ ۱ۧ Ù„Ù…Űł Ú©Ù†ÛŒŰŻ و Ù†ÚŻÙ‡ ŰŻŰ§Ű±ÛŒŰŻ"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"لŰșو Ú©Ű±ŰŻÙ†"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Ű§Ú©Ù†ÙˆÙ† Ú†Ű±ŰźŰ§Ù†ŰŻÙ‡ ŰŽÙˆŰŻ"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"ŰšŰ±Ű§ÛŒ ŰźÙˆÛŒŰŽ‌ÚŻŰ±ÙŰȘ ŰšÙ‡ŰȘŰ±ŰŒ ŰȘلفن ۱ۧ ۚۧŰČ Ú©Ù†ÛŒŰŻ"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"ŰšŰ±Ű§ÛŒ ŰźÙˆÛŒŰŽ‌ÚŻŰ±ÙŰȘ ŰšÙ‡ŰȘŰ±ŰŒ ۧŰČ Ù†Ù…Ű§ÛŒŰŽÚŻŰ± ŰŹÙ„Ùˆ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ŰŽÙˆŰŻŰŸ"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"ŰšŰ±Ű§ÛŒ ŰčÚ©ŰłÛŒ ŰčŰ±ÛŒŰ¶‌ŰȘ۱ ۚۧ ÙˆŰ¶ÙˆŰ­ ŰšŰ§Ù„Ű§ŰȘŰ±ŰŒ ۧŰČ ŰŻÙˆŰ±ŰšÛŒÙ† ŰčÙ‚Űš ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ú©Ù†ÛŒŰŻ."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Ű§ÛŒÙ† Ű”ÙŰ­Ù‡‌Ù†Ù…Ű§ÛŒŰŽ ŰźŰ§Ù…ÙˆŰŽ ŰźÙˆŰ§Ù‡ŰŻ ŰŽŰŻ"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ŰŻŰłŰȘÚŻŰ§Ù‡ ŰȘŰ§ŰŽÙˆ ŰŻŰ±Ű­Ű§Ù„ ۚۧŰČ ŰŽŰŻÙ†"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ŰŻŰłŰȘÚŻŰ§Ù‡ ŰȘŰ§ŰŽÙˆ ŰŻŰ±Ű­Ű§Ù„ چ۱۟ێ ŰšÙ‡ Ű§Ű·Ű±Ű§Ù"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> ۚۧŰȘŰ±ÛŒ ŰšŰ§Ù‚ÛŒ Ù…Ű§Ù†ŰŻÙ‡ ۧ۳ŰȘ"</string>
@@ -1026,4 +1050,5 @@
     <string name="call_from_work_profile_text" msgid="3458704745640229638">"۟۷‌Ù…ŰŽÛŒ Ú©Ű§Ű±ÛŒ ŰŽÙ…Ű§ ÙÙ‚Ű· ŰšÙ‡ ŰšŰ±Ù‚Ű±Ű§Ű±ÛŒ ŰȘÙ…Ű§Űł ۧŰČŰ·Ű±ÛŒÙ‚ Ù†Ù…Ű§ÛŒÙ‡ Ú©Ű§Ű±ÛŒ ۧۏۧŰČه می‌ŰŻÙ‡ŰŻ"</string>
     <string name="call_from_work_profile_action" msgid="2937701298133010724">"Ű±ÙŰȘن ŰšÙ‡ Ù†Ù…Ű§ÛŒÙ‡ Ú©Ű§Ű±ÛŒ"</string>
     <string name="call_from_work_profile_close" msgid="7927067108901068098">"ۚ۳ŰȘن"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"ŰȘÙ†ŰžÛŒÙ…Ű§ŰȘ Ű”ÙŰ­Ù‡ قفل"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fa/tiles_states_strings.xml b/packages/SystemUI/res/values-fa/tiles_states_strings.xml
index 85f0bfd..436ea31 100644
--- a/packages/SystemUI/res/values-fa/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fa/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"ŰźŰ§Ù…ÙˆŰŽ"</item>
     <item msgid="5966994759929723339">"Ű±ÙˆŰŽÙ†"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index ffea882..7191d6c 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Alareuna <xliff:g id="PERCENT">%1$d</xliff:g> prosenttia"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Vasen reuna <xliff:g id="PERCENT">%1$d</xliff:g> prosenttia"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Oikea reuna <xliff:g id="PERCENT">%1$d</xliff:g> prosenttia"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Työprofiilin kuvakaappaukset tallennetaan sovellukseen: <xliff:g id="APP">%1$s</xliff:g>"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Tallennettu työprofiiliin tässä sovelluksessa: <xliff:g id="APP">%1$s</xliff:g>"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Files"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> havaitsi tämän kuvakaappauksen."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> ja jotkin muut sovellukset havaitsivat tämän kuvakaappauksen."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Näytön tallentaja"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Näytön tallennusta käsitellään"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Pysyvä ilmoitus näytön tallentamisesta"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Kirkkaus"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Käänteiset värit"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Värinkorjaus"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Ylläpidä käyttäjiä"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Valmis"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Sulje"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automaattinen"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Ei ääntä tai värinää"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Ei ääntä tai värinää ja näkyy alempana keskusteluosiossa"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Voi soida tai väristä laitteen asetuksista riippuen"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Voi soida tai väristä laitteen asetuksista riippuen. Keskusteluista (<xliff:g id="APP_NAME">%1$s</xliff:g>) luodaan oletuksena kuplia."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Järjestelmä valitsee, kuuluuko tästä ilmoituksesta ääntä tai väriseekö se"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Tila:&lt;/b&gt; valittu oletusarvoiseksi"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Tila:&lt;/b&gt; hiljennetty"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"näytön tallennus"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Ei nimeä"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Virransäästötila"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Suurennusikkuna"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Suurennusikkunan ohjaimet"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Lähennä"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Valitse sovellus lisätäksesi säätimiä"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# säädin lisätty.}other{# säädintä lisätty.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Poistettu"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Lisätäänkö <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Kun <xliff:g id="APPNAME">%s</xliff:g> lisätään, se voi lisätä asetuksia ja sisältöä tähän paneeliin. Joissakin sovelluksissa voit valita, mitä asetukset näkyvät täällä."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Lisätty suosikkeihin"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Lisätty suosikkeihin sijalle <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Poistettu suosikeista"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Avaa <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Soita <xliff:g id="SONG_NAME">%1$s</xliff:g> (<xliff:g id="ARTIST_NAME">%2$s</xliff:g>) sovelluksessa <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Soita <xliff:g id="SONG_NAME">%1$s</xliff:g> (<xliff:g id="APP_LABEL">%2$s</xliff:g>)"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Sinulle"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Kumoa"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Siirry lähemmäs, jotta <xliff:g id="DEVICENAME">%1$s</xliff:g> voi toistaa tämän"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Siirry lähemmäs laitetta (<xliff:g id="DEVICENAME">%1$s</xliff:g>) toistaaksesi täällä"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Jotain meni pieleen. Yritä uudelleen."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Latautuminen"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tabletti"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Striimataan mediaa"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Striimataan <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Epäaktiivinen, tarkista sovellus"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Ei löydy"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Ohjain ei ole käytettävissä"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Virhe, yritä uudelleen"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Lisää säätimiä"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Muokkaa säätimiä"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Lisää sovellus"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Lisää toistotapoja"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Ryhmä"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 laite valittu"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Kaiuttimet ja näytöt"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Ehdotetut laitteet"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Edellyttää premium-tiliä"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Miten lähetys toimii"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Lähetys"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Lähistöllä olevat ihmiset, joilla on yhteensopiva Bluetooth-laite, voivat kuunnella lähettämääsi mediaa"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Ei voi lähettää"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Tallennus ei onnistu. Yritä uudelleen."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Tallennus ei onnistu."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Koontiversion numero"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Koontiversion numero kopioitu leikepöydälle"</string>
     <string name="basic_status" msgid="2315371112182658176">"Avaa keskustelu"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Ainakin yksi laite on käytettävissä"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Kosketa pikakuvaketta pitkään"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Peru"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Käännä nyt"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Saat paremman selfien, kun levität puhelimen"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Käännä etunäytölle, jotta saat paremman selfien?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Voit ottaa laajemman kuvan korkeammalla resoluutiolla, kun käytät takakameraa."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Tämä näyttö sammutetaan"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Taitettava laite taitetaan"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Taitettava laite käännetään ympäri"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Akkua jäljellä <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Yhdistä näyttökynä laturiin"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Näyttökynän akku vähissä"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Tästä profiilista ei voi soittaa"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Työkäytäntö sallii sinun soittaa puheluita vain työprofiilista"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Vaihda työprofiiliin"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Sulje"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Lukitusnäytön asetukset"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fi/tiles_states_strings.xml b/packages/SystemUI/res/values-fi/tiles_states_strings.xml
index 1505dc5..4bec4b3 100644
--- a/packages/SystemUI/res/values-fi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fi/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Poissa päältä"</item>
     <item msgid="5966994759929723339">"Päällä"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 0d2db40..dbd3518 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Limite inférieure : <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Limite gauche : <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Limite droite : <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Les captures d\'écran du profil professionnel sont enregistrées dans l\'application <xliff:g id="APP">%1$s</xliff:g>"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Enregistré dans <xliff:g id="APP">%1$s</xliff:g> dans le profil professionnel"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Fichiers"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> a détecté cette capture d\'écran."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> et d\'autres applications ouvertes ont détecté cette capture d\'écran."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Enregistreur d\'écran"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Trait. de l\'enregist. d\'écran…"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notification en cours pour une session d\'enregistrement d\'écran"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Luminosité"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversion des couleurs"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correction des couleurs"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Gérer les utilisateurs"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Terminé"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Fermer"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatique"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Aucun son ni vibration"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Aucun son ni vibration, et s\'affiche plus bas dans la section des conversations"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Peut sonner ou vibrer, selon les paramètres de l\'appareil"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Peut sonner ou vibrer, selon les paramètres de l\'appareil. Conversations des bulles de <xliff:g id="APP_NAME">%1$s</xliff:g> par défaut."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Faire en sorte que le système détermine si cette notification devrait émettre un son ou vibrer"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;État :&lt;/b&gt; élevé à la catégorie Par défaut"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;État :&lt;/b&gt; abaissé à la catégorie Silencieux"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"enregistrement d\'écran"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Sans titre"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Veille"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Fenêtre d\'agrandissement"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Commandes pour la fenêtre d\'agrandissement"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Effectuer un zoom avant"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Sélectionnez l\'application pour laquelle ajouter des commandes"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# commande ajoutée.}one{# commande ajoutée.}many{# de commandes ajoutées.}other{# commandes ajoutées.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Supprimé"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Ajouter <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Lorsque vous ajoutez <xliff:g id="APPNAME">%s</xliff:g>, elle peut ajouter des commandes et du contenu à ce panneau. Dans certaines applications, vous pouvez choisir les commandes qui s\'affichent ici."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Ajouté aux favoris"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Ajouté aux favoris, en position <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Supprimé des favoris"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Ouvrez <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Lecture de <xliff:g id="SONG_NAME">%1$s</xliff:g> par <xliff:g id="ARTIST_NAME">%2$s</xliff:g> à partir de <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Lecture de <xliff:g id="SONG_NAME">%1$s</xliff:g> à partir de <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Pour vous"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Annuler"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Rapprochez-vous pour faire jouer le contenu sur <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Pour faire jouer le contenu ici, rapprochez-vous de <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Une erreur s\'est produite. Réessayez."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Chargement en cours…"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablette"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Diffusion de votre contenu multimédia en cours…"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Diffusion de <xliff:g id="APP_LABEL">%1$s</xliff:g> en cours…"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Délai expiré, vérifiez l\'appli"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Introuvable"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"La commande n\'est pas accessible"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Erreur. Veuillez réessayer."</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Ajouter des commandes"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Modifier des commandes"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Ajouter une application"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Ajouter des sorties"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Groupe"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Un appareil sélectionné"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Haut-parleurs et écrans"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Appareils suggérés"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Nécessite un compte payant"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Fonctionnement de la diffusion"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Diffusion"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Les personnes à proximité disposant d\'appareils Bluetooth compatibles peuvent écouter le contenu multimédia que vous diffusez"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Impossible de diffuser"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Impossible d\'enregistrer. Réessayez."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Impossible d\'enregistrer."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numéro de version"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Le numéro de version a été copié dans le presse-papiers."</string>
     <string name="basic_status" msgid="2315371112182658176">"Ouvrir la conversation"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• qu\'au moins un appareil est utilisable;"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Maintenir le doigt sur raccourci"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Annuler"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Retourner maintenant"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Déplier le téléphone pour un meilleur égoportrait"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Retourner l\'écran pour un meilleur égoportrait?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Utilisez l\'appareil photo arrière pour une photo plus large avec une résolution supérieure."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"* Cet écran va s\'éteindre"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Appareil pliable en cours de dépliage"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Appareil pliable en train d\'être retourné"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Charge restante de la pile : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connectez votre stylet à un chargeur"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Pile du stylet faible"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Mode vidéo"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Impossible de passer un appel à partir de ce profil"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Votre politique de l\'entreprise vous autorise à passer des appels téléphoniques uniquement à partir de votre profil professionnel"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Passer au profil professionnel"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Fermer"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Paramètres écran de verrouillage"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
index c408865..788f56d 100644
--- a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Désactivé"</item>
     <item msgid="5966994759929723339">"Activé"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index e0ee5c4..ff8c44f 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Limite inférieure : <xliff:g id="PERCENT">%1$d</xliff:g> pour cent"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Limite gauche : <xliff:g id="PERCENT">%1$d</xliff:g> pour cent"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Limite droite : <xliff:g id="PERCENT">%1$d</xliff:g> pour cent"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Les captures d\'écran du profil professionnel sont enregistrées dans l\'appli <xliff:g id="APP">%1$s</xliff:g>"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Enregistré dans <xliff:g id="APP">%1$s</xliff:g>, dans le profil professionnel"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Fichiers"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> a détecté cette capture d\'écran."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> et d\'autres applis ouvertes ont détecté cette capture d\'écran."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Enregistreur d\'écran"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Enregistrement de l\'écran…"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notification en cours pour une session d\'enregistrement de l\'écran"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Luminosité"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversion des couleurs"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correction des couleurs"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Gérer les utilisateurs"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"OK"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Fermer"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatique"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Ni son, ni vibreur"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Ni son, ni vibreur ; s\'affiche plus bas dans la section des conversations"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Peut sonner ou vibrer en fonction des paramètres de l\'appareil"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Peut sonner ou vibrer en fonction des paramètres de l\'appareil. Les conversations provenant de <xliff:g id="APP_NAME">%1$s</xliff:g> s\'affichent sous forme de bulles par défaut."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Laisser le système déterminer si cette notification doit être accompagnée d\'un son ou d\'une vibration"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;État :&lt;/b&gt; Élevée à la catégorie \"Par défaut\""</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;État :&lt;/b&gt; Abaissée à la catégorie \"Silencieux\""</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"enregistrement écran"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Sans titre"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Mode Veille imminent"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Fenêtre d\'agrandissement"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Fenêtre des commandes d\'agrandissement"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Faire un zoom avant"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Sélectionnez l\'appli pour laquelle ajouter des commandes"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# commande ajoutée.}one{# commande ajoutée.}many{# commandes ajoutées.}other{# commandes ajoutées.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Supprimé"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Ajouter <xliff:g id="APPNAME">%s</xliff:g> ?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Lorsque vous ajoutez l\'appli <xliff:g id="APPNAME">%s</xliff:g>, elle peut ajouter des commandes et contenus dans ce panneau. Dans certaines applis, vous pouvez choisir les commandes à afficher ici."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Ajouté aux favoris"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Ajouté aux favoris, en position <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Supprimé des favoris"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Ouvre <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Mets <xliff:g id="SONG_NAME">%1$s</xliff:g> par <xliff:g id="ARTIST_NAME">%2$s</xliff:g> depuis <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Mets <xliff:g id="SONG_NAME">%1$s</xliff:g> depuis <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Pour vous"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Annuler"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Rapprochez-vous de votre <xliff:g id="DEVICENAME">%1$s</xliff:g> pour y lire le contenu"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Pour lancer la lecture ici, rapprochez-vous de <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Un problème est survenu. Réessayez."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Chargement…"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablette"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Casting de vos contenus multimédias"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Casting de <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Délai expiré, vérifier l\'appli"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Introuvable"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Commande indisponible"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Erreur. Veuillez réessayer."</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Ajouter des commandes"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Modifier des commandes"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Ajouter une appli"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Ajouter des sorties"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Groupe"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 appareil sélectionné"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Enceintes et écrans"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Appareils suggérés"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Nécessite un compte premium"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Fonctionnement des annonces"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Annonce"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Les personnes à proximité équipées d\'appareils Bluetooth compatibles peuvent écouter le contenu multimédia que vous diffusez"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Impossible de diffuser"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Impossible d\'enregistrer. Réessayez."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Impossible d\'enregistrer."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numéro de build"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Numéro de build copié dans le presse-papiers."</string>
     <string name="basic_status" msgid="2315371112182658176">"Conversation ouverte"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Au moins un appareil est disponible"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Appuyez de manière prolongée sur raccourci"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Annuler"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Retourner maintenant"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Déplier le téléphone pour un meilleur selfie"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Passer à l\'écran frontal pour un meilleur selfie ?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Utilisez la caméra arrière pour prendre une photo plus large avec une résolution supérieure."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Cet écran sera désactivé"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Appareil pliable qui est déplié"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Appareil pliable qui est retourné"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> de batterie restante"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connectez votre stylet à un chargeur"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"La batterie du stylet est faible"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Caméra vidéo"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Impossible d\'appeler depuis ce profil"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Votre règle professionnelle ne vous permet de passer des appels que depuis le profil professionnel"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Passer au profil professionnel"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Fermer"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Paramètres écran de verrouillage"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fr/tiles_states_strings.xml b/packages/SystemUI/res/values-fr/tiles_states_strings.xml
index 8c6c4f5..3f63a96 100644
--- a/packages/SystemUI/res/values-fr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fr/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Désactivé"</item>
     <item msgid="5966994759929723339">"Activé"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 87b0178..62d0a30 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Bordo inferior: <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Bordo esquerdo: <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Bordo dereito: <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"As capturas de pantalla do perfil de traballo gárdanse na aplicación <xliff:g id="APP">%1$s</xliff:g>"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Captura de pantalla gardada na aplicación <xliff:g id="APP">%1$s</xliff:g> do perfil de traballo"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Ficheiros"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> detectou esta captura de pantalla."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> e outras aplicacións abertas detectaron esta captura de pantalla."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Gravadora da pantalla"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Procesando gravación pantalla"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificación en curso sobre unha sesión de gravación de pantalla"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillo"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversión da cor"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Corrección da cor"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Administrar usuarios"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Feito"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Pechar"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Sen son nin vibración"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Sen son nin vibración, e aparecen máis abaixo na sección de conversas"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Poderían facer que o dispositivo soe ou vibre en función da súa configuración"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Poderían facer que o dispositivo soe ou vibre en función da súa configuración. As conversas de <xliff:g id="APP_NAME">%1$s</xliff:g> móstranse en burbullas de forma predeterminada."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Fai que o sistema determine se a notificación debe emitir un son ou unha vibración"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Estado:&lt;/b&gt; ascendeuse a Predeterminada"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Estado:&lt;/b&gt; o nivel diminuíuse a Silencioso"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"gravación pantalla"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Sen título"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Modo de espera"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Ventá de superposición"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Controis de ampliación da ventá"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Achegar"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Escolle unha aplicación para engadir controis"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Engadiuse # control.}other{Engadíronse # controis.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Quitouse"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Queres engadir <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Cando engadas a aplicación <xliff:g id="APPNAME">%s</xliff:g>, poderá incluír controis e contido neste panel Nalgunhas aplicacións, podes escoller os controis que se mostrarán aquí."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Está entre os controis favoritos"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Está entre os controis favoritos (posición: <xliff:g id="NUMBER">%d</xliff:g>)"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Non está entre os controis favoritos"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Abre <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Reproduce <xliff:g id="SONG_NAME">%1$s</xliff:g>, de <xliff:g id="ARTIST_NAME">%2$s</xliff:g>, en <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Reproduce <xliff:g id="SONG_NAME">%1$s</xliff:g> en <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Para ti"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Desfacer"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Achega o dispositivo para reproducir o contido en: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Para reproducir o contido aquí, achégate ao dispositivo (<xliff:g id="DEVICENAME">%1$s</xliff:g>)"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Produciuse un erro. Téntao de novo."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Cargando"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tableta"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Emitindo contido multimedia"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Emitindo <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inactivo. Comproba a app"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Non se atopou"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"O control non está dispoñible"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Erro. Téntao de novo"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Engadir controis"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Editar controis"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Engadir aplicación"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Engadir saídas"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupo"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Seleccionouse 1 dispositivo"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Altofalantes e pantallas"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositivos suxeridos"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Require unha conta premium"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funcionan as difusións?"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Difusión"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"As persoas que estean preto de ti e que dispoñan de dispositivos Bluetooth compatibles poden escoitar o contido multimedia que difundas"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Non se puido iniciar a emisión"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Non se puido gardar a información. Téntao de novo."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Non se pode gardar a información."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número de compilación"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Copiouse o número de compilación no portapapeis."</string>
     <string name="basic_status" msgid="2315371112182658176">"Conversa aberta"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Ten que haber polo menos un dispositivo dispoñible"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Mantén premido o atallo"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Cancelar"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Voltear agora"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Desprega o teléfono para unha autofoto mellor"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Usar a cámara dianteira para unha autofoto mellor?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Usa a cámara traseira para sacar unha foto máis ampla e con maior resolución."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Desactivarase esta pantalla"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositivo pregable abríndose"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositivo pregable xirando"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Batería restante: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecta o lapis óptico a un cargador"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"O lapis óptico ten pouca batería"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Videocámara"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Non se pode chamar desde este perfil"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"A política do teu traballo só che permite facer chamadas de teléfono desde o perfil de traballo"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Cambiar ao perfil de traballo"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Pechar"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Configuración pantalla bloqueo"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-gl/tiles_states_strings.xml b/packages/SystemUI/res/values-gl/tiles_states_strings.xml
index 590ec4a..94fc3f4 100644
--- a/packages/SystemUI/res/values-gl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-gl/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Desactivado"</item>
     <item msgid="5966994759929723339">"Activado"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index d287f88..ab37dbb 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"àȘ‘àȘ«àȘżàȘžàȘšàȘŸ àȘžà«àȘ•્àȘ°à«€àȘšàȘ¶à«‰àȘŸ <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>
     <string name="screenrecord_name" msgid="2596401223859996572">"àȘžà«àȘ•્àȘ°à«€àȘš àȘ°à«‡àȘ•à«‹àȘ°à«àȘĄàȘ°"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"àȘžà«àȘ•્àȘ°à«€àȘš àȘ°à«‡àȘ•ૉàȘ°à«àȘĄàȘżàȘ‚àȘ— àȘšàȘŸàȘČુ àȘ›à«‡"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"àȘžà«àȘ•્àȘ°à«€àȘš àȘ°à«‡àȘ•à«‹àȘ°à«àȘĄàȘżàȘ‚àȘ— àȘžàȘ€à«àȘ° àȘźàȘŸàȘŸà«‡ àȘšàȘŸàȘČુ àȘšà«‹àȘŸàȘżàȘ«àȘżàȘ•ેàȘ¶àȘš"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"àȘ€à«‡àȘœ"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"àȘ”àȘżàȘȘàȘ°à«€àȘ€ àȘ°àȘ‚àȘ—àȘźàȘŸàȘ‚ àȘŹàȘŠàȘČàȘ”ુàȘ‚"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"àȘ°àȘ‚àȘ— àȘžà«àȘ§àȘŸàȘ°àȘŁàȘŸ"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"àȘ”àȘȘàȘ°àȘŸàȘ¶àȘ•àȘ°à«àȘ€àȘŸàȘ“àȘšà«‡ àȘźà«‡àȘšà«‡àȘœ àȘ•àȘ°à«‹"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"àȘ„àȘˆ àȘ—àȘŻà«àȘ‚"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"àȘŹàȘ‚àȘ§ àȘ•àȘ°à«‹"</string>
@@ -775,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"àȘžà«àȘ•્àȘ°à«€àȘš àȘ°à«‡àȘ•à«‹àȘ°à«àȘĄàȘżàȘ‚àȘ—"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"àȘ•à«‹àȘˆ àȘ¶à«€àȘ°à«àȘ·àȘ• àȘšàȘ„ી"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"àȘžà«àȘŸà«…àȘšà«àȘĄàȘŹàȘŸàȘŻ"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"àȘ”àȘżàȘžà«àȘ€à«ƒàȘ€à«€àȘ•àȘ°àȘŁ àȘ”àȘżàȘ‚àȘĄà«‹"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"àȘ”àȘżàȘžà«àȘ€à«ƒàȘ€à«€àȘ•àȘ°àȘŁ àȘ”àȘżàȘ‚àȘĄà«‹àȘšàȘŸ àȘšàȘżàȘŻàȘ‚àȘ€à«àȘ°àȘŁà«‹"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"àȘźà«‹àȘŸà«àȘ‚ àȘ•àȘ°à«‹"</string>
@@ -800,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"àȘšàȘżàȘŻàȘ‚àȘ€à«àȘ°àȘŁà«‹ àȘ‰àȘźà«‡àȘ°àȘ”àȘŸ àȘźàȘŸàȘŸà«‡ àȘàȘȘ àȘȘàȘžàȘ‚àȘŠ àȘ•àȘ°à«‹"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# àȘšàȘżàȘŻàȘ‚àȘ€à«àȘ°àȘŁ àȘ‰àȘźà«‡àȘ°à«àȘŻà«àȘ‚.}one{# àȘšàȘżàȘŻàȘ‚àȘ€à«àȘ°àȘŁ àȘ‰àȘźà«‡àȘ°à«àȘŻà«àȘ‚.}other{# àȘšàȘżàȘŻàȘ‚àȘ€à«àȘ°àȘŁ àȘ‰àȘźà«‡àȘ°à«àȘŻàȘŸ.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"àȘ•àȘŸàȘąà«€ àȘšàȘŸàȘ–્àȘŻà«àȘ‚"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g> àȘ‰àȘźà«‡àȘ°à«€àȘ?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"àȘœà«àȘŻàȘŸàȘ°à«‡ àȘ€àȘźà«‡ <xliff:g id="APPNAME">%s</xliff:g> àȘ‰àȘźà«‡àȘ°à«‹, àȘ€à«àȘŻàȘŸàȘ°à«‡ àȘ€à«‡ àȘ† àȘȘૅàȘšàȘČàȘźàȘŸàȘ‚ àȘšàȘżàȘŻàȘ‚àȘ€à«àȘ°àȘŁà«‹ àȘ…àȘšà«‡ àȘ•àȘšà«àȘŸà«‡àȘšà«àȘŸ àȘ‰àȘźà«‡àȘ°à«€ àȘ¶àȘ•ે àȘ›à«‡. àȘ•ેàȘŸàȘČીàȘ• àȘàȘȘàȘźàȘŸàȘ‚, àȘ…àȘčીàȘ‚ àȘ•àȘŻàȘŸ àȘšàȘżàȘŻàȘ‚àȘ€à«àȘ°àȘŁà«‹ àȘŠà«‡àȘ–àȘŸàȘŻ àȘ€à«‡ àȘ€àȘźà«‡ àȘȘàȘžàȘ‚àȘŠ àȘ•àȘ°à«€ àȘ¶àȘ•à«‹ àȘ›à«‹."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"àȘźàȘšàȘȘàȘžàȘ‚àȘŠàȘźàȘŸàȘ‚ àȘ‰àȘźà«‡àȘ°à«àȘŻà«àȘ‚"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"àȘźàȘšàȘȘàȘžàȘ‚àȘŠàȘźàȘŸàȘ‚ àȘ‰àȘźà«‡àȘ°à«àȘŻà«àȘ‚, àȘžà«àȘ„àȘŸàȘš <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"àȘźàȘšàȘȘàȘžàȘ‚àȘŠàȘźàȘŸàȘ‚àȘ„ી àȘ•àȘŸàȘąà«€ àȘšàȘŸàȘ–્àȘŻà«àȘ‚"</string>
@@ -817,7 +829,7 @@
     <string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"àȘ…àȘšà«àȘŻ"</string>
     <string name="controls_dialog_title" msgid="2343565267424406202">"àȘĄàȘżàȘ”àȘŸàȘ‡àȘžàȘšàȘŸàȘ‚ àȘšàȘżàȘŻàȘ‚àȘ€à«àȘ°àȘŁà«‹àȘźàȘŸàȘ‚ àȘ‰àȘźà«‡àȘ°à«‹"</string>
     <string name="controls_dialog_ok" msgid="2770230012857881822">"àȘ‰àȘźà«‡àȘ°à«‹"</string>
-    <string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g> àȘŠà«àȘ”àȘŸàȘ°àȘŸ àȘžà«‚àȘšàȘš àȘ•àȘ°à«‡àȘČàȘŸ"</string>
+    <string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g> àȘŠà«àȘ”àȘŸàȘ°àȘŸ àȘžà«‚àȘšàȘ”ેàȘČàȘŸ"</string>
     <string name="controls_tile_locked" msgid="731547768182831938">"àȘĄàȘżàȘ”àȘŸàȘ‡àȘž àȘČૉàȘ• àȘ•àȘ°à«‡àȘČુàȘ‚ àȘ›à«‡"</string>
     <string name="controls_settings_show_controls_dialog_title" msgid="3357852503553809554">"àȘČૉàȘ• àȘžà«àȘ•્àȘ°à«€àȘšàȘźàȘŸàȘ‚àȘ„ી àȘĄàȘżàȘ”àȘŸàȘ‡àȘž àȘŹàȘ€àȘŸàȘ”ીàȘ àȘ…àȘšà«‡ àȘšàȘżàȘŻàȘ‚àȘ€à«àȘ°àȘżàȘ€ àȘ•àȘ°à«€àȘ?"</string>
     <string name="controls_settings_show_controls_dialog_message" msgid="7666211700524587969">"àȘ€àȘźà«‡ àȘ€àȘźàȘŸàȘ°àȘŸ àȘŹàȘŸàȘč્àȘŻ àȘĄàȘżàȘ”àȘŸàȘ‡àȘž àȘźàȘŸàȘŸà«‡àȘšàȘŸ àȘšàȘżàȘŻàȘ‚àȘ€à«àȘ°àȘŁà«‹ àȘČૉàȘ• àȘžà«àȘ•્àȘ°à«€àȘš àȘȘàȘ° àȘ‰àȘźà«‡àȘ°à«€ àȘ¶àȘ•à«‹ àȘ›à«‹.\n\nàȘ€àȘźàȘŸàȘ°à«€ àȘĄàȘżàȘ”àȘŸàȘ‡àȘž àȘàȘȘ àȘ•àȘŠàȘŸàȘš àȘ€àȘźàȘšà«‡ àȘ€àȘźàȘŸàȘ°à«‹ àȘ«à«‹àȘš àȘ•ે àȘŸà«…àȘŹà«àȘČેàȘŸ àȘ…àȘšàȘČૉàȘ• àȘ•àȘ°à«àȘŻàȘŸ àȘ”àȘżàȘšàȘŸ àȘ…àȘźà«àȘ• àȘĄàȘżàȘ”àȘŸàȘ‡àȘž àȘšàȘżàȘŻàȘ‚àȘ€à«àȘ°àȘżàȘ€ àȘ•àȘ°àȘ”àȘŸàȘšà«€ àȘźàȘ‚àȘœà«‚àȘ°à«€ àȘ†àȘȘી àȘ¶àȘ•ે.\n\nàȘ€àȘźà«‡ àȘ—àȘźà«‡ àȘ€à«àȘŻàȘŸàȘ°à«‡ àȘžà«‡àȘŸàȘżàȘ‚àȘ—àȘźàȘŸàȘ‚ àȘœàȘˆàȘšà«‡ àȘ«à«‡àȘ°àȘ«àȘŸàȘ° àȘ•àȘ°à«€ àȘ¶àȘ•à«‹ àȘ›à«‹."</string>
@@ -850,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> àȘ–à«‹àȘČો"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> àȘȘàȘ° <xliff:g id="ARTIST_NAME">%2$s</xliff:g>àȘšà«àȘ‚ <xliff:g id="SONG_NAME">%1$s</xliff:g> àȘ—à«€àȘ€ àȘšàȘČàȘŸàȘ”ો"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> àȘȘàȘ° <xliff:g id="SONG_NAME">%1$s</xliff:g> àȘ—à«€àȘ€ àȘšàȘČàȘŸàȘ”ો"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"àȘ€àȘźàȘŸàȘ°àȘŸ àȘźàȘŸàȘŸà«‡"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"àȘ›à«‡àȘČ્àȘČો àȘ«à«‡àȘ°àȘ«àȘŸàȘ° àȘ°àȘŠ àȘ•àȘ°à«‹"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> àȘȘàȘ° àȘšàȘČàȘŸàȘ”àȘ”àȘŸ àȘźàȘŸàȘŸà«‡ àȘ”àȘ§à« àȘšàȘœà«€àȘ• àȘ–àȘžà«‡àȘĄà«‹"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"àȘ…àȘčીàȘ‚ àȘšàȘČàȘŸàȘ”àȘ”àȘŸ àȘźàȘŸàȘŸà«‡, <xliff:g id="DEVICENAME">%1$s</xliff:g>àȘšà«€ àȘšàȘœà«€àȘ• àȘČàȘŸàȘ”ો"</string>
@@ -857,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"àȘ•àȘ‚àȘˆàȘ• àȘ–à«‹àȘŸà«àȘ‚ àȘ„àȘŻà«àȘ‚. àȘ«àȘ°à«€ àȘȘ્àȘ°àȘŻàȘŸàȘž àȘ•àȘ°à«‹."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"àȘČોàȘĄ àȘ„àȘˆ àȘ°àȘč્àȘŻà«àȘ‚ àȘ›à«‡"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"àȘŸà«…àȘŹà«àȘČેàȘŸ"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"àȘ€àȘźàȘŸàȘ°à«àȘ‚ àȘźà«€àȘĄàȘżàȘŻàȘŸ àȘ•àȘŸàȘžà«àȘŸ àȘ•àȘ°à«€ àȘ°àȘč્àȘŻàȘŸàȘ‚ àȘ›à«€àȘ"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"<xliff:g id="APP_LABEL">%1$s</xliff:g> àȘ•àȘŸàȘžà«àȘŸ àȘ•àȘ°à«€ àȘ°àȘč્àȘŻàȘŸàȘ‚ àȘ›à«€àȘ"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"àȘšàȘżàȘ·à«àȘ•્àȘ°àȘżàȘŻ, àȘàȘȘàȘšà«‡ àȘšà«‡àȘ• àȘ•àȘ°à«‹"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"àȘźàȘłà«àȘŻà«àȘ‚ àȘšàȘ„ી"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"àȘšàȘżàȘŻàȘ‚àȘ€à«àȘ°àȘŁ àȘ‰àȘȘàȘČàȘŹà«àȘ§ àȘšàȘ„ી"</string>
@@ -866,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"àȘ­à«‚àȘČ, àȘ«àȘ°à«€àȘ„ી àȘȘ્àȘ°àȘŻàȘŸàȘž àȘ•àȘ°à«‹"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"àȘšàȘżàȘŻàȘ‚àȘ€à«àȘ°àȘŁà«‹ àȘ‰àȘźà«‡àȘ°à«‹"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"àȘšàȘżàȘŻàȘ‚àȘ€à«àȘ°àȘŁà«‹àȘźàȘŸàȘ‚ àȘ«à«‡àȘ°àȘ«àȘŸàȘ° àȘ•àȘ°à«‹"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"àȘàȘȘ àȘ‰àȘźà«‡àȘ°à«‹"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"àȘ†àȘ‰àȘŸàȘȘુàȘŸ àȘ‰àȘźà«‡àȘ°à«‹"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"àȘ—્àȘ°à«‚àȘȘ"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 àȘĄàȘżàȘ”àȘŸàȘ‡àȘž àȘȘàȘžàȘ‚àȘŠ àȘ•àȘ°à«àȘŻà«àȘ‚"</string>
@@ -881,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"àȘžà«àȘȘીàȘ•àȘ° àȘ…àȘšà«‡ àȘĄàȘżàȘžà«àȘȘ્àȘČે"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"àȘžà«‚àȘšàȘ”ેàȘČàȘŸ àȘĄàȘżàȘ”àȘŸàȘ‡àȘž"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Premium àȘàȘ•àȘŸàȘ‰àȘšà«àȘŸ àȘœàȘ°à«‚àȘ°à«€ àȘ›à«‡"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"àȘŹà«àȘ°à«‹àȘĄàȘ•àȘŸàȘžà«àȘŸ àȘȘ્àȘ°àȘ•્àȘ°àȘżàȘŻàȘŸàȘšà«€ àȘ•àȘŸàȘź àȘ•àȘ°àȘ”àȘŸàȘšà«€ àȘ°à«€àȘ€"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"àȘŹà«àȘ°à«‹àȘĄàȘ•àȘŸàȘžà«àȘŸ àȘ•àȘ°à«‹"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"àȘžà«àȘžàȘ‚àȘ—àȘ€ àȘŹà«àȘČૂàȘŸà«‚àȘ„ àȘĄàȘżàȘ”àȘŸàȘ‡àȘž àȘ§àȘ°àȘŸàȘ”àȘ€àȘŸ àȘšàȘœà«€àȘ•àȘšàȘŸ àȘČોàȘ•à«‹ àȘ€àȘźà«‡ àȘœà«‡ àȘźà«€àȘĄàȘżàȘŻàȘŸ àȘŹà«àȘ°à«‹àȘĄàȘ•àȘŸàȘžà«àȘŸ àȘ•àȘ°à«€ àȘ°àȘč્àȘŻàȘŸàȘ‚ àȘ›à«‹ àȘ€à«‡ àȘžàȘŸàȘ‚àȘ­àȘłà«€ àȘ¶àȘ•ે àȘ›à«‡"</string>
@@ -892,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"àȘŹà«àȘ°à«‹àȘĄàȘ•àȘŸàȘžà«àȘŸ àȘ•àȘ°à«€ àȘ¶àȘ•àȘ€àȘŸ àȘšàȘ„ી"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"àȘžàȘŸàȘšàȘ”ી àȘ¶àȘ•àȘ€àȘŸ àȘšàȘ„ી. àȘ«àȘ°à«€ àȘȘ્àȘ°àȘŻàȘŸàȘž àȘ•àȘ°à«‹."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"àȘžàȘŸàȘšàȘ”ી àȘ¶àȘ•àȘ€àȘŸ àȘšàȘ„ી."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"àȘŹàȘżàȘČ્àȘĄ àȘšàȘ‚àȘŹàȘ°"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"àȘŹàȘżàȘČ્àȘĄ àȘšàȘ‚àȘŹàȘ° àȘ•્àȘČàȘżàȘȘàȘŹà«‰àȘ°à«àȘĄ àȘȘàȘ° àȘ•ૉàȘȘàȘż àȘ•àȘ°à«àȘŻà«‹."</string>
     <string name="basic_status" msgid="2315371112182658176">"àȘ”àȘŸàȘ€àȘšà«€àȘ€ àȘ–à«‹àȘČો"</string>
@@ -1011,11 +1032,16 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• àȘ“àȘ›àȘŸàȘźàȘŸàȘ‚ àȘ“àȘ›à«àȘ‚ àȘàȘ• àȘĄàȘżàȘ”àȘŸàȘ‡àȘž àȘ‰àȘȘàȘČàȘŹà«àȘ§ àȘ›à«‡"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"àȘ¶à«‰àȘ°à«àȘŸàȘ•àȘŸàȘšà«‡ àȘŸàȘš àȘ”àȘĄà«‡ àȘȘàȘłàȘ­àȘ° àȘŠàȘŹàȘŸàȘ”ી àȘ°àȘŸàȘ–à«‹"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"àȘ°àȘŠ àȘ•àȘ°à«‹"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"àȘčàȘźàȘŁàȘŸàȘ‚ àȘœ àȘ«à«àȘČàȘżàȘȘ àȘ•àȘ°à«‹"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"àȘŹàȘčેàȘ€àȘ° àȘžà«‡àȘČ્àȘ«à«€ àȘČેàȘ”àȘŸ àȘźàȘŸàȘŸà«‡ àȘ«à«‹àȘš àȘ–à«‹àȘČો"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"àȘŹàȘčેàȘ€àȘ° àȘžà«‡àȘČ્àȘ«à«€ àȘČેàȘ”àȘŸ àȘ«à«àȘ°àȘšà«àȘŸ àȘĄàȘżàȘžà«àȘȘ્àȘČે àȘȘàȘ° àȘ«à«àȘČàȘżàȘȘ àȘ•àȘ°à«€àȘ?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"àȘ”àȘ§à« àȘ‰àȘšà«àȘš àȘ°àȘżàȘà«‹àȘČ્àȘŻà«àȘ¶àȘšàȘ”àȘŸàȘłà«‹ àȘ”àȘżàȘ¶àȘŸàȘł àȘ«à«‹àȘŸà«‹ àȘČેàȘ”àȘŸ àȘźàȘŸàȘŸà«‡ àȘȘàȘŸàȘ›àȘČàȘŸ àȘ•à«…àȘźà«‡àȘ°àȘŸàȘšà«‹ àȘ‰àȘȘàȘŻà«‹àȘ— àȘ•àȘ°à«‹."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ àȘ† àȘžà«àȘ•્àȘ°à«€àȘš àȘŹàȘ‚àȘ§ àȘ„àȘˆ àȘœàȘ¶à«‡"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"àȘ«à«‹àȘČ્àȘĄ àȘ•àȘ°à«€ àȘ¶àȘ•àȘŸàȘŻ àȘàȘ”ુàȘ‚ àȘĄàȘżàȘ”àȘŸàȘ‡àȘž àȘ…àȘšàȘ«à«‹àȘČ્àȘĄ àȘ•àȘ°àȘ”àȘŸàȘźàȘŸàȘ‚ àȘ†àȘ”ી àȘ°àȘč્àȘŻà«àȘ‚ àȘ›à«‡"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"àȘ«à«‹àȘČ્àȘĄ àȘ•àȘ°à«€ àȘ¶àȘ•àȘŸàȘŻ àȘàȘ”ુàȘ‚ àȘĄàȘżàȘ”àȘŸàȘ‡àȘž àȘ«à«àȘČàȘżàȘȘ àȘ•àȘ°àȘ”àȘŸàȘźàȘŸàȘ‚ àȘ†àȘ”ી àȘ°àȘč્àȘŻà«àȘ‚ àȘ›à«‡"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> àȘŹà«…àȘŸàȘ°à«€ àȘŹàȘŸàȘ•à«€ àȘ›à«‡"</string>
@@ -1026,4 +1052,5 @@
     <string name="call_from_work_profile_text" msgid="3458704745640229638">"àȘ€àȘźàȘŸàȘ°à«€ àȘ‘àȘ«àȘżàȘžàȘšà«€ àȘȘૉàȘČàȘżàȘžà«€ àȘ€àȘźàȘšà«‡ àȘźàȘŸàȘ€à«àȘ° àȘ‘àȘ«àȘżàȘžàȘšà«€ àȘȘ્àȘ°à«‹àȘ«àȘŸàȘ‡àȘČ àȘȘàȘ°àȘ„ી àȘœ àȘ«à«‹àȘš àȘ•ૉàȘČ àȘ•àȘ°àȘ”àȘŸàȘšà«€ àȘźàȘ‚àȘœà«‚àȘ°à«€ àȘ†àȘȘે àȘ›à«‡"</string>
     <string name="call_from_work_profile_action" msgid="2937701298133010724">"àȘ‘àȘ«àȘżàȘžàȘšà«€ àȘȘ્àȘ°à«‹àȘ«àȘŸàȘ‡àȘČ àȘȘàȘ° àȘžà«àȘ”àȘżàȘš àȘ•àȘ°à«‹"</string>
     <string name="call_from_work_profile_close" msgid="7927067108901068098">"àȘŹàȘ‚àȘ§ àȘ•àȘ°à«‹"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"àȘČૉàȘ• àȘžà«àȘ•્àȘ°à«€àȘšàȘšàȘŸ àȘžà«‡àȘŸàȘżàȘ‚àȘ—"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-gu/tiles_states_strings.xml b/packages/SystemUI/res/values-gu/tiles_states_strings.xml
index cc062a77..e92168c 100644
--- a/packages/SystemUI/res/values-gu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-gu/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"àȘŹàȘ‚àȘ§ àȘ›à«‡"</item>
     <item msgid="5966994759929723339">"àȘšàȘŸàȘČુ àȘ›à«‡"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 8fe43ac..22933ba 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"à€”à€°à„à€• à€Șà„à€°à„‹à€«à€Œà€Ÿà€‡à€Č à€žà„‡ à€Čà€żà€ à€—à€ à€žà„à€•à„à€°à„€à€šà€¶à„‰à€Ÿ, <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">"Files"</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>
     <string name="screenrecord_name" msgid="2596401223859996572">"à€žà„à€•à„à€°à„€à€š à€°à€żà€•à„‰à€°à„à€Ąà€°"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"à€žà„à€•à„à€°à„€à€š à€°à€żà€•à„‰à€°à„à€Ąà€żà€‚à€— à€•à„‹ à€Șà„à€°à„‹à€žà„‡à€ž à€•à€żà€Żà€Ÿ à€œà€Ÿ à€°à€čà€Ÿ à€čà„ˆ"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"à€žà„à€•à„à€°à„€à€š à€°à€żà€•à„‰à€°à„à€Ą à€žà„‡à€¶à€š à€•à„‡ à€Čà€żà€ à€œà€Ÿà€°à„€ à€žà„‚à€šà€šà€Ÿ"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"à€žà„à€•à„à€°à„€à€š à€•à„€ à€°à„‹à€¶à€šà„€"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"à€°à€‚à€— à€Źà€Šà€Čà€šà„‡ à€•à„€ à€žà„à€”à€żà€§à€Ÿ"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"à€°à€‚à€— à€źà„‡à€‚ à€žà„à€§à€Ÿà€° à€•à€°à€šà„‡ à€•à„€ à€žà„à€”à€żà€§à€Ÿ"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"à€‰à€Șà€Żà„‹à€—à€•à€°à„à€€à€Ÿà€“à€‚ à€•à„‹ à€źà„ˆà€šà„‡à€œ à€•à€°à„‡à€‚"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"à€čà„‹ à€—à€Żà€Ÿ"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"à€°à€Šà„à€Š à€•à€°à„‡à€‚"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"à€…à€Șà€šà„‡-à€†à€Ș"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"à€•à€żà€žà„€ à€€à€°à€č à€•à„€ à€†à€”à€Ÿà€œà€Œ à€Żà€Ÿ à€”à€Ÿà€‡à€Źà„à€°à„‡à€¶à€š à€š à€čà„‹"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"à€‡à€žà€žà„‡ à€•à€żà€žà„€ à€€à€°à€č à€•à„€ à€†à€”à€Ÿà€œà€Œ à€Żà€Ÿ à€”à€Ÿà€‡à€Źà„à€°à„‡à€¶à€š à€šà€čà„€à€‚ à€čà„‹à€€à€Ÿ à€”à€° à€Źà€Ÿà€€à€šà„€à€€, à€žà„‡à€•à„à€¶à€š à€źà„‡à€‚ à€žà€Źà€žà„‡ à€šà„€à€šà„‡ à€Šà€żà€–à€€à„€ à€čà„ˆ"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"à€Ąà€żà€”à€Ÿà€‡à€ž à€•à„€ à€žà„‡à€Ÿà€żà€‚à€— à€•à„‡ à€†à€§à€Ÿà€° à€Șà€°, à€žà„‚à€šà€šà€Ÿ à€†à€šà„‡ à€Șà€° à€˜à€‚à€Ÿà„€ à€Źà€œ à€žà€•à€€à„€ à€čà„ˆ à€Żà€Ÿ à€”à€Ÿà€‡à€Źà„à€°à„‡à€¶à€š à€čà„‹ à€žà€•à€€à€Ÿ à€čà„ˆ"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"à€Ąà€żà€”à€Ÿà€‡à€ž à€•à„€ à€žà„‡à€Ÿà€żà€‚à€— à€•à„‡ à€†à€§à€Ÿà€° à€Șà€°, à€žà„‚à€šà€šà€Ÿ à€†à€šà„‡ à€Șà€° à€˜à€‚à€Ÿà„€ à€Źà€œ à€žà€•à€€à„€ à€čà„ˆ à€Żà€Ÿ à€”à€Ÿà€‡à€Źà„à€°à„‡à€¶à€š à€čà„‹ à€žà€•à€€à€Ÿ à€čà„ˆ. <xliff:g id="APP_NAME">%1$s</xliff:g> à€Șà€° à€čà„‹à€šà„‡ à€”à€Ÿà€Čà„€ à€Źà€Ÿà€€à€šà„€à€€, à€Ąà€żà€«à€Œà„‰à€Čà„à€Ÿ à€°à„‚à€Ș à€žà„‡ à€Źà€Źà€Č à€•à„‡ à€€à„Œà€° à€Șà€° à€Šà€żà€–à€€à„€ à€čà„ˆ."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"à€žà€żà€žà„à€Ÿà€ź à€•à„‹ à€Żà€č à€€à€Ż à€•à€°à€šà„‡ à€•à„€ à€…à€šà„à€źà€€à€ż à€Šà„‡à€‚ à€•à€ż à€‡à€ž à€žà„‚à€šà€šà€Ÿ à€•à„‡ à€źà€żà€Čà€šà„‡ à€Șà€° à€†à€”à€Ÿà€œà€Œ à€čà„‹ à€Żà€Ÿ à€”à€Ÿà€‡à€Źà„à€°à„‡à€¶à€š à€čà„‹"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;à€žà„à€„à€żà€€à€ż:&lt;/b&gt; à€Čà„‡à€”à€Č à€Źà€ąà€Œà€Ÿà€•à€°, à€Ąà€żà€«à€Œà„‰à€Čà„à€Ÿ à€•à„‡ à€€à„Œà€° à€Șà€° à€žà„‡à€Ÿ à€•à€żà€Żà€Ÿ à€—à€Żà€Ÿ"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;à€žà„à€„à€żà€€à€ż:&lt;/b&gt; à€Čà„‡à€”à€Č à€˜à€Ÿà€Ÿà€•à€°, à€žà€Ÿà€‡à€Čà„‡à€‚à€Ÿ à€Șà€° à€žà„‡à€Ÿ à€•à€żà€Żà€Ÿ à€—à€Żà€Ÿ"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"à€žà„à€•à„à€°à„€à€š à€°à€żà€•à„‰à€°à„à€Ąà€żà€‚à€—"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"à€•à„‹à€ˆ à€¶à„€à€°à„à€·à€• à€šà€čà„€à€‚"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"à€žà„à€Ÿà„ˆà€‚à€Ąà€Źà€Ÿà€ˆ"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"à€žà„à€•à„à€°à„€à€š à€•à„‹ à€Źà€Ąà€Œà€Ÿ à€•à€°à€•à„‡ à€Šà€żà€–à€Ÿà€šà„‡ à€”à€Ÿà€Čà„€ à€”à€żà€‚à€Ąà„‹"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"à€žà„à€•à„à€°à„€à€š à€•à„‹ à€Źà€Ąà€Œà€Ÿ à€•à€°à€•à„‡ à€Šà€żà€–à€Ÿà€šà„‡ à€”à€Ÿà€Čà„€ à€”à€żà€‚à€Ąà„‹ à€•à„‡ à€šà€żà€Żà€‚à€€à„à€°à€Ł"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"à€œà€Œà„‚à€ź à€‡à€š à€•à€°à„‡à€‚"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"à€•à€‚à€Ÿà„à€°à„‹à€Č à€œà„‹à€Ąà€Œà€šà„‡ à€•à„‡ à€Čà€żà€ à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€šà„à€šà„‡à€‚"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# à€•à€‚à€Ÿà„à€°à„‹à€Č à€œà„‹à€Ąà€Œà€Ÿ à€—à€Żà€Ÿ.}one{# à€•à€‚à€Ÿà„à€°à„‹à€Č à€œà„‹à€Ąà€Œà€Ÿ à€—à€Żà€Ÿ.}other{# à€•à€‚à€Ÿà„à€°à„‹à€Č à€œà„‹à€Ąà€Œà„‡ à€—à€.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"à€čà€Ÿà€Ÿà€Żà€Ÿ à€—à€Żà€Ÿ"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g> à€•à„‹ à€œà„‹à€Ąà€Œà€šà€Ÿ à€čà„ˆ?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"<xliff:g id="APPNAME">%s</xliff:g> à€•à„‹ à€œà„‹à€Ąà€Œà€šà„‡ à€Șà€°, à€”à€č à€‡à€ž à€Șà„ˆà€šà€Č à€Șà€° à€•à„à€› à€•à€‚à€Ÿà„à€°à„‹à€Č à€”à€° à€•à„‰à€šà„à€Ÿà„‡à€‚à€Ÿ à€Šà€żà€–à€Ÿ à€žà€•à€€à€Ÿ à€čà„ˆ. à€•à„à€› à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€•à„‡ à€Čà€żà€ à€Żà€č à€šà„à€šà€Ÿ à€œà€Ÿ à€žà€•à€€à€Ÿ à€čà„ˆ à€•à€ż à€”à„‡ à€‡à€ž à€Șà„ˆà€šà€Č à€Șà€° à€•à„Œà€šà€žà„‡ à€•à€‚à€Ÿà„à€°à„‹à€Č à€Šà€żà€–à€Ÿà€à€‚."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"à€Șà€žà€‚à€Šà„€à€Šà€Ÿ à€Źà€šà€Ÿà€Żà€Ÿ à€—à€Żà€Ÿ"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"à€Șà€žà€‚à€Šà„€à€Šà€Ÿ à€Źà€šà€Ÿà€Żà€Ÿ à€—à€Żà€Ÿ, à€•à„à€°à€ź à€žà€‚à€–à„à€Żà€Ÿ <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"à€Șà€žà€‚à€Šà„€à€Šà€Ÿ à€žà„‡ à€čà€Ÿà€Ÿà€Żà€Ÿ à€—à€Żà€Ÿ"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> à€–à„‹à€Čà„‡à€‚"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> à€Șà€°, <xliff:g id="ARTIST_NAME">%2$s</xliff:g> à€•à€Ÿ <xliff:g id="SONG_NAME">%1$s</xliff:g> à€šà€Čà€Ÿà€à€‚"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> à€Șà€°, <xliff:g id="SONG_NAME">%1$s</xliff:g> à€šà€Čà€Ÿà€à€‚"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"à€†à€Șà€•à„‡ à€Čà€żà€ à€žà„à€à€Ÿà€”"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"à€Șà€čà€Čà„‡ à€œà„ˆà€žà€Ÿ à€•à€°à„‡à€‚"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> à€Șà€° à€źà„€à€Ąà€żà€Żà€Ÿ à€šà€Čà€Ÿà€šà„‡ à€•à„‡ à€Čà€żà€, à€…à€Șà€šà„‡ à€Ąà€żà€”à€Ÿà€‡à€ž à€•à„‹ à€‰à€žà€•à„‡ à€Șà€Ÿà€ž à€Čà„‡ à€œà€Ÿà€à€‚"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"à€źà„€à€Ąà€żà€Żà€Ÿ à€Ÿà„à€°à€Ÿà€‚à€žà€«à€Œà€° à€•à€°à€šà„‡ à€•à„‡ à€Čà€żà€, <xliff:g id="DEVICENAME">%1$s</xliff:g> à€•à„‡ à€•à€°à„€à€Ź à€œà€Ÿà€à€‚"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"à€•à„‹à€ˆ à€—à€Ąà€Œà€Źà€Ąà€Œà„€ à€čà„à€ˆ. à€«à€żà€° à€žà„‡ à€•à„‹à€¶à€żà€¶ à€•à€°à„‡à€‚."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"à€Čà„‹à€Ą à€čà„‹ à€°à€čà€Ÿ à€čà„ˆ"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"à€Ÿà„ˆà€Źà€Čà„‡à€Ÿ"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"à€†à€Șà€•à€Ÿ à€źà„€à€Ąà€żà€Żà€Ÿ à€•à€Ÿà€žà„à€Ÿ à€•à€żà€Żà€Ÿ à€œà€Ÿ à€°à€čà€Ÿ à€čà„ˆ"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"<xliff:g id="APP_LABEL">%1$s</xliff:g> à€•à„‹ à€•à€Ÿà€žà„à€Ÿ à€•à€żà€Żà€Ÿ à€œà€Ÿ à€°à€čà€Ÿ à€čà„ˆ"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"à€•à€Ÿà€ź à€šà€čà„€à€‚ à€•à€° à€°à€čà€Ÿ, à€à€Ș à€œà€Ÿà€‚à€šà„‡à€‚"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"à€•à€‚à€Ÿà„à€°à„‹à€Č à€šà€čà„€à€‚ à€čà„ˆ"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"à€•à€‚à€Ÿà„à€°à„‹à€Č à€źà„Œà€œà„‚à€Š à€šà€čà„€à€‚ à€čà„ˆ"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"à€—à€Ąà€Œà€Źà€Ąà€Œà„€ à€čà„à€ˆ, à€«à€żà€° à€žà„‡ à€•à„‹à€¶à€żà€¶ à€•à€°à„‡à€‚"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"à€•à€‚à€Ÿà„à€°à€Ÿà„‡à€Č à€œà„‹à€Ąà€Œà„‡à€‚"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"à€•à€‚à€Ÿà„à€°à„‹à€Č à€źà„‡à€‚ à€Źà€Šà€Čà€Ÿà€” à€•à€°à„‡à€‚"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"à€à€Șà„à€Čà€żà€•à„‡à€¶à€š à€œà„‹à€Ąà€Œà„‡à€‚"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"à€†à€‰à€Ÿà€Șà„à€Ÿ à€œà„‹à€Ąà€Œà„‡à€‚"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"à€—à„à€°à„à€Ș"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"à€à€• à€Ąà€żà€”à€Ÿà€‡à€ž à€šà„à€šà€Ÿ à€—à€Żà€Ÿ"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"à€žà„à€Șà„€à€•à€° à€”à€° à€Ąà€żà€žà€Șà„à€Čà„‡"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"à€žà„à€à€Ÿà€ à€—à€ à€Ąà€żà€”à€Ÿà€‡à€ž"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"à€Șà„à€°à„€à€źà€żà€Żà€ź à€–à€Ÿà€€à€Ÿ à€čà„‹à€šà€Ÿ à€œà€Œà€°à„‚à€°à„€ à€čà„ˆ"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"à€Źà„à€°à„‰à€Ąà€•à€Ÿà€žà„à€Ÿ à€•à€°à€šà„‡ à€•à„€ à€žà„à€”à€żà€§à€Ÿ à€•à„ˆà€žà„‡ à€•à€Ÿà€ź à€•à€°à€€à„€ à€čà„ˆ"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"à€Źà„à€°à„‰à€Ąà€•à€Ÿà€žà„à€Ÿ à€•à€°à„‡à€‚"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"à€†à€Șà€•à„‡ à€†à€ž-à€Șà€Ÿà€ž à€źà„Œà€œà„‚à€Š à€Čà„‹à€—, à€Źà„à€°à„‰à€Ąà€•à€Ÿà€žà„à€Ÿ à€•à€żà€ à€œà€Ÿ à€°à€čà„‡ à€źà„€à€Ąà€żà€Żà€Ÿ à€•à„‹ à€žà„à€š à€žà€•à€€à„‡ à€čà„ˆà€‚. à€čà€Ÿà€Čà€Ÿà€‚à€•à€ż, à€‡à€žà€•à„‡ à€Čà€żà€ à€‰à€šà€•à„‡ à€Șà€Ÿà€ž à€à€žà„‡ à€Źà„à€Čà„‚à€Ÿà„‚à€„ à€Ąà€żà€”à€Ÿà€‡à€ž à€čà„‹à€šà„‡ à€šà€Ÿà€čà€żà€ à€œà€żà€š à€Șà€° à€źà„€à€Ąà€żà€Żà€Ÿ à€šà€Čà€Ÿà€Żà€Ÿ à€œà€Ÿ à€žà€•à„‡"</string>
@@ -894,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"à€Źà„à€°à„‰à€Ąà€•à€Ÿà€žà„à€Ÿ à€šà€čà„€à€‚ à€•à€żà€Żà€Ÿ à€œà€Ÿ à€žà€•à€€à€Ÿ"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"à€žà„‡à€” à€šà€čà„€à€‚ à€•à€żà€Żà€Ÿ à€œà€Ÿ à€žà€•à€Ÿ. à€«à€żà€° à€žà„‡ à€•à„‹à€¶à€żà€¶ à€•à€°à„‡à€‚."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"à€žà„‡à€” à€šà€čà„€à€‚ à€•à€żà€Żà€Ÿ à€œà€Ÿ à€žà€•à€Ÿ."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"à€•à€ź à€žà„‡ à€•à€ź à€šà€Ÿà€° à€”à€°à„à€Ł à€‡à€žà„à€€à„‡à€źà€Ÿà€Č à€•à€°à„‡à€‚"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"16 à€”à€°à„à€Ł à€žà„‡ à€•à€ź à€‡à€žà„à€€à„‡à€źà€Ÿà€Č à€•à€°à„‡à€‚"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"à€Źà€żà€Čà„à€Ą à€šà€‚à€Źà€°"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"à€Źà€żà€Čà„à€Ą à€šà€‚à€Źà€° à€•à„‹ à€•à„à€Čà€żà€Șà€Źà„‹à€°à„à€Ą à€Șà€° à€•à„‰à€Șà„€ à€•à€żà€Żà€Ÿ à€—à€Żà€Ÿ."</string>
     <string name="basic_status" msgid="2315371112182658176">"à€à€žà„€ à€Źà€Ÿà€€à€šà„€à€€ à€œà€żà€žà€źà„‡à€‚ à€‡à€‚à€Ÿà€°à„ˆà€•à„à€¶à€š à€Ąà„‡à€Ÿà€Ÿ à€źà„Œà€œà„‚à€Š à€šà€čà„€à€‚ à€čà„ˆ"</string>
@@ -1013,24 +1030,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• à€•à€ź à€žà„‡ à€•à€ź à€à€• à€Ąà€żà€”à€Ÿà€‡à€ž à€‰à€Șà€Čà€Źà„à€§ à€čà„ˆ"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"à€¶à„‰à€°à„à€Ÿà€•à€Ÿ à€•à„‹ à€Šà€Źà€Ÿà€•à€° à€°à€–à„‡à€‚"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"à€°à€Šà„à€Š à€•à€°à„‡à€‚"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"à€•à„ˆà€źà€°à€Ÿ à€…à€­à„€ à€žà„à€”à€żà€š à€•à€°à„‡à€‚"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"à€Źà„‡à€čà€€à€° à€žà„‡à€Čà„à€«à€Œà„€ à€•à„‡ à€Čà€żà€ à€«à€Œà„‹à€š à€•à„‹ à€…à€šà€«à€Œà„‹à€Čà„à€Ą à€•à€°à„‡à€‚"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"à€Źà„‡à€čà€€à€° à€žà„‡à€Čà„à€«à€Œà„€ à€•à„‡ à€Čà€żà€ à€«à€Œà„à€°à€‚à€Ÿ à€Ąà€żà€žà€Șà„à€Čà„‡ à€Șà€° à€žà„à€”à€żà€š à€•à€°à„‡à€‚?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"à€”à€Ÿà€‡à€Ą à€à€‚à€—à€Č à€źà„‡à€‚ à€čà€Ÿà€ˆ à€°à€żà€œà€Œà„‰à€Čà„à€Żà„‚à€¶à€š à€”à€Ÿà€Čà„€ à€«à€Œà„‹à€Ÿà„‹ à€Čà„‡à€šà„‡ à€•à„‡ à€Čà€żà€, à€Șà„€à€›à„‡ à€•à€Ÿ à€•à„ˆà€źà€°à€Ÿ à€‡à€žà„à€€à„‡à€źà€Ÿà€Č à€•à€°à„‡à€‚."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ à€Żà€č à€žà„à€•à„à€°à„€à€š à€Źà€‚à€Š à€čà„‹ à€œà€Ÿà€à€—à„€"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"à€«à€Œà„‹à€Čà„à€Ą à€•à€żà€Żà€Ÿ à€œà€Ÿ à€žà€•à€šà„‡ à€”à€Ÿà€Čà€Ÿ à€Ąà€żà€”à€Ÿà€‡à€ž à€…à€šà€«à€Œà„‹à€Čà„à€Ą à€•à€żà€Żà€Ÿ à€œà€Ÿ à€°à€čà€Ÿ à€čà„ˆ"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"à€«à€Œà„‹à€Čà„à€Ą à€•à€żà€Żà€Ÿ à€œà€Ÿ à€žà€•à€šà„‡ à€”à€Ÿà€Čà€Ÿ à€Ąà€żà€”à€Ÿà€‡à€ž à€Șà€Čà€Ÿà€Ÿ à€œà€Ÿ à€°à€čà€Ÿ à€čà„ˆ"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> à€Źà„ˆà€Ÿà€°à„€ à€Źà€šà„€ à€čà„ˆ"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"à€…à€Șà€šà„‡ à€žà„à€Ÿà€Ÿà€‡à€Čà€ž à€•à„‹ à€šà€Ÿà€°à„à€œ à€•à€°à„‡à€‚"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"à€žà„à€Ÿà€Ÿà€‡à€Čà€ž à€•à„€ à€Źà„ˆà€Ÿà€°à„€ à€•à€ź à€čà„ˆ"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"à€”à„€à€Ąà€żà€Żà„‹ à€•à„ˆà€źà€°à€Ÿ"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"à€Żà€č à€Șà„à€°à„‹à€«à€Œà€Ÿà€‡à€Č à€čà„‹à€šà„‡ à€Șà€° à€•à„‰à€Č à€šà€čà„€à€‚ à€•à„€ à€œà€Ÿ à€žà€•à€€à„€"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"à€‘à€«à€Œà€żà€ž à€•à„€ à€šà„€à€€à€ż à€•à„‡ à€€à€čà€€, à€”à€°à„à€• à€Șà„à€°à„‹à€«à€Œà€Ÿà€‡à€Č à€čà„‹à€šà„‡ à€Șà€° à€čà„€ à€«à€Œà„‹à€š à€•à„‰à€Č à€•à€żà€ à€œà€Ÿ à€žà€•à€€à„‡ à€čà„ˆà€‚"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"à€”à€°à„à€• à€Șà„à€°à„‹à€«à€Œà€Ÿà€‡à€Č à€Șà€° à€žà„à€”à€żà€š à€•à€°à„‡à€‚"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"à€Źà€‚à€Š à€•à€°à„‡à€‚"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"à€Čà„‰à€• à€žà„à€•à„à€°à„€à€š à€•à„€ à€žà„‡à€Ÿà€żà€‚à€—"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hi/tiles_states_strings.xml b/packages/SystemUI/res/values-hi/tiles_states_strings.xml
index a156b0c..0abf8b3 100644
--- a/packages/SystemUI/res/values-hi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hi/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"à€Źà€‚à€Š à€čà„ˆ"</item>
     <item msgid="5966994759929723339">"à€šà€Ÿà€Čà„‚ à€čà„ˆ"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index d8d969b..5de0d54 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Donji rub <xliff:g id="PERCENT">%1$d</xliff:g> posto"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Lijevi rub <xliff:g id="PERCENT">%1$d</xliff:g> posto"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Desni rub <xliff:g id="PERCENT">%1$d</xliff:g> posto"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Snimke zaslona s poslovnog profila spremaju se u aplikaciju <xliff:g id="APP">%1$s</xliff:g>"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Spremljeno u aplikaciju <xliff:g id="APP">%1$s</xliff:g> u poslovnom profilu"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Datoteke"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"Aplikacija <xliff:g id="APPNAME">%1$s</xliff:g> otkrila je ovu snimku zaslona."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> i druge otvorene aplikacije otkrile su ovu snimku zaslona."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Snimač zaslona"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Obrada snimanja zaslona"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Tekuća obavijest za sesiju snimanja zaslona"</string>
@@ -255,6 +257,7 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Svjetlina"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inverzija boja"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Korekcija boja"</string>
+    <string name="quick_settings_font_scaling_label" msgid="5289001009876936768">"Veličina fonta"</string>
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Upravljajte korisnicima"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Gotovo"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Zatvori"</string>
@@ -775,6 +778,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"snimanje zaslona"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Bez naslova"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Stanje mirovanja"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Prozor za povećavanje"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kontrole prozora za povećavanje"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Povećaj"</string>
@@ -800,6 +809,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Odabir aplikacije za dodavanje kontrola"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Dodana je # kontrola.}one{Dodana je # kontrola.}few{Dodane su # kontrole.}other{Dodano je # kontrola.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Uklonjeno"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Ćœelite li dodati aplikaciju <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Kada dodate aplikaciju <xliff:g id="APPNAME">%s</xliff:g>, moĆŸe dodati kontrole i sadrĆŸaj na ovu ploču. U nekim aplikacijama moĆŸete odabrati koje se kontrole prikazuju ovdje."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Dodano u favorite"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Dodano u favorite, poloĆŸaj <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Uklonjeno iz favorita"</string>
@@ -850,6 +861,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Otvori <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Pustite <xliff:g id="SONG_NAME">%1$s</xliff:g>, <xliff:g id="ARTIST_NAME">%2$s</xliff:g> putem aplikacije <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Pustite <xliff:g id="SONG_NAME">%1$s</xliff:g> putem aplikacije <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Za vas"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Poništi"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"PribliĆŸite se radi reprodukcije na uređaju <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Da biste reproducirali ovdje, pribliĆŸite se uređaju <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -857,6 +869,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Nešto nije u redu. Pokušajte ponovo."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Učitavanje"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Emitiranje medijskih sadrĆŸaja"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Emitiranje aplikacije <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Neaktivno, provjerite aplik."</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Nije pronađeno"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Kontrola nije dostupna"</string>
@@ -866,6 +880,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Pogreška, pokušajte ponovo"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Dodaj kontrole"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Uredi kontrole"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Dodavanje aplikacije"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Dodavanje izlaza"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupa"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Odabran je jedan uređaj"</string>
@@ -881,6 +896,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Zvučnici i zasloni"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"PredloĆŸeni uređaji"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Zahtijeva račun s naplatom"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kako emitiranje funkcionira"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Emitiranje"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Osobe u blizini s kompatibilnim Bluetooth uređajima mogu slušati medije koje emitirate"</string>
@@ -892,6 +908,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Emitiranje nije uspjelo"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Spremanje nije uspjelo. Pokušajte ponovo."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Spremanje nije uspjelo."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Upotrijebite barem četiri znaka"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Upotrijebite manje od 16 znakova"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Broj međuverzije"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Broj međuverzije kopiran je u međuspremnik."</string>
     <string name="basic_status" msgid="2315371112182658176">"Otvoreni razgovor"</string>
@@ -1011,11 +1029,11 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Dostupan je najmanje jedan uređaj"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Prečac za dodirnuti i zadrĆŸati"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Odustani"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Okreni odmah"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Otvorite telefon da biste snimili bolji selfie"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Prebaciti na prednji zaslon za bolji selfie?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Upotrijebite straĆŸnji fotoaparat za širu fotografiju s višom razlučivošÄ‡u."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Ovaj će se zaslon isključiti"</b></string>
+    <string name="rear_display_bottom_sheet_confirm" msgid="1507591562761552899">"Promijenite zaslon odmah"</string>
+    <string name="rear_display_folded_bottom_sheet_title" msgid="3930008746560711990">"Otklopite telefon"</string>
+    <string name="rear_display_unfolded_bottom_sheet_title" msgid="6291111173057304055">"Ćœelite li promijeniti zaslon?"</string>
+    <string name="rear_display_folded_bottom_sheet_description" msgid="6842767125783222695">"Za višu razlučivost upotrijebite straĆŸnju kameru"</string>
+    <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Za višu razlučivost okrenite telefon"</string>
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Rasklopljen sklopivi uređaj"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Okretanje sklopivog uređaja sa svih strana"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Preostalo je <xliff:g id="PERCENTAGE">%s</xliff:g> baterije"</string>
@@ -1026,4 +1044,5 @@
     <string name="call_from_work_profile_text" msgid="3458704745640229638">"Vaša pravila za poslovne uređaje omogućuju vam upućivanje poziva samo s poslovnog profila"</string>
     <string name="call_from_work_profile_action" msgid="2937701298133010724">"Prijeđite na poslovni profil"</string>
     <string name="call_from_work_profile_close" msgid="7927067108901068098">"Zatvori"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Postavke zaključanog zaslona"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hr/tiles_states_strings.xml b/packages/SystemUI/res/values-hr/tiles_states_strings.xml
index b69b064..32051ef 100644
--- a/packages/SystemUI/res/values-hr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hr/tiles_states_strings.xml
@@ -176,4 +176,9 @@
     <item msgid="8014986104355098744">"Isključeno"</item>
     <item msgid="5966994759929723339">"Uključeno"</item>
   </string-array>
+  <string-array name="tile_states_font_scaling">
+    <item msgid="3173069902082305985">"Nedostupno"</item>
+    <item msgid="2478289035899842865">"Isključeno"</item>
+    <item msgid="5137565285664080143">"Uključeno"</item>
+  </string-array>
 </resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 24d80ec..ffeeb14 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Alsó rész <xliff:g id="PERCENT">%1$d</xliff:g> százaléka"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Bal oldali rész <xliff:g id="PERCENT">%1$d</xliff:g> százaléka"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Jobb oldali rész <xliff:g id="PERCENT">%1$d</xliff:g> százaléka"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"A munkahelyi képernyƑképeket a(z) <xliff:g id="APP">%1$s</xliff:g> alkalmazásba menti a rendszer"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Mentve a(z) <xliff:g id="APP">%1$s</xliff:g> alkalmazás munkaprofiljába"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Fájlok"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"A(z) <xliff:g id="APPNAME">%1$s</xliff:g> észlelte ezt a képernyƑképet."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"A(z) <xliff:g id="APPNAME">%1$s</xliff:g> és más nyitott alkalmazások észlelték ezt a képernyƑképet."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"KépernyƑrögzítƑ"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"KépernyƑrögzítés feldolgozása"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Folyamatban lévƑ értesítés képernyƑrögzítési munkamenethez"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"FényerƑ"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Színek invertálása"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Színjavítás"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Felhasználók kezelése"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Kész"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Bezárás"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatikus"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Nincs hang és rezgés"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Nincs hang és rezgés, továbbá lejjebb jelenik meg a beszélgetések szakaszában"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Az eszközbeállítások alapján csöröghet és rezeghet"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Az eszközbeállítások alapján csöröghet és rezeghet. A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazásban lévƑ beszélgetések alapértelmezés szerint buborékban jelennek meg."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"A rendszer határozza meg, hogy ez az értesítés adjon-e ki hangot, illetve rezegjen-e"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Állapot:&lt;/b&gt; alapértelmezettre állítva"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Állapot:&lt;/b&gt; némára állítva"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"képernyƑrögzítés"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Nincs cím"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Készenléti mód"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Nagyítás ablaka"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Nagyítási vezérlƑk ablaka"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Nagyítás"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Válasszon alkalmazást a vezérlƑk hozzáadásához"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# vezérlƑ hozzáadva.}other{# vezérlƑ hozzáadva.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Eltávolítva"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Hozzáadja a(z) <xliff:g id="APPNAME">%s</xliff:g> alkalmazást?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"A(z) <xliff:g id="APPNAME">%s</xliff:g> hozzáadását követƑen az alkalmazás vezérlƑelemeket és tartalmakat adhat hozzá ehhez a panelhez. Egyes alkalmazásokban kiválasztható, hogy mely vezérlƑelemek jelenjenek meg itt."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Hozzáadva a kedvencekhez"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Hozzáadva a kedvencekhez <xliff:g id="NUMBER">%d</xliff:g>. helyen"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Eltávolítva a kedvencek közül"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> megnyitása"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> <xliff:g id="SONG_NAME">%1$s</xliff:g> címƱ számának lejátszása innen: <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> lejátszása innen: <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Neked"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Visszavonás"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Menjen közelebb, ha itt szeretné lejátszani: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Ha szeretné itt lejátszani, helyezkedjen közelebb a következƑhöz: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Hiba történt. Próbálkozzon újra."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Betöltés…"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"táblagép"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"A médiatartalom átküldése folyamatban van"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"<xliff:g id="APP_LABEL">%1$s</xliff:g> átküldése"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inaktív, ellenƑrizze az appot"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Nem található"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Nem hozzáférhetƑ vezérlƑ"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Hiba történt. Próbálja újra."</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"VezérlƑk hozzáadása"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"VezérlƑk szerkesztése"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Alkalmazás hozzáadása"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Kimenetek hozzáadása"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Csoport"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 eszköz kiválasztva"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Hangfalak és kijelzƑk"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Javasolt eszközök"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Prémiumfiók szükséges"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"A közvetítés mƱködése"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Közvetítés"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"A közelben tartózkodó, kompatibilis Bluetooth-eszközzel rendelkezƑ személyek meghallgathatják az Ön közvetített médiatartalmait"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Nem sikerült a közvetítés"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"A mentés nem sikerült. Próbálja újra."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"A mentés nem sikerült."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Buildszám"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Buildszám a vágólapra másolva."</string>
     <string name="basic_status" msgid="2315371112182658176">"Beszélgetés megnyitása"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Legalább egy eszköz rendelkezésre áll"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Tartsa nyomva a parancsikont"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Mégse"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Átfordítás most"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Hajtsa ki a telefont jobb szelfi készítéséhez"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Átfordítja az elƑlapi kijelzƑre a jobb szelfiért?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Használja az elƑlapi kamerát, hogy nagyobb felbontású, szélesebb fotót készíthessen"</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ A képernyƑ kikapcsol"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Összehajtható eszköz kihajtása"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Összehajtható eszköz körbeforgatása"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Akkumulátor töltöttségi szintje: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Tegye töltƑre az érintƑceruzát"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Az érintƑceruza töltöttsége alacsony"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Nem lehet hívást kezdeményezni ebbƑl a profilból"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"A munkahelyi házirend csak munkaprofilból kezdeményezett telefonhívásokat engedélyez"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Váltás munkaprofilra"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Bezárás"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Lezárási képernyƑ beállításai"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hu/tiles_states_strings.xml b/packages/SystemUI/res/values-hu/tiles_states_strings.xml
index 050bc14..0416a55 100644
--- a/packages/SystemUI/res/values-hu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hu/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Ki"</item>
     <item msgid="5966994759929723339">"Be"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 7cf0c81..4bd3a65 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"Ô±Ő·Ő­ŐĄŐżŐĄŐ¶Ö„ŐĄŐ”Ő«Ő¶ ŐœÖ„Ö€Ő«Ő¶Ő·ŐžŐ©Ő¶Ő„Ö€Őš ŐșŐĄŐ°ŐŸŐžÖ‚ŐŽ Ő„Ő¶ «<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>
     <string name="screenrecord_name" msgid="2596401223859996572">"Ô·ŐŻÖ€ŐĄŐ¶Ő« ŐżŐ„ŐœŐĄŐŁÖ€Ő«Őč"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ô·ŐŻÖ€ŐĄŐ¶Ő« ŐżŐ„ŐœŐĄŐŁÖ€ŐžÖ‚Ő©Ő”ŐĄŐ¶ ŐŽŐ·ŐĄŐŻŐžÖ‚ŐŽ"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ô·ŐŻÖ€ŐĄŐ¶Ő« ŐżŐ„ŐœŐĄŐŁÖ€ŐŽŐĄŐ¶ ŐĄŐ·Ő­ŐĄŐżŐĄŐ·Ö€Ő»ŐĄŐ¶Ő« ՚նթեցիկ ՟են՞ւց՞ւՎ"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ŐŠŐĄŐ”ŐźŐĄŐŒŐžÖ‚Ő©Ő”ŐžÖ‚Ő¶"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ÔłŐžÖ‚Ő¶ŐĄŐ·Ö€Ő»ŐžÖ‚ŐŽ"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ÔłŐžÖ‚Ő¶ŐĄŐ·ŐżŐŻŐžÖ‚ŐŽ"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"ÔżŐĄŐŒŐĄŐŸŐĄÖ€Ő„ŐŹ Ö…ŐŁŐżŐĄŐżŐ„Ö€Ő„Ö€Ő«Ő¶"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"ŐŠŐĄŐżÖ€ŐĄŐœŐż Ő§"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Ő“ŐĄŐŻŐ„ŐŹ"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Ô±ŐŸŐżŐžŐŽŐĄŐż"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Ô±ŐŒŐĄŐ¶Ö Ő±ŐĄŐ”Ő¶Ő« ŐŻŐĄŐŽ Ő©Ö€Ő©ŐŒŐžÖŐ«"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Ô±ŐŒŐĄŐ¶Ö Ő±ŐĄŐ”Ő¶Ő« և Ő©Ö€Ő©ŐŒŐžÖŐ«, Ő°ŐĄŐ”ŐżŐ¶ŐŸŐžÖ‚ŐŽ Ő§ Պր՞ւՔցնՄրի ցենկի Ő¶Ő„Ö€Ö„Ö‡ŐžÖ‚ŐŽ"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"ԿարվŐČ Ő§ ŐŠŐ¶ŐŁŐĄŐŹ ŐŻŐĄŐŽ Ő©Ö€Ő©ŐŒŐĄŐŹŐ ŐŻŐĄŐ­ŐŸŐĄŐź ŐœŐĄÖ€Ö„Ő« ŐŻŐĄÖ€ŐŁŐĄŐŸŐžÖ€ŐžÖ‚ŐŽŐ¶Ő„Ö€Ő«Ö"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"ԿարվŐČ Ő§ ŐŠŐ¶ŐŁŐĄŐŹ ŐŻŐĄŐŽ Ő©Ö€Ő©ŐŒŐĄŐŹŐ ŐŻŐĄŐ­ŐŸŐĄŐź ŐœŐĄÖ€Ö„Ő« ŐŻŐĄÖ€ŐŁŐĄŐŸŐžÖ€ŐžÖ‚ŐŽŐ¶Ő„Ö€Ő«ÖÖ‰ <xliff:g id="APP_NAME">%1$s</xliff:g>-Ő« Պր՞ւՔցնՄրն ŐšŐœŐż ŐŻŐĄŐ¶Ő­ŐĄŐ€Ö€ŐŽŐĄŐ¶ Ő°ŐĄŐ”ŐżŐ¶ŐŸŐžÖ‚ŐŽ Ő„Ő¶ ŐĄŐŽŐșŐ«ŐŻŐ¶Ő„Ö€Ő« ŐżŐ„ŐœÖ„ŐžŐŸÖ‰"</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ÔčŐžŐČ Ő°ŐĄŐŽŐĄŐŻŐĄÖ€ŐŁŐ¶ ŐĄŐŸŐżŐžŐŽŐĄŐż ŐžÖ€ŐžŐ·Ő«Ő ŐĄÖ€Ő€Ő”ŐžÖ„ ŐĄŐ”Őœ ՟են՞ւց՞ւՎ՚ Ő±ŐĄŐ”Ő¶ŐžŐŸ, Ő©Ő„ Ő©Ö€Ő©ŐŒŐžÖŐžŐŸ Ő§ ŐșŐ„ŐżÖ„ Վետ՞ւցՄՏ"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;ÔżŐĄÖ€ŐŁŐĄŐŸŐ«ŐłŐĄŐŻŐšâ€€&lt;/b&gt; ŐąŐĄÖ€Ő±Ö€ŐĄÖŐŸŐ„ŐŹ Ő§ և Ő€ŐĄÖ€Ő±Ő„ŐŹ ŐŻŐĄŐ¶Ő­ŐĄŐ€Ö€ŐŸŐĄŐź"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;ÔżŐĄÖ€ŐŁŐĄŐŸŐ«ŐłŐĄŐŻŐšâ€€&lt;/b&gt; Ő«Ő»Ő„ÖŐŸŐ„ŐŹ Ő§ և Ő€ŐĄÖ€Ő±Ő„ŐŹ ŐĄŐ¶Ő±ŐĄŐ”Ő¶"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"Ő§ŐŻÖ€ŐĄŐ¶Ő« ŐżŐ„ŐœŐĄŐŁÖ€ŐžÖ‚ŐŽ"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Ô±Ő¶ŐĄŐ¶ŐžÖ‚Ő¶"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"ՍŐșŐĄŐœŐŽŐĄŐ¶ ŐŒŐ„ŐȘŐ«ŐŽ"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"ÔœŐžŐ·ŐžÖ€ŐĄÖŐŽŐĄŐ¶ ŐșŐĄŐżŐžÖ‚Ő°ŐĄŐ¶"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"ÔœŐžŐ·ŐžÖ€ŐĄÖŐŽŐĄŐ¶ ŐșŐĄŐżŐžÖ‚Ő°ŐĄŐ¶Ő« ŐŻŐĄŐŒŐĄŐŸŐĄÖ€ŐŽŐĄŐ¶ ŐżŐĄÖ€Ö€Ő„Ö€"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ՄՄ՟եցնՄՏ"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"ÔžŐ¶ŐżÖ€Ő„Ö„ Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐź` ŐŻŐĄŐŒŐĄŐŸŐĄÖ€ŐŽŐĄŐ¶ ŐżŐĄÖ€Ö€Ő„Ö€ ŐĄŐŸŐ„ŐŹŐĄÖŐ¶Ő„ŐŹŐžÖ‚ հածար"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Ô±ŐŸŐ„ŐŹŐĄÖŐŸŐ„Ö ŐŻŐĄŐŒŐĄŐŸŐĄÖ€ŐŽŐĄŐ¶ # տարր։}one{Ô±ŐŸŐ„ŐŹŐĄÖŐŸŐ„Ö ŐŻŐĄŐŒŐĄŐŸŐĄÖ€ŐŽŐĄŐ¶ # տարր։}other{Ô±ŐŸŐ„ŐŹŐĄÖŐŸŐ„Ö ŐŻŐĄŐŒŐĄŐŸŐĄÖ€ŐŽŐĄŐ¶ # տարր։}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Ő€Ő„ŐŒŐĄÖŐŸŐĄŐź Ő§"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Ô±ŐŸŐ„ŐŹŐĄÖŐ¶Ő„ŐžŐŹ <xliff:g id="APPNAME">%s</xliff:g> Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐš"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Ô”Ő©Ő„ ŐĄŐŸŐ„ŐŹŐĄÖŐ¶Ő„Ö„ <xliff:g id="APPNAME">%s</xliff:g> Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐš, ŐĄŐ”Ő¶ կարվŐČ Ő§ ŐŻŐĄÖ€ŐŁŐĄŐŸŐžÖ€ŐžÖ‚ŐŽŐ¶Ő„Ö€ և ŐąŐžŐŸŐĄŐ¶Ő€ŐĄŐŻŐžÖ‚Ő©Ő”ŐžÖ‚Ő¶ ŐĄŐŸŐ„ŐŹŐĄÖŐ¶Ő„ŐŹ ŐĄŐ”Őœ ŐŸŐĄŐ°ŐĄŐ¶ŐĄŐŻŐžÖ‚ŐŽÖ‰ ŐˆÖ€ŐžŐ· Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐ¶Ő„Ö€ŐžÖ‚ŐŽ Ő€ŐžÖ‚Ö„ կարվŐČ Ő„Ö„ ŐšŐ¶ŐżÖ€Ő„ŐŹ, Ő©Ő„ վր ŐŻŐĄÖ€ŐŁŐĄŐŸŐžÖ€ŐžÖ‚ŐŽŐ¶Ő„Ö€Őš ÖŐžÖ‚ÖŐĄŐ€Ö€ŐŸŐ„Ő¶ ŐĄŐ”ŐœŐżŐ„ŐČ։"</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Ô±ŐŸŐ„ŐŹŐĄÖŐŸŐĄŐź Ő§ ŐšŐ¶ŐżÖ€ŐĄŐ¶Ő«ŐžÖ‚ŐŽ"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Ô±ŐŸŐ„ŐŹŐĄÖŐŸŐĄŐź Ő§ ŐšŐ¶ŐżÖ€ŐĄŐ¶Ő«ŐžÖ‚ŐŽ, Ő€Ő«Ö€Ö„ŐšŐ <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Ő€Ő„ŐŒŐĄÖŐŸŐĄŐź Ő§ ՚նտրեն՞ւց"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"ÔČեցՄք <xliff:g id="APP_LABEL">%1$s</xliff:g> Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐš"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Ő†ŐŸŐĄŐŁŐĄÖ€ŐŻŐ„ŐŹ <xliff:g id="SONG_NAME">%1$s</xliff:g> Ő„Ö€ŐŁŐš <xliff:g id="ARTIST_NAME">%2$s</xliff:g>-Ő« կատարծածձ <xliff:g id="APP_LABEL">%3$s</xliff:g> Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐ«Ö"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Ő†ŐŸŐĄŐŁŐĄÖ€ŐŻŐ„ŐŹ <xliff:g id="SONG_NAME">%1$s</xliff:g> Ő„Ö€ŐŁŐš <xliff:g id="APP_LABEL">%2$s</xliff:g> Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐ«Ö"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"ՁՄՊ հածար"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Ő€Ő„ŐżŐĄÖ€ŐŻŐ„ŐŹ"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Ô±ŐŸŐ„ŐŹŐ« ŐŽŐžŐż Ő„ŐŻŐ„Ö„Ő <xliff:g id="DEVICENAME">%1$s</xliff:g> ŐœŐĄÖ€Ö„ŐžÖ‚ŐŽ Ő¶ŐŸŐĄŐŁŐĄÖ€ŐŻŐ„ŐŹŐžÖ‚ հածար"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Ô±Ő”ŐœŐżŐ„ŐČ Ő¶ŐŸŐĄŐŁŐĄÖ€ŐŻŐ„ŐŹŐžÖ‚ հածար Վ՞տՄցՄք <xliff:g id="DEVICENAME">%1$s</xliff:g> ŐœŐĄÖ€Ö„Ő«Ő¶"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Սխալ ŐĄŐŒŐĄŐ»ŐĄÖŐĄŐŸÖ‰ Ն՞րից ÖƒŐžÖ€Ő±Ő„Ö„Ö‰"</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"ÔČŐ„ŐŒŐ¶ŐŸŐžÖ‚ŐŽ Ő§"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ŐșŐŹŐĄŐ¶Ő·Ő„Őż"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Ő„Ő„Ő€Ő«ŐĄ ŐąŐžŐŸŐĄŐ¶Ő€ŐĄŐŻŐžÖ‚Ő©Ő”ŐĄŐ¶ Ő°Ő„ŐŒŐĄÖ€Ő±ŐĄŐŻŐžÖ‚ŐŽ"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"<xliff:g id="APP_LABEL">%1$s</xliff:g> Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐ« Ő°Ő„ŐŒŐĄÖ€Ő±ŐĄŐŻŐžÖ‚ŐŽ"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Ô±ŐŻŐżŐ«ŐŸ ŐčŐ§, ŐœŐżŐžÖ‚ŐŁŐ„Ö„ Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐźŐš"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Ő‰Ő« ŐŁŐżŐ¶ŐŸŐ„ŐŹ"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"ÔżŐĄŐŒŐĄŐŸŐĄÖ€ŐŽŐĄŐ¶ տարրչ Ő°ŐĄŐœŐĄŐ¶Ő„ŐŹŐ« ŐčŐ§"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Սխալ ŐĄŐŒŐĄŐ»ŐĄÖŐĄŐŸÖ‰ Ն՞րից ÖƒŐžÖ€Ő±Ő„Ö„Ö‰"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Ô±ŐŸŐ„ŐŹŐĄÖŐ¶Ő„ŐŹ ŐŻŐĄŐŒŐĄŐŸŐĄÖ€ŐŽŐĄŐ¶ ŐżŐĄÖ€Ö€Ő„Ö€"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Ő“ŐžÖƒŐžŐ­Ő„ŐŹ ŐŻŐĄŐŒŐĄŐŸŐĄÖ€ŐŽŐĄŐ¶ ŐżŐĄÖ€Ö€Ő„Ö€Őš"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Ô±ŐŸŐ„ŐŹŐĄÖŐ¶Ő„ŐŹ Ő°ŐĄŐŸŐ„ŐŹŐŸŐĄŐź"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Ô±ŐŸŐ„ŐŹŐĄÖÖ€Ő„Ö„ ŐŽŐžÖ‚ŐżÖ„ŐĄŐŁÖ€ŐŽŐĄŐ¶ ŐœŐĄÖ€Ö„Ő„Ö€"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"ÔœŐžÖ‚ŐŽŐą"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"ÔžŐ¶ŐżÖ€ŐŸŐĄŐź Ő§ 1 ŐœŐĄÖ€Ö„"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ÔČŐĄÖ€Ő±Ö€ŐĄŐ­ŐžŐœŐ¶Ő„Ö€ և Ő§ŐŻÖ€ŐĄŐ¶Ő¶Ő„Ö€"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Ô±ŐŒŐĄŐ»ŐĄÖ€ŐŻŐŸŐžŐČ ŐœŐĄÖ€Ö„Ő„Ö€"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"ŐŠŐĄŐ°ŐĄŐ¶Ő»ŐŸŐžÖ‚ŐŽ Ő§ ŐșÖ€Ő„ŐŽŐ«ŐžÖ‚ŐŽ Ő°ŐĄŐ·Ő«ŐŸ"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Ô»Ő¶ŐčŐșŐ„Őœ Ő§ ŐĄŐ·Ő­ŐĄŐżŐžÖ‚ŐŽ Ő°Ő„ŐŒŐĄÖ€Ő±ŐĄŐŻŐžÖ‚ŐŽŐš"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Ő€Ő„ŐŒŐĄÖ€Ő±ŐĄŐŻŐžÖ‚ŐŽ"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ՁՄր ŐŽŐžŐżŐĄŐŻŐĄŐ”Ö„ŐžÖ‚ŐŽ ŐŁŐżŐ¶ŐŸŐžŐČ՝ Ő°ŐĄŐŽŐĄŐżŐ„ŐČŐ„ŐŹŐ« Bluetooth ŐœŐĄÖ€Ö„Ő„Ö€ŐžŐŸ ŐŽŐĄÖ€Ő€Ő«ŐŻ կարվŐČ Ő„Ő¶ ŐŹŐœŐ„ŐŹ ŐŽŐ„Ő€Ő«ŐĄ Ö†ŐĄŐ”ŐŹŐ„Ö€Őš, ŐžÖ€ŐžŐ¶Ö„ Ő€ŐžÖ‚Ö„ Ő°Ő„ŐŒŐĄÖ€Ő±ŐĄŐŻŐžÖ‚ŐŽ Ő„Ö„Ö‰"</string>
@@ -894,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Ő‰Ő°ŐĄŐ»ŐžŐČŐŸŐ„Ö Ő°Ő„ŐŒŐĄÖ€Ő±ŐĄŐŻŐ„ŐŹ"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Ő‰Ő°ŐĄŐ»ŐžŐČŐŸŐ„Ö ŐșŐĄŐ°Ő„ŐŹÖ‰ Ն՞րից ÖƒŐžÖ€Ő±Ő„Ö„Ö‰"</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Ő‰Ő°ŐĄŐ»ŐžŐČŐŸŐ„Ö ŐșŐĄŐ°Ő„ŐŹÖ‰"</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Ő•ŐŁŐżŐĄŐŁŐžÖ€ŐźŐ„Ö„ ŐĄŐŒŐ¶ŐŸŐĄŐŠŐ¶ 4 Ő¶Ő«Ő·"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Ő•ŐŁŐżŐĄŐŁŐžÖ€ŐźŐ„Ö„ ŐžŐč ŐĄŐŸŐ„ŐŹ Ö„ŐĄŐ¶ 16 Ő¶Ő«Ő·"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"ÔżŐĄŐŒŐžÖ‚ÖŐŽŐĄŐ¶ հածարչ"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"ÔżŐĄŐŒŐžÖ‚ÖŐŽŐĄŐ¶ հածարչ ŐșŐĄŐżŐłŐ„Ő¶ŐŸŐ„Ö ŐœŐ„ŐČŐŽŐĄŐżŐĄŐ­ŐżŐĄŐŻŐ«Ő¶Ö‰"</string>
     <string name="basic_status" msgid="2315371112182658176">"ÔČեց Պր՞ւՔց"</string>
@@ -1013,24 +1030,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Ő€ŐĄŐœŐĄŐ¶Ő„ŐŹŐ« Ő§ ŐĄŐŒŐ¶ŐŸŐĄŐŠŐ¶ ŐŽŐ„ŐŻ ŐœŐĄÖ€Ö„"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"ՀŐșŐ„Ö„ ՀՔ՞ւրենցՎեն՚ և ŐșŐĄŐ°Ő„Ö„"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Ő‰Ő„ŐČŐĄÖ€ŐŻŐ„ŐŹ"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Ő‡Ö€Ő»Ő„ŐŹ Ő°Ő«ŐŽŐĄ"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"ÔČեցՄք Ő°Ő„ŐŒŐĄŐ­ŐžŐœŐ« փՄŐČկչ՝ ŐĄŐŸŐ„ŐŹŐ« ŐŹŐĄŐŸ ŐœŐ„ŐŹÖ†Ő« ŐĄŐ¶Ő„ŐŹŐžÖ‚ հածար"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Ő€Ő„ŐŒŐĄŐ­ŐžŐœŐš Ő§ŐŻÖ€ŐĄŐ¶ŐžŐŸ Ő€Ő„ŐșŐ« Ő±Ő„ŐžŐŠ շրջՄցիք"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Ő•ŐŁŐżŐĄŐŁŐžÖ€ŐźŐ„Ö„ Ő°Ő„ŐżÖ‡Ő« ŐżŐ„ŐœŐĄŐ­ÖŐ«ŐŻŐšŐ ŐĄŐŸŐ„ŐŹŐ« ŐąŐĄÖ€Ő±Ö€ լվւռաŐčŐĄÖƒŐžŐŸ և ŐĄŐŸŐ„ŐŹŐ« ŐŹŐĄŐ”Ő¶ ŐŹŐžÖ‚ŐœŐĄŐ¶ŐŻŐĄÖ€ ŐœŐżŐĄŐ¶ŐĄŐŹŐžÖ‚ հածար։"</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Ô±Ő”Őœ Ő§ŐŻÖ€ŐĄŐ¶Őš ŐŻŐĄŐ¶Ő»ŐĄŐżŐŸŐ«"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ÔŸŐĄŐŹŐžŐŸŐ« ŐœŐĄÖ€Ö„Ő ŐąŐĄÖŐŸŐĄŐź ŐŸŐ«ŐłŐĄŐŻŐžÖ‚ŐŽ"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ÔŸŐĄŐŹŐžŐŸŐ« ŐœŐĄÖ€Ö„Ő Ő·Ö€Ő»ŐŸŐĄŐź ŐŸŐ«ŐłŐĄŐŻŐžÖ‚ŐŽ"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Մերտկ՞ցի ŐŹŐ«ÖÖ„ŐšŐ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ՁՄր ŐœŐżŐ«ŐŹŐžÖ‚ŐœŐš ՎիեցրՄք ŐŹŐ«ÖÖ„ŐĄŐŸŐžÖ€Ő«ŐčŐ«"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"ŐŐżŐ«ŐŹŐžÖ‚ŐœŐ« Վերտկ՞ցի Տիցքի ցառր ŐŽŐĄŐŻŐĄÖ€Ő€ŐĄŐŻ"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"ŐŐ„ŐœŐĄŐ­ÖŐ«ŐŻ"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Ő€Ő¶ŐĄÖ€ŐĄŐŸŐžÖ€ ŐčŐ§ ŐŠŐĄŐ¶ŐŁŐ„ŐŹ ŐĄŐ”Őœ Őșր՞ֆիՏից"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"ՁՄր ŐĄŐ·Ő­ŐĄŐżŐĄŐ¶Ö„ŐĄŐ”Ő«Ő¶ ŐŻŐĄŐ¶ŐžŐ¶Ő¶Ő„Ö€Ő« Ő°ŐĄŐŽŐĄŐ±ŐĄŐ”Ő¶Ő Ő€ŐžÖ‚Ö„ կարվŐČ Ő„Ö„ ŐŠŐĄŐ¶ŐŁŐ„Ö€ ŐŻŐĄŐżŐĄÖ€Ő„ŐŹ ŐĄŐ·Ő­ŐĄŐżŐĄŐ¶Ö„ŐĄŐ”Ő«Ő¶ Őșր՞ֆիՏից"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"ԱնցնՄՏ ŐĄŐ·Ő­ŐĄŐżŐĄŐ¶Ö„ŐĄŐ”Ő«Ő¶ ŐșÖ€ŐžÖ†Ő«ŐŹ"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Ő“ŐĄŐŻŐ„ŐŹ"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"ÔżŐžŐČŐșŐ§ŐŻÖ€ŐĄŐ¶Ő« ŐŻŐĄÖ€ŐŁŐĄŐŸŐžÖ€ŐžÖ‚ŐŽŐ¶Ő„Ö€"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hy/tiles_states_strings.xml b/packages/SystemUI/res/values-hy/tiles_states_strings.xml
index 6015fbd..9f30f1c 100644
--- a/packages/SystemUI/res/values-hy/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hy/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Ô±Ő¶Ő»ŐĄŐżŐŸŐĄŐź Ő§"</item>
     <item msgid="5966994759929723339">"Ő„Ő«ŐĄÖŐŸŐĄŐź Ő§"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 877b7de..0618ec6 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Batas bawah <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Batas kiri <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Batas kanan <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Screenshot dengan profil kerja disimpan di aplikasi <xliff:g id="APP">%1$s</xliff:g>"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Disimpan di <xliff:g id="APP">%1$s</xliff:g> di profil kerja"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"File"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> mendeteksi screenshot ini."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> dan aplikasi terbuka lainnya mendeteksi screenshot ini."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Perekam Layar"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Memproses perekaman layar"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notifikasi yang sedang berjalan untuk sesi rekaman layar"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Kecerahan"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversi warna"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Koreksi warna"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Kelola pengguna"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Selesai"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Tutup"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Otomatis"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Tidak ada suara atau getaran"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Tidak ada suara atau getaran dan ditampilkan lebih rendah di bagian percakapan"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Dapat berdering atau bergetar berdasarkan setelan perangkat"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Dapat berdering atau bergetar berdasarkan setelan perangkat. Percakapan <xliff:g id="APP_NAME">%1$s</xliff:g> ditampilkan sebagai balon secara default."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Biarkan sistem menentukan apakah notifikasi ini akan berbunyi atau bergetar"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Status:&lt;/b&gt; Dipromosikan menjadi Default"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Status:&lt;/b&gt; Didemosikan menjadi Senyap"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"perekaman layar"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Tanpa judul"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Siaga"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Jendela Pembesaran"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kontrol Jendela Pembesaran"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Perbesar"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Pilih aplikasi untuk menambahkan kontrol"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# kontrol ditambahkan.}other{# kontrol ditambahkan.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Dihapus"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Tambahkan <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Jika Anda menambahkannya, <xliff:g id="APPNAME">%s</xliff:g> dapat menambahkan kontrol dan konten ke panel ini. Di beberapa aplikasi, Anda dapat memilih kontrol yang akan muncul di sini."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Difavoritkan"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Difavoritkan, posisi <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Batal difavoritkan"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Buka <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Putar <xliff:g id="SONG_NAME">%1$s</xliff:g> oleh <xliff:g id="ARTIST_NAME">%2$s</xliff:g> dari <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Putar <xliff:g id="SONG_NAME">%1$s</xliff:g> dari <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Untuk Anda"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Urungkan"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Dekatkan untuk memutar di <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Untuk memutar di sini, dekatkan ke <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Terjadi error. Coba lagi."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Memuat"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Mentransmisikan media"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Mentransmisikan <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Nonaktif, periksa aplikasi"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Tidak ditemukan"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Kontrol tidak tersedia"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Error, coba lagi"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Tambahkan kontrol"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Edit kontrol"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Tambahkan aplikasi"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Tambahkan output"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Grup"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 perangkat dipilih"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speaker &amp; Layar"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Perangkat yang Disarankan"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Memerlukan akun premium"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cara kerja siaran"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Siaran"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Orang di dekat Anda dengan perangkat Bluetooth yang kompatibel dapat mendengarkan media yang sedang Anda siarkan"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Tidak dapat menyiarkan"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Tidak dapat menyimpan. Coba lagi."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Tidak dapat menyimpan."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Nomor build"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Nomor versi disalin ke papan klip."</string>
     <string name="basic_status" msgid="2315371112182658176">"Membuka percakapan"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Tersedia minimal satu perangkat"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Sentuh lama pintasan"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Batal"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Balik sekarang"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Bentangkan ponsel untuk selfie yang lebih baik"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Gunakan layar depan untuk selfie yang lebih baik?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Gunakan kamera belakang untuk foto dengan resolusi lebih tinggi dan lebih lebar."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Layar ini akan dinonaktifkan"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Perangkat foldable sedang dibentangkan"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Perangkat foldable sedang dibalik"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Baterai tersisa <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Hubungkan stilus ke pengisi daya"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Baterai stilus lemah"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Kamera video"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Tidak dapat melakukan panggilan dari profil ini"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Kebijakan kantor mengizinkan Anda melakukan panggilan telepon hanya dari profil kerja"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Beralih ke profil kerja"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Tutup"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Setelan layar kunci"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-in/tiles_states_strings.xml b/packages/SystemUI/res/values-in/tiles_states_strings.xml
index 5416c8f..c314040 100644
--- a/packages/SystemUI/res/values-in/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-in/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Nonaktif"</item>
     <item msgid="5966994759929723339">"Aktif"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 1e7934a..afdaa5d 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Neðri mörk <xliff:g id="PERCENT">%1$d</xliff:g> prósent"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Vinstri mörk <xliff:g id="PERCENT">%1$d</xliff:g> prósent"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Hægri mörk <xliff:g id="PERCENT">%1$d</xliff:g> prósent"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Vinnuskjámyndir eru vistaðar í forritinu <xliff:g id="APP">%1$s</xliff:g>"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Vistað á vinnusniði í <xliff:g id="APP">%1$s</xliff:g>"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Skrár"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> greindi skjámyndina."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> og önnur opin forrit greindu skjámyndina."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Skjáupptaka"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Vinnur úr skjáupptöku"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Áframhaldandi tilkynning fyrir skjáupptökulotu"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Birtustig"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Umsnúningur lita"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Litaleiðrétting"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Stjórna notendum"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Lokið"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Loka"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Sjálfvirk"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Ekkert hljóð eða titringur"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Ekkert hljóð eða titringur og birtist neðar í samtalshluta"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Gæti hringt eða titrað en það fer eftir stillingum tækisins"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Gæti hringt eða titrað en það fer eftir stillingum tækisins. Samtöl frá <xliff:g id="APP_NAME">%1$s</xliff:g> birtast sjálfkrafa í blöðru."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Láta kerfið ákvarða hvort hljóð eða titringur fylgir þessari tilkynningu"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Staða:&lt;/b&gt; gerð sjálfgefin"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Staða:&lt;/b&gt; var gerð þögul"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"skjáupptaka"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Enginn titill"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Biðstaða"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Stækkunargluggi"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Stækkunarstillingar glugga"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Auka aðdrátt"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Veldu forrit til að bæta við stýringum"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# stýringu bætt við.}one{# stýringu bætt við.}other{# stýringum bætt við.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Fjarlægt"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Viltu bæta <xliff:g id="APPNAME">%s</xliff:g> við?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Þegar þú bætir <xliff:g id="APPNAME">%s</xliff:g> við getur það bætt stýringum og efni við þetta svæði. Í sumum forritum geturðu valið hvaða stýringar birtast hér."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Eftirlæti"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Eftirlæti, staða <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Fjarlægt úr eftirlæti"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Opna <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Spila <xliff:g id="SONG_NAME">%1$s</xliff:g> með <xliff:g id="ARTIST_NAME">%2$s</xliff:g> í <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Spila <xliff:g id="SONG_NAME">%1$s</xliff:g> í <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Fyrir þig"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Afturkalla"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Færðu nær til að spila í <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Farðu nær <xliff:g id="DEVICENAME">%1$s</xliff:g> til að spila hér"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Eitthvað fór úrskeiðis. Reyndu aftur."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Hleður"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"spjaldtölva"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Sendir út efni frá þér"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Sendir út <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Óvirkt, athugaðu forrit"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Fannst ekki"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Stýring er ekki tiltæk"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Villa, reyndu aftur"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Bæta við stýringum"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Breyta stýringum"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Bæta við forriti"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Bæta við úttaki"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Hópur"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 tæki valið"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Hátalarar og skjáir"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Tillögur að tækjum"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Krefst úrvalsreiknings"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Svona virkar útsending"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Útsending"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Fólk nálægt þér með samhæf Bluetooth-tæki getur hlustað á efnið sem þú sendir út"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Ekki hægt að senda út"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Ekki hægt að vista. Reyndu aftur."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Ekki hægt að vista."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Útgáfunúmer smíðar"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Útgáfunúmer smíðar afritað á klippiborð."</string>
     <string name="basic_status" msgid="2315371112182658176">"Opna samtal"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Að minnsta kosti eitt tæki er tiltækt"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Haltu flýtilyklinum inni"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Hætta við"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Snúa núna"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Opnaðu símann til að taka betri sjálfsmynd"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Snúa á framskjá til að ná betri sjálfsmynd?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Notaðu aftari myndavélina til að ná víðara sjónarhorni með meiri upplausn."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Slökkt verður á þessum skjá"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Samanbrjótanlegt tæki opnað"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Samanbrjótanlegu tæki snúið við"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> hleðsla eftir á rafhlöðu"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Tengdu pennann við hleðslutæki"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Rafhlaða pennans er að tæmast"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Kvikmyndatökuvél"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Ekki er hægt að hringja úr þessu sniði"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Vinnureglur gera þér aðeins kleift að hringja símtöl úr vinnusniði"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Skipta yfir í vinnusnið"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Loka"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Stillingar fyrir lásskjá"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-is/tiles_states_strings.xml b/packages/SystemUI/res/values-is/tiles_states_strings.xml
index 12dd776..cca4943 100644
--- a/packages/SystemUI/res/values-is/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-is/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Slökkt"</item>
     <item msgid="5966994759929723339">"Kveikt"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 211d01d..2f366ec 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Limite inferiore, <xliff:g id="PERCENT">%1$d</xliff:g> percento"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Limite sinistro, <xliff:g id="PERCENT">%1$d</xliff:g> percento"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Limite destro, <xliff:g id="PERCENT">%1$d</xliff:g> percento"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Gli screenshot acquisiti nel profilo di lavoro sono salvati nell\'app <xliff:g id="APP">%1$s</xliff:g>"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Salvato nell\'app <xliff:g id="APP">%1$s</xliff:g> nel profilo di lavoro"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"File"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> ha rilevato questo screenshot."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> e altre app aperte hanno rilevato questo screenshot."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Registrazione dello schermo"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Elaboraz. registraz. schermo"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notifica costante per una sessione di registrazione dello schermo"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Luminosità"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversione dei colori"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correzione del colore"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Gestisci utenti"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Fine"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Chiudi"</string>
@@ -775,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"Registraz. schermo"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Senza titolo"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Standby"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Finestra ingrandimento"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Finestra controlli di ingrandimento"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Aumenta lo zoom"</string>
@@ -800,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Scegli un\'app per aggiungere controlli"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# controllo aggiunto.}many{# controlli aggiunti.}other{# controlli aggiunti.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Rimosso"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Vuoi aggiungere <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Se la aggiungi, l\'app <xliff:g id="APPNAME">%s</xliff:g> può aggiungere controlli e contenuti a questo riquadro. In alcune app puoi scegliere quali controlli visualizzare qui."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Aggiunto ai preferiti"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Preferito, posizione <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Rimosso dai preferiti"</string>
@@ -850,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Apri <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Riproduci <xliff:g id="SONG_NAME">%1$s</xliff:g> di <xliff:g id="ARTIST_NAME">%2$s</xliff:g> da <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Riproduci <xliff:g id="SONG_NAME">%1$s</xliff:g> da <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Per te"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Annulla"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Avvicinati per riprodurre su <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Per riprodurre qui i contenuti, avvicinati a <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -857,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Si è verificato un errore. Riprova."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Caricamento in corso…"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Trasmissione di contenuti multimediali"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Trasmissione di <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inattivo, controlla l\'app"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Controllo non trovato"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Il controllo non è disponibile"</string>
@@ -866,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Errore, riprova"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Aggiungi controlli"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Modifica controlli"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Aggiungi app"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Aggiungi uscite"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Gruppo"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 dispositivo selezionato"</string>
@@ -881,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speaker e display"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositivi consigliati"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Occorre un account premium"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Come funziona la trasmissione"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Annuncio"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Le persone vicine a te che hanno dispositivi Bluetooth compatibili possono ascoltare i contenuti multimediali che stai trasmettendo"</string>
@@ -892,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Impossibile trasmettere"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Impossibile salvare. Riprova."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Impossibile salvare."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numero build"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Numero build copiato negli appunti."</string>
     <string name="basic_status" msgid="2315371112182658176">"Apri conversazione"</string>
@@ -1011,11 +1032,16 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Ci sia almeno un dispositivo disponibile"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Tocca scorciatoia/tieni premuto"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Annulla"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Gira ora"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Apri il telefono per un selfie migliore"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Girare su display frontale per un selfie migliore?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Utilizza la fotocamera posteriore per una foto più ampia con maggiore risoluzione."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Questo schermo verrà disattivato"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositivo pieghevole che viene aperto"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositivo pieghevole che viene capovolto"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> batteria rimanente"</string>
@@ -1026,4 +1052,5 @@
     <string name="call_from_work_profile_text" msgid="3458704745640229638">"Le norme di lavoro ti consentono di fare telefonate soltanto dal profilo di lavoro"</string>
     <string name="call_from_work_profile_action" msgid="2937701298133010724">"Passa a profilo di lavoro"</string>
     <string name="call_from_work_profile_close" msgid="7927067108901068098">"Chiudi"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Impostazioni schermata di blocco"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-it/tiles_states_strings.xml b/packages/SystemUI/res/values-it/tiles_states_strings.xml
index 5ec557b..79e5aca 100644
--- a/packages/SystemUI/res/values-it/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-it/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Off"</item>
     <item msgid="5966994759929723339">"On"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index fbec1f7..840744b 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"ŚŠŚ™ŚœŚ•ŚžŚ™ ŚžŚĄŚš Ś‘Ś€ŚšŚ•Ś€Ś™Śœ Ś”ŚąŚ‘Ś•Ś“Ś” Ś Ś©ŚžŚšŚ™Ś Ś‘ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś” <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>
     <string name="screenrecord_name" msgid="2596401223859996572">"ŚžŚ§ŚœŚ™Ś˜ Ś”ŚžŚĄŚš"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ŚžŚȘŚ‘ŚŠŚą ŚąŚ™Ś‘Ś•Ś“ کڜ Ś”Ś§ŚœŚ˜ŚȘ ŚžŚĄŚš"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ś”ŚȘŚšŚŚ” ŚžŚȘŚžŚ©Ś›ŚȘ ŚœŚĄŚ©ŚŸ Ś”Ś§ŚœŚ˜ŚȘ ŚžŚĄŚš"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Ś‘Ś”Ś™ŚšŚ•ŚȘ"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Ś”Ś™Ś€Ś•Śš ŚŠŚ‘ŚąŚ™Ś"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ŚȘŚ™Ś§Ś•ŚŸ ŚŠŚ‘Śą"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Ś Ś™Ś”Ś•Śœ ŚžŚ©ŚȘŚžŚ©Ś™Ś"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"ŚĄŚ™Ś•Ś"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"ŚĄŚ’Ś™ŚšŚ”"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Ś‘ŚŚ•Ś€ŚŸ ŚŚ•Ś˜Ś•ŚžŚ˜Ś™"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"ڜڜڐ ŚŠŚœŚ™Śœ ڐڕ ŚšŚ˜Ś˜"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"ڜڜڐ ŚŠŚœŚ™Śœ ڐڕ ŚšŚ˜Ś˜ Ś•ŚžŚ•Ś€Ś™ŚąŚ” ŚœŚžŚ˜Ś” Ś‘Ś§Ś˜Śą Ś”ŚȘŚšŚŚ•ŚȘ ڔکڙڗڔ"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"ڙڙŚȘŚ›ŚŸ Ś©Ś™Ś•Ś€ŚąŚœ ŚŠŚœŚŠŚ•Śœ ڐڕ ŚšŚ˜Ś˜ ڑڔŚȘŚŚ ŚœŚ”Ś’Ś“ŚšŚ•ŚȘ Ś‘ŚžŚ›Ś©Ś™Śš"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"ڙڙŚȘŚ›ŚŸ Ś©Ś™Ś•Ś€ŚąŚœ ŚŠŚœŚŠŚ•Śœ ڐڕ ŚšŚ˜Ś˜ ڑڔŚȘŚŚ ŚœŚ”Ś’Ś“ŚšŚ•ŚȘ Ś‘ŚžŚ›Ś©Ś™Śš. کڙڗڕŚȘ ŚžŚ”ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś” <xliff:g id="APP_NAME">%1$s</xliff:g> ŚžŚ•Ś€Ś™ŚąŚ•ŚȘ Ś‘Ś‘Ś•ŚąŚ•ŚȘ Ś›Ś‘ŚšŚ™ŚšŚȘ ŚžŚ—Ś“Śœ."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ŚŚ€Ś©Śš ڜŚȘŚȘ ŚœŚžŚąŚšŚ›ŚȘ ŚœŚ§Ś‘Ś•Śą ŚŚ ڔڔŚȘŚšŚŚ” ڔږڐŚȘ ŚŠŚšŚ™Ś›Ś” ŚœŚ”Ś™Ś•ŚȘ ŚžŚœŚ•Ś•Ś” Ś‘ŚŠŚœŚ™Śœ ڐڕ Ś‘ŚšŚ˜Ś˜"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"‏&lt;b&gt;Ś”ŚĄŚ˜Ś˜Ś•ŚĄ:&lt;/b&gt; Ś”Ś•ŚąŚœŚ” Ś‘Ś“ŚšŚ’Ś” ڜ\'Ś‘ŚšŚ™ŚšŚȘ ŚžŚ—Ś“Śœ\'"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"‏&lt;b&gt;Ś”ŚĄŚ˜Ś˜Ś•ŚĄ:&lt;/b&gt; Ś”Ś•ŚšŚ“ Ś‘Ś“ŚšŚ’Ś” ڜ\'کڧژ\'"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"Ś”Ś§ŚœŚ˜ŚȘ Ś”ŚžŚĄŚš"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"ڜڜڐ Ś©Ś"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Ś”ŚžŚȘŚ Ś”"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Ś—ŚœŚ•ŚŸ Ś”Ś’Ś“ŚœŚ”"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Ś‘Ś§ŚšŚ•ŚȘ کڜ Ś—ŚœŚ•ŚŸ Ś”Ś”Ś’Ś“ŚœŚ”"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Ś”ŚȘŚ§ŚšŚ‘Ś•ŚȘ"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"ڙک ŚœŚ‘Ś—Ś•Śš ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś” ڛړڙ ŚœŚ”Ś•ŚĄŚ™ŚŁ Ś€Ś§Ś“Ś™Ś"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Ś Ś•ŚĄŚŁ ŚŚžŚŠŚąŚ™ Ś‘Ś§ŚšŚ” ڐڗړ (#).}one{Ś Ś•ŚĄŚ€Ś• # ŚŚžŚŠŚąŚ™ Ś‘Ś§ŚšŚ”.}two{Ś Ś•ŚĄŚ€Ś• # ŚŚžŚŠŚąŚ™ Ś‘Ś§ŚšŚ”.}other{Ś Ś•ŚĄŚ€Ś• # ŚŚžŚŠŚąŚ™ Ś‘Ś§ŚšŚ”.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Ś”Ś•ŚĄŚš"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"ŚœŚ”Ś•ŚĄŚ™ŚŁ ڐŚȘ <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Ś›Ś©ŚžŚ•ŚĄŚ™Ś€Ś™Ś ڐŚȘ Ś”ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś” <xliff:g id="APPNAME">%s</xliff:g>, ڔڙڐ ŚȘŚ•Ś›Śœ ŚœŚ”Ś•ŚĄŚ™ŚŁ ŚŚžŚŠŚąŚ™ Ś‘Ś§ŚšŚ” Ś•ŚȘŚ•Ś›ŚŸ ŚœŚ—ŚœŚ•Ś Ś™ŚȘ ڔږڕ. Ś—ŚœŚ§ ŚžŚ”ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś•ŚȘ ŚžŚŚ€Ś©ŚšŚ•ŚȘ ŚœŚ‘Ś—Ś•Śš ŚŚ™ŚœŚ• ŚŚžŚŠŚąŚ™ Ś‘Ś§ŚšŚ” Ś™Ś•ŚŠŚ’Ś• Ś›ŚŚŸ."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"ŚĄŚ•ŚžŚŸ Ś›ŚžŚ•ŚąŚ“ŚŁ"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"ŚĄŚ•ŚžŚŸ Ś›ŚžŚ•ŚąŚ“ŚŁ, Ś‘ŚžŚ™Ś§Ś•Ś <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Ś”Ś•ŚĄŚš ŚžŚ”ŚžŚ•ŚąŚ“Ś€Ś™Ś"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Ś€ŚȘڙڗڔ کڜ <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Ś”Ś€ŚąŚœŚȘ <xliff:g id="SONG_NAME">%1$s</xliff:g> کڜ <xliff:g id="ARTIST_NAME">%2$s</xliff:g> Śž-<xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Ś”Ś€ŚąŚœŚȘ <xliff:g id="SONG_NAME">%1$s</xliff:g> Śž-<xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Ś‘Ś©Ś‘Ś™ŚœŚš"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Ś‘Ś™Ś˜Ś•Śœ"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"ŚŠŚšŚ™Śš ŚœŚ”ŚȘŚ§ŚšŚ‘ ڛړڙ ŚœŚ”Ś€ŚąŚ™Śœ ŚžŚ“Ś™Ś” Ś‘ŚžŚ›Ś©Ś™Śš <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"ڛړڙ ŚœŚ”Ś€ŚąŚ™Śœ Ś‘ŚžŚ›Ś©Ś™Śš ڔږڔ, ڙک ŚœŚ”ŚȘŚ§ŚšŚ‘ ڐڜ <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"ŚžŚ©Ś”Ś• ڔکŚȘڑک. ڙک ŚœŚ ŚĄŚ•ŚȘ کڕڑ."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Ś‘Ś˜ŚąŚ™Ś Ś”"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"Ś˜ŚŚ‘ŚœŚ˜"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"‏Ś”ŚąŚ‘ŚšŚ” (cast) کڜ ŚžŚ“Ś™Ś”"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"‏ŚžŚȘŚ‘ŚŠŚąŚȘ Ś”ŚąŚ‘ŚšŚ” (cast) کڜ <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ڜڐ Ś€ŚąŚ™Śœ, ڙک ŚœŚ‘Ś“Ś•Ś§ ڐŚȘ Ś”ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś”"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"ڜڐ Ś ŚžŚŠŚ"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"ڔڀڧړ ڜڐ Ś–ŚžŚ™ŚŸ"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"کڒڙڐڔ, ڙک ŚœŚ ŚĄŚ•ŚȘ کڕڑ"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Ś”Ś•ŚĄŚ€ŚȘ Ś€Ś§Ś“Ś™Ś"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"ŚąŚšŚ™Ś›ŚȘ Ś€Ś§Ś“Ś™Ś"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Ś”Ś•ŚĄŚ€ŚȘ ŚŚ€ŚœŚ™Ś§ŚŠŚ™Ś”"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Ś”Ś•ŚĄŚ€ŚȘ ŚžŚ›Ś©Ś™ŚšŚ™ ڀڜژ"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Ś§Ś‘Ś•ŚŠŚ”"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Ś Ś‘Ś—Śš ŚžŚ›Ś©Ś™Śš ڐڗړ"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"‎<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%‎‎"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ŚšŚžŚ§Ś•ŚœŚ™Ś Ś•ŚžŚĄŚ›Ś™Ś"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Ś”ŚŠŚąŚ•ŚȘ ŚœŚžŚ›Ś©Ś™ŚšŚ™Ś"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Ś Ś“ŚšŚ© Ś—Ś©Ś‘Ś•ŚŸ Ś€ŚšŚ™ŚžŚ™Ś•Ś"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Ś”ŚĄŚ‘Śš ŚąŚœ Ś©Ś™Ś“Ś•ŚšŚ™Ś"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Ś©Ś™Ś“Ś•Śš"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"‏ŚŚ Ś©Ś™Ś Ś‘Ś§ŚšŚ‘ŚȘ ŚžŚ§Ś•Ś ŚąŚ ŚžŚ›Ś©Ś™ŚšŚ™ Bluetooth ŚȘŚ•ŚŚžŚ™Ś Ś™Ś›Ś•ŚœŚ™Ś ŚœŚ”ŚŚ–Ś™ŚŸ ŚœŚžŚ“Ś™Ś” Ś©ŚžŚ©Ś•Ś“ŚšŚȘ ŚąŚœ Ś™Ś“Śš"</string>
@@ -894,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"ڜڐ Ś Ś™ŚȘڟ ŚœŚ©Ś“Śš"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"ڜڐ Ś Ś™ŚȘڟ ŚœŚ©ŚžŚ•Śš. ڛړڐڙ ŚœŚ ŚĄŚ•ŚȘ کڕڑ."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"ڜڐ Ś Ś™ŚȘڟ ŚœŚ©ŚžŚ•Śš."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"ڙک ŚœŚ”Ś–Ś™ŚŸ 4 ŚȘŚ•Ś•Ś™Ś ŚœŚ€Ś—Ś•ŚȘ"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"ŚŚ€Ś©Śš ŚœŚ”Ś–Ś™ŚŸ ŚąŚ“ 16 ŚȘŚ•Ś•Ś™Ś"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"‏ŚžŚĄŚ€Śš Build"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"‏ŚžŚĄŚ€Śš Ś”-Build Ś”Ś•ŚąŚȘڧ ŚœŚœŚ•Ś—."</string>
     <string name="basic_status" msgid="2315371112182658176">"Ś€ŚȘڙڗŚȘ کڙڗڔ"</string>
@@ -1013,24 +1030,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• ڙک ŚœŚ€Ś—Ś•ŚȘ ŚžŚ›Ś©Ś™Śš ڐڗړ Ś–ŚžŚ™ŚŸ"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"ŚžŚ§Ś© Ś§Ś™ŚŠŚ•Śš ŚœŚœŚ—Ś™ŚŠŚ” ŚŚšŚ•Ś›Ś”"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Ś‘Ś™Ś˜Ś•Śœ"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"ڔڀڛŚȘ ڐŚȘ Ś”ŚžŚ›Ś©Ś™Śš"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"ڛړڙ ŚœŚŠŚœŚ ŚȘŚžŚ•Ś ŚȘ ŚĄŚœŚ€Ś™ Ś˜Ś•Ś‘Ś” ڙڕŚȘŚš, ڀڕŚȘŚ—Ś™Ś ڐŚȘ Ś”Ś˜ŚœŚ€Ś•ŚŸ"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"ŚœŚ”Ś€Ś•Śš ŚœŚžŚĄŚš Ś”Ś§Ś“ŚžŚ™ ڛړڙ ŚœŚŠŚœŚ ŚȘŚžŚ•Ś ŚȘ ŚĄŚœŚ€Ś™ Ś˜Ś•Ś‘Ś” ڙڕŚȘŚš?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Ś‘ŚžŚŠŚœŚžŚ” Ś”ŚŚ—Ś•ŚšŚ™ŚȘ ŚŚ€Ś©Śš ŚœŚŠŚœŚ ŚȘŚžŚ•Ś Ś” ŚšŚ—Ś‘Ś” ڙڕŚȘŚš Ś‘ŚšŚ–Ś•ŚœŚ•ŚŠŚ™Ś” ڒڑڕڔڔ ڙڕŚȘŚš."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Ś”ŚžŚĄŚš ڙڛڑڔ"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ŚžŚ›Ś©Ś™Śš ŚžŚȘڧڀڜ ŚąŚ•Ś‘Śš ŚœŚžŚŠŚ‘ ڜڐ ŚžŚ§Ś•Ś€Śœ"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ŚžŚ›Ś©Ś™Śš ŚžŚȘڧڀڜ ŚąŚ•Ś‘Śš ŚœŚžŚŠŚ‘ ŚžŚ”Ś•Ś€Śš"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"ŚšŚžŚȘ Ś”Ś˜ŚąŚ™Ś Ś” کڠڕŚȘŚšŚ” Ś‘ŚĄŚ•ŚœŚœŚ”: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ڛړڐڙ ŚœŚ—Ś‘Śš ڐŚȘ Ś”ŚĄŚ˜Ś™Ś™ŚœŚ•ŚĄ ŚœŚžŚ˜ŚąŚŸ"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Ś”ŚĄŚ•ŚœŚœŚ” کڜ Ś”ŚĄŚ˜Ś™Ś™ŚœŚ•ŚĄ Ś—ŚœŚ©Ś”"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"ŚžŚŠŚœŚžŚȘ ڕڙړڐڕ"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"ڐڙ ŚŚ€Ś©Śš ŚœŚ”ŚȘŚ§Ś©Śš ŚžŚ”Ś€ŚšŚ•Ś€Ś™Śœ ڔږڔ"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Ś”ŚžŚ“Ś™Ś Ś™Ś•ŚȘ کڜ ŚžŚ§Ś•Ś Ś”ŚąŚ‘Ś•Ś“Ś” ŚžŚŚ€Ś©ŚšŚȘ ڜښ ŚœŚ‘ŚŠŚą کڙڗڕŚȘ Ś˜ŚœŚ€Ś•ŚŸ ŚšŚ§ ŚžŚ€ŚšŚ•Ś€Ś™Śœ Ś”ŚąŚ‘Ś•Ś“Ś”"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"ŚžŚąŚ‘Śš ŚœŚ€ŚšŚ•Ś€Ś™Śœ ŚąŚ‘Ś•Ś“Ś”"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"ŚĄŚ’Ś™ŚšŚ”"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Ś”Ś’Ś“ŚšŚ•ŚȘ ŚžŚĄŚš Ś”Ś ŚąŚ™ŚœŚ”"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-iw/tiles_states_strings.xml b/packages/SystemUI/res/values-iw/tiles_states_strings.xml
index 91577b8..374f5af 100644
--- a/packages/SystemUI/res/values-iw/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-iw/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"ڛڑڕڙ"</item>
     <item msgid="5966994759929723339">"ŚžŚ•Ś€ŚąŚœ"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 8e66ec3..4e9c1bd 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"仕äș‹ç”šăźă‚čクăƒȘăƒŒăƒłă‚·ăƒ§ăƒƒăƒˆăŻ <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>
     <string name="screenrecord_name" msgid="2596401223859996572">"ă‚čクăƒȘăƒŒăƒł ăƒŹă‚łăƒŒăƒ€ăƒŒ"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ç”»éąăźéŒČç”»ă‚’ć‡Šç†ă—ăŠă„ăŸă™"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"ç”»éąăźéŒČç”»ă‚»ăƒƒă‚·ăƒ§ăƒłäž­ăźé€šçŸ„"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ç”»éąăźæ˜Žă‚‹ă•"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"è‰Čćè»ą"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"è‰ČèŁœæ­Ł"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"ăƒŠăƒŒă‚¶ăƒŒă‚’çźĄç†"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"漌äș†"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"閉じる"</string>
@@ -447,7 +451,7 @@
     <string name="volume_odi_captions_content_description" msgid="4172765742046013630">"歗ćč•たă‚ȘăƒŒăƒăƒŒăƒŹă‚€"</string>
     <string name="volume_odi_captions_hint_enable" msgid="2073091194012843195">"有ćŠčにする"</string>
     <string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"無ćŠčにする"</string>
-    <string name="sound_settings" msgid="8874581353127418308">"ă‚”ă‚Šăƒłăƒ‰ăšăƒă‚€ăƒ–ăƒŹăƒŒă‚·ăƒ§ăƒł"</string>
+    <string name="sound_settings" msgid="8874581353127418308">"éŸłăšăƒă‚€ăƒ–ăƒŹăƒŒă‚·ăƒ§ăƒł"</string>
     <string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"èš­ćźš"</string>
     <string name="screen_pinning_title" msgid="9058007390337841305">"ケプăƒȘはć›șćźšă•ă‚ŒăŠă„ăŸă™"</string>
     <string name="screen_pinning_description" msgid="8699395373875667743">"ć›șćźšă‚’è§Łé™€ă™ă‚‹ăŸă§ç”»éąăŒćžžă«èĄšç€șă•ă‚Œă‚‹ă‚ˆă†ă«ăȘă‚ŠăŸă™ă€‚[æˆ»ă‚‹] ず [æœ€èż‘] ă‚’ćŒæ™‚ă«æŠŒă—ç¶šă‘ă‚‹ăšć›șćźšăŒè§Łé™€ă•ă‚ŒăŸă™ă€‚"</string>
@@ -775,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"ç”»éąăźéŒČ画"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"ă‚żă‚€ăƒˆăƒ«ăȘし"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"ă‚čă‚żăƒłăƒă‚€"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"æ‹Ąć€§ă‚Šă‚Łăƒłăƒ‰ă‚Š"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"æ‹Ąć€§ă‚Šă‚Łăƒłăƒ‰ă‚Š ă‚łăƒłăƒˆăƒ­ăƒŒăƒ«"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"æ‹Ąć€§"</string>
@@ -800,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"ă‚łăƒłăƒˆăƒ­ăƒŒăƒ«ă‚’èżœćŠ ă™ă‚‹ă‚ąăƒ—ăƒȘăźéžæŠž"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# ä»¶ăźă‚łăƒłăƒˆăƒ­ăƒŒăƒ«ă‚’èżœćŠ ă—ăŸă—ăŸă€‚}other{# ä»¶ăźă‚łăƒłăƒˆăƒ­ăƒŒăƒ«ă‚’èżœćŠ ă—ăŸă—ăŸă€‚}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"ć‰Šé™€æžˆăż"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g> ă‚’èżœćŠ ă—ăŸă™ă‹ïŒŸ"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"<xliff:g id="APPNAME">%s</xliff:g> ă‚’èżœćŠ ă™ă‚‹ă“ăšă§ă€ă‚łăƒłăƒˆăƒ­ăƒŒăƒ«ă‚„ă‚łăƒłăƒ†ăƒłăƒ„ă‚’ă“ăźăƒ‘ăƒăƒ«ă«èżœćŠ ă§ăăŸă™ă€‚äž€éƒšăźă‚ąăƒ—ăƒȘă§ăŻă€ă“ă“ă«èĄšç€șă•ă‚Œă‚‹ă‚łăƒłăƒˆăƒ­ăƒŒăƒ«ă‚’éžæŠžă§ăăŸă™ă€‚"</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"ăŠæ°—ă«ć…„ă‚Šă«èżœćŠ æžˆăż"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"ăŠæ°—ă«ć…„ă‚Šă«èżœćŠ æžˆăżă€äœçœź: <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"ăŠæ°—ă«ć…„ă‚Šă‹ă‚‰ć‰Šé™€æžˆăż"</string>
@@ -850,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> を開く"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="SONG_NAME">%1$s</xliff:g>ïŒˆă‚ąăƒŒăƒ†ă‚Łă‚čト損: <xliff:g id="ARTIST_NAME">%2$s</xliff:g>ïŒ‰ă‚’ <xliff:g id="APP_LABEL">%3$s</xliff:g> で憍生"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> を <xliff:g id="APP_LABEL">%2$s</xliff:g> で憍生"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"おすすめ"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"ć…ƒă«æˆ»ă™"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g>ă§ć†ç”Ÿă™ă‚‹ă«ăŻă‚‚ăŁăšèż‘ă„ă‘ăŠăă ă•ă„"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"ă“ăźăƒ‡ăƒă‚€ă‚čă§ć†ç”Ÿă™ă‚‹ă«ăŻă€<xliff:g id="DEVICENAME">%1$s</xliff:g> ă«ă‚‚ăŁăšèż‘ă„ă‘ăŠăă ă•ă„"</string>
@@ -857,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"ă‚šăƒ©ăƒŒăŒç™șç”Ÿă—ăŸă—ăŸă€‚ă‚‚ă†äž€ćșŠăŠè©Šă—ăă ă•ă„ă€‚"</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"èȘ­ăżèŸŒă‚“ă§ă„ăŸă™"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"タブレット"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"メディケをキャă‚čăƒˆă—ăŠă„ăŸă™"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"<xliff:g id="APP_LABEL">%1$s</xliff:g> をキャă‚čăƒˆă—ăŠă„ăŸă™"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"無ćŠč: ケプăƒȘをごçąșèȘăă ă•い"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"èŠ‹ă€ă‹ă‚ŠăŸă›ă‚“ă§ă—ăŸ"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"ă‚łăƒłăƒˆăƒ­ăƒŒăƒ«ă‚’äœżç”šă§ăăŸă›ă‚“"</string>
@@ -866,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"ă‚šăƒ©ăƒŒ: もう侀ćșŠăŠè©Šă—ăă ă•ă„"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"ă‚łăƒłăƒˆăƒ­ăƒŒăƒ«ă‚’èżœćŠ "</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"ă‚łăƒłăƒˆăƒ­ăƒŒăƒ«ă‚’ç·šé›†"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"ケプăƒȘă‚’èżœćŠ "</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"ć‡șćŠ›ăźèżœćŠ "</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"ă‚°ăƒ«ăƒŒăƒ—"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"éžæŠžă—ăŸăƒ‡ăƒă‚€ă‚č: 1 揰"</string>
@@ -881,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ă‚čăƒ”ăƒŒă‚«ăƒŒăšăƒ‡ă‚Łă‚čăƒ—ăƒŹă‚€"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"ăƒ‡ăƒă‚€ă‚čăźć€™èŁœ"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"プレミケム ă‚ąă‚«ă‚ŠăƒłăƒˆăŒćż…èŠă§ă™"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ăƒ–ăƒ­ăƒŒăƒ‰ă‚­ăƒŁă‚čăƒˆăźä»•ç”„ăż"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"ăƒ–ăƒ­ăƒŒăƒ‰ă‚­ăƒŁă‚čト"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Bluetooth ćŻŸćżœăƒ‡ăƒă‚€ă‚čă‚’æŒăŁăŠă„ă‚‹ä»˜èż‘ăźăƒŠăƒŒă‚¶ăƒŒăŻă€ă‚ăȘăŸăŒăƒ–ăƒ­ăƒŒăƒ‰ă‚­ăƒŁă‚čăƒˆă—ăŠă„ă‚‹ăƒĄăƒ‡ă‚Łă‚ąă‚’èŽă‘ăŸă™"</string>
@@ -892,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"ăƒ–ăƒ­ăƒŒăƒ‰ă‚­ăƒŁă‚čăƒˆă§ăăŸă›ă‚“"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"äżć­˜ă§ăăŸă›ă‚“ă€‚ă‚‚ă†äž€ćșŠăŠè©Šă—ăă ă•ă„ă€‚"</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"äżć­˜ă§ăăŸă›ă‚“ă€‚"</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"4 æ–‡ć­—ä»„äžŠă«ă—ăŠăă ă•ă„"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"äœżç”šă§ăă‚‹æ–‡ć­—æ•°ăŻ 16 æ–‡ć­—æœȘæș€ă§ă™"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"ăƒ“ăƒ«ăƒ‰ç•Șć·"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"ăƒ“ăƒ«ăƒ‰ç•Șć·ă‚’ă‚ŻăƒȘăƒƒăƒ—ăƒœăƒŒăƒ‰ă«ă‚łăƒ”ăƒŒă—ăŸă—ăŸă€‚"</string>
     <string name="basic_status" msgid="2315371112182658176">"ç©șăźäŒšè©±"</string>
@@ -1011,11 +1030,16 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• ćˆ©ç”šă§ăă‚‹ăƒ‡ăƒă‚€ă‚čが 1 ć°ä»„äžŠă‚ă‚‹"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"ă‚·ăƒ§ăƒŒăƒˆă‚«ăƒƒăƒˆăźé•·æŠŒă—ăŒćż…èŠă§ă™"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"ă‚­ăƒŁăƒłă‚»ăƒ«"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"ćˆ‡ă‚Šæ›żăˆăŸă—ă‚‡ă†"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"高画èłȘă§æ’źă‚‹ă«ăŻă‚čăƒžăƒŒăƒˆăƒ•ă‚©ăƒłă‚’é–‹ă„ăŠăă ă•ă„"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"才靱ディă‚čăƒ—ăƒŹă‚€ă«ćˆ‡ă‚Šæ›żăˆăŠç¶șéș—ă«æ’źă‚ŠăŸă—ă‚‡ă†"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"é«˜è§ŁćƒćșŠă§ćșƒă„範ć›Čă‚’æ’źćœ±ă™ă‚‹ă«ăŻă€èƒŒéąă‚«ăƒĄăƒ©ă‚’äœżç”šă—ăŠăă ă•ă„ă€‚"</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"âœ±ă“ăźç”»éąăŻ OFF にăȘă‚ŠăŸă™"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"æŠ˜ă‚ŠăŸăŸăżćŒăƒ‡ăƒă‚€ă‚čがćșƒă’ă‚‰ă‚ŒăŠă„ă‚‹"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"æŠ˜ă‚ŠăŸăŸăżćŒăƒ‡ăƒă‚€ă‚čがăČăŁăă‚Šèż”ă•ă‚ŒăŠă„ă‚‹"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"バッテăƒȘăƒŒæź‹é‡ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
@@ -1026,4 +1050,5 @@
     <string name="call_from_work_profile_text" msgid="3458704745640229638">"仕äș‹ç”šăƒăƒȘă‚·ăƒŒă§ăŻă€é€šè©±ăźç™ș俥を仕äș‹ç”šăƒ—ăƒ­ăƒ•ă‚Ąă‚€ăƒ«ă‹ă‚‰ăźăżă«ćˆ¶é™ă§ăăŸă™"</string>
     <string name="call_from_work_profile_action" msgid="2937701298133010724">"仕äș‹ç”šăƒ—ăƒ­ăƒ•ă‚Ąă‚€ăƒ«ă«ćˆ‡ă‚Šæ›żăˆă‚‹"</string>
     <string name="call_from_work_profile_close" msgid="7927067108901068098">"閉じる"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"ăƒ­ăƒƒă‚Żç”»éąăźèš­ćźš"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ja/tiles_states_strings.xml b/packages/SystemUI/res/values-ja/tiles_states_strings.xml
index c2a3321..64f7c39 100644
--- a/packages/SystemUI/res/values-ja/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ja/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"OFF"</item>
     <item msgid="5966994759929723339">"ON"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 599b9d9..b6ab02c 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"სამუჹაო ეკრანის ანაბეჭდები ინაჼება <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>
     <string name="screenrecord_name" msgid="2596401223859996572">"ეკრანის áƒ©áƒáƒ›áƒŹáƒ”áƒ áƒ˜"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ეკრანის áƒ©áƒáƒœáƒáƒŹáƒ”áƒ áƒ˜ მუჹავდება"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"უწყვეჱი ჹეჱყობინება ეკრანის áƒ©áƒáƒŹáƒ”áƒ áƒ˜áƒĄ სესიისთვის"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"განათება"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Ⴠერთა ინვერსია"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Ⴠერთა კორეჄáƒȘია"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"მომჼმარებლების მართვა"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"დასრულდა"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"დაჼურვა"</string>
@@ -775,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"ეკრანის áƒ©áƒáƒŹáƒ”áƒ áƒ"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"უსათაურო"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"მოლოდინის რეჟიმი"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"გადიდების áƒ€áƒáƒœáƒŻáƒáƒ áƒ"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"გადიდების კონჱროლის áƒ€áƒáƒœáƒŻáƒáƒ áƒ"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"მასჹჱაბის გადიდება"</string>
@@ -800,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"აირჩიეთ აპი მართვის საჹუალებების დასამაჱებლად"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{დაემაჱა მართვის # საჹუალება.}other{დაემაჱა მართვის # საჹუალება.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"ამოიჹალა"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"გსურთ <xliff:g id="APPNAME">%s</xliff:g>-ის დამაჱება?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"როდესაáƒȘ <xliff:g id="APPNAME">%s</xliff:g>-ქ ამაჱებთ, მან ლეიძლება დაამაჱოს მართვის საჹუალებები და კონჱენჱი მოáƒȘემულ არეჹი. ზოგიერთ აპჹი ლეგიძლიათ აირჩიოთ, რომელი მართვის საჹუალებები უნდა áƒ’áƒáƒ›áƒáƒ©áƒœáƒ“áƒ”áƒĄ აჄ."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"áƒ áƒ©áƒ”áƒŁáƒšáƒ”áƒ‘áƒšáƒ˜áƒ"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"áƒ áƒ©áƒ”áƒŁáƒšáƒ”áƒ‘áƒšáƒ˜áƒ, პოზიáƒȘიაზე <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"áƒ áƒ©áƒ”áƒŁáƒšáƒ”áƒ‘áƒ˜áƒ“áƒáƒœ ამოჩებულია"</string>
@@ -850,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"გაჼსენით <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"დაუკარით <xliff:g id="SONG_NAME">%1$s</xliff:g>, <xliff:g id="ARTIST_NAME">%2$s</xliff:g>, <xliff:g id="APP_LABEL">%3$s</xliff:g>-დან"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"დაუკარით <xliff:g id="SONG_NAME">%1$s</xliff:g> <xliff:g id="APP_LABEL">%2$s</xliff:g>-დან"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"áƒ—áƒ„áƒ•áƒ”áƒœáƒ—áƒ•áƒ˜áƒĄ"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"მოჄმედ.áƒ’áƒáƒŁáƒ„áƒ›áƒ”áƒ‘áƒ"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"მიიჱანეთ áƒŁáƒ€áƒ áƒ აჼლოს, რომ დაუკრათ <xliff:g id="DEVICENAME">%1$s</xliff:g>-ზე"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"აჄ სათამაჹოდ, მიუაჼლოვდით <xliff:g id="DEVICENAME">%1$s</xliff:g>-ქ"</string>
@@ -857,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"რა჊აáƒȘ ჹეáƒȘდომა მოჼდა. áƒȘადეთ ჼელაჼლა."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"იჱვირთება"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ჱაბლეჱი"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"მედიის ჱრანსლირება"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"მიმდინარეობს <xliff:g id="APP_LABEL">%1$s</xliff:g>-ის ჱრანსლირება"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"არააჄჹიურია, გადაამოწმეთ აპი"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"ვერ მოიძებნა"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"კონჱროლი მიუწვდომელია"</string>
@@ -866,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"ჹეáƒȘდომა, ისევ áƒȘადეთ"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"მართვის საჹუალებების დამაჱება"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"მართვის საჹუალებათა áƒ áƒ”áƒ“áƒáƒ„áƒąáƒ˜áƒ áƒ”áƒ‘áƒ"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"აპის დამაჱება"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"მედია-გამოსავლების დამაჱება"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"áƒŻáƒ’áƒŁáƒ€áƒ˜"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"áƒáƒ áƒ©áƒ”áƒŁáƒšáƒ˜áƒ 1 მოწყობილობა"</string>
@@ -881,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"დინამიკები და დისპლეები"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"ჹემოთავაზებული მოწყობილობები"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"საჭიროა პრემიუმ-ანგარიჹი"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ჱრანსლირების მუჹაობის პრინáƒȘიპი"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"ჱრანსლაáƒȘია"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"თჄვენთან აჼლოს მყოჀ ჼალჼს თავსებადი Bluetooth მოწყობილობით áƒšáƒ”áƒŁáƒ«áƒšáƒ˜áƒáƒ— თჄვენ მიერ ჱრანსლირებული მედიის მოსმენა"</string>
@@ -892,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"ჱრანსლაáƒȘია áƒšáƒ”áƒŁáƒ«áƒšáƒ”áƒ‘áƒ”áƒšáƒ˜áƒ"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"ჹენაჼვა ვერ ჼერჼდება. áƒȘადეთ ჼელაჼლა."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"ჹენაჼვა ვერ ჼერჼდება."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"გამოიყენეთ მინიმუმ 4 სიმბოლო."</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"გამოიყენეთ 16-ზე ნაკლები სიმბოლო"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"ანაწყობის ნომერი"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"ანაწყობის ნომერი დაკოპირებულია გაáƒȘვლის áƒ‘áƒŁáƒ€áƒ”áƒ áƒšáƒ˜."</string>
     <string name="basic_status" msgid="2315371112182658176">"მიმოწერის გაჼსნა"</string>
@@ -1011,11 +1030,16 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• ჼელმისაწვდომია მინიმუმ ერთი მოწყობილობა"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"ჹეჼების დაamp; მოáƒȘდის მალსაჼმობი"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"áƒ’áƒáƒŁáƒ„áƒ›áƒ”áƒ‘áƒ"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"გადააჱრიალეთ აჼლა"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"გაჹალეთ áƒąáƒ”áƒšáƒ”áƒ€áƒáƒœáƒ˜ უკეთესი áƒĄáƒ”áƒšáƒ€áƒ˜áƒĄáƒ—áƒ•áƒ˜áƒĄ"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"გადააბრუნეთ წინა ეკრანზე უკეთესი áƒĄáƒ”áƒšáƒ€áƒ˜áƒĄ მისაჩებად?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"გამოიყენეთ უკანა კამერა áƒŁáƒ€áƒ áƒ Ⴠართო áƒ€áƒáƒąáƒáƒĄ გადასაჩებად მაჩალი გარჩევადობით."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ეს ეკრანი გამოირთვება"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"დასაკეáƒȘი მოწყობილობა იჼსნება"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"დასაკეáƒȘი მოწყობილობა ჱრიალებს"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"დარჩენილია ბაჱარეის <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
@@ -1026,4 +1050,5 @@
     <string name="call_from_work_profile_text" msgid="3458704745640229638">"თჄვენი სამსაჼურის წესები საჹუალებას გაძლევთ, áƒĄáƒáƒąáƒ”áƒšáƒ”áƒ€áƒáƒœáƒ ზარები განაჼორáƒȘიელოთ მჼოლოდ სამსაჼურის პროჀილიდან"</string>
     <string name="call_from_work_profile_action" msgid="2937701298133010724">"სამსაჼურის პროჀილზე გადართვა"</string>
     <string name="call_from_work_profile_close" msgid="7927067108901068098">"დაჼურვა"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"áƒ©áƒáƒ™áƒ”áƒąáƒ˜áƒšáƒ˜ ეკრანის პარამეჱრები"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ka/tiles_states_strings.xml b/packages/SystemUI/res/values-ka/tiles_states_strings.xml
index c951874..99c5263 100644
--- a/packages/SystemUI/res/values-ka/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ka/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"გამორთულია"</item>
     <item msgid="5966994759929723339">"áƒ©áƒáƒ áƒ—áƒŁáƒšáƒ˜áƒ"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 0dd10d0..79d54c5 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"Đ–Ò±ĐŒŃ‹Ń ĐżŃ€ĐŸŃ„ĐžĐ»Ń–ĐœĐ”Đœ Đ°Đ»Ń‹ĐœÒ“Đ°Đœ сĐșŃ€ĐžĐœŃˆĐŸŃ‚Ń‚Đ°Ń€ <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">"Files"</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>
     <string name="screenrecord_name" msgid="2596401223859996572">"Đ­ĐșŃ€Đ°Đœ Đ¶Đ°Đ·Ò“Ń‹Ńˆ"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Đ­ĐșŃ€Đ°Đœ Đ¶Đ°Đ·Ò“Ń‹Ńˆ бДĐčĐœĐ”ŃŃ–Đœ Ó©ÒŁĐŽĐ”Ńƒ"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Đ­ĐșŃ€Đ°ĐœĐŽŃ‹ бДĐčĐœĐ”ĐłĐ” Đ¶Đ°Đ·ŃƒĐŽŃ‹ÒŁ Đ°Ò“Ń‹ĐŒĐŽĐ°Ò“Ń‹ Ń…Đ°Đ±Đ°Ń€Đ»Đ°ĐœĐŽŃ‹Ń€ŃƒŃ‹"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Đ–Đ°Ń€Ń‹Ò›Ń‚Ń‹Ò“Ń‹"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ĐąÒŻŃ ĐžĐœĐČĐ”Ń€ŃĐžŃŃŃ‹"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ĐąÒŻŃŃ‚Ń– Ń‚ÒŻĐ·Đ”Ń‚Ńƒ"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"ПаĐčĐŽĐ°Đ»Đ°ĐœŃƒŃˆŃ‹Đ»Đ°Ń€ĐŽŃ‹ Đ±Đ°ŃÒ›Đ°Ń€Ńƒ"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"ДаĐčŃ‹Đœ"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Đ–Đ°Đ±Ńƒ"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"АĐČŃ‚ĐŸĐŒĐ°Ń‚Ń‚Ń‹"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Дыбыс ĐœĐ” Ўіріл Đ±ĐŸĐ»ĐŒĐ°ĐčЮы."</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Дыбыс ĐœĐ” Ўіріл Đ±ĐŸĐ»ĐŒĐ°ĐčЮы, Ó™ÒŁĐłŃ–ĐŒĐ”Đ»Đ”Ń€ Đ±Ó©Đ»Ń–ĐŒŃ–ĐœŃ–ÒŁ Ń‚Ó©ĐŒĐ”Đœ Đ¶Đ°Ò“Ń‹ĐœĐŽĐ° Ń‚Ò±Ń€Đ°ĐŽŃ‹."</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"ÒšÒ±Ń€Ń‹Đ»Ò“Ń‹ ĐżĐ°Ń€Đ°ĐŒĐ”Ń‚Ń€Đ»Đ”Ń€Ń–ĐœĐ” баĐčĐ»Đ°ĐœŃ‹ŃŃ‚Ń‹ ŃˆŃ‹Ń€Ń‹Đ»ĐŽĐ°ŃƒŃ‹ ĐœĐ” ĐŽŃ–Ń€Ń–Đ»ĐŽĐ”ŃƒŃ– ĐŒÒŻĐŒĐșŃ–Đœ"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"ÒšÒ±Ń€Ń‹Đ»Ò“Ń‹ ĐżĐ°Ń€Đ°ĐŒĐ”Ń‚Ń€Đ»Đ”Ń€Ń–ĐœĐ” баĐčĐ»Đ°ĐœŃ‹ŃŃ‚Ń‹ ŃˆŃ‹Ń€Ń‹Đ»ĐŽĐ°ŃƒŃ‹ ĐœĐ” ĐŽŃ–Ń€Ń–Đ»ĐŽĐ”ŃƒŃ– ĐŒÒŻĐŒĐșŃ–Đœ. <xliff:g id="APP_NAME">%1$s</xliff:g> чаттары әЎДпĐșŃ–ŃŃ–ĐœŃˆĐ” Ò›Đ°Đ»Ò›Ń‹ĐŒĐ°Đ»Ń‹ Дтіп ĐșөрсДтілДЎі."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Đ„Đ°Đ±Đ°Ń€Đ»Đ°ĐœĐŽŃ‹Ń€Ńƒ ĐŽŃ‹Đ±Ń‹ŃŃ‹ĐœŃ‹ÒŁ ĐœĐ”ĐŒĐ”ŃĐ” ĐŽŃ–Ń€Ń–Đ»ĐŽŃ–ÒŁ Ò›ĐŸŃŃ‹Đ»ŃƒŃ‹Đœ Đ¶ÒŻĐčĐ” Đ°ĐœŃ‹Ò›Ń‚Đ°ĐčŃ‚Ń‹Đœ Đ±ĐŸĐ»Đ°ĐŽŃ‹"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;ĐšÒŻĐčі:&lt;/b&gt; \"ӘЎДпĐșі\" ŃĐ°ĐœĐ°Ń‚Ń‹ĐœĐ° ĐșÓ©Ń‚Đ”Ń€Ń–Đ»ĐłĐ”Đœ"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;ĐšÒŻĐčі:&lt;/b&gt; \"ÒźĐœŃŃ–Đ·\" ŃĐ°ĐœĐ°Ń‚Ń‹ĐœĐ° Ń‚Ó©ĐŒĐ”ĐœĐŽĐ”Ń‚Ń–Đ»ĐłĐ”Đœ"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"эĐșŃ€Đ°ĐœĐŽŃ‹ бДĐčĐœĐ”ĐłĐ” жазу"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Атауы Đ¶ĐŸÒ›"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"ĐšÒŻŃ‚Ńƒ Ń€Đ”Đ¶ĐžĐŒŃ–"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Ò°Đ»Ò“Đ°Đčту тДрДзДсі"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Ò°Đ»Ò“Đ°Đčту Ń‚Đ”Ń€Đ”Đ·Đ”ŃŃ–ĐœŃ–ÒŁ Đ±Đ°ŃÒ›Đ°Ń€Ńƒ ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Ń‚Đ”Ń€Ń–"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Ò°Đ»Ò“Đ°Đčту"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Đ‘Đ°ŃÒ›Đ°Ń€Ńƒ ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Ń‚Đ”Ń€Ń– Ò›ĐŸŃŃ‹Đ»Đ°Ń‚Ń‹Đœ Ò›ĐŸĐ»ĐŽĐ°ĐœĐ±Đ°ĐœŃ‹ Ń‚Đ°ÒŁĐŽĐ°ÒŁŃ‹Đ·"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# Đ±Đ°ŃÒ›Đ°Ń€Ńƒ ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Ń– Ò›ĐŸŃŃ‹Đ»ĐŽŃ‹.}other{# Đ±Đ°ŃÒ›Đ°Ń€Ńƒ ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Ń– Ò›ĐŸŃŃ‹Đ»ĐŽŃ‹.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"ÓšŃˆŃ–Ń€Ń–Đ»ĐŽŃ–"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g> Ò›ĐŸĐ»ĐŽĐ°ĐœĐ±Đ°ŃŃ‹Đœ Ò›ĐŸŃŃƒ ĐșДрДĐș пД?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"<xliff:g id="APPNAME">%s</xliff:g> Ò›ĐŸĐ»ĐŽĐ°ĐœĐ±Đ°ŃŃ‹Đœ Ò›ĐŸŃÒ›Đ°Đœ ĐșДзЎД, Đ±Đ°ŃÒ›Đ°Ń€Ńƒ ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Ń‚Đ”Ń€Ń– ĐŒĐ”Đœ ĐșĐŸĐœŃ‚Đ”ĐœŃ‚ ĐŸŃŃ‹ ĐżĐ°ĐœĐ”Đ»ŃŒĐłĐ” Đ”ĐœĐłŃ–Đ·Ń–Đ»Đ”ĐŽŃ–. КДĐčбір Ò›ĐŸĐ»ĐŽĐ°ĐœĐ±Đ°ĐŽĐ° Đ±Đ°ŃÒ›Đ°Ń€Ńƒ ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Ń‚Đ”Ń€Ń–ĐœŃ–ÒŁ Ò›Đ°Đčсысы ĐŸŃŃ‹ жДрЎД ĐșÓ©Ń€ŃĐ”Ń‚Ń–Đ»Đ”Ń‚Ń–ĐœŃ–Đœ Ń‚Đ°ÒŁĐŽĐ°Đč аласыз."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"ĐąĐ°ÒŁĐŽĐ°ŃƒĐ»Ń‹Đ»Đ°Ń€Ò“Đ° Ò›ĐŸŃŃ‹Đ»ĐŽŃ‹"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"ĐąĐ°ÒŁĐŽĐ°ŃƒĐ»Ń‹Đ»Đ°Ń€Ò“Đ° Ò›ĐŸŃŃ‹Đ»ĐŽŃ‹, <xliff:g id="NUMBER">%d</xliff:g>-ĐżĐŸĐ·ĐžŃ†ĐžŃ"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"ĐąĐ°ÒŁĐŽĐ°ŃƒĐ»Ń‹Đ»Đ°Ń€ĐŽĐ°Đœ алып тасталЎы"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> Ò›ĐŸĐ»ĐŽĐ°ĐœĐ±Đ°ŃŃ‹Đœ ашу"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> Ò›ĐŸĐ»ĐŽĐ°ĐœĐ±Đ°ŃŃ‹ĐœĐŽĐ° <xliff:g id="ARTIST_NAME">%2$s</xliff:g> ĐŸŃ€Ń‹ĐœĐŽĐ°ĐčŃ‚Ń‹Đœ \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" Ó™ĐœŃ–Đœ ĐŸĐčĐœĐ°Ń‚Ńƒ"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> Ò›ĐŸĐ»ĐŽĐ°ĐœĐ±Đ°ŃŃ‹ĐœĐŽĐ° \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" Ó™ĐœŃ–Đœ ĐŸĐčĐœĐ°Ń‚Ńƒ"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"ĐĄŃ–Đ· ÒŻŃˆŃ–Đœ"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"ÒšĐ°Đčтару"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> Ò›Ò±Ń€Ń‹Đ»Ò“Ń‹ŃŃ‹ĐœĐŽĐ° ĐŒŃƒĐ·Ń‹Đșа ĐŸĐčĐœĐ°Ń‚Ńƒ ÒŻŃˆŃ–Đœ ĐŸÒ“Đ°Đœ Đ¶Đ°Ò›Ń‹ĐœĐŽĐ°ÒŁŃ‹Đ·."</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Осы жДрЎД ĐŸĐčĐœĐ°Ń‚Ńƒ ÒŻŃˆŃ–Đœ <xliff:g id="DEVICENAME">%1$s</xliff:g> Ò›Ò±Ń€Ń‹Đ»Ò“Ń‹ŃŃ‹ĐœĐ° Đ¶Đ°Ò›Ń‹ĐœĐŽĐ°ÒŁŃ‹Đ·."</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Đ‘Ń–Ń€ĐŽĐ”ÒŁĐ” ĐŽÒ±Ń€Ń‹Ń Đ±ĐŸĐ»ĐŒĐ°ĐŽŃ‹. ÒšĐ°Đčталап ĐșÓ©Ń€Ń–ÒŁŃ–Đ·."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Đ–ÒŻĐșтДліп жатыр"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ĐżĐ»Đ°ĐœŃˆĐ”Ń‚"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"ĐœĐ”ĐŽĐžĐ°ĐșĐŸĐœŃ‚Đ”ĐœŃ‚Ń‚Ń– Ń‚Ń€Đ°ĐœŃĐ»ŃŃ†ĐžŃĐ»Đ°Ńƒ"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"ĐąŃ€Đ°ĐœŃĐ»ŃŃ†ĐžŃ: <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ÓšŃˆŃ–Ń€ŃƒĐ»Ń–. ÒšĐŸĐ»ĐŽĐ°ĐœĐ±Đ° тДĐșŃĐ”Ń€Ń–ÒŁŃ–Đ·."</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"ĐąĐ°Đ±Ń‹Đ»ĐŒĐ°ĐŽŃ‹"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Đ‘Đ°ŃÒ›Đ°Ń€Ńƒ ĐČОЎжДті Ò›ĐŸĐ»Đ¶Đ”Ń‚Ń–ĐŒŃŃ–Đ·"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"ÒšĐ°Ń‚Đ” ŃˆŃ‹Ò›Ń‚Ń‹. ÒšĐ°Đčталап ĐșÓ©Ń€Ń–ÒŁŃ–Đ·."</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Đ‘Đ°ŃÒ›Đ°Ń€Ńƒ ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Ń‚Đ”Ń€Ń–Đœ Ò›ĐŸŃŃƒ"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Đ‘Đ°ŃÒ›Đ°Ń€Ńƒ ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Ń‚Đ”Ń€Ń–Đœ Ó©Đ·ĐłĐ”Ń€Ń‚Ńƒ"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"ÒšĐŸĐ»ĐŽĐ°ĐœĐ±Đ° Ò›ĐŸŃŃƒ"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"ĐšŃ‹Ò“Ń‹Ń ŃĐžĐłĐœĐ°Đ»ĐŽĐ°Ń€ĐŽŃ‹ Ò›ĐŸŃŃƒ"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"ĐąĐŸĐż"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 Ò›Ò±Ń€Ń‹Đ»Ò“Ń‹ Ń‚Đ°ÒŁĐŽĐ°Đ»ĐŽŃ‹."</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Đ”ĐžĐœĐ°ĐŒĐžĐșтДр ĐŒĐ”Đœ ЎОсплДĐčлДр"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Ò°ŃŃ‹ĐœŃ‹Đ»Ò“Đ°Đœ Ò›Ò±Ń€Ń‹Đ»Ò“Ń‹Đ»Đ°Ń€"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"ĐŸŃ€Đ”ĐŒĐžŃƒĐŒ аĐșĐșĐ°ŃƒĐœŃ‚ Ò›Đ°Đ¶Đ”Ń‚"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"барату Ò›Đ°Đ»Đ°Đč Đ¶ÒŻĐ·Đ”ĐłĐ” асаЮы"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"барату"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ÒźĐčĐ»Đ”ŃŃ–ĐŒĐŽŃ– Bluetooth Ò›Ò±Ń€Ń‹Đ»Ò“Ń‹Đ»Đ°Ń€Ń‹ бар ĐŒĐ°ÒŁĐ°ĐčĐŽĐ°Ò“Ń‹ Đ°ĐŽĐ°ĐŒĐŽĐ°Ń€ сіз таратып Đ¶Đ°Ń‚Ò›Đ°Đœ ĐŒĐ”ĐŽĐžĐ°ĐŒĐ°Đ·ĐŒÒ±ĐœĐŽŃ‹ Ń‚Ń‹ÒŁĐŽĐ°Đč алаЎы."</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"барату ĐŒÒŻĐŒĐșŃ–Đœ Đ”ĐŒĐ”Ń"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"ĐĄĐ°Ò›Ń‚Đ°Đ»ĐŒĐ°ĐčЮы. ÒšĐ°Đčталап ĐșÓ©Ń€Ń–ÒŁŃ–Đ·."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"ĐĄĐ°Ò›Ń‚Đ°Đ»ĐŒĐ°ĐčЮы."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"ÒšÒ±Ń€Đ°ĐŒĐ° ĐœÓ©ĐŒŃ–Ń€Ń–"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"ÒšÒ±Ń€Đ°ĐŒĐ° ĐœÓ©ĐŒŃ–Ń€Ń– Đ±ŃƒŃ„Đ”Ń€ĐłĐ” ĐșÓ©ŃˆŃ–Ń€Ń–Đ»ĐŽŃ–."</string>
     <string name="basic_status" msgid="2315371112182658176">"ĐŃˆŃ‹Ò› Ó™ÒŁĐłŃ–ĐŒĐ”"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• ĐšĐ”ĐŒŃ–ĐœĐŽĐ” бір Ò›Ò±Ń€Ń‹Đ»Ò“Ń‹ Ò›ĐŸĐ»Đ¶Đ”Ń‚Ń–ĐŒĐŽŃ–"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"ĐąĐ°ÒŁĐ±Đ°ŃˆĐ°ĐœŃ‹ басып Ń‚Ò±Ń€Ń‹ÒŁŃ‹Đ·."</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Бас тарту"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"АĐčĐœĐ°Đ»ĐŽŃ‹Ń€Ńƒ"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Đ–Đ°Ò›ŃŃ‹Ń€Đ°Ò› сДлфО Ń‚ÒŻŃŃ–Ń€Ńƒ ÒŻŃˆŃ–Đœ Ń‚Đ”Đ»Đ”Ń„ĐŸĐœĐŽŃ‹ Đ¶Đ°Đ·Ń‹ÒŁŃ‹Đ·"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Đ–Đ°Ò›ŃŃ‹Ń€Đ°Ò› сДлфО ÒŻŃˆŃ–Đœ Đ°Đ»ĐŽŃ‹ÒŁÒ“Ń‹ эĐșŃ€Đ°ĐœÒ“Đ° Đ°ŃƒŃ‹ŃĐ°ŃŃ‹Đ· ба?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"ĐĐ¶Ń‹Ń€Đ°Ń‚Ń‹ĐŒĐŽŃ‹Đ»Ń‹Ò“Ń‹ Đ¶ĐŸÒ“Đ°Ń€Ń‹ ĐșĐ”ÒŁŃ–Ń€Đ”Đș Ń„ĐŸŃ‚ĐŸŃŃƒŃ€Đ”Ń‚ Ń‚ÒŻŃŃ–Ń€Ńƒ ÒŻŃˆŃ–Đœ Đ°Ń€Ń‚Ò›Ń‹ ĐșĐ°ĐŒĐ”Ń€Đ°ĐœŃ‹ паĐčĐŽĐ°Đ»Đ°ĐœŃ‹ÒŁŃ‹Đ·."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Đ‘Ò±Đ» эĐșŃ€Đ°Đœ Ó©ŃˆŃ–Ń€Ń–Đ»Đ”ĐŽŃ–."</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Đ‘ÒŻĐșŃ‚Đ”ĐŒĐ”Đ»Ń– Ò›Ò±Ń€Ń‹Đ»Ò“Ń‹ Đ°ŃˆŃ‹Đ»Ń‹Đż жатыр."</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Đ‘ÒŻĐșŃ‚Đ”ĐŒĐ”Đ»Ń– Ò›Ò±Ń€Ń‹Đ»Ò“Ń‹ Đ°ŃƒĐŽĐ°Ń€Ń‹Đ»Ń‹Đż жатыр."</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"ÒšĐ°Đ»Ò“Đ°Đœ Đ±Đ°Ń‚Đ°Ń€Đ”Ń Đ·Đ°Ń€ŃĐŽŃ‹: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ĐĄŃ‚ĐžĐ»ŃƒŃŃ‚Ń‹ Đ·Đ°Ń€ŃĐŽŃ‚Đ°Ò“Ń‹ŃˆÒ›Đ° Đ¶Đ°Đ»Ò“Đ°ÒŁŃ‹Đ·."</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"ĐĄŃ‚ĐžĐ»ŃƒŃ Đ±Đ°Ń‚Đ°Ń€Đ”ŃŃŃ‹ĐœŃ‹ÒŁ Đ·Đ°Ń€ŃĐŽŃ‹ аз"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"БДĐčĐœĐ”ĐșĐ°ĐŒĐ”Ń€Đ°"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Đ‘Ò±Đ» ĐżŃ€ĐŸŃ„ĐžĐ»ŃŒĐŽĐ”Đœ Ò›ĐŸÒŁŃ‹Ń€Đ°Ńƒ шалу ĐŒÒŻĐŒĐșŃ–Đœ Đ”ĐŒĐ”Ń"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Đ–Ò±ĐŒŃ‹Ń ŃĐ°ŃŃĐ°Ń‚Ń‹ÒŁŃ‹Đ·Ò“Đ° сәĐčĐșДс тДĐș Đ¶Ò±ĐŒŃ‹Ń ĐżŃ€ĐŸŃ„ĐžĐ»Ń–ĐœĐ”Đœ Ò›ĐŸÒŁŃ‹Ń€Đ°Ńƒ ŃˆĐ°Đ»ŃƒÒ“Đ° Đ±ĐŸĐ»Đ°ĐŽŃ‹."</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Đ–Ò±ĐŒŃ‹Ń ĐżŃ€ĐŸŃ„ĐžĐ»Ń–ĐœĐ” ауысу"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Đ–Đ°Đ±Ńƒ"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Đ­ĐșŃ€Đ°Đœ Ò›Ò±Đ»ĐżŃ‹ĐœŃ‹ÒŁ ĐżĐ°Ń€Đ°ĐŒĐ”Ń‚Ń€Đ»Đ”Ń€Ń–"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-kk/tiles_states_strings.xml b/packages/SystemUI/res/values-kk/tiles_states_strings.xml
index c312b49..be7546e 100644
--- a/packages/SystemUI/res/values-kk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-kk/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"ÓšŃˆŃ–Ń€ŃƒĐ»Ń–."</item>
     <item msgid="5966994759929723339">"ÒšĐŸŃŃƒĐ»Ń‹."</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index eea029b..306d104 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"ážšážŒáž”ážážážąáŸáž€áŸ’ážšáž„áŸ‹â€‹áž€áŸ’áž“áž»áž„áž€áž˜áŸ’ážšáž„áž–áŸážáŸŒáž˜áž¶áž“áž€áž¶ážšáž„áž¶ážšâ€‹ážáŸ’ážšážŒážœáž”áž¶áž“ážšáž€áŸ’ážŸáž¶áž‘áž»áž€áž“áŸ…áž€áŸ’áž“áž»áž„áž€áž˜áŸ’áž˜ážœáž·áž’ážž <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>
     <string name="screenrecord_name" msgid="2596401223859996572">"áž˜áž»ážáž„áž¶ážšážážâ€‹ážœážžážŠáŸážąážŒážąáŸáž€áŸ’ážšáž„áŸ‹"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"áž€áŸ†áž–áž»áž„â€‹ážŠáŸ†ážŽážŸážšáž€áž¶ážšâ€‹áž€áž¶ážšážážážąáŸáž€áŸ’ážšáž„áŸ‹"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"ការជឌនដំណážčáž„â€‹ážŠáŸ‚áž›â€‹áž€áŸ†áž–áž»áž„â€‹ážŠáŸ†ážŽážŸážšáž€áž¶ážšâ€‹ážŸáž˜áŸ’ážšáž¶áž”áŸ‹â€‹ážšáž™áŸˆáž–áŸáž›áž”áŸ’ážšážŸâ€‹áž€áž¶ážšážážâ€‹ážŸáž€áž˜áŸ’áž˜áž—áž¶áž–â€‹ážąáŸáž€áŸ’ážšáž„áŸ‹"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ពន្លážș"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ការបញ្ច្រាស​ពណ៌"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ការ​កែតម្រឌវ​ពណ៌"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"áž‚áŸ’ážšáž”áŸ‹áž‚áŸ’ážšáž„â€‹ážąáŸ’áž“áž€â€‹áž”áŸ’ážšážŸáž”áŸ’ážšáž¶ážŸáŸ‹"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"រវចរាល់"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"បិទ"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"ស្វ័យប្រវត្តិ"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"áž‚áŸ’áž˜áž¶áž“â€‹ážŸáŸ†ážĄáŸáž„ ážŹáž€áž¶ážšáž‰áŸážšáž‘áŸ"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"áž‚áŸ’áž˜áž¶áž“ážŸáŸ†ážĄáŸáž„â€‹ážŹáž€áž¶ážšáž‰áŸážš និងបង្ហាញ​ទាបជាង​នៅក្នុង​ផ្នែកសន្ទនា"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"ážąáž¶áž…ážšáŸ„áž‘áŸ ឬញ័រ ážŠáŸ„áž™áž•áŸ’ážąáŸ‚áž€áž›ážŸâ€‹áž€áž¶ážšáž€áŸ†ážŽážáŸ‹áž§áž”áž€ážšážŽáŸ"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"ážąáž¶áž…ážšáŸ„áž‘áŸ ឬញ័រ ážŠáŸ„áž™áž•áŸ’ážąáŸ‚áž€áž›ážŸâ€‹áž€áž¶ážšáž€áŸ†ážŽážáŸ‹áž§áž”áž€ážšážŽáŸáŸ” áž€áž¶ážšážŸáž“áŸ’áž‘áž“áž¶â€‹áž–ážžáž•áŸ’áž‘áž¶áŸ†áž„ážąážŽáŸ’ážŠáŸ‚áž <xliff:g id="APP_NAME">%1$s</xliff:g> តាម​លំនាំដសម​។"</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ឱ្យប្រព័ន្ធកំណត់ថាតស​ការជឌនដំណážčងនេះ​គវរតែបន្លážșសំឡេង ឬញ័រ"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;ស្ថានភាព៖&lt;/b&gt; áž”áž¶áž“ážŠáŸ†ážĄážŸáž„áž‘áŸ…â€‹áž›áŸ†áž“áž¶áŸ†ážŠážŸáž˜"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;ស្ថានភាព៖&lt;/b&gt; បានបញ្ចុះទៅស្ងាត់"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"áž€áž¶ážšážážážœážžážŠáŸážąážŒážąáŸáž€áŸ’ážšáž„áŸ‹"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"គ្មាន​ចំណងជសង"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"áž•áŸ’ážąáž¶áž€â€‹ážŠáŸ†ážŽážŸážšáž€áž¶ážš"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"វិនដឌ​ការពង្រឞក"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"វិនដឌគ្រប់គ្រង​​ការពង្រឞក"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ពង្រើក"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"ជ្រសសរសស​កម្មវិធឞដែលត្រឌវបញ្ចឌល​ផ្ទាំងគ្រប់គ្រង"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{បានបញ្ចឌល​ការគ្រប់គ្រង #។}other{បានបញ្ចឌល​ការគ្រប់គ្រង #។}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"បានដកចេញ"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"បញ្ចឌល <xliff:g id="APPNAME">%s</xliff:g> ឬ?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"áž“áŸ…áž–áŸáž›ážąáŸ’áž“áž€áž”áž‰áŸ’áž…ážŒáž› <xliff:g id="APPNAME">%s</xliff:g> áž€áž˜áŸ’áž˜ážœáž·áž’ážžáž“áŸáŸ‡ážąáž¶áž…áž”áž‰áŸ’áž…ážŒáž›áž€áž¶ážšáž‚áŸ’ážšáž”áŸ‹áž‚áŸ’ážšáž„ និងខ្លážčមសារទៅផ្ទាំងនេះបាន។ ក្នុងកម្មវិធឞមវយចំនវន ážąáŸ’áž“áž€ážąáž¶áž…áž‡áŸ’ážšážŸážŸážšážŸážŸáž±áŸ’áž™áž€áž¶ážšáž‚áŸ’ážšáž”áŸ‹áž‚áŸ’ážšáž„ážŽáž¶ážáŸ’áž›áŸ‡áž”áž„áŸ’áž áž¶áž‰áž“áŸ…áž‘ážžáž“áŸáŸ‡áž”áž¶áž“áŸ”"</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"បានដាក់ជា​សំណព្វ"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"បានដាក់ជា​សំណព្វ ទឞតាំង​ទឞ <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"បានដកចេញ​ពឞ​សំណព្វ"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"បសក <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"ចាក់ <xliff:g id="SONG_NAME">%1$s</xliff:g> ច្រៀងដោយ <xliff:g id="ARTIST_NAME">%2$s</xliff:g> ពើ <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"ចាក់ <xliff:g id="SONG_NAME">%1$s</xliff:g> ពើ <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"ážŸáž˜áŸ’ážšáž¶áž”áŸ‹ážąáŸ’áž“áž€"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"ážáŸ’ážšážĄáž”áŸ‹ážœáž·áž‰"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"រំកិលឱ្យកាន់តែជិត ដសម្បឞចាក់នៅលស <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"ដសម្បឞចាក់នៅទឞនេះ សឌមខិតទៅជិត <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"áž˜áž¶áž“ážąáŸ’ážœážžáž˜ážœáž™ážáž»ážŸáž”áŸ’ážšáž€áŸ’ážšážážžáŸ” សឌមព្យាយាមម្ដងទៀត។"</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"កំពុងផ្ទុក"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ថេប្លេត"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"áž€áŸ†áž–áž»áž„áž—áŸ’áž‡áž¶áž”áŸ‹áž˜áŸážŒáŸ€â€‹ážšáž”ážŸáŸ‹ážąáŸ’áž“áž€"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"កំពុង​ភ្ជាប់ <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ឱសកម្ម ពិនិត្យមសល​កម្មវិធឞ"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"រកមិន​ឃសញទេ"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"áž˜áž·áž“ážąáž¶áž…â€‹áž‚áŸ’ážšáž”áŸ‹áž‚áŸ’ážšáž„â€‹áž”áž¶áž“áž‘áŸ"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"មានបញ្ហា សឌម​ព្យាយាម​ម្តងទៀត"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"បញ្ចឌល​ផ្ទាំងគ្រប់គ្រង"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"កែ​ផ្ទាំងគ្រប់គ្រង"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"បញ្ចឌល​កម្មវិធឞ"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"បញ្ចឌល​ឧបករណ៍​មេឌៀ"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"ក្រុម"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"បានជ្រសសរសស​ឧបករណ៍ 1"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ឧបករណ៍បំពងសំឡេង áž“áž·áž„áž•áŸ’áž‘áž¶áŸ†áž„ážąáŸáž€áŸ’ážšáž„áŸ‹"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"ឧបករណ៍​ដែលបានណែនាំ"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"ត្រឌវការគណនឞលំដាប់ខ្ពស់"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"របៀបដែលការផ្សាយដំណសរការ"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"ការ​ផ្សាយ"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"áž˜áž“áž»ážŸáŸ’ážŸáž“áŸ…áž‡áž·ážâ€‹ážąáŸ’áž“áž€ážŠáŸ‚áž›áž˜áž¶áž“â€‹áž§áž”áž€ážšážŽáŸáž”áŸŠáŸ’áž›ážŒáž’ážŒážŸâ€‹ážáŸ’ážšážŒážœáž‚áŸ’áž“áž¶â€‹ážąáž¶áž…ážŸáŸ’ážáž¶áž”áŸ‹â€‹áž˜áŸážŒáŸ€â€‹ážŠáŸ‚áž›ážąáŸ’áž“áž€áž€áŸ†áž–áž»áž„áž•áŸ’ážŸáž¶áž™áž”áž¶áž“"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"áž˜áž·áž“ážąáž¶áž…áž•áŸ’ážŸáž¶áž™áž”áž¶áž“áž‘áŸ"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"áž˜áž·áž“ážąáž¶áž…â€‹ážšáž€áŸ’ážŸáž¶áž‘áž»áž€â€‹áž”áž¶áž“áž‘áŸáŸ” សឌមព្យាយាមម្ដងទៀត។"</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"áž˜áž·áž“ážąáž¶áž…â€‹ážšáž€áŸ’ážŸáž¶áž‘áž»áž€â€‹áž”áž¶áž“áž‘áŸáŸ”"</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"លេខ​កំណែបង្កសត"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"បានចម្លងលេខ​កំណែបង្កសតទៅឃ្លឞបបត។"</string>
     <string name="basic_status" msgid="2315371112182658176">"បសកការសន្ទនា"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• áž§áž”áž€ážšážŽáŸáž™áŸ‰áž¶áž„ážáž·áž…áž˜ážœáž™ážąáž¶áž…áž”áŸ’ážšážŸáž”áž¶áž“"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"ចុចឱ្យជាប់លសផ្លឌវកាត់"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"បោះបង់"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"ážáŸ’ážšážĄáž”áŸ‹áž„ážĄážŒážœáž“áŸáŸ‡"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"លាតទឌរសព្ទ ដសម្បឞសែលហ្វឞកាន់តែប្រសសរ"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"ážáŸ’ážšážĄáž”áŸ‹áž‘áŸ…áž•áŸ’áž‘áž¶áŸ†áž„ážąáŸáž€áŸ’ážšáž„áŸ‹ážáž¶áž„áž˜áž»ážâ€‹ ážŠážŸáž˜áŸ’áž”ážžâ€‹ážážâ€‹ážŸáŸ‚áž›áž áŸ’ážœážžáž€áž¶áž“áŸ‹ážáŸ‚áž”áž¶áž“áž›áŸ’ážąážŹ?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"ប្រសកាមេរ៉ាខាងក្រោយ ដសម្បឞទទវលបានរឌបថតកាន់តែធំជាមវយនážčងកម្រិតគុណភាពកាន់តែខ្ពស់។"</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ឱេក្រង់នេះនážčងបិទ"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"áž§áž”áž€ážšážŽáŸážąáž¶áž…â€‹áž”ážáŸ‹áž”áž¶áž“áž€áŸ†áž–áž»áž„ážáŸ’ážšážŒážœáž”áž¶áž“áž›áž¶"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"áž§áž”áž€ážšážŽáŸážąáž¶áž…â€‹áž”ážáŸ‹áž”áž¶áž“áž€áŸ†áž–áž»áž„ážáŸ’ážšážŒážœáž”áž¶áž“áž›áž¶"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"ថ្មនៅសល់ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"áž—áŸ’áž‡áž¶áž”áŸ‹áž”áŸŠáž·áž€ážšáž”ážŸáŸ‹ážąáŸ’áž“áž€áž‡áž¶áž˜ážœáž™áž†áŸ’áž“áž¶áŸ†áž„ážŸáž¶áž€"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"ថ្មប៊ិកនៅសល់តិច"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"áž€áž¶áž˜áŸážšáŸ‰áž¶â€‹ážœážžážŠáŸážąážŒ"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"áž˜áž·áž“ážąáž¶áž…áž áŸ…áž‘ážŒážšážŸáž–áŸ’áž‘áž–ážžáž€áž˜áŸ’ážšáž„áž–áŸážáŸŒáž˜áž¶áž“áž“áŸáŸ‡áž”áž¶áž“áž‘áŸ"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"áž‚áŸ„áž›áž€áž¶ážšážŽáŸáž€áž¶ážšáž„áž¶ážšážšáž”ážŸáŸ‹ážąáŸ’áž“áž€ážąáž“áž»áž‰áŸ’áž‰áž¶ážáž±áŸ’áž™ážąáŸ’áž“áž€áž’áŸ’ážœážŸáž€áž¶ážšáž áŸ…áž‘ážŒážšážŸáž–áŸ’áž‘áž”áž¶áž“ážáŸ‚áž–ážžáž€áž˜áŸ’ážšáž„áž–áŸážáŸŒáž˜áž¶áž“áž€áž¶ážšáž„áž¶ážšáž”áŸ‰áž»ážŽáŸ’ážŽáŸ„áŸ‡"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"ប្ដឌរ​ទៅ​កម្រង​ព័ត៌មាន​ការងារ"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"បិទ"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"áž€áž¶ážšáž€áŸ†ážŽážáŸ‹â€‹ážąáŸáž€áŸ’ážšáž„áŸ‹áž…áž¶áž€áŸ‹ážŸáŸ„"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-km/tiles_states_strings.xml b/packages/SystemUI/res/values-km/tiles_states_strings.xml
index ec748cf..37e839f 100644
--- a/packages/SystemUI/res/values-km/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-km/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"បិទ"</item>
     <item msgid="5966994759929723339">"បសក"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 92956ac..1b18614 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"<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>
     <string name="screenrecord_name" msgid="2596401223859996572">"àČžàłàČ•àłàČ°àł€àČšàł àČ°àł†àȕàČŸàČ°àłàČĄàČ°àł"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"àČžàłàČ•àłàČ°àł€àČšàł àČ°àł†àȕàČŸàČ°àłàČĄàČżàȂàČ—àł àȆàČ—àłàČ€àłàČ€àČżàČŠàł†"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"àČžàłàČ•àłàČ°àł€àČšàł àČ°àł†àȕàČŸàČ°àłàČĄàČżàȂàČ—àł àČžàł†àȶàČšàł‌àȗàČŸàȗàČż àȚàČŸàČČàłàČ€àČżàČŻàČČàłàČČàČżàČ°àłàČ” àȅàȧàČżàČžàł‚àȚàČšàł†"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"àČȘàłàȰàȕàČŸàȶàČźàČŸàČš"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"àȕàČČàČ°àł àȇàČšàł‍àČ”àČ°àłàȶàČšàł"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"àČŹàČŁàłàČŁàČŠ àČ€àČżàČŠàłàČŠàłàČȘàČĄàČż"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"àČŹàČłàČ•àł†àČŠàČŸàȰàȰàČšàłàČšàł àČšàČżàČ°àłàČ”àČčàČżàČžàČż"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"àČźàłàȗàČżàČŠàČżàČŠàł†"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"àČźàłàČšàłàȚàČżàȰàČż"</string>
@@ -775,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"àČžàłàČ•àłàČ°àł€àČšàł àČ°àł†àȕàČŸàČ°àłàČĄàČżàȂàČ—àł"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"àČŻàČŸàČ”àłàČŠàł‡ àČ¶àł€àČ°àłàČ·àČżàČ•àł†àČŻàČżàČČàłàČČ"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"àČžàłàČŸàłàČŻàČŸàȂàČĄàł‌àČŹàłˆ"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"àČ”àČ°àłàȧàČšàł†àČŻ àČ”àČżàȂàČĄàł‹"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"àČ”àČ°àłàȧàČšàł†àČŻ àČ”àČżàȂàČĄàł‹ àČšàČżàČŻàȂàČ€àłàȰàČŁàȗàČłàł"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"àČàł‚àČźàł àȇàČšàł àČźàČŸàČĄàČż"</string>
@@ -800,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"àČšàČżàČŻàȂàČ€àłàȰàČŁàȗàČłàČšàłàČšàł àČžàł‡àȰàČżàČžàČČàł àČ†àłàČŻàČȘàł àȅàČšàłàČšàł àȆàČŻàłàČ•àł†àČźàČŸàČĄàČż"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# àČšàČżàČŻàȂàČ€àłàȰàČŁàČ”àČšàłàČšàł àČžàł‡àȰàČżàČžàČČàČŸàȗàČżàČŠàł†.}one{# àČšàČżàČŻàȂàČ€àłàȰàČŁàȗàČłàČšàłàČšàł àČžàł‡àȰàČżàČžàČČàČŸàȗàČżàČŠàł†.}other{# àČšàČżàČŻàȂàČ€àłàȰàČŁàȗàČłàČšàłàČšàł àČžàł‡àȰàČżàČžàČČàČŸàȗàČżàČŠàł†.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"àČ€àł†àČ—àł†àČŠàłàČčàČŸàȕàČČàČŸàȗàČżàČŠàł†"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g> àȅàČšàłàČšàł àČžàł‡àȰàČżàČžàČŹàł‡àČ•àł†?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"àČšàł€àČ”àł <xliff:g id="APPNAME">%s</xliff:g> àȅàČšàłàČšàł àČžàł‡àȰàČżàČžàČżàČŠàČŸàȗ, àȅàČŠàł àȈ àČȘàłàČŻàČŸàČšàł†àČČàł‌àČ—àł† àČšàČżàČŻàȂàČ€àłàȰàČŁàȗàČłàł àČźàČ€àłàČ€àł àČ”àČżàČ·àČŻàČ”àČšàłàČšàł àČžàł‡àȰàČżàČžàČŹàČčàłàČŠàł. àČ•àł†àČČàČ”àł àČ†àłàČŻàČȘàł‌àȗàČłàČČàłàČČàČż, àȇàČČàłàČČàČż àČŻàČŸàČ” àČšàČżàČŻàȂàČ€àłàȰàČŁàȗàČłàł àȕàČŸàČŁàČżàČžàČŹàł‡àČ•àł àȎàȂàČŹàłàČŠàČšàłàČšàł àČšàł€àČ”àł àȆàČŻàłàČ•àł†àČźàČŸàČĄàČŹàČčàłàČŠàł."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"àČźàł†àČšàłàȚàČČàČŸàȗàČżàČ°àłàČ”àłàČŠàł"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"àČźàł†àČšàłàȚàČČàČŸàȗàČżàČ°àłàČ”àłàČŠàł, àČžàłàČ„àČŸàČš <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"àČźàł†àČšàłàȚàČżàČšàČŠàČČàłàČČàČŠàłàČŠàł"</string>
@@ -850,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> àȅàČšàłàČšàł àČ€àł†àČ°àł†àČŻàČżàȰàČż"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> àȅàČ”àȰ <xliff:g id="SONG_NAME">%1$s</xliff:g> àČčàČŸàČĄàČšàłàČšàł <xliff:g id="APP_LABEL">%3$s</xliff:g> àČšàČČàłàČČàČż àČȘàłàČČàł‡ àČźàČŸàČĄàČż"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> àČčàČŸàČĄàČšàłàČšàł <xliff:g id="APP_LABEL">%2$s</xliff:g> àČšàČČàłàČČàČż àČȘàłàČČàł‡ àČźàČŸàČĄàČż"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"àČšàČżàČźàȗàČŸàȗàČż"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"àȰàČŠàłàČŠàłàČ—àłŠàČłàČżàČžàČż"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> àČšàČČàłàČČàČż àČȘàłàČČàł‡ àČźàČŸàČĄàČČàł àȅàČŠàȰ àČčàČ€àłàČ€àČżàȰàČ•àłàČ•àł† àČžàȰàČżàČŻàČżàȰàČż"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"àȇàČČàłàČČàČż àČȘàłàČČàł‡ àČźàČŸàČĄàČČàł, <xliff:g id="DEVICENAME">%1$s</xliff:g> àČžàČźàł€àČȘàČ•àłàČ•àł† àČžàȰàČżàČŻàČżàȰàČż"</string>
@@ -857,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"àȏàČšàł‹ àČ€àČȘàłàČȘàČŸàȗàČżàČŠàł†. àČȘàłàČšàȃ àČȘàłàȰàČŻàČ€àłàČšàČżàČžàČż."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"àČČàł‹àČĄàł àȆàČ—àłàČ€àłàČ€àČżàČŠàł†"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"àČŸàłàČŻàČŸàČŹàłàČČàł†àČŸàł"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"àČšàČżàČźàłàČź àČźàČŸàČ§àłàČŻàČźàČ”àČšàłàČšàł àČŹàČżàČ€àłàČ€àȰàČżàČžàČČàČŸàČ—àłàČ€àłàČ€àČżàČŠàł†"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"<xliff:g id="APP_LABEL">%1$s</xliff:g> àȅàČšàłàČšàł àČŹàČżàČ€àłàČ€àȰàČżàČžàČČàČŸàČ—àłàČ€àłàČ€àČżàČŠàł†"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"àČšàČżàČ·àłàČ•àłàȰàČżàČŻ, àČ†àłàČŻàČȘàł àČȘàȰàČżàČ¶àł€àČČàČżàČžàČż"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"àȕàȂàČĄàłàČŹàȂàČŠàČżàČČàłàČČ"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"àČšàČżàČŻàȂàČ€àłàȰàČŁ àČČàČ­àłàČŻàČ”àČżàČČàłàČČ"</string>
@@ -866,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"àČŠàł‹àČ·, àČźàČ€àłàČ€àł† àČȘàłàȰàČŻàČ€àłàČšàČżàČžàČż"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"àČšàČżàČŻàȂàČ€àłàȰàČŁàȗàČłàČšàłàČšàł àČžàł‡àȰàČżàČžàČż"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"àČšàČżàČŻàȂàČ€àłàȰàČŁàȗàČłàČšàłàČšàł àȎàČĄàČżàČŸàł àČźàČŸàČĄàČż"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"àČ†àłàČŻàČȘàł àȅàČšàłàČšàł àČžàł‡àȰàČżàČžàČż"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"àȔàČŸàł‌àČȘàłàČŸàł‌àȗàČłàČšàłàČšàł àČžàł‡àȰàČżàČžàČż"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"àČ—àłàȂàČȘàł"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 àČžàČŸàȧàČšàČ”àČšàłàČšàł àȆàČŻàłàČ•àł† àČźàČŸàČĄàČČàČŸàȗàČżàČŠàł†"</string>
@@ -881,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"àČžàłàČȘàł€àȕàČ°àł‌àȗàČłàł àČźàČ€àłàČ€àł àČĄàČżàČžàł‌àČȘàłàČČàł‡àȗàČłàł"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"àČžàł‚àȚàČżàČžàČżàČŠ àČžàČŸàȧàČšàȗàČłàł"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"àČȘàłàČ°àł€àČźàČżàČŻàȂ àȖàČŸàČ€àł†àČŻ àȅàȗàČ€àłàČŻàČ”àČżàČŠàł†"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"àČȘàłàȰàČžàČŸàȰàČ”àł àČčàł‡àČ—àł† àȕàČŸàČ°àłàČŻàČšàČżàČ°àłàČ”àČčàČżàČžàłàČ€àłàČ€àČŠàł†"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"àČȘàłàȰàČžàČŸàȰ"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"àČčàłŠàȂàČŠàČŸàČŁàČżàČ•àł†àČŻàČŸàČ—àłàČ” àČŹàłàČČàł‚àČŸàł‚àČ€àł àČžàČŸàȧàČšàȗàČłàČšàłàČšàł àČčàłŠàȂàČŠàČżàČ°àłàČ” àČžàČźàł€àČȘàČŠàČČàłàČČàČżàČ°àłàČ” àȜàČšàČ°àł àČšàł€àČ”àł àČȘàłàȰàČžàČŸàȰ àČźàČŸàČĄàłàČ€àłàČ€àČżàČ°àłàČ” àČźàČŸàČ§àłàČŻàČźàČ”àČšàłàČšàł àȆàČČàČżàČžàČŹàČčàłàČŠàł"</string>
@@ -892,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"àČȘàłàȰàČžàČŸàȰ àČźàČŸàČĄàČČàł àČžàČŸàČ§àłàČŻàČ”àČżàČČàłàČČ"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"àȉàČłàČżàČžàČČàł àČžàČŸàČ§àłàČŻàČ”àČżàČČàłàČČ. àČȘàłàČšàȃ àČȘàłàȰàČŻàČ€àłàČšàČżàČžàČż."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"àȉàČłàČżàČžàČČàł àČžàČŸàČ§àłàČŻàČ”àČżàČČàłàČČ."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"àȕàČšàČżàČ·àłàČ  4 àȅàČ•àłàČ·àȰàȗàČłàČšàłàČšàł àČŹàČłàČžàČż"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"16 àČ•àłàȕàČżàȂàČ€ àȕàČĄàČżàČźàł† àȅàČ•àłàČ·àȰàȗàČłàČšàłàČšàł àČŹàČłàČžàČż"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"àČŹàČżàČČàłàČĄàł àČžàȂàČ–àłàČŻàł†"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"àČŹàČżàČČàłàČĄàł àČžàȂàČ–àłàČŻàł†àČŻàČšàłàČšàł àČ•àłàČČàČżàČȘàł‌àČŹàł‹àČ°àłàČĄàł‌àČšàČČàłàČČàČż àČšàȕàČČàČżàČžàČČàČŸàȗàČżàČŠàł†."</string>
     <string name="basic_status" msgid="2315371112182658176">"àČžàȂàČ­àČŸàČ·àČŁàł†àČŻàČšàłàČšàł àČ€àł†àČ°àł†àČŻàČżàȰàČż"</string>
@@ -1011,11 +1030,16 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• àȕàČšàČżàČ·àłàČ  àȒàȂàČŠàł àČžàČŸàȧàČš àČČàČ­àłàČŻàČ”àČżàČŠàł†"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"àČžàłàČȘàČ°àłàȶàČżàČžàČż àČčàł‹àČČàłàČĄàł àČźàČŸàČĄàČż àȶàČŸàČ°àłàČŸàł‌àȕàČŸàł"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"àȰàČŠàłàČŠàłàČ—àłŠàČłàČżàČžàČż"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"àȈàȗ àČ«àłàČČàČżàČȘàł àČźàČŸàČĄàČż"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"àȉàČ€àłàČ€àČź àČžàł†àČČàłàČ«àł€àȗàČŸàȗàČż àČ«àł‹àČšàł àȅàČšàłàČšàł àȅàČšàł‌àČ«àł‹àČČàłàČĄàł àČźàČŸàČĄàČż"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"àȉàČ€àłàČ€àČź àČžàł†àČČàłàČ«àł€àȗàČŸàȗàČż àČźàłàȂàČ­àČŸàȗàČŠ àČ•àłàČŻàČŸàČźàȰàČŸàČ—àł† àČ«àłàČČàČżàČȘàł àČźàČŸàČĄàČŹàł‡àČ•àł†?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"àČčàł†àČšàłàȚàČżàČš àČ°àł†àČžàČČàłàČŻàł‚àČ·àČšàł àČčàłŠàȂàČŠàČżàČ°àłàČ” àČ”àČżàȶàČŸàČČàČ”àČŸàČŠ àČ«àł‹àČŸàł‹àȗàČŸàȗàČż àČčàČżàȂàČ­àČŸàȗàČŠ àČ•àłàČŻàČŸàČźàȰàČŸàČ”àČšàłàČšàł àČŹàČłàČžàČż."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ àȈ àČžàłàČ•àłàČ°àł€àČšàł àȆàČ«àł àȆàČ—àłàČ€àłàČ€àČŠàł†"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"àČ«àł‹àČČàłàČĄàł àČźàČŸàČĄàČŹàČčàłàČŠàČŸàČŠ àČžàČŸàȧàČšàČ”àČšàłàČšàł àȅàČšàł‌àČ«àł‹àČČàłàČĄàł àČźàČŸàČĄàČČàČŸàČ—àłàČ€àłàČ€àČżàČŠàł†"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"àČ«àł‹àČČàłàČĄàł àČźàČŸàČĄàČŹàČčàłàČŠàČŸàČŠ àČžàČŸàȧàČšàČ”àČšàłàČšàł àČžàłàČ€àłàČ€àČČàł‚ àČ€àČżàČ°àłàȗàČżàČžàČČàČŸàČ—àłàČ€àłàČ€àČżàČŠàł†"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> àČŹàłàČŻàČŸàȟàȰàČż àȉàČłàČżàČŠàČżàČŠàł†"</string>
@@ -1026,4 +1050,5 @@
     <string name="call_from_work_profile_text" msgid="3458704745640229638">"àČšàČżàČźàłàČź àČ•àł†àČČàČžàČŠ àČšàł€àČ€àČżàČŻàł àȉàČŠàłàČŻàł‹àȗ àČȘàłàČ°àłŠàČ«àłˆàČČàł‌àČšàČżàȂàČŠ àČźàČŸàČ€àłàȰ àČ«àł‹àČšàł àȕàČ°àł†àȗàČłàČšàłàČšàł àČźàČŸàČĄàČČàł àČšàČżàČźàČ—àł† àȅàČšàłàČźàČ€àČżàČžàłàČ€àłàČ€àČŠàł†"</string>
     <string name="call_from_work_profile_action" msgid="2937701298133010724">"àȉàČŠàłàČŻàł‹àȗ àČȘàłàČ°àłŠàČ«àłˆàČČàł‌àČ—àł† àČŹàČŠàČČàČżàČžàČż"</string>
     <string name="call_from_work_profile_close" msgid="7927067108901068098">"àČźàłàČšàłàȚàČżàȰàČż"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"àČČàČŸàČ•àł àČžàłàČ•àłàČ°àł€àČšàł àČžàł†àČŸàłàȟàČżàȂàČ—àł‌àȗàČłàł"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-kn/tiles_states_strings.xml b/packages/SystemUI/res/values-kn/tiles_states_strings.xml
index 864a607..8a2ad2a 100644
--- a/packages/SystemUI/res/values-kn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-kn/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"àȆàČ«àł àČźàČŸàČĄàČż"</item>
     <item msgid="5966994759929723339">"àȆàČšàł àČźàČŸàČĄàČż"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 0815dbf..12cef55 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"직임 í”„ëĄœí•„ì˜ ìŠ€íŹëŠ°ìƒ·ì€ <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>
     <string name="screenrecord_name" msgid="2596401223859996572">"화멎 ë…č화"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"화멎 ë…č화 ìȘ늏 쀑"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"화멎 ë…č화 섞션에 ꎀ한 지속적읞 알늌"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"밝Ʞ"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"색상 반전"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"색상 ëłŽì •"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"ì‚Źìš©ìž êŽ€ëŠŹ"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"ì™„ëŁŒ"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"ë‹«êž°"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"자동"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"ì†ŒëŠŹ 또는 진동 없음"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"ì†ŒëŠŹë‚˜ 진동읎 ìšžëŠŹì§€ 않윌며 대화 ì„č션 하닚에 표시됚"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"êž°êž° 섀정에 따띌 ëČšì†ŒëŠŹë‚˜ 진동읎 욞늎 수 있음"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"êž°êž° 섀정에 따띌 ëČšì†ŒëŠŹë‚˜ 진동읎 욞늎 수 있슔니닀. êž°ëłžì ìœŒëĄœ <xliff:g id="APP_NAME">%1$s</xliff:g>의 대화는 ëŒ€í™”ì°œìœŒëĄœ 표시됩니닀."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"시슀템에서 알늌 시 ì†ŒëŠŹ 또는 진동을 ì‚Źìš©í• ì§€ êČ°ì •í•˜ë„ëĄ 허용합니닀."</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;상태:&lt;/b&gt; êž°ëłžìœŒëĄœ 높임"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;상태:&lt;/b&gt; ëŹŽìŒìœŒëĄœ ë‚źì¶€"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"화멎 ë…č화"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"제ëȘ© 없음"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"대Ʞ"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"확대 ì°œ"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"확대 ì°œ ì»šíŠžëĄ€"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"확대"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"ì»šíŠžëĄ€ì„ 추가할 앱을 선택하섞요"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{섀정읎 #개 추가되었슔니닀.}other{섀정읎 #개 추가되었슔니닀.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"삭제됚"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g>을(넌) 추가할êčŒìš”?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"<xliff:g id="APPNAME">%s</xliff:g> 앱을 추가하멎 읎 팚널에 ì»šíŠžëĄ€êłŒ 윘텐잠가 추가됩니닀. 음부 앱에서는 ì—Źêž° 표시되는 ì»šíŠžëĄ€ì„ 선택할 수 있슔니닀."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"슐êČšì°Ÿêž°ì— 추가됚"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"슐êČšì°Ÿêž°ì— 추가됚, 위ìč˜ <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"슐êČšì°Ÿêž°ì—ì„œ 삭제됚"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> ì—Žêž°"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g>에서 <xliff:g id="ARTIST_NAME">%2$s</xliff:g>의 <xliff:g id="SONG_NAME">%1$s</xliff:g> ìžŹìƒ"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g>에서 <xliff:g id="SONG_NAME">%1$s</xliff:g> ìžŹìƒ"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"추ìȜ"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"싀행췚소"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g>에서 ìžŹìƒí•˜ë €ë©Ž ꞰꞰ넌 더 가êčŒìŽëĄœ ì˜źêž°ì„žìš”."</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"ì—Źêž°ì—ì„œ ìžŹìƒí•˜ë €ë©Ž <xliff:g id="DEVICENAME">%1$s</xliff:g>에 더 가êčŒìŽ 읎동하섞요."</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"ëŹžì œê°€ 발생했슔니닀. 닀시 시도핎 ìŁŒì„žìš”."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"로드 쀑"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"태뾔멿"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"ëŻžë””ì–Ž ì „ì†Ą"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"<xliff:g id="APP_LABEL">%1$s</xliff:g> ì „ì†Ą 쀑"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ëč„활성. 앱을 확읞하섞요."</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"찟을 수 없음"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"ì»šíŠžëĄ€ì„ ì‚Źìš©í•  수 없음"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"였넘. 닀시 시도하섞요."</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"ì»šíŠžëĄ€ 추가"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"ì»šíŠžëĄ€ 수정"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"앱 추가"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"출렄 추가"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"ê·žëŁč"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"êž°êž° 1대 선택됚"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"슀플컀 및 디슀플레읎"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"추ìȜ êž°êž°"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"í”„ëŠŹëŻžì—„ êł„ì • 필요"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"뾌로드ìșìŠ€íŒ… 작동 ì›ëŠŹ"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"뾌로드ìșìŠ€íŠž"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"혾환되는 ëž”ëŁšíˆŹìŠ€ ꞰꞰ넌 가진 ê·ŒìČ˜ì˜ ì‚Źìš©ìžê°€ 낎가 뾌로드ìșìŠ€íŠž 쀑읞 ëŻžë””ì–Žë„Œ 수신 대Ʞ할 수 있슔니닀."</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"ë°©ì†Ąí•  수 없음"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"저임할 수 없슔니닀. 닀시 시도핎 ìŁŒì„žìš”."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"저임할 수 없슔니닀."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"ëčŒë“œ ëȈ혞"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"ëčŒë“œ ëČˆí˜žê°€ íŽëŠœëłŽë“œì— ëł”ì‚Źë˜ì—ˆìŠ”ë‹ˆë‹€."</string>
     <string name="basic_status" msgid="2315371112182658176">"대화 ì—Žêž°"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• 1대 읎상의 ꞰꞰ넌 ì‚Źìš©í•  수 있슔니닀."</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"ë°”ëĄœê°€êž°ë„Œ êžžêȌ 터ìč˜í•˜ì„žìš”."</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"췚소"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"지ꞈ 뒀집Ʞ"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"휎대전화넌 엎얎서 더 나은 셀ìčŽë„Œ ì°ì–ŽëłŽì„žìš”"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"전멎 디슀플레읎가 ëłŽìŽë„ëĄ 뒀집얎서 더 나은 셀ìčŽë„Œ ì°ì–ŽëłŽì„žìš”"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"후멎 ìčŽë©”띌넌 톔핎 넓은 ê°ë„ëĄœ 핎상도가 높은 ì‚Źì§„ì„ ì°ì–ŽëłŽì„žìš”."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ 읎 화멎읎 êșŒì§‘니닀."</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"폮더뾔 ꞰꞰ넌 펌ìč˜ëŠ” ëȘšìŠ”"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"폮더뾔 ꞰꞰ넌 뒀집는 ëȘšìŠ”"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"배터멬 <xliff:g id="PERCENTAGE">%s</xliff:g> 낚음"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ìŠ€íƒ€ìŒëŸŹìŠ€ë„Œ 충전Ʞ에 연êČ°í•˜ì„žìš”"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"ìŠ€íƒ€ìŒëŸŹìŠ€ 배터멬 ë¶€ìĄ±"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"ëč„디였 ìčŽë©”띌"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"읎 í”„ëĄœí•„ì—ì„œ 전화넌 걞 수 없음"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"직임 정책읎 직임 í”„ëĄœí•„ì—ì„œë§Œ 전화넌 ê±žë„ëĄ 허용합니닀."</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"직임 프로필로 전환"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"ë‹«êž°"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"잠ꞈ 화멎 섀정"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ko/tiles_states_strings.xml b/packages/SystemUI/res/values-ko/tiles_states_strings.xml
index c52c17c..c3d9d44 100644
--- a/packages/SystemUI/res/values-ko/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ko/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"êșŒì§"</item>
     <item msgid="5966994759929723339">"쌜짐"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 4d026f5..834d1bf 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"Đ–ŃƒĐŒŃƒŃˆ сĐșŃ€ĐžĐœŃˆĐŸŃ‚Ń‚ĐŸŃ€Ńƒ <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">"Files"</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>
     <string name="screenrecord_name" msgid="2596401223859996572">"эĐșŃ€Đ°ĐœĐŽĐ°Đœ ĐČĐžĐŽĐ”ĐŸ жазЎырып алуу"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Đ­ĐșŃ€Đ°ĐœĐŽĐ°Đœ жазЎырылып Đ°Đ»Ń‹ĐœĐłĐ°Đœ ĐČĐžĐŽĐ”ĐŸ ĐžŃˆŃ‚Đ”Ń‚ĐžĐ»ÒŻÒŻĐŽÓ©"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Đ­ĐșŃ€Đ°ĐœĐŽŃ‹ Đ¶Đ°Đ·ĐŽŃ‹Ń€ŃƒŃƒ ŃĐ”Đ°ĐœŃŃ‹ Đ±ĐŸŃŽĐœŃ‡Đ° учурЮагы Đ±ĐžĐ»ĐŽĐžŃ€ĐŒĐ”"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ЖарыĐșтыгы"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ĐąÒŻŃŃ‚Ó©Ń€ĐŽÒŻ ĐžĐœĐČĐ”Ń€ŃĐžŃĐ»ĐŸĐŸ"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ĐąÒŻŃŃ‚Ó©Ń€ĐŽÒŻ Ń‚ŃƒŃƒŃ€Đ°Đ»ĐŸĐŸ"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"ĐšĐŸĐ»ĐŽĐŸĐœŃƒŃƒŃ‡ŃƒĐ»Đ°Ń€ĐŽŃ‹ тДсĐșÓ©Ó©"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Đ‘ÒŻŃ‚Ń‚ÒŻ"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Đ–Đ°Đ±ŃƒŃƒ"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"АĐČŃ‚ĐŸĐŒĐ°Ń‚Ń‚Ń‹Đș"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"ÒźĐœÒŻ чыĐșпаĐčт Đ¶Đ°ĐœĐ° ЎОрОлЎДбДĐčт"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"ÒźĐœÒŻ чыĐșпаĐčт жД ЎОрОлЎДбДĐčт Đ¶Đ°ĐœĐ° ŃÒŻĐčĐ»Ó©ŃˆÒŻÒŻĐ»Ó©Ń€ Ń‚ĐžĐ·ĐŒĐ”ŃĐžĐœĐžĐœ ылЎыĐč Đ¶Đ°ĐłŃ‹ĐœĐŽĐ° ĐșÓ©Ń€ÒŻĐœÓ©Ń‚"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"ĐąÒŻĐ·ĐŒÓ©ĐșŃ‚ÒŻĐœ ĐżĐ°Ń€Đ°ĐŒĐ”Ń‚Ń€Đ»Đ”Ń€ĐžĐœĐ” Đ¶Đ°Ń€Đ°ŃˆĐ° ŃˆŃ‹ÒŁĐłŃ‹Ń€Đ°Đż жД ĐŽĐžŃ€ĐžĐ»ĐŽĐ”ŃˆĐž ĐŒÒŻĐŒĐșÒŻĐœ"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"ĐąÒŻĐ·ĐŒÓ©ĐșŃ‚ÒŻĐœ ĐżĐ°Ń€Đ°ĐŒĐ”Ń‚Ń€Đ»Đ”Ń€ĐžĐœĐ” Đ¶Đ°Ń€Đ°ŃˆĐ° ŃˆŃ‹ÒŁĐłŃ‹Ń€Đ°Đż жД ĐŽĐžŃ€ĐžĐ»ĐŽĐ”ŃˆĐž ĐŒÒŻĐŒĐșÒŻĐœ. <xliff:g id="APP_NAME">%1$s</xliff:g> ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸŃŃƒĐœĐŽĐ°ĐłŃ‹ ŃÒŻĐčĐ»Ó©ŃˆÒŻÒŻĐ»Ó©Ń€ ĐŽĐ”ĐŒĐ”ĐčĐșĐž шартта ĐșалĐșып чыĐșĐŒĐ° Đ±ĐžĐ»ĐŽĐžŃ€ĐŒĐ”Đ»Đ”Ń€ Đ±ĐŸĐ»ŃƒĐż ĐșÓ©Ń€ÒŻĐœÓ©Ń‚."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Đ‘ĐžĐ»ĐŽĐžŃ€ĐŒĐ”ĐœĐžĐœ ÒŻĐœÒŻĐœ Ń‡Ń‹ĐłĐ°Ń€Ń‚ŃƒŃƒĐœŃƒ жД Đ±Đ°ŃŃƒŃƒĐœŃƒ Ń‚ŃƒŃ‚ŃƒĐŒĐłĐ° Ń‚Đ°ĐżŃˆŃ‹Ń€Ń‹ÒŁŃ‹Đ·"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Абалы:&lt;/b&gt; Đ”Đ”ĐŒĐ”ĐčĐșОгД Ó©Đ·ĐłÓ©Ń€ĐŽÒŻ"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Абалы:&lt;/b&gt; ÒźĐœŃÒŻĐ· абалга Ń‚Ó©ĐŒÓ©ĐœĐŽÓ©ĐŽÒŻ"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"эĐșŃ€Đ°ĐœĐŽŃ‹ Đ¶Đ°Đ·ĐŽŃ‹Ń€ŃƒŃƒ"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"ĐŃ‚Đ°Đ»Ń‹ŃˆŃ‹ Đ¶ĐŸĐș"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"ĐšÓ©ŃˆÒŻÒŻ Ń€Đ”Đ¶ĐžĐŒĐž"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Đ§ĐŸÒŁĐŸĐčтуу тДрДзДсО"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Đ§ĐŸÒŁĐŸĐčтуу Ń‚Đ”Ń€Đ”Đ·Đ”ŃĐžĐœ башĐșаруу Đșаражаттары"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ЖаĐșŃ‹ĐœĐŽĐ°Ń‚ŃƒŃƒ"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"БашĐșаруу ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Ń‚Đ”Ń€Đž ĐșĐŸŃˆŃƒĐ»Đ° Ń‚ŃƒŃ€ĐłĐ°Đœ ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸĐœŃƒ Ń‚Đ°ĐœĐŽĐŸĐŸ"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# ĐșÓ©Đ·Ó©ĐŒÓ©Đ» ĐșĐŸŃˆŃƒĐ»ĐŽŃƒ.}other{# ĐșÓ©Đ·Ó©ĐŒÓ©Đ» ĐșĐŸŃˆŃƒĐ»ĐŽŃƒ.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"ÓšŃ‡ÒŻŃ€ÒŻĐ»ĐŽÒŻ"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g> ĐșĐŸŃˆŃƒĐ»ŃŃƒĐœĐ±Ńƒ?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"<xliff:g id="APPNAME">%s</xliff:g> ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸŃŃƒĐœ ĐșĐŸŃˆŃĐŸÒŁŃƒĐ·, ал бул ĐżĐ°ĐœĐ”Đ»ĐłĐ” башĐșаруу ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Ń‚Đ”Ń€ĐžĐœ Đ¶Đ°ĐœĐ° ĐșĐŸĐœŃ‚Đ”ĐœŃ‚Ń‚Đž ĐșĐŸŃˆĐŸ алат. АĐčŃ€Ń‹ĐŒ ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸĐ»ĐŸŃ€ĐŽĐŸ бул жДрЎД ĐșÓ©Ń€ÒŻĐœÒŻÒŻŃ‡ÒŻ башĐșаруу ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Ń‚Đ”Ń€ĐžĐœ Ń‚Đ°ĐœĐŽĐ°Đč аласыз."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"ĐĄÒŻĐčÒŻĐșŃ‚ÒŻÒŻĐ»Ó©Ń€ĐłÓ© ĐșĐŸŃˆŃƒĐ»ĐŽŃƒ"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"ĐĄÒŻĐčÒŻĐșŃ‚ÒŻÒŻĐ»Ó©Ń€ĐłÓ© <xliff:g id="NUMBER">%d</xliff:g>-ĐżĐŸĐ·ĐžŃ†ĐžŃĐłĐ° ĐșĐŸŃˆŃƒĐ»ĐŽŃƒ"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"ĐĄÒŻĐčÒŻĐșŃ‚ÒŻÒŻĐ»Ó©Ń€ĐŽÓ©Đœ чыгарылЎы"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸŃŃƒĐœ ачуу"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="SONG_NAME">%1$s</xliff:g> Ń‹Ń€Ń‹Đœ (атĐșаруучу: <xliff:g id="ARTIST_NAME">%2$s</xliff:g>) <xliff:g id="APP_LABEL">%3$s</xliff:g> ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸŃŃƒĐœĐ°Đœ ĐŸĐčĐœĐŸŃ‚ŃƒŃƒ"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> Ń‹Ń€Ń‹Đœ <xliff:g id="APP_LABEL">%2$s</xliff:g> ĐșĐŸĐ»ĐŽĐŸĐœĐŒĐŸŃŃƒĐœĐ°Đœ ĐŸĐčĐœĐŸŃ‚ŃƒŃƒ"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"ХОзгД"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"КаĐčтаруу"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> Ń‚ÒŻĐ·ĐŒÓ©ĐłÒŻĐœĐŽÓ© ĐŸĐčĐœĐŸŃ‚ŃƒŃƒ ÒŻŃ‡ÒŻĐœ жаĐșŃ‹ĐœĐŽĐ°Ń‚Ń‹ÒŁŃ‹Đ·"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Ушул жДрЎД ĐŸĐčĐœĐŸŃ‚ŃƒŃƒ ÒŻŃ‡ÒŻĐœ <xliff:g id="DEVICENAME">%1$s</xliff:g> Ń‚ÒŻĐ·ĐŒÓ©ĐłÒŻĐœÓ© жаĐșŃ‹ĐœĐŽĐ°ÒŁŃ‹Đ·"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Бор Đ¶Đ”Ń€ĐŽĐ”Đœ Đșата ĐșДттО. КаĐčра араĐșДт ĐșŃ‹Đ»Ń‹ÒŁŃ‹Đ·."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Đ–ÒŻĐșŃ‚Ó©Đ»ÒŻÒŻĐŽÓ©"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ĐżĐ»Đ°ĐœŃˆĐ”Ń‚"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"ĐœĐ”ĐŽĐžĐ° тышĐșы эĐșŃ€Đ°ĐœĐłĐ° Ń‡Ń‹ĐłĐ°Ń€Ń‹Đ»ŃƒŃƒĐŽĐ°"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"<xliff:g id="APP_LABEL">%1$s</xliff:g> Ń‚ÒŻĐ·ĐŒÓ©ĐłÒŻĐœÓ© Ń‡Ń‹ĐłĐ°Ń€Ń‹Đ»ŃƒŃƒĐŽĐ°"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ЖОгДрсОз. ĐšĐŸĐ»ĐŽĐŸĐœĐŒĐŸĐœŃƒ тДĐșŃˆĐ”Ń€ĐžÒŁĐžĐ·"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"ĐąĐ°Đ±Ń‹Đ»ĐłĐ°Đœ Đ¶ĐŸĐș"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"БашĐșара албаĐčсыз"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Ката, ĐșаĐčталап ĐșÓ©Ń€ÒŻÒŁÒŻĐ·"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"БашĐșаруу ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Ń‚Đ”Ń€ĐžĐœ ĐșĐŸŃˆŃƒŃƒ"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"БашĐșаруу ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Ń‚Đ”Ń€ĐžĐœ Ń‚ÒŻĐ·Ó©Ń‚ÒŻÒŻ"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"ĐšĐŸĐ»ĐŽĐŸĐœĐŒĐŸ ĐșĐŸŃˆŃƒŃƒ"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"ĐœĐ”ĐŽĐžĐ° Ń‚ÒŻĐ·ĐŒÓ©ĐșŃ‚Ó©Ń€ĐŽÒŻ ĐșĐŸŃˆŃƒŃƒ"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"ĐąĐŸĐż"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 Ń‚ÒŻĐ·ĐŒÓ©Đș Ń‚Đ°ĐœĐŽĐ°Đ»ĐŽŃ‹"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Đ”ĐžĐœĐ°ĐŒĐžĐșтДр Đ¶Đ°ĐœĐ° ЎОсплДĐčлДр"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"ĐĄŃƒĐœŃƒŃˆŃ‚Đ°Đ»ĐłĐ°Đœ Ń‚ÒŻĐ·ĐŒÓ©Đșтөр"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"ĐŸŃ€Đ”ĐŒĐžŃƒĐŒ аĐșĐșĐ°ŃƒĐœŃ‚ талап ĐșŃ‹Đ»Ń‹ĐœĐ°Ń‚"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ĐšĐ°Đ±Đ°Ń€Đ»ĐŸĐŸ ĐșĐ°ĐœŃ‚ĐžĐż ĐžŃˆŃ‚Đ”Đčт"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"ĐšĐ°Đ±Đ°Ń€Đ»ĐŸĐŸ"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"КаĐčĐșДш Bluetooth Ń‚ÒŻĐ·ĐŒÓ©ĐșŃ‚Ó©Ń€ÒŻ Đ±ĐŸĐ»ĐłĐŸĐœ жаĐșŃ‹Đœ жДрЎДгО ĐșĐžŃˆĐžĐ»Đ”Ń€ Đșабарлап жатĐșĐ°Đœ ĐŒĐ”ĐŽĐžĐ°ÒŁŃ‹Đ·ĐŽŃ‹ уга Đ°Đ»Ń‹ŃˆĐ°Ń‚"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"ĐšĐ°Đ±Đ°Ń€Đ»ĐŸĐŸĐłĐŸ Đ±ĐŸĐ»Đ±ĐŸĐčт"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"ХаĐșŃ‚Đ°Đ»ĐłĐ°Đœ Đ¶ĐŸĐș. КаĐčталап ĐșÓ©Ń€ÒŻÒŁÒŻĐ·."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"ХаĐșŃ‚Đ°Đ»ĐłĐ°Đœ Đ¶ĐŸĐș."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"ĐšŃƒŃ€Đ°ĐŒĐ° ĐœĐŸĐŒĐ”Ń€Đž"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"ĐšŃƒŃ€Đ°ĐŒĐ° ĐœĐŸĐŒĐ”Ń€Đž Đ°Đ»ĐŒĐ°ŃˆŃƒŃƒ Đ±ŃƒŃ„Đ”Ń€ĐžĐœĐ” ĐșÓ©Ń‡ÒŻŃ€ÒŻĐ»ĐŽÒŻ."</string>
     <string name="basic_status" msgid="2315371112182658176">"АчыĐș ŃÒŻĐčĐ»Ó©ŃˆÒŻÒŻ"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• ĐšĐ”ĐŒĐžĐœĐŽĐ” бОр Ń‚ÒŻĐ·ĐŒÓ©Đș жДтĐșОлОĐșŃ‚ÒŻÒŻ"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Đ«ĐșŃ‡Đ°ĐŒ басĐșычты басып Ń‚ŃƒŃ€ŃƒÒŁŃƒĐ·"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"ĐąĐŸĐșŃ‚ĐŸŃ‚ŃƒŃƒ"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Азыр ĐșĐŸŃ‚ĐŸŃ€ŃƒŃƒ"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"ЖаĐșшы сДлфО тартуу ÒŻŃ‡ÒŻĐœ ĐœĐ”ĐłĐžĐ·ĐłĐž ĐșĐ°ĐŒĐ”Ń€Đ°ĐłĐ° ĐșĐŸŃ‚ĐŸŃ€ŃƒÒŁŃƒĐ·"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"ЖаĐșшы сДлфО тартуу ÒŻŃ‡ÒŻĐœ ĐŒĐ°ÒŁĐŽĐ°ĐčĐșы эĐșŃ€Đ°ĐœĐłĐ° ĐșĐŸŃ‚ĐŸŃ€ĐŸŃŃƒĐ·Đ±Ńƒ?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"ĐšĐ”ÒŁ Đ¶Đ°ĐœĐ° Đ¶ĐŸĐłĐŸŃ€Đșу ĐŽĐ°Đ°ĐœĐ°Đ»Ń‹Đșтагы ŃÒŻŃ€Ó©Ń‚Ń‚ÒŻ тартуу ÒŻŃ‡ÒŻĐœ ĐœĐ”ĐłĐžĐ·ĐłĐž ĐșĐ°ĐŒĐ”Ń€Đ°ĐœŃ‹ ĐșĐŸĐ»ĐŽĐŸĐœŃƒÒŁŃƒĐ·."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Đ‘ŃƒĐ» эĐșŃ€Đ°Đœ өчөт"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Ачылып Ń‚ŃƒŃ€ĐłĐ°Đœ Đ±ÒŻĐșŃ‚Ó©Đ»ĐŒÓ© Ń‚ÒŻĐ·ĐŒÓ©Đș"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ĐžĐŸĐŽĐ°Ń€Ń‹Đ»Ń‹Đż жатĐșĐ°Đœ Đ±ÒŻĐșŃ‚Ó©Đ»ĐŒÓ© Ń‚ÒŻĐ·ĐŒÓ©Đș"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Đ‘Đ°Ń‚Đ°Ń€Đ”ŃĐœŃ‹Đœ ĐșŃƒĐ±Đ°Ń‚Ń‹: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ĐĄŃ‚ĐžĐ»ŃƒŃŃ‚Ńƒ ĐșŃƒĐ±Đ°Ń‚Ń‚Đ°ÒŁŃ‹Đ·"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"ĐĄŃ‚ĐžĐ»ŃƒŃŃ‚ŃƒĐœ Đ±Đ°Ń‚Đ°Ń€Đ”ŃŃŃ‹ ĐŸŃ‚ŃƒŃ€Đ°ĐčŃ‹Đœ ЎДп ĐșалЎы"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Đ’ĐžĐŽĐ”ĐŸ ĐșĐ°ĐŒĐ”Ń€Đ°"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Đ‘ŃƒĐ» ĐżŃ€ĐŸŃ„ĐžĐ»ĐŽĐ”Đœ чала албаĐčсыз"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Đ–ŃƒĐŒŃƒŃˆ ŃĐ°ŃŃĐ°Ń‚Ń‹ÒŁŃ‹Đ·ĐłĐ° ылаĐčыĐș, Đ¶ŃƒĐŒŃƒŃˆ ĐżŃ€ĐŸŃ„ĐžĐ»ĐžĐœĐ”Đœ ĐłĐ°ĐœĐ° Ń‡Đ°Đ»ŃƒŃƒĐ»Đ°Ń€ĐŽŃ‹ атĐșара аласыз"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Đ–ŃƒĐŒŃƒŃˆ ĐżŃ€ĐŸŃ„ĐžĐ»ĐžĐœĐ” ĐșĐŸŃ‚ĐŸŃ€ŃƒĐ»ŃƒŃƒ"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Đ–Đ°Đ±ŃƒŃƒ"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"ĐšŃƒĐ»ĐżŃƒĐ»Đ°ĐœĐłĐ°Đœ эĐșŃ€Đ°Đœ ĐżĐ°Ń€Đ°ĐŒĐ”Ń‚Ń€Đ»Đ”Ń€Đž"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ky/tiles_states_strings.xml b/packages/SystemUI/res/values-ky/tiles_states_strings.xml
index f872926..f4b8447 100644
--- a/packages/SystemUI/res/values-ky/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ky/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"ÓšŃ‡ÒŻĐș"</item>
     <item msgid="5966994759929723339">"ĐšÒŻĐčÒŻĐș"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index fff2544..ac81dcc 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -64,4 +64,6 @@
 
     <dimen name="qs_panel_padding_top">@dimen/qqs_layout_margin_top</dimen>
     <dimen name="qs_panel_padding_top_combined_headers">@dimen/qs_panel_padding_top</dimen>
+
+    <dimen name="controls_padding_horizontal">16dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index e147c22..0b4c2dc 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"àșźàșčàșšà»œà»‰àșČàșˆà»àș§àșœàșàș–àș·àșàșšàș±àș™àș—àș¶àșàșąàșč່ໃàș™à»àș­àș±àșš <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>
     <string name="screenrecord_name" msgid="2596401223859996572">"ໂàș›àșŁà»àșàșŁàșĄàșšàș±àș™àș—àș¶àșà»œà»‰àșČàșˆà»"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"àșàșłàș„àș±àș‡àș›àș°àșĄàș§àș™àșœàș»àș™àșàșČàș™àșšàș±àș™àș—àș¶àșà»œà»‰àșČàșˆà»"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"àșàșČàș™à»àșˆà»‰àș‡à»€àș•àș·àș­àș™àșȘàșłàș„àș±àșšà»€àșŠàș”àșŠàș±àș™àșàșČàș™àșšàș±àș™àș—àș¶àșà»œà»‰àșČàșˆà»à»ƒàș”ໜàș¶à»ˆàș‡"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"àș„àș§àșČàșĄà»àșˆà»‰àș‡"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"àșàșČàș™àș›àș”້àș™àșȘàș”"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"àșàșČàș™à»àșà»‰à»„àș‚àșȘàș”"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"àșˆàș±àș”àșàșČàș™àșœàșč້ໃàșŠà»‰"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"ແàș„້àș§à»†"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"àș›àșŽàș”"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"àș­àș±àș”àș•àș°à»‚àș™àșĄàș±àș”"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"àșšà»à»ˆàșĄàș”àșȘàșœàș‡ àș«àșŒàș· àșàșČàș™àșȘàș±à»ˆàș™à»€àș•àș·àș­àș™"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"àșšà»à»ˆàșĄàș”àșȘàșœàș‡ àș«àșŒàș· àșàșČàș™àșȘàș±à»ˆàș™à»€àș•àș·àș­àș™ ແàș„àș° àș›àșČàșàș»àș”àșąàșč່àș—àșČàș‡àș„àșžà»ˆàșĄàș‚àș­àș‡àșžàșČàșàșȘ່àș§àș™àșàșČàș™àșȘàș»àș™àș—àș°àș™àșČ"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"àș­àșČàș”àșȘàș»à»ˆàș‡àșȘàșœàș‡ àș«àșŒàș· àșȘàș±à»ˆàș™à»€àș•àș·àș­àș™à»‚àș”àșàș­à»‰àșČàș‡àș­àș”àș‡àșˆàșČàșàșàșČàș™àș•àș±à»‰àș‡àș„່àșČàș­àșžàș›àș°àșàș­àș™"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"àș­àșČàș”àșȘàș»à»ˆàș‡àșȘàșœàș‡ àș«àșŒàș· àșȘàș±à»ˆàș™à»€àș•àș·àș­àș™à»‚àș”àșàș­à»‰àșČàș‡àș­àș”àș‡àșˆàșČàșàșàșČàș™àș•àș±à»‰àș‡àș„່àșČàș­àșžàș›àș°àșàș­àș™. àșàșČàș™àșȘàș»àș™àș—àș°àș™àșČàșˆàșČàș <xliff:g id="APP_NAME">%1$s</xliff:g> àșˆàș°à»€àș›àș±àș™ bubble àș•àșČàșĄàș„່àșČເàș„àș”່àșĄàș•àș»à»‰àș™."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ໃàș«à»‰àș„àș°àșšàș»àșšàșàșłàș™àș»àș”àș§à»ˆàșČàșàșČàș™à»àșˆà»‰àș‡à»€àș•àș·àș­àș™àș™àșŽà»‰àș„àș§àș™àșĄàș”àșȘàșœàș‡ àș«àșŒàș· àșȘàș±à»ˆàș™à»€àș•àș·àș­àș™àș«àșŒàș·àșšà»à»ˆ"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;àșȘàș°àș–àșČàș™àș°:&lt;/b&gt; ເàș„àș·à»ˆàș­àș™àș„àș°àș”àș±àșšà»€àș›àș±àș™àș„່àșČເàș„àș”່àșĄàș•àș»à»‰àș™à»àș„້àș§"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;àșȘàș°àș–àșČàș™àș°:&lt;/b&gt; àș«àșŒàșžàș”àș„àș°àș”àș±àșšà»€àș›àș±àș™àș›àșŽàș”àșȘàșœàș‡à»àș„້àș§"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"àșàșČàș™àșšàș±àș™àș—àș¶àșà»œà»‰àșČàșˆà»"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"àșšà»à»ˆàșĄàș”àșŠàș·à»ˆ"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"àșȘàș°à»àș•àș™àșšàșČàș"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"ໜ້àșČàșˆà»àșàșČàș™àș‚àș°àș«àșàșČàș"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"àșàșČàș™àș„àș§àșšàș„àșžàșĄà»œà»‰àșČàșˆà»àșàșČàș™àș‚àș°àș«àșàșČàș"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"àșŠàșčàșĄà»€àș‚àș»à»‰àșČ"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"ເàș„àș·àș­àșà»àș­àș±àșšà»€àșžàș·à»ˆàș­à»€àșžàș”່àșĄàșàșČàș™àș„àș§àșšàș„àșžàșĄ"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{ເàșžàș”່àșĄ # àșàșČàș™àș„àș§àșšàș„àșžàșĄà»àș„້àș§.}other{ເàșžàș”່àșĄ # àșàșČàș™àș„àș§àșšàș„àșžàșĄà»àș„້àș§.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"àș„àș¶àșšàș­àș­àșà»àș„້àș§"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"ເàșžàș”່àșĄ <xliff:g id="APPNAME">%s</xliff:g> àșšà»?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"ເàșĄàș·à»ˆàș­àș—່àșČàș™à»€àșžàș”່àșĄ <xliff:g id="APPNAME">%s</xliff:g>, àșĄàș±àș™àșˆàș°àșȘàșČàșĄàșČàș”ເàșžàș”່àșĄàșàșČàș™àș„àș§àșšàș„àșžàșĄ ແàș„àș° ເàș™àș·à»‰àș­àș«àșČໃàșȘ່ແàșœàș‡àș™àș”້ໄàș”້. ໃàș™àșšàșČàș‡à»àș­àș±àșš, àș—່àșČàș™àșȘàșČàșĄàșČàș”ເàș„àș·àș­àșàș§à»ˆàșČàșˆàș°à»ƒàș«à»‰àșȘ່àș§àș™àș„àș§àșšàș„àșžàșĄà»ƒàș”àșȘàș°à»àș”àș‡àș‚àș¶à»‰àș™àșąàșč່àșšà»ˆàș­àș™àș™àș”້ໄàș”້."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"ເàșžàș”່àșĄàș„àșČàșàșàșČàș™àș—àș”່àșĄàș±àșà»àș„້àș§"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"ເàșžàș”່àșĄàș„àșČàșàșàșČàș™àș—àș”່àșĄàș±àșà»àș„້àș§, àș•àșłà»à»œà»ˆàș‡ <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"àșàș»àșà»€àș„àș”àșàș„àșČàșàșàșČàș™àș—àș”່àșĄàș±àșà»àș„້àș§"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"ເàș›àș”àș” <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"àș«àșŒàșŽà»‰àș™ <xliff:g id="SONG_NAME">%1$s</xliff:g> ໂàș”àș <xliff:g id="ARTIST_NAME">%2$s</xliff:g> àșˆàșČàș <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"àș«àșŒàșŽà»‰àș™ <xliff:g id="SONG_NAME">%1$s</xliff:g> àșˆàșČàș <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"àșȘàșłàș„àș±àșšàș—່àșČàș™"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"àșàș»àșà»€àș„àș”àș"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"àșà»‰àșČàșà»„àș›à»ƒàșà»‰àș‚àș¶à»‰àș™à»€àșžàș·à»ˆàș­àș«àșŒàșŽà»‰àș™àșąàșč່ <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"ເàșžàș·à»ˆàș­àș«àșŒàșŽà»‰àș™àșąàșč່àșšà»ˆàș­àș™àș™àș”້, ໃàș«à»‰à»€àș‚àș»à»‰àșČໃàșà»‰ <xliff:g id="DEVICENAME">%1$s</xliff:g> àș«àșŒàșČàșàș‚àș¶à»‰àș™"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"àșĄàș”àșšàșČàș‡àșąà»ˆàșČàș‡àșœàșŽàș”àșžàșČàș”ເàșàș”àș”àș‚àș¶à»‰àș™. àșàș°àș„àșžàș™àșČàș„àș­àș‡à»ƒà»à»ˆ."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"àșàșłàș„àș±àș‡à»‚àș«àșŒàș”"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ແàș—àș±àșšà»€àș„àș±àș”"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"àșàșČàș™â€‹àșàșłâ€‹àș™àș»àș”​àșšàș»àș”​àșšàșČàș”​àșȘàș·à»ˆâ€‹àș‚àș­àș‡â€‹àș—່àșČàș™"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"àșàșČàș™â€‹àșàșłâ€‹àș™àș»àș”​àșšàș»àș”​àșšàșČàș” <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"àșšà»à»ˆà»€àșźàș±àș”àș§àșœàș, àșàș°àș„àșžàș™àșČàșàș§àș”àșȘàș­àșšà»àș­àș±àșš"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"àșšà»à»ˆàșžàș»àșš"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"àșšà»à»ˆàșȘàșČàșĄàșČàș”ໃàșŠà»‰àșàșČàș™àș„àș§àșšàș„àșžàșĄà»„àș”້"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"​àșœàșŽàș”​àșžàșČàș”​, àșàș°àș„àșžàș™àșČàș„àș­àș‡à»ƒà»à»ˆ"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"ເàșžàș”່àșĄàșàșČàș™àș„àș§àșšàș„àșžàșĄ"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"ແàșà»‰à»„àș‚àșàșČàș™àș„àș§àșšàș„àșžàșĄ"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"ເàșžàș”່àșĄà»àș­àș±àșš"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"ເàșžàș”່àșĄà»€àș­àș»à»‰àșČàșžàșžàș”"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"àșàșžà»ˆàșĄ"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"ເàș„àș·àș­àș 1 àș­àșžàș›àș°àșàș­àș™à»àș„້àș§"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"àș„àșłà»‚àșžàș‡ ແàș„àș° àșˆà»àșȘàș°à»àș”àș‡àșœàș»àș™"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"àș­àșžàș›àș°àșàș­àș™àș—àș”່ແàș™àș°àș™àșł"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"àș•້àș­àș‡à»ƒàșŠà»‰àșšàș±àș™àșŠàș”àșžàșŁàș”àșĄàșœàșĄ"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"àșàșČàș™àș­àș­àșàș­àșČàșàșČàș”ເàșźàș±àș”àș§àșœàșà»àș™àș§à»ƒàș”"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"àș­àș­àșàș­àșČàșàșČàș”"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"àș„àș»àș™àș—àș”່àșąàșč່ໃàșà»‰àș—່àșČàș™àș—àș”່àșĄàș”àș­àșžàș›àș°àșàș­àș™ Bluetooth àș—àș”່ເàș‚àș»à»‰àșČàșàș±àș™à»„àș”້àșˆàș°àșȘàșČàșĄàșČàș”àșŸàș±àș‡àșĄàș”ເàș”àșàș—àș”່àș—່àșČàș™àșàșłàș„àș±àș‡àș­àș­àșàș­àșČàșàșČàș”àșąàșč່ໄàș”້"</string>
@@ -894,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"àșšà»à»ˆàșȘàșČàșĄàșČàș”àș­àș­àșàș­àșČàșàșČàș”ໄàș”້"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"àșšà»à»ˆàșȘàșČàșĄàșČàș”àșšàș±àș™àș—àș¶àșà»„àș”້. àșàș°àș„àșžàș™àșČàș„àș­àș‡à»ƒà»à»ˆ."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"àșšà»à»ˆàșȘàșČàșĄàșČàș”àșšàș±àș™àș—àș¶àșà»„àș”້."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"ໃàșŠà»‰àșąà»ˆàșČàș‡à»œà»‰àș­àș 4 àș•àș»àș§àș­àș±àșàșȘàș­àș™"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"ໃàșŠà»‰à»œà»‰àș­àșàșàș§à»ˆàșČ 16 àș•àș»àș§àș­àș±àșàșȘàș­àș™"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"ໝàșČàșà»€àș„àșàșȘ້àșČàș‡"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"àșȘàșłà»€àș™àș»àșČໝàșČàșà»€àș„àșàșȘ້àșČàș‡à»„àș›à»ƒàșȘ່àș„àș„àșŽàșšàșšàș­àș”ແàș„້àș§."</string>
     <string name="basic_status" msgid="2315371112182658176">"ເàș›àș”àș”àșàșČàș™àșȘàș»àș™àș—àș°àș™àșČ"</string>
@@ -1013,24 +1030,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• àșĄàș”àșąà»ˆàșČàș‡à»œà»‰àș­àș 1 àș­àșžàș›àș°àșàș­àș™àșžà»‰àș­àșĄà»ƒàș«à»‰àș™àșłà»ƒàșŠà»‰"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"ແàș•àș°àș—àșČàș‡àș„àș±àș”àș„້àșČàș‡à»„àș§à»‰"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"àșàș»àșà»€àș„àș”àș"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"àș›àș”້àș™àș”àșœàș§àș™àș”້"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"àșàșČàș‡à»‚àș—àș„àș°àșȘàș±àșšàș­àș­àșà»€àșžàș·à»ˆàș­àșàșČàș™àș–່àșČàșà»€àșŠàș§àșŸàș”àș—àș”່àș”àș”àș‚àș¶à»‰àș™"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"àș›àș”້àș™à»€àș›àș±àș™àșˆà»àșȘàș°à»àș”àș‡àșœàș»àș™àș”້àșČàș™à»œà»‰àșČເàșžàș·à»ˆàș­àșàșČàș™àș–່àșČàșà»€àșŠàș§àșŸàș”àș—àș”່àș”àș”àș‚àș¶à»‰àș™àșšà»?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"ໃàșŠà»‰àșà»‰àș­àș‡àș«àșŒàș±àș‡à»€àșžàș·à»ˆàș­àșàșČàș™àș–່àșČàșàșźàșčàșšàș—àș”່àșàș§à»‰àșČàș‡àș‚àș¶à»‰àș™àș”້àș§àșàș„àș§àșČàșĄàș„àș°àș­àșœàș”àșȘàșčàș‡àș‚àș¶à»‰àș™."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ໜ້àșČàșˆà»àș™àș”້àșˆàș°àș›àșŽàș”"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"àș­àșžàș›àș°àșàș­àș™àș—àș”່àșžàș±àșšà»„àș”້àșàșłàș„àș±àș‡àșàșČàș‡àș­àș­àș"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"àș­àșžàș›àș°àșàș­àș™àș—àș”່àșžàș±àșà»„àș”້àșàșłàș„àș±àș‡àș›àș”້àș™à»„àș›àșĄàșČ"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"ແàșšàș±àș”ເàș•àș”àșŁàș”ເàș«àșŒàș·àș­ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ເàșŠàș·à»ˆàș­àșĄàș•ໍ່àș›àșČàșàșàșČàș‚àș­àș‡àș—່àșČàș™àșàș±àșšàșȘàșČàșàșȘàșČàș"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"ແàșšàș±àș”ເàș•àș”àșŁàș”àș›àșČàșàșàșČເàș«àșŒàș·àș­à»œà»‰àș­àș"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"àșà»‰àș­àș‡àș§àșŽâ€‹àș”àș”​ໂàș­"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"àșšà»à»ˆàșȘàșČàșĄàșČàș”ໂàș—àșˆàșČàșà»‚àș›àșŁà»„àșŸàș„໌àș™àș”້ໄàș”້"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"àș™àș°à»‚àșàșšàșČàșàșšà»ˆàș­àș™à»€àșźàș±àș”àș§àșœàșàș‚àș­àș‡àș—່àșČàș™àș­àș°àș™àșžàșàșČàș”ໃàș«à»‰àș—່àșČàș™à»‚àș—àș„àș°àșȘàș±àșšà»„àș”້àșˆàșČàșà»‚àș›àșŁà»„àșŸàș„໌àșšà»ˆàș­àș™à»€àșźàș±àș”àș§àșœàșà»€àș—àș»à»ˆàșČàș™àș±à»‰àș™"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"àșȘàș°àș«àșŒàș±àșšà»„àș›à»ƒàșŠà»‰à»‚àș›àșŁà»„àșŸàș„໌àșšà»ˆàș­àș™à»€àșźàș±àș”àș§àșœàș"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"àș›àșŽàș”"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"àșàșČàș™àș•àș±à»‰àș‡àș„່àșČໜ້àșČàșˆà»àș„àș±àș­àș"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lo/tiles_states_strings.xml b/packages/SystemUI/res/values-lo/tiles_states_strings.xml
index 6ae37e4..f7f4b62 100644
--- a/packages/SystemUI/res/values-lo/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lo/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"àș›àșŽàș”"</item>
     <item msgid="5966994759929723339">"ເàș›àș”àș”"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 0d5c045..583624f 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Apatinė riba – <xliff:g id="PERCENT">%1$d</xliff:g> proc."</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Kairioji riba – <xliff:g id="PERCENT">%1$d</xliff:g> proc."</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Dešinioji riba – <xliff:g id="PERCENT">%1$d</xliff:g> proc."</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Ekrano kopijos, uĆŸfiksuotos naudojantis darbo profiliu, išsaugomos programoje „<xliff:g id="APP">%1$s</xliff:g>“"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Išsaugota programoje „<xliff:g id="APP">%1$s</xliff:g>“ darbo profilyje"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Failai"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"„<xliff:g id="APPNAME">%1$s</xliff:g>“ aptiko šią ekrano kopiją."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"„<xliff:g id="APPNAME">%1$s</xliff:g>“ ir kitos atidarytos programos aptiko šią ekrano kopiją."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Ekrano vaizdo ÄŻrašytuvas"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Apdorojam. ekrano vaizdo ÄŻraš."</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Šiuo metu rodomas ekrano ÄŻrašymo sesijos pranešimas"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Šviesumas"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"SpalvĆł inversija"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"SpalvĆł taisymas"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Tvarkyti naudotojus"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Atlikta"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"UĆŸdaryti"</string>
@@ -775,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"ekrano ÄŻrašymas"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Nėra pavadinimo"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Budėjimo laikas"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Didinimo langas"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Didinimo lango valdikliai"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Artinti"</string>
@@ -800,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Pasirinkite programą, kad pridėtumėte valdikliƳ"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Pridėtas # valdiklis.}one{Pridėtas # valdiklis.}few{Pridėti # valdikliai.}many{Pridėta # valdiklio.}other{Pridėta # valdikliƳ.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Pašalinta"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Pridėti „<xliff:g id="APPNAME">%s</xliff:g>“?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Pridėjus programą „<xliff:g id="APPNAME">%s</xliff:g>“, ji gali pridėti valdikliĆł ir turinio prie šio skydelio. Kai kuriose programose galite pasirinkti, kurie valdikliai čia rodomi."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"ļtraukta į mėgstamiausius"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"ļtraukta į mėgstamiausius, padėtis: <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Pašalinta iš mėgstamiausiĆł"</string>
@@ -850,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Atidaryti „<xliff:g id="APP_LABEL">%1$s</xliff:g>“"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Leisti <xliff:g id="ARTIST_NAME">%2$s</xliff:g> – „<xliff:g id="SONG_NAME">%1$s</xliff:g>“ iš „<xliff:g id="APP_LABEL">%3$s</xliff:g>“"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Leisti „<xliff:g id="SONG_NAME">%1$s</xliff:g>“ iš „<xliff:g id="APP_LABEL">%2$s</xliff:g>“"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Jums"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Anuliuoti"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Prieikite arčiau, kad galėtumėte leisti ÄŻrenginyje „<xliff:g id="DEVICENAME">%1$s</xliff:g>“"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Jei norite leisti čia, eikite arčiau ÄŻrenginio „<xliff:g id="DEVICENAME">%1$s</xliff:g>“"</string>
@@ -857,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"KaĆŸkas ne taip. Bandykite dar kartą."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Äźkeliama"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"planšetinis kompiuteris"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Perduodama medija"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Perduodama programa „<xliff:g id="APP_LABEL">%1$s</xliff:g>“"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Neaktyvu, patikrinkite progr."</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Nerasta"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Valdiklis nepasiekiamas"</string>
@@ -866,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Klaida, bandykite dar kartą"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Pridėti valdikliƳ"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Redaguoti valdiklius"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Pridėti programą"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"IšvesčiĆł pridėjimas"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupė"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Pasirinktas 1 ÄŻrenginys"</string>
@@ -881,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Garsiakalbiai ir ekranai"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Siƫlomi įrenginiai"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Reikalinga mokama paskyra"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kaip veikia transliacija"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Transliacija"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Netoliese esantys ĆŸmonės, turintys suderinamus „Bluetooth“ ÄŻrenginius, gali klausyti jĆ«sĆł transliuojamos medijos"</string>
@@ -892,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Nepavyko transliuoti"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Nepavyko išsaugoti. Bandykite dar kartą."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Nepavyko išsaugoti."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Naudokite bent 4 simbolius"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Naudokite daugiausia 16 simboliĆł"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Versijos numeris"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Versijos numeris nukopijuotas ÄŻ iškarpinę."</string>
     <string name="basic_status" msgid="2315371112182658176">"Atidaryti pokalbÄŻ"</string>
@@ -1011,11 +1030,16 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Pasiekiamas bent vienas ÄŻrenginys"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Paliesk. ir palaik. spart. klav."</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Atšaukti"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Apversti dabar"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"UĆŸfiksuokite geresnę asmenukę atlenkę telefoną"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"UĆŸfiksuoti geresnę asmenukę ÄŻjungus priekinÄŻ rodinÄŻ?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Naudokite galinÄŻ fotoaparatą, kad nuotrauka bĆ«tĆł platesnė ir didesnės skyros."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Šis ekranas išsijungs"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Lankstomasis ÄŻrenginys išlankstomas"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Lankstomasis įrenginys apverčiamas"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Liko akumuliatoriaus ÄŻkrovos: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
@@ -1026,4 +1050,5 @@
     <string name="call_from_work_profile_text" msgid="3458704745640229638">"Pagal jĆ«sĆł darbo politiką galite skambinti telefonu tik iš darbo profilio"</string>
     <string name="call_from_work_profile_action" msgid="2937701298133010724">"Perjungti ÄŻ darbo profilÄŻ"</string>
     <string name="call_from_work_profile_close" msgid="7927067108901068098">"UĆŸdaryti"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"UĆŸrakinimo ekrano nustatymai"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lt/tiles_states_strings.xml b/packages/SystemUI/res/values-lt/tiles_states_strings.xml
index 03d98c4..5837915 100644
--- a/packages/SystemUI/res/values-lt/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lt/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Išjungta"</item>
     <item msgid="5966994759929723339">"Äźjungta"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index ae22afd..2b68f2a 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Apakšmala: <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Kreisā mala: <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Labā mala: <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Darba profila ekrānuzƆēmumi tiek saglabāti lietotnē <xliff:g id="APP">%1$s</xliff:g>."</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Saglabāts lietotnē <xliff:g id="APP">%1$s</xliff:g> darba profilā"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Faili"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> konstatēja, ka tika veikts ekrānuzƆēmums."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> un citas atvērtas lietotnes konstatēja, ka tika veikts ekrānuzƆēmums."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Ekrāna ierakstītājs"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekrāna ieraksta apstrāde"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"AktÄ«vs paziƆojums par ekrāna ierakstÄ«šanas sesiju"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Spilgtums"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Krāsu inversija"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Krāsu korekcija"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Pārvaldīt lietotājus"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Gatavs"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Aizvērt"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automātiski"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Nav skaƆas signāla vai vibrācijas"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Nav skaƆas signāla vai vibrācijas, kā arÄ« atrodas tālāk sarunu sadaČā"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Atkarībā no iestatījumiem var zvanīt vai vibrēt"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Atkarībā no ierīces iestatījumiem var zvanīt vai vibrēt. Sarunas no lietotnes <xliff:g id="APP_NAME">%1$s</xliff:g> pēc noklusējuma tiek parādītas burbulī."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Iestatiet, lai sistēma noteiktu, vai šim paziƆojumam bĆ«s skaƆa vai vibrācija"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Statuss:&lt;/b&gt; svarīgums paaugstināts līdz noklusējumam"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Statuss:&lt;/b&gt; svarÄ«gums pazemināts, un paziƆojums tiks rādÄ«ts bez skaƆas"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"ekrāna ierakstÄ«šana"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Nav nosaukuma"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Gaidstāve"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Palielināšanas logs"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Palielināšanas loga vadÄ«klas"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Tuvināt"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Izvēlieties lietotni, lai pievienotu vadīklas"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Pievienota # vadÄ«kla.}zero{Pievienotas # vadÄ«klas.}one{Pievienota # vadÄ«kla.}other{Pievienotas # vadÄ«klas.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"NoƆemta"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Vai pievienot lietotni <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Ja pievienosiet lietotni <xliff:g id="APPNAME">%s</xliff:g>, tā varēs pievienot vadÄ«klas un saturu šim panelim. DaĆŸÄs lietotnēs varat izvēlēties, kuras vadÄ«klas šeit rādÄ«t."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Pievienota izlasei"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Pievienota izlasei, <xliff:g id="NUMBER">%d</xliff:g>. pozÄ«cija"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"NoƆemta no izlases"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Atveriet lietotni <xliff:g id="APP_LABEL">%1$s</xliff:g>."</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"AtskaƆojiet failu “<xliff:g id="SONG_NAME">%1$s</xliff:g>” (izpildÄ«tājs: <xliff:g id="ARTIST_NAME">%2$s</xliff:g>) no lietotnes <xliff:g id="APP_LABEL">%3$s</xliff:g>."</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"AtskaƆojiet failu “<xliff:g id="SONG_NAME">%1$s</xliff:g>” no lietotnes <xliff:g id="APP_LABEL">%2$s</xliff:g>."</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Tieši jums"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Atsaukt"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Pārvietojiet savu ierÄ«ci tuvāk, lai atskaƆotu mĆ«ziku ierÄ«cē “<xliff:g id="DEVICENAME">%1$s</xliff:g>”."</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Lai atskaƆotu šeit, jums jāatrodas tuvāk šai ierÄ«cei: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Radās kÄŒĆ«da. Mēģiniet vēlreiz."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Notiek ielāde"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"planšetdators"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Notiek multivides satura apraide"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Notiek lietotnes <xliff:g id="APP_LABEL">%1$s</xliff:g> apraide"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Neaktīva, pārbaudiet lietotni"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Netika atrasta"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Vadīkla nav pieejama"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Radās kÄŒĆ«da. Mēģiniet vēlreiz."</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Pievienot vadīklas"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Rediģēt vadÄ«klas"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Pievienot lietotni"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Izejas ierīču pievienošana"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupa"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Atlasīta viena ierīce"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"SkaÄŒruƆi un displeji"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Ieteiktās ierīces"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Nepieciešams maksas konts"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kā darbojas apraide"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Apraide"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Tuvumā esošÄs personas ar saderÄ«gām Bluetooth ierÄ«cēm var klausÄ«ties jĆ«su apraidÄ«to multivides saturu."</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Nevar apraidīt"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Nevar saglabāt. Mēģiniet vēlreiz."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Nevar saglabāt."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Versijas numurs"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Versijas numurs ir kopēts starpliktuvē."</string>
     <string name="basic_status" msgid="2315371112182658176">"Atvērt sarunu"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Ir pieejama vismaz viena ierÄ«ce."</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Pieskarieties saīsnei un turiet."</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Atcelt"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Apvērst tĆ«lÄ«t"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Labākas pašbildes uzƆemšana, atlokot tālruni"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Vai apvērst uz priekšÄ“jo kameru labākai pašbildei?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Lai uzƆemtu platāku fotoattēlu ar augstāku izšÄ·irtspēju, izmantojiet aizmugurējo kameru."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Šis ekrāns tiks izslēgts."</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Salokāma ierīce tiek atlocīta"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Salokāma ierīce tiek apgriezta"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Atlikušais uzlādes lÄ«menis: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Pievienojiet skārienekrāna pildspalvu lādētājam"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Zems skārienekrāna pildspalvas akumulatora līmenis"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Nevar zvanÄ«t no šÄ« profila"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"SaskaƆā ar jĆ«su darba politiku tālruƆa zvanus drÄ«kst veikt tikai no darba profila"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Pārslēgties uz darba profilu"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Aizvērt"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Bloķēšanas ekrāna iestatÄ«jumi"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lv/tiles_states_strings.xml b/packages/SystemUI/res/values-lv/tiles_states_strings.xml
index 6e9264d..9a534c4 100644
--- a/packages/SystemUI/res/values-lv/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lv/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Izslēgts"</item>
     <item msgid="5966994759929723339">"Ieslēgts"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index afe276f..3ba3bc06 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"ХлОĐșОтД ĐŸĐŽ Đ”ĐșŃ€Đ°ĐœĐŸŃ‚ ĐČĐŸ Ń€Đ°Đ±ĐŸŃ‚ĐœĐžĐŸŃ‚ ĐżŃ€ĐŸŃ„ĐžĐ» сД Đ·Đ°Ń‡ŃƒĐČуĐČаат ĐČĐŸ аплОĐșацојата <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>
     <string name="screenrecord_name" msgid="2596401223859996572">"ĐĄĐœĐžĐŒĐ°Ń‡ ĐœĐ° Đ”ĐșŃ€Đ°Đœ"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ĐĄĐ” ĐŸĐ±Ń€Đ°Đ±ĐŸŃ‚ŃƒĐČа ŃĐœĐžĐŒĐșа ĐŸĐŽ Đ”ĐșŃ€Đ°Đœ"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"йДĐșĐŸĐČĐœĐŸ ОзĐČĐ”ŃŃ‚ŃƒĐČањД за сДсОја за ŃĐœĐžĐŒĐ°ŃšĐ” ĐœĐ° Đ”ĐșŃ€Đ°ĐœĐŸŃ‚"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ОсĐČĐ”Ń‚Đ»Đ”ĐœĐŸŃŃ‚"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Đ˜ĐœĐČĐ”Ń€Đ·ĐžŃ˜Đ° ĐœĐ° Đ±ĐŸĐžŃ‚Đ”"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ĐšĐŸŃ€Đ”Đșцоја ĐœĐ° Đ±ĐŸĐžŃ‚Đ”"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"УпраĐČуĐČĐ°Ń˜Ń‚Đ” ŃĐŸ ĐșĐŸŃ€ĐžŃĐœĐžŃ†ĐžŃ‚Đ”"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Đ“ĐŸŃ‚ĐŸĐČĐŸ"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"ЗатĐČĐŸŃ€Đž"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"АĐČŃ‚ĐŸĐŒĐ°Ń‚ŃĐșĐž"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"БДз Đ·ĐČуĐș ОлО ĐČОбрацОО"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"БДз Đ·ĐČуĐș ОлО ĐČОбрацОО Đž сД ĐżĐŸŃ˜Đ°ĐČуĐČа ĐżĐŸĐŽĐŸĐ»Ńƒ ĐČĐŸ ĐŽĐ”Đ»ĐŸŃ‚ ŃĐŸ Ń€Đ°Đ·ĐłĐŸĐČĐŸŃ€Đž"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"ĐœĐŸĐ¶Đ” Ўа ѕĐČĐŸĐœĐž ОлО ĐČОбрОра ĐČĐŸ заĐČĐžŃĐœĐŸŃŃ‚ ĐŸĐŽ ĐżĐŸŃŃ‚Đ°ĐČĐșОтД ĐœĐ° ŃƒŃ€Đ”ĐŽĐŸŃ‚"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"ĐœĐŸĐ¶Đ” Ўа ѕĐČĐŸĐœĐž ОлО ĐČОбрОра ĐČĐŸ заĐČĐžŃĐœĐŸŃŃ‚ ĐŸĐŽ ĐżĐŸŃŃ‚Đ°ĐČĐșОтД ĐœĐ° ŃƒŃ€Đ”ĐŽĐŸŃ‚. ĐĄŃ‚Đ°ĐœĐŽĐ°Ń€ĐŽĐœĐŸ, Ń€Đ°Đ·ĐłĐŸĐČĐŸŃ€ĐžŃ‚Đ” ĐŸĐŽ <xliff:g id="APP_NAME">%1$s</xliff:g> сД ĐČĐŸ Đ±Đ°Đ»ĐŸĐœŃ‡ĐžŃšĐ°."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Đ”ĐŸĐ·ĐČĐŸĐ»Đ”Ń‚Đ” ŃĐžŃŃ‚Đ”ĐŒĐŸŃ‚ Ўа ĐŸĐżŃ€Đ”ĐŽĐ”Đ»Đž ЎалО ОзĐČĐ”ŃŃ‚ŃƒĐČањДĐČĐŸ трДба Ўа оспушто Đ·ĐČуĐș ОлО Ўа ĐČОбрОра"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Статус:&lt;/b&gt; ĐżĐŸŃŃ‚Đ°ĐČĐ”ĐœĐŸ ĐœĐ° „ĐĄŃ‚Đ°ĐœĐŽĐ°Ń€ĐŽĐœĐŸ“"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Статус:&lt;/b&gt; ĐœĐ°ĐŒĐ°Đ»Đ”ĐœĐŸ ĐœĐ° „йОĐČĐșĐŸ“"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"ŃĐœĐžĐŒĐ°ŃšĐ” ĐœĐ° Đ”ĐșŃ€Đ°ĐœĐŸŃ‚"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"БДз ĐœĐ°ŃĐ»ĐŸĐČ"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"ĐŸĐŸĐŽĐłĐŸŃ‚ĐČĐ”ĐœĐŸŃŃ‚"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"ĐŸŃ€ĐŸĐ·ĐŸŃ€Đ”Ń† за Đ·ĐłĐŸĐ»Đ”ĐŒŃƒĐČањД"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"ĐšĐŸĐœŃ‚Ń€ĐŸĐ»Đž ĐœĐ° ĐżŃ€ĐŸĐ·ĐŸŃ€Đ”Ń† за Đ·ĐłĐŸĐ»Đ”ĐŒŃƒĐČањД"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Đ—ŃƒĐŒĐžŃ€Đ°Ń˜"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Đ˜Đ·Đ±Đ”Ń€Đ”Ń‚Đ” аплОĐșацоја за Ўа ĐŽĐŸĐŽĐ°ĐŽĐ”Ń‚Đ” ĐșĐŸĐœŃ‚Ń€ĐŸĐ»Đž"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Đ”ĐŸĐŽĐ°ĐŽĐ”ĐœĐ° Đ” # ĐșĐŸĐœŃ‚Ń€ĐŸĐ»Đ°.}one{Đ”ĐŸĐŽĐ°ĐŽĐ”ĐœĐž сД # ĐșĐŸĐœŃ‚Ń€ĐŸĐ»Đ°.}other{Đ”ĐŸĐŽĐ°ĐŽĐ”ĐœĐž сД # ĐșĐŸĐœŃ‚Ń€ĐŸĐ»Đž.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"ĐžŃ‚ŃŃ‚Ń€Đ°ĐœĐ”Ń‚Đ°"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Да сД ĐŽĐŸĐŽĐ°ĐŽĐ” <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"ĐšĐŸĐłĐ° ŃœĐ” ја ĐŽĐŸĐŽĐ°ĐŽĐ”Ń‚Đ” <xliff:g id="APPNAME">%s</xliff:g>, таа ŃœĐ” ĐŒĐŸĐ¶Đ” Ўа ĐŽĐŸĐŽĐ°ĐČа ĐșĐŸĐœŃ‚Ń€ĐŸĐ»Đž Đž ŃĐŸĐŽŃ€Đ¶ĐžĐœĐž ĐœĐ° таблаĐČа. Кај ĐœĐ”ĐșĐŸĐž аплОĐșацоо, ĐŒĐŸĐ¶Đ” Ўа ОзбДрДтД ĐșĐŸĐž ĐșĐŸĐœŃ‚Ń€ĐŸĐ»Đž ŃœĐ” сД проĐșажуĐČаат туĐșа."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"ĐžĐŒĐžĐ»Đ”ĐœĐ°"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"ĐžĐŒĐžĐ»Đ”ĐœĐ°, ĐżĐŸĐ·ĐžŃ†ĐžŃ˜Đ° <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"ĐĐ”ĐŸĐŒĐžĐ»Đ”ĐœĐ°"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"ОтĐČĐŸŃ€Đ”Ń‚Đ” <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"ĐŸŃƒŃˆŃ‚Đ”Ń‚Đ” <xliff:g id="SONG_NAME">%1$s</xliff:g> ĐŸĐŽ <xliff:g id="ARTIST_NAME">%2$s</xliff:g> ĐœĐ° <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"ĐŸŃƒŃˆŃ‚Đ”Ń‚Đ” <xliff:g id="SONG_NAME">%1$s</xliff:g> ĐœĐ° <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"За ĐČас"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Врато"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"ĐŸŃ€ĐžĐ±Đ»ĐžĐ¶Đ”Ń‚Đ” сД за Ўа ĐżŃƒŃˆŃ‚ĐžŃ‚Đ” ĐœĐ° <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"За Ўа ĐżŃƒŃˆŃ‚Đ°Ń‚Đ” ĐŸĐČĐŽĐ”, прОблОжДтД сД ĐŽĐŸ <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"ĐĐ”ŃˆŃ‚ĐŸ ĐœĐ” Đ” ĐČĐŸ рДЎ. ОбОЎДтД сД ĐżĐŸĐČŃ‚ĐŸŃ€ĐœĐŸ."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"ĐĄĐ” ĐČчотуĐČа"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"таблДт"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Đ•ĐŒĐžŃ‚ŃƒĐČањД ĐœĐ° ĐČĐ°ŃˆĐžŃ‚Đ” Đ°ŃƒĐŽĐžĐŸĐČĐžĐ·ŃƒĐ”Đ»ĐœĐž ŃĐŸĐŽŃ€Đ¶ĐžĐœĐž"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"ĐĄĐ” Đ”ĐŒĐžŃ‚ŃƒĐČа <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ĐĐ”Đ°ĐșтоĐČĐœĐ°, ĐżŃ€ĐŸĐČДрО аплОĐșацоја"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"ĐĐ” Đ” ĐœĐ°Ń˜ĐŽĐ”ĐœĐŸ"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"ĐšĐŸĐœŃ‚Ń€ĐŸĐ»Đ°Ń‚Đ° ĐœĐ” Đ” ĐŽĐŸŃŃ‚Đ°ĐżĐœĐ°"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Đ“Ń€Đ”ŃˆĐșа, ĐŸĐ±ĐžĐŽĐ”Ń‚Đ” сД ĐżĐŸĐČŃ‚ĐŸŃ€ĐœĐŸ"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Đ”ĐŸĐŽĐ°Ń˜Ń‚Đ” ĐșĐŸĐœŃ‚Ń€ĐŸĐ»Đž"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Đ˜Đ·ĐŒĐ”ĐœĐ”Ń‚Đ” гО ĐșĐŸĐœŃ‚Ń€ĐŸĐ»ĐžŃ‚Đ”"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Đ”ĐŸĐŽĐ°Ń˜Ń‚Đ” аплОĐșацоја"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Đ”ĐŸĐŽĐ°Ń˜Ń‚Đ” ОзлДзО"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Група"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Đ˜Đ·Đ±Ń€Đ°Đœ Đ” 1 ŃƒŃ€Đ”ĐŽ"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ЗĐČŃƒŃ‡ĐœĐžŃ†Đž Đž Đ”ĐșŃ€Đ°ĐœĐž"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"ĐŸŃ€Đ”ĐŽĐ»ĐŸĐ¶Đ”ĐœĐž ŃƒŃ€Đ”ĐŽĐž"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"ĐŸĐŸŃ‚Ń€Đ”Đ±ĐœĐ° Đ” ĐżŃ€Đ”ĐŒĐžŃƒĐŒ ŃĐŒĐ”Ń‚Đșа"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"КаĐșĐŸ Ń„ŃƒĐœĐșŃ†ĐžĐŸĐœĐžŃ€Đ° Đ”ĐŒĐžŃ‚ŃƒĐČĐ°ŃšĐ”Ń‚ĐŸ"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Đ•ĐŒĐžŃ‚ŃƒĐČањД"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Đ›ŃƒŃ“Đ”Ń‚ĐŸ ĐČĐŸ ĐČаша Đ±Đ»ĐžĐ·ĐžĐœĐ° ŃĐŸ ĐșĐŸĐŒĐżĐ°Ń‚ĐžĐ±ĐžĐ»ĐœĐž ŃƒŃ€Đ”ĐŽĐž ŃĐŸ Bluetooth ĐŒĐŸĐ¶Đ” Ўа гО ŃĐ»ŃƒŃˆĐ°Đ°Ń‚ Đ°ŃƒĐŽĐžĐŸĐ·Đ°ĐżĐžŃĐžŃ‚Đ” ŃˆŃ‚ĐŸ гО Đ”ĐŒĐžŃ‚ŃƒĐČатД"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"ĐĐ” ĐŒĐŸĐ¶Đ” Ўа сД Đ”ĐŒĐžŃ‚ŃƒĐČа"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"ĐĐ” ĐŒĐŸĐ¶Đ” Ўа сД Đ·Đ°Ń‡ŃƒĐČа. ОбОЎДтД сД ĐżĐŸĐČŃ‚ĐŸŃ€ĐœĐŸ."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"ĐĐ” ĐŒĐŸĐ¶Đ” Ўа сД Đ·Đ°Ń‡ŃƒĐČа."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Đ‘Ń€ĐŸŃ˜ ĐœĐ° ĐČĐ”Ń€Đ·ĐžŃ˜Đ°"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Đ‘Ń€ĐŸŃ˜ĐŸŃ‚ ĐœĐ° ĐČĐ”Ń€Đ·ĐžŃ˜Đ°Ń‚Đ° Đ” ĐșĐŸĐżĐžŃ€Đ°Đœ ĐČĐŸ проĐČŃ€Đ”ĐŒĐ”ĐœĐ°Ń‚Đ° ĐŒĐ”ĐŒĐŸŃ€ĐžŃ˜Đ°."</string>
     <string name="basic_status" msgid="2315371112182658176">"Đ—Đ°ĐżĐŸŃ‡ĐœĐž Ń€Đ°Đ·ĐłĐŸĐČĐŸŃ€"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• ĐŽĐŸŃŃ‚Đ°ĐżĐ”Đœ Đ” ĐœĐ°Ń˜ĐŒĐ°Đ»Đșу Đ”ĐŽĐ”Đœ ŃƒŃ€Đ”ĐŽ"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Đ”ĐŸĐżŃ€Đ”Ń‚Đ” Đž заЎржДтД ја ĐșŃ€Đ°Ń‚Đ”ĐœĐșата"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"ОтĐșажО"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"ĐŸŃ€Đ”Ń„Ń€Đ»Đž сДга"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"ОтĐČĐŸŃ€Đ”Ń‚Đ” ĐłĐŸ Ń‚Đ”Đ»Đ”Ń„ĐŸĐœĐŸŃ‚ за ĐżĐŸĐŽĐŸĐ±Ń€ĐŸ сДлфО"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Да сД прДфрлО ĐœĐ° ĐżŃ€Đ”ĐŽĐœĐžĐŸŃ‚ Đ”ĐșŃ€Đ°Đœ за ĐżĐŸĐŽĐŸĐ±Ń€ĐŸ сДлфО?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"ĐšĐŸŃ€ĐžŃŃ‚Đ”Ń‚Đ” ја Đ·Đ°ĐŽĐœĐ°Ń‚Đ° ĐșĐ°ĐŒĐ”Ń€Đ° за ĐżĐŸŃˆĐžŃ€ĐŸĐșа Ń„ĐŸŃ‚ĐŸĐłŃ€Đ°Ń„ĐžŃ˜Đ° ŃĐŸ ĐżĐŸĐČĐžŃĐŸĐșа Ń€Đ”Đ·ĐŸĐ»ŃƒŃ†ĐžŃ˜Đ°."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ЕĐșŃ€Đ°ĐœĐŸĐČ ŃœĐ” сД ОсĐșĐ»ŃƒŃ‡Đž"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ĐŸŃ€Đ”ĐșĐ»ĐŸĐżŃƒĐČачĐșĐž ŃƒŃ€Đ”ĐŽ сД ĐŸŃ‚ĐșĐ»ĐŸĐżŃƒĐČа"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ĐŸŃ€Đ”ĐșĐ»ĐŸĐżŃƒĐČачĐșĐž ŃƒŃ€Đ”ĐŽ сД ĐČрто"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"ĐŸŃ€Đ”ĐŸŃŃ‚Đ°ĐœĐ°Ń‚Đ° Đ±Đ°Ń‚Đ”Ń€ĐžŃ˜Đ°: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ĐŸĐŸĐČрзДтД ĐłĐŸ ĐżĐ”ĐœĐșĐ°Đ»ĐŸŃ‚ĐŸ ŃĐŸ ĐżĐŸĐ»ĐœĐ°Ń‡"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Хлаба Đ±Đ°Ń‚Đ”Ń€ĐžŃ˜Đ° ĐœĐ° ĐżĐ”ĐœĐșĐ°Đ»ĐŸ"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Đ’ĐžĐŽĐ”ĐŸĐșĐ°ĐŒĐ”Ń€Đ°"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"ĐĐ” ĐŒĐŸĐ¶Đ”Ń‚Đ” Ўа сД јаĐČОтД ĐŸĐŽ ĐżŃ€ĐŸŃ„ĐžĐ»ĐŸĐČ"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Đ’Đ°ŃˆĐ”Ń‚ĐŸ Ń€Đ°Đ±ĐŸŃ‚ĐœĐŸ праĐČĐžĐ»ĐŸ ĐČĐž ĐŽĐŸĐ·ĐČĐŸĐ»ŃƒĐČа Ўа упатуĐČатД ĐżĐŸĐČоцо ŃĐ°ĐŒĐŸ ĐŸĐŽ Ń€Đ°Đ±ĐŸŃ‚ĐœĐžĐŸŃ‚ ĐżŃ€ĐŸŃ„ĐžĐ»"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"ĐŸŃ€Đ”Ń„Ń€Đ»Đž сД ĐœĐ° Ń€Đ°Đ±ĐŸŃ‚Đ”Đœ ĐżŃ€ĐŸŃ„ĐžĐ»"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"ЗатĐČĐŸŃ€Đž"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"ĐŸĐŸŃŃ‚Đ°ĐČĐșĐž за заĐșĐ»ŃƒŃ‡Đ”Đœ Đ”ĐșŃ€Đ°Đœ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mk/tiles_states_strings.xml b/packages/SystemUI/res/values-mk/tiles_states_strings.xml
index 96c8a49..d088f12 100644
--- a/packages/SystemUI/res/values-mk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mk/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"ИсĐșĐ»ŃƒŃ‡Đ”ĐœĐŸ"</item>
     <item msgid="5966994759929723339">"ВĐșĐ»ŃƒŃ‡Đ”ĐœĐŸ"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 1e23455..123c424 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"àŽ”àŽŠà”àŽŻà”‹àŽ—àŽżàŽ• àŽȘà”àŽ°à”ŠàŽ«à”ˆàŽČàŽżàŽšà”àŽ±à”† àŽžà”àŽ•à”àŽ°à”€àŽšà”‍àŽ·à”‹àŽŸà”àŽŸà”àŽ•à”Ÿ <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>
     <string name="screenrecord_name" msgid="2596401223859996572">"àŽžà”àŽ•à”àŽ°à”€à”» àŽ±à”†àŽ•à”àŽ•à”‹à”ŒàŽĄà”Œ"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"àŽžà”àŽ•à”àŽ°à”€à”» àŽ±à”†àŽ•à”àŽ•à”‹à”ŒàŽĄàŽżàŽ‚àŽ—à” àŽȘà”àŽ°à”‹àŽžàŽžà”àŽšà”†àŽŻà”àŽŻà”àŽšà”àŽšà”"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"àŽ’àŽ°à” àŽžà”àŽ•à”àŽ°à”€à”» àŽ±à”†àŽ•à”àŽ•à”‹à”ŒàŽĄàŽżàŽ‚àŽ—à” àŽžà”†àŽ·àŽšàŽŸàŽŻàŽż àŽšàŽżàŽČàŽ”àŽżàŽČà”àŽłà”àŽł àŽ…àŽ±àŽżàŽŻàŽżàŽȘà”àŽȘà”"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"àŽ€à”†àŽłàŽżàŽšà”àŽšàŽ‚"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"àŽšàŽżàŽ±àŽ‚ àŽ”àŽżàŽȘàŽ°à”€àŽ€àŽźàŽŸàŽ•à”àŽ•à”œ"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"àŽšàŽżàŽ±àŽ‚ àŽ¶àŽ°àŽżàŽŻàŽŸàŽ•à”àŽ•à”œ"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"àŽ‰àŽȘàŽŻà”‹àŽ•à”àŽ€àŽŸàŽ•à”àŽ•àŽłà”† àŽźàŽŸàŽšà”‡àŽœà” àŽšà”†àŽŻà”àŽŻà”àŽ•"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"àŽȘà”‚à”ŒàŽ€à”àŽ€àŽżàŽŻàŽŸàŽ•à”àŽ•àŽż"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"àŽ…àŽŸàŽŻà”àŽ•à”àŽ•à”àŽ•"</string>
@@ -775,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"àŽžà”àŽ•à”àŽ°à”€à”» àŽ±à”†àŽ•à”àŽ•à”‹à”ŒàŽĄàŽżàŽ‚àŽ—à”"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"àŽȘà”‡àŽ°àŽżàŽČà”àŽČ"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"àŽžà”‌àŽ±à”àŽ±àŽŸà”»àŽĄà”‌àŽŹà”ˆ"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"àŽźàŽŸàŽ—à”àŽšàŽżàŽ«àŽżàŽ•à”àŽ•à”‡àŽ·à”» àŽ”àŽżà”»àŽĄà”‹"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"àŽźàŽŸàŽ—à”àŽšàŽżàŽ«àŽżàŽ•à”àŽ•à”‡àŽ·à”» àŽ”àŽżà”»àŽĄà”‹ àŽšàŽżàŽŻàŽšà”àŽ€à”àŽ°àŽŁàŽ™à”àŽ™à”Ÿ"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"àŽžà”‚àŽ‚ àŽ‡à”» àŽšà”†àŽŻà”àŽŻà”àŽ•"</string>
@@ -800,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"àŽšàŽżàŽŻàŽšà”àŽ€à”àŽ°àŽŁàŽ™à”àŽ™à”Ÿ àŽšà”‡à”ŒàŽ•à”àŽ•àŽŸà”» àŽ†àŽȘà”àŽȘà” àŽ€àŽżàŽ°àŽžà”àŽžà”†àŽŸà”àŽ•à”àŽ•à”àŽ•"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# àŽšàŽżàŽŻàŽšà”àŽ€à”àŽ°àŽŁàŽ‚ àŽšà”‡à”ŒàŽ€à”àŽ€à”.}other{# àŽšàŽżàŽŻàŽšà”àŽ€à”àŽ°àŽŁàŽ™à”àŽ™à”Ÿ àŽšà”‡à”ŒàŽ€à”àŽ€à”.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"àŽšà”€àŽ•à”àŽ•àŽ‚ àŽšà”†àŽŻà”‌àŽ€à”"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g> àŽšà”‡à”ŒàŽ•à”àŽ•àŽŁà”‹?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"àŽšàŽżàŽ™à”àŽ™à”Ÿ <xliff:g id="APPNAME">%s</xliff:g> àŽšà”‡à”ŒàŽ€à”àŽ€àŽŸà”œ, àŽ…àŽ€àŽżàŽšà” àŽˆ àŽȘàŽŸàŽšàŽČàŽżàŽČà”‡àŽ•à”àŽ•à” àŽšàŽżàŽŻàŽšà”àŽ€à”àŽ°àŽŁàŽ™à”àŽ™àŽłà”àŽ‚ àŽ‰àŽłà”àŽłàŽŸàŽ•à”àŽ•àŽ”à”àŽ‚ àŽšà”‡à”ŒàŽ•à”àŽ•àŽŸàŽšàŽŸàŽ•à”àŽ‚. àŽšàŽżàŽČ àŽ†àŽȘà”àŽȘà”àŽ•àŽłàŽżà”œ, àŽ‡àŽ”àŽżàŽŸà”† àŽàŽ€à” àŽšàŽżàŽŻàŽšà”àŽ€à”àŽ°àŽŁàŽ™à”àŽ™à”Ÿ àŽŠà”ƒàŽ¶à”àŽŻàŽźàŽŸàŽ•àŽŁàŽźà”†àŽšà”àŽšà” àŽšàŽżàŽ™à”àŽ™à”ŸàŽ•à”àŽ•à” àŽ€àŽżàŽ°àŽžà”àŽžà”†àŽŸà”àŽ•à”àŽ•àŽŸàŽšàŽŸàŽ•à”àŽ‚."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"àŽȘà”àŽ°àŽżàŽŻàŽȘà”àŽȘà”†àŽŸà”àŽŸàŽ€àŽŸàŽ•à”àŽ•àŽż"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"àŽȘà”àŽ°àŽżàŽŻàŽȘà”àŽȘà”†àŽŸà”àŽŸàŽ€àŽŸàŽ•à”àŽ•àŽż, àŽžà”àŽ„àŽŸàŽšàŽ‚ <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"àŽȘà”àŽ°àŽżàŽŻàŽȘà”àŽȘà”†àŽŸà”àŽŸàŽ€àŽČà”àŽČàŽŸàŽ€àŽŸàŽ•à”àŽ•àŽż"</string>
@@ -850,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> àŽ€à”àŽ±àŽ•à”àŽ•à”àŽ•"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> àŽŽàŽšà”àŽš àŽ†à”ŒàŽŸà”àŽŸàŽżàŽžà”àŽ±à”àŽ±àŽżàŽšà”àŽ±à”† <xliff:g id="SONG_NAME">%1$s</xliff:g> àŽŽàŽšà”àŽš àŽ—àŽŸàŽšàŽ‚ <xliff:g id="APP_LABEL">%3$s</xliff:g> àŽ†àŽȘà”àŽȘàŽżà”œ àŽȘà”àŽČà”‡ àŽšà”†àŽŻà”àŽŻà”àŽ•"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> àŽŽàŽšà”àŽš àŽ—àŽŸàŽšàŽ‚ <xliff:g id="APP_LABEL">%2$s</xliff:g> àŽ†àŽȘà”àŽȘàŽżà”œ àŽȘà”àŽČà”‡ àŽšà”†àŽŻà”àŽŻà”àŽ•"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"àŽšàŽżàŽ™à”àŽ™à”ŸàŽ•à”àŽ•à”àŽłà”àŽłàŽ”"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"àŽȘàŽŽàŽŻàŽȘàŽŸàŽżàŽŻàŽŸàŽ•à”àŽ•à”àŽ•"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> àŽŽàŽšà”àŽšàŽ€àŽżà”œ àŽȘà”àŽČà”‡ àŽšà”†àŽŻà”àŽŻàŽŸà”» àŽ…àŽŸà”àŽ€à”àŽ€à”‡àŽ•à”àŽ•à” àŽšà”€àŽ•à”àŽ•à”àŽ•"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"àŽ‡àŽ”àŽżàŽŸà”† àŽȘà”àŽČà”‡ àŽšà”†àŽŻà”àŽŻàŽŸà”» <xliff:g id="DEVICENAME">%1$s</xliff:g> àŽŽàŽšà”àŽšàŽ€àŽżàŽšàŽŸà”àŽ€à”àŽ€à”‡àŽ•à”àŽ•à” àŽšà”€àŽ•à”àŽ•à”àŽ•"</string>
@@ -857,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"àŽŽàŽšà”àŽ€à”‹ àŽ•à”àŽŽàŽȘà”àŽȘàŽźà”àŽŁà”àŽŸàŽŸàŽŻàŽż. àŽ”à”€àŽŁà”àŽŸà”àŽ‚ àŽ¶à”àŽ°àŽźàŽżàŽ•à”àŽ•à”àŽ•."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"àŽČà”‹àŽĄà” àŽšà”†àŽŻà”àŽŻà”àŽšà”àŽšà”"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"àŽŸàŽŸàŽŹà”‌àŽČà”†àŽ±à”àŽ±à”"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"àŽšàŽżàŽ™à”àŽ™àŽłà”àŽŸà”† àŽźà”€àŽĄàŽżàŽŻ àŽ•àŽŸàŽžà”àŽ±à”àŽ±à” àŽšà”†àŽŻà”àŽŻà”àŽšà”àŽšà”"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"<xliff:g id="APP_LABEL">%1$s</xliff:g> àŽ•àŽŸàŽžà”‌àŽ±à”àŽ±à” àŽšà”†àŽŻà”àŽŻà”àŽšà”àŽšà”"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"àŽšàŽżàŽ·à”‌àŽ•à”àŽ°àŽżàŽŻàŽ‚, àŽ†àŽȘà”àŽȘà” àŽȘàŽ°àŽżàŽ¶à”‹àŽ§àŽżàŽ•à”àŽ•à”‚"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"àŽ•àŽŁà”àŽŸà”†àŽ€à”àŽ€àŽżàŽŻàŽżàŽČà”àŽČ"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"àŽšàŽżàŽŻàŽšà”àŽ€à”àŽ°àŽŁàŽ‚ àŽČàŽ­à”àŽŻàŽźàŽČà”àŽČ"</string>
@@ -866,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"àŽȘàŽżàŽ¶àŽ•à”, àŽ”à”€àŽŁà”àŽŸà”àŽ‚ àŽ¶à”àŽ°àŽźàŽżàŽ•à”àŽ•à”àŽ•"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"àŽšàŽżàŽŻàŽšà”àŽ€à”àŽ°àŽŁàŽ™à”àŽ™à”Ÿ àŽšà”‡à”ŒàŽ•à”àŽ•à”àŽ•"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"àŽšàŽżàŽŻàŽšà”àŽ€à”àŽ°àŽŁàŽ™à”àŽ™à”Ÿ àŽŽàŽĄàŽżàŽ±à”àŽ±à” àŽšà”†àŽŻà”àŽŻà”àŽ•"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"àŽ†àŽȘà”àŽȘà” àŽšà”‡à”ŒàŽ•à”àŽ•à”àŽ•"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"àŽ”àŽŸà”àŽŸà”àŽȘà”àŽŸà”àŽŸà”àŽ•à”Ÿ àŽšà”‡à”ŒàŽ•à”àŽ•à”àŽ•"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"àŽ—à”àŽ°à”‚àŽȘà”àŽȘà”"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"àŽ’àŽ°à” àŽ‰àŽȘàŽ•àŽ°àŽŁàŽ‚ àŽ€àŽżàŽ°àŽžà”àŽžà”†àŽŸà”àŽ€à”àŽ€à”"</string>
@@ -881,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"àŽžà”‌àŽȘà”€àŽ•à”àŽ•àŽ±à”àŽ•àŽłà”àŽ‚ àŽĄàŽżàŽžà”àŽȘà”àŽČà”‡àŽ•àŽłà”àŽ‚"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"àŽšàŽżà”ŒàŽŠà”àŽŠà”‡àŽ¶àŽżàŽšà”àŽš àŽ‰àŽȘàŽ•àŽ°àŽŁàŽ™à”àŽ™à”Ÿ"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"àŽȘà”àŽ°à”€àŽźàŽżàŽŻàŽ‚ àŽ…àŽ•à”àŽ•à”—àŽŁà”àŽŸà” àŽ†àŽ”àŽ¶à”àŽŻàŽźàŽŸàŽŁà”"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"àŽŹà”àŽ°à”‹àŽĄà”‌àŽ•àŽŸàŽžà”‌àŽ±à”àŽ±à” àŽŽàŽ™à”àŽ™àŽšà”†àŽŻàŽŸàŽŁà” àŽȘà”àŽ°àŽ”à”ŒàŽ€à”àŽ€àŽżàŽ•à”àŽ•à”àŽšà”àŽšàŽ€à”"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"àŽŹà”àŽ°à”‹àŽĄà”‌àŽ•àŽŸàŽžà”àŽ±à”àŽ±à”"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"àŽ…àŽšà”àŽŻà”‹àŽœà”àŽŻàŽźàŽŸàŽŻ Bluetooth àŽ‰àŽȘàŽ•àŽ°àŽŁàŽ™à”àŽ™àŽłà”‹àŽŸà”† àŽžàŽźà”€àŽȘàŽźà”àŽłà”àŽł àŽ†àŽłà”àŽ•à”ŸàŽ•à”àŽ•à” àŽšàŽżàŽ™à”àŽ™à”Ÿ àŽŹà”àŽ°à”‹àŽĄà”‌àŽ•àŽŸàŽžà”‌àŽ±à”àŽ±à” àŽšà”†àŽŻà”àŽŻà”àŽšà”àŽš àŽźà”€àŽĄàŽżàŽŻ àŽ•à”‡à”ŸàŽ•à”àŽ•àŽŸàŽšàŽŸàŽ•à”àŽ‚"</string>
@@ -892,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"àŽŹà”àŽ°à”‹àŽĄà”‌àŽ•àŽŸàŽžà”‌àŽ±à”àŽ±à” àŽšà”†àŽŻà”àŽŻàŽŸàŽšàŽŸàŽ•à”àŽšà”àŽšàŽżàŽČà”àŽČ"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"àŽžàŽ‚àŽ°àŽ•à”àŽ·àŽżàŽ•à”àŽ•àŽŸà”» àŽ•àŽŽàŽżàŽŻàŽżàŽČà”àŽČ. àŽ”à”€àŽŁà”àŽŸà”àŽ‚ àŽ¶à”àŽ°àŽźàŽżàŽ•à”àŽ•à”àŽ•."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"àŽžàŽ‚àŽ°àŽ•à”àŽ·àŽżàŽ•à”àŽ•àŽŸà”» àŽ•àŽŽàŽżàŽŻàŽżàŽČà”àŽČ."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"àŽ•à”àŽ±àŽžà”àŽžàŽ€à” 4 àŽȘà”àŽ°àŽ€à”€àŽ•àŽ™à”àŽ™àŽłà”†àŽ™à”àŽ•àŽżàŽČà”àŽ‚ àŽ‰àŽȘàŽŻà”‹àŽ—àŽżàŽ•à”àŽ•à”àŽ•"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"16-à”œ àŽ•à”àŽ±àŽ”à” àŽȘà”àŽ°àŽ€à”€àŽ•àŽ™à”àŽ™à”Ÿ àŽ‰àŽȘàŽŻà”‹àŽ—àŽżàŽ•à”àŽ•à”àŽ•"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"àŽŹàŽżà”œàŽĄà” àŽšàŽźà”àŽȘà”Œ"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"àŽ•à”àŽČàŽżàŽȘà”àŽȘà”àŽŹà”‹à”ŒàŽĄàŽżàŽČà”‡àŽ•à”àŽ•à” àŽŹàŽżà”œàŽĄà” àŽšàŽźà”àŽȘà”Œ àŽȘàŽ•à”ŒàŽ€à”àŽ€àŽż."</string>
     <string name="basic_status" msgid="2315371112182658176">"àŽžàŽ‚àŽ­àŽŸàŽ·àŽŁàŽ‚ àŽ€à”àŽ±àŽ•à”àŽ•à”àŽ•"</string>
@@ -1011,11 +1030,16 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• àŽ’àŽ°à” àŽ‰àŽȘàŽ•àŽ°àŽŁàŽźà”†àŽ™à”àŽ•àŽżàŽČà”àŽ‚ àŽČàŽ­à”àŽŻàŽźàŽŸàŽŁà”"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"àŽžà”àŽȘà”ŒàŽ¶àŽżàŽšà”àŽšà” àŽȘàŽżàŽŸàŽżàŽ•à”àŽ•à”àŽ• àŽ•à”àŽ±à”àŽ•à”àŽ•à”àŽ”àŽŽàŽż"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"àŽ±àŽŠà”àŽŠàŽŸàŽ•à”àŽ•à”àŽ•"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"àŽ‡àŽȘà”àŽȘà”‹à”Ÿ àŽ«à”àŽČàŽżàŽȘà”àŽȘà” àŽšà”†àŽŻà”àŽŻà”‚"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"àŽ•à”‚àŽŸà”àŽ€à”œ àŽźàŽżàŽ•àŽšà”àŽš àŽžà”†à”œàŽ«àŽż àŽČàŽ­àŽżàŽ•à”àŽ•àŽŸà”» àŽ«à”‹à”ș àŽ…à”șàŽ«à”‹à”ŸàŽĄà” àŽšà”†àŽŻà”àŽŻà”‚"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"àŽźàŽżàŽ•àŽšà”àŽš àŽžà”†à”œàŽ«àŽżàŽ•à”àŽ•à” àŽ«à”àŽ°àŽŁà”àŽŸà” àŽĄàŽżàŽžà”àŽȘà”àŽČà”‡àŽŻàŽżàŽČà”‡àŽ•à”àŽ•à” àŽźàŽŸàŽ±àŽŁà”‹?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"àŽ‰àŽŻà”ŒàŽšà”àŽš àŽ±à”†àŽžàŽČà”àŽŻà”‚àŽ·à”» àŽ‰àŽłà”àŽł, àŽ”à”€àŽ€àŽż àŽ•à”‚àŽŸàŽżàŽŻ àŽ«à”‹àŽŸà”àŽŸà”‹àŽŻà”àŽ•à”àŽ•à”, àŽȘàŽżà”»àŽ­àŽŸàŽ—àŽ€à”àŽ€à”† àŽ•à”àŽŻàŽŸàŽźàŽ± àŽ‰àŽȘàŽŻà”‹àŽ—àŽżàŽ•à”àŽ•à”àŽ•."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ àŽˆ àŽžà”àŽ•à”àŽ°à”€à”» àŽ“àŽ«àŽŸàŽ•à”àŽ‚"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"àŽ«à”‹à”ŸàŽĄà” àŽšà”†àŽŻà”àŽŻàŽŸàŽ”à”àŽšà”àŽš àŽ‰àŽȘàŽ•àŽ°àŽŁàŽ‚ àŽ…à”șàŽ«à”‹à”ŸàŽĄà” àŽ†àŽ•à”àŽšà”àŽšà”"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"àŽ«à”‹à”ŸàŽĄà” àŽšà”†àŽŻà”àŽŻàŽŸàŽ”à”àŽšà”àŽš àŽ‰àŽȘàŽ•àŽ°àŽŁàŽ‚, àŽ•àŽ±àŽ™à”àŽ™à”àŽšà”àŽš àŽ”àŽżàŽ§àŽ€à”àŽ€àŽżà”œ àŽ«à”àŽČàŽżàŽȘà”àŽȘà” àŽ†àŽ•à”àŽšà”àŽšà”"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> àŽŹàŽŸàŽ±à”àŽ±àŽ±àŽż àŽšàŽŸà”ŒàŽœà” àŽ¶à”‡àŽ·àŽżàŽ•à”àŽ•à”àŽšà”àŽšà”"</string>
@@ -1026,4 +1050,5 @@
     <string name="call_from_work_profile_text" msgid="3458704745640229638">"àŽ”àŽŠà”àŽŻà”‹àŽ—àŽżàŽ• àŽȘà”àŽ°à”ŠàŽ«à”ˆàŽČàŽżà”œ àŽšàŽżàŽšà”àŽšà” àŽźàŽŸàŽ€à”àŽ°àŽ‚ àŽ«à”‹à”ș àŽ•à”‹àŽłà”àŽ•à”Ÿ àŽšà”†àŽŻà”àŽŻàŽŸàŽšàŽŸàŽŁà” àŽšàŽżàŽ™à”àŽ™àŽłà”àŽŸà”† àŽ”àŽŠà”àŽŻà”‹àŽ—àŽżàŽ• àŽšàŽŻàŽ‚ àŽ…àŽšà”àŽ”àŽŠàŽżàŽ•à”àŽ•à”àŽšà”àŽšàŽ€à”"</string>
     <string name="call_from_work_profile_action" msgid="2937701298133010724">"àŽ”àŽŠà”àŽŻà”‹àŽ—àŽżàŽ• àŽȘà”àŽ°à”ŠàŽ«à”ˆàŽČàŽżàŽČà”‡àŽ•à”àŽ•à” àŽźàŽŸàŽ±à”àŽ•"</string>
     <string name="call_from_work_profile_close" msgid="7927067108901068098">"àŽ…àŽŸàŽŻà”àŽ•à”àŽ•à”àŽ•"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"àŽČà”‹àŽ•à”àŽ•à” àŽžà”àŽ•à”àŽ°à”€à”» àŽ•à”àŽ°àŽźà”€àŽ•àŽ°àŽŁàŽ‚"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ml/tiles_states_strings.xml b/packages/SystemUI/res/values-ml/tiles_states_strings.xml
index 7a07873..108e173 100644
--- a/packages/SystemUI/res/values-ml/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ml/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"àŽ“àŽ«àŽŸàŽŁà”"</item>
     <item msgid="5966994759929723339">"àŽ“àŽŁàŽŸàŽŁà”"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 4d06e1f..d74e7dc 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"ĐĐ¶Đ»Ń‹Đœ ĐŽŃĐ»ĐłŃŃ†ĐžĐčĐœ Đ°ĐłŃˆĐœŃƒŃƒĐŽŃ‹Đł <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>
     <string name="screenrecord_name" msgid="2596401223859996572">"Đ”ŃĐ»ĐłŃŃ†ĐžĐčĐœ ÒŻĐčĐ»ĐŽŃĐ» бОчОгч"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Đ”ŃĐ»ĐłŃŃ† Đ±ĐžŃ‡Đ»ŃĐł Đ±ĐŸĐ»ĐŸĐČŃŃ€ŃƒŃƒĐ»Đ¶ баĐčĐœĐ°"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Đ”ŃĐ»ĐłŃŃ† бОчОх ĐłĐŸŃ€ĐžĐŒŃ‹Đœ ÒŻŃ€ĐłŃĐ»Đ¶ĐžĐ»Đ¶ буĐč ĐŒŃĐŽŃĐłĐŽŃĐ»"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ĐąĐŸĐŽŃ€ĐŸĐ»"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ÓšĐœĐłÓ© хуĐČОргалт"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ÓšĐœĐłÓ© Ń‚ĐŸŃ…ĐžŃ€ŃƒŃƒĐ»ĐłĐ°"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Đ„ŃŃ€ŃĐłĐ»ŃĐłŃ‡ĐŽĐžĐčĐł уЮорЮах"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Đ”ŃƒŃƒŃŃĐ°Đœ"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Єаах"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"АĐČŃ‚ĐŸĐŒĐ°Ń‚"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Дуу эсĐČŃĐ» чочоргээ баĐčŃ…ĐłÒŻĐč"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Дуу эсĐČŃĐ» чочоргээ баĐčŃ…ĐłÒŻĐč бөгөөЎ Ń…Đ°Ń€ĐžĐ»Ń†Đ°Đœ ŃŃ€ĐžĐ°ĐœŃ‹ хэсгоĐčĐœ ĐŽĐŸĐŸĐŽ талЎ Ń…Đ°Ń€Đ°ĐłĐŽĐ°ĐœĐ°"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"ĐąÓ©Ń…Ó©Ó©Ń€Ó©ĐŒĐ¶ĐžĐčĐœ Ń‚ĐŸŃ…ĐžŃ€ĐłĐŸĐŸĐœĐŽ Ń‚ŃƒĐ»ĐłŃƒŃƒŃ€Đ»Đ°Đœ Ń…ĐŸĐœŃ… Юуугаргах эсĐČŃĐ» Ń‡ĐžŃ‡ĐžŃ€ĐłŃĐ¶ Đ±ĐŸĐ»Đ·ĐŸŃˆĐłÒŻĐč"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"ĐąÓ©Ń…Ó©Ó©Ń€Ó©ĐŒĐ¶ĐžĐčĐœ Ń‚ĐŸŃ…ĐžŃ€ĐłĐŸĐŸĐœĐŽ Ń‚ŃƒĐ»ĐłŃƒŃƒŃ€Đ»Đ°Đœ Ń…ĐŸĐœŃ… Юуугаргах эсĐČŃĐ» Ń‡ĐžŃ‡ĐžŃ€ĐłŃĐ¶ Đ±ĐŸĐ»Đ·ĐŸŃˆĐłÒŻĐč. <xliff:g id="APP_NAME">%1$s</xliff:g>-Đœ Ń…Đ°Ń€ĐžĐ»Ń†Đ°Đœ яроаг Ó©ĐłÓ©ĐłĐŽĐŒÓ©Đ»Ó©Ó©Ń€ Đ±Ó©ĐŒĐ±Ó©Đ»Ó©Đł Đ±ĐŸĐ»ĐłĐŸĐœĐŸ."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Đ­ĐœŃ ĐŒŃĐŽŃĐłĐŽŃĐ» Ўуу гаргах эсĐČŃĐ» чочрэх эсэхоĐčĐł ŃĐžŃŃ‚Đ”ĐŒŃŃŃ€ Ń‚ĐŸĐŽĐŸŃ€Ń…ĐŸĐčĐ»ŃƒŃƒĐ»Đ°Đ°Ń€Đ°Đč"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;йөлөĐČ:&lt;/b&gt; ÓšĐłÓ©ĐłĐŽĐŒÓ©Đ» Đ±ĐŸĐ»ĐłĐŸĐ¶ ЮэĐČŃˆÒŻÒŻĐ»ŃŃĐœ"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;йөлөĐČ:&lt;/b&gt; Đ§ĐžĐŒŃŃĐłÒŻĐč Đ±ĐŸĐ»ĐłĐŸĐ¶ Đ·ŃŃ€ŃĐłĐ»ŃĐ»ĐžĐčĐł ĐœŃŒ Đ±ŃƒŃƒŃ€ŃƒŃƒĐ»ŃĐ°Đœ"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"ĐŽŃĐ»ĐłŃŃ†ĐžĐčĐœ Đ±ĐžŃ‡Đ»ŃĐł"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Đ“Đ°Ń€Ń‡ĐžĐłĐłÒŻĐč"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Đ—ĐŸĐłŃĐŸĐ»Ń‚Ń‹Đœ ĐłĐŸŃ€ĐžĐŒ"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"ĐąĐŸĐŒŃ€ŃƒŃƒĐ»Đ°Đ»Ń‚Ń‹Đœ Ń†ĐŸĐœŃ…"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"ĐąĐŸĐŒŃ€ŃƒŃƒĐ»Đ°Đ»Ń‚Ń‹Đœ Ń†ĐŸĐœŃ…ĐœŃ‹ Ń…ŃĐœĐ°Đ»Ń‚"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ĐąĐŸĐŒŃ€ŃƒŃƒĐ»Đ°Ń…"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Đ„ŃĐœĐ°Đ»Ń‚ŃƒŃƒĐŽ ĐœŃĐŒŃŃ…ĐžĐčĐœ Ń‚ŃƒĐ»ĐŽ аппыг ŃĐŸĐœĐłĐŸĐœĐŸ уу"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# Ń…ŃĐœĐ°Đ»Ń‚ ĐœŃĐŒŃŃĐœ.}other{# Ń…ŃĐœĐ°Đ»Ń‚ ĐœŃĐŒŃŃĐœ.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Đ„Đ°ŃŃĐ°Đœ"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g>-Đł ĐœŃĐŒŃŃ… ÒŻÒŻ?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"йа <xliff:g id="APPNAME">%s</xliff:g>-Đł ĐœŃĐŒŃŃ… ÒŻĐ”ĐŽ ŃĐœŃ ĐœŃŒ уг Ń‚ÒŻŃ€ Đ·ŃƒŃƒŃ€Ń‹Đœ ŃĐ°ĐŒĐ±Đ°Ń€Ń‚ Ń‚ĐŸŃ…ĐžŃ€ĐłĐŸĐŸ Đ±ĐŸĐ»ĐŸĐœ ĐșĐŸĐœŃ‚Đ”ĐœŃ‚ ĐœŃĐŒŃŃ… Đ±ĐŸĐ»ĐŸĐŒĐ¶Ń‚ĐŸĐč. Đ—Đ°Ń€ĐžĐŒ аппаЎ та ŃĐœĐŽ ŃĐŒĐ°Ń€ Ń‚ĐŸŃ…ĐžŃ€ĐłĐŸĐŸĐł Ń…Đ°Ń€ŃƒŃƒĐ»Đ°Ń…Ń‹Đł ŃĐŸĐœĐłĐŸŃ… Đ±ĐŸĐ»ĐŸĐŒĐ¶Ń‚ĐŸĐč."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"ДуртаĐč ĐłŃĐ¶ Ń‚ŃĐŒĐŽŃĐłĐ»ŃŃŃĐœ"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"<xliff:g id="NUMBER">%d</xliff:g>-р баĐčŃ€ŃˆĐžĐ»ĐŽ ЮуртаĐč ĐłŃĐ¶ Ń‚ŃĐŒĐŽŃĐłĐ»ŃŃŃĐœ"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Đ”ŃƒŃ€ĐłÒŻĐč ĐłŃĐ¶ Ń‚ŃĐŒĐŽŃĐłĐ»ŃŃŃĐœ"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g>-Đł ĐœŃŃŃ…"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g>-Đœ <xliff:g id="SONG_NAME">%1$s</xliff:g>-Đł <xliff:g id="APP_LABEL">%3$s</xliff:g> Юээр Ń‚ĐŸĐłĐ»ŃƒŃƒĐ»Đ°Ń…"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g>-Đł <xliff:g id="APP_LABEL">%2$s</xliff:g> Юээр Ń‚ĐŸĐłĐ»ŃƒŃƒĐ»Đ°Ń…"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"ĐąĐ°ĐœĐŽ Đ·ĐŸŃ€ĐžŃƒĐ»ŃĐ°Đœ"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Đ‘ĐŸĐ»ĐžŃ…"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> Юээр Ń‚ĐŸĐłĐ»ŃƒŃƒĐ»Đ°Ń…Ń‹Đœ Ń‚ŃƒĐ»ĐŽ Ń‚Ó©Ń…Ó©Ó©Ń€Ó©ĐŒĐ¶Ó©Ó© ĐŸĐčŃ€Ń‚ŃƒŃƒĐ»ĐœĐ° уу"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Đ­ĐœĐŽ Ń‚ĐŸĐłĐ»ŃƒŃƒĐ»Đ°Ń…Ń‹Đœ Ń‚ŃƒĐ»ĐŽ <xliff:g id="DEVICENAME">%1$s</xliff:g> руу ĐŸĐčŃ€Ń‚ŃƒŃƒĐ»ĐœĐ° уу"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"АлЎаа гарлаа. Đ”Đ°Ń…ĐžĐœ ĐŸŃ€ĐŸĐ»ĐŽĐŸĐœĐŸ уу."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Ачаалж баĐčĐœĐ°"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"таблДт"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"ĐąĐ°ĐœŃ‹ ĐŒĐ”ĐŽĐžĐ°Đł ĐŽĐ°ĐŒĐ¶ŃƒŃƒĐ»Đ¶ баĐčĐœĐ°"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"<xliff:g id="APP_LABEL">%1$s</xliff:g>-Đł ĐŽĐ°ĐŒĐ¶ŃƒŃƒĐ»Đ¶ баĐčĐœĐ°"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ИЮэĐČŃ…ĐłÒŻĐč баĐčĐœĐ°, аппыг ŃˆĐ°Đ»ĐłĐ°ĐœĐ° уу"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"ĐžĐ»ĐŽŃĐŸĐœĐłÒŻĐč"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Đ„ŃĐœĐ°Đ»Ń‚ Đ±ĐŸĐ»ĐŸĐŒĐ¶ĐłÒŻĐč баĐčĐœĐ°"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"АлЎаа гараĐČ, ĐŽĐ°Ń…ĐžĐœ ĐŸŃ€ĐŸĐ»ĐŽĐŸĐœĐŸ уу"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Đ„ŃĐœĐ°Đ»Ń‚ ĐœŃĐŒŃŃ…"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Đ„ŃĐœĐ°Đ»Ń‚Ń‹Đł өөрчлөх"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Апп ĐœŃĐŒŃŃ…"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Гаралт ĐœŃĐŒŃŃ…"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Đ‘ÒŻĐ»ŃĐł"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 Ń‚Ó©Ń…Ó©Ó©Ń€Ó©ĐŒĐ¶ ŃĐŸĐœĐłĐŸŃĐŸĐœ"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Đ§Đ°ĐœĐłĐ° ярогч ба ĐŽŃĐ»ĐłŃŃ†"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"ĐĄĐ°ĐœĐ°Đ» Đ±ĐŸĐ»ĐłĐŸŃĐŸĐœ Ń‚Ó©Ń…Ó©Ó©Ń€Ó©ĐŒĐ¶ÒŻÒŻĐŽ"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"йусгаĐč Đ±ÒŻŃ€Ń‚ĐłŃĐ» шаарЮЮаг"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"НэĐČŃ‚Ń€ÒŻÒŻĐ»ŃĐ»Ń‚ Ń…ŃŃ€Ń…ŃĐœ ажОллаЎаг ĐČэ?"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"НэĐČŃ‚Ń€ÒŻÒŻĐ»ŃĐ»Ń‚"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ĐąĐŸŃ…ĐžŃ€ĐŸĐŒĐ¶Ń‚ĐŸĐč Bluetooth Ń‚Ó©Ń…Ó©Ó©Ń€Ó©ĐŒĐ¶ÒŻÒŻĐŽŃ‚ŃĐč Ń‚Đ°ĐœŃ‹ ĐŸĐčŃ€ĐŸĐ»Ń†ĐŸĐŸŃ… Ń…ÒŻĐŒÒŻÒŻŃ Ń‚Đ°ĐœŃ‹ ĐœŃĐČŃ‚Ń€ÒŻÒŻĐ»Đ¶ буĐč ĐŒĐ”ĐŽĐžĐ°Đł ŃĐŸĐœŃĐŸŃ… Đ±ĐŸĐ»ĐŸĐŒĐ¶Ń‚ĐŸĐč"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"НэĐČŃ‚Ń€ÒŻÒŻĐ»ŃŃ… Đ±ĐŸĐ»ĐŸĐŒĐ¶ĐłÒŻĐč"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"ЄаЎгалах Đ±ĐŸĐ»ĐŸĐŒĐ¶ĐłÒŻĐč. Đ”Đ°Ń…ĐžĐœ ĐŸŃ€ĐŸĐ»ĐŽĐŸĐœĐŸ уу."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"ЄаЎгалах Đ±ĐŸĐ»ĐŸĐŒĐ¶ĐłÒŻĐč."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"ЄОĐčцоĐčĐœ Юугаар"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"ЄОĐčцоĐčĐœ Юугаарыг Ń‚ÒŻŃ€ ŃĐ°ĐœĐ°Ń… ĐŸĐčĐŽ Ń…ŃƒŃƒĐ»ŃĐ°Đœ."</string>
     <string name="basic_status" msgid="2315371112182658176">"Đ„Đ°Ń€ĐžĐ»Ń†Đ°Đœ яроаг ĐœŃŃŃ…"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Đ”ĐŸŃ€ Ń…Đ°ŃĐ¶ ĐœŃĐł Ń‚Ó©Ń…Ó©Ó©Ń€Ó©ĐŒĐ¶ Đ±ĐŸĐ»ĐŸĐŒĐ¶Ń‚ĐŸĐč"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"ĐąĐŸĐČŃ‡Đ»ĐŸĐ»ĐŽ Ń…ÒŻŃ€ŃŃĐŽ ŃƒĐŽĐ°Đ°Đœ ĐŽĐ°Ń€ĐœĐ° уу"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"ĐŠŃƒŃ†Đ»Đ°Ń…"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"ĐžĐŽĐŸĐŸ Ń…Ó©ĐœŃ‚Ń€Ó©Ń…"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Đ˜Đ»ÒŻÒŻ саĐčĐœ сДлфО хоĐčхоĐčĐœ Ń‚ŃƒĐ»ĐŽ утсаа ĐŽŃĐ»ĐłŃĐœŃ ÒŻÒŻ"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"ХаĐčĐœ ŃĐ”Đ»ŃŒŃ„Đž аĐČахаар урЮ Ń‚Đ°Đ»Ń‹Đœ ĐŽŃĐ»ĐłŃŃ† Ń€ÒŻÒŻ Ń…Ó©ĐœŃ‚Ń€Ó©Ń… ÒŻÒŻ?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Đ˜Đ»ÒŻÒŻ Ó©ĐœĐŽÓ©Ń€ ĐœŃĐłŃ‚Đ°Ń€ŃˆĐžĐ»Ń‚Đ°Đč ĐžĐ»ÒŻÒŻ Ó©Ń€ĐłÓ©Đœ Đ·ŃƒŃ€Đ°Đł аĐČĐ°Ń…Ń‹Đœ Ń‚ŃƒĐ»ĐŽ Đ°Ń€Ń‹Đœ ĐșĐ°ĐŒĐ”Ń€Ń‹Đł Đ°ŃˆĐžĐłĐ»Đ°ĐœĐ° уу."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Đ­ĐœŃ ĐŽŃĐ»ĐłŃŃ† ŃƒĐœŃ‚Đ°Ń€ĐœĐ°"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Đ­ĐČхэгЮЮэг Ń‚Ó©Ń…Ó©Ó©Ń€Ó©ĐŒĐ¶ĐžĐčĐł ĐŽŃĐ»ĐłŃĐ¶ баĐčĐœĐ°"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Đ­ĐČхэгЮЮэг Ń‚Ó©Ń…Ó©Ó©Ń€Ó©ĐŒĐ¶ĐžĐčĐł Ń…Ó©ĐœŃ‚Ó©Ń€Ń‡ баĐčĐœĐ°"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> батарДĐč ÒŻĐ»ĐŽĐ»ŃŃ"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"МэЮрэгч ÒŻĐ·ĐłŃŃ Ń†ŃĐœŃĐłĐ»ŃĐłŃ‡Ń‚ŃĐč Ń…ĐŸĐ»Đ±ĐŸĐŸŃ€ĐŸĐč"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"МэЮрэгч ÒŻĐ·ŃĐłĐœĐžĐč батарДĐč бага баĐčĐœĐ°"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Đ’ĐžĐŽĐ”ĐŸ ĐșĐ°ĐŒĐ”Ń€"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Đ­ĐœŃ ĐżŃ€ĐŸŃ„Đ°Đčлаас залгах Đ±ĐŸĐ»ĐŸĐŒĐ¶ĐłÒŻĐč"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"ĐąĐ°ĐœŃ‹ Đ°Đ¶Đ»Ń‹Đœ Đ±ĐŸĐŽĐ»ĐŸĐłĐŸ Ń‚Đ°ĐœĐŽ Đ·Ó©ĐČŃ…Ó©Đœ Đ°Đ¶Đ»Ń‹Đœ ĐżŃ€ĐŸŃ„Đ°Đčлаас ŃƒŃ‚Đ°ŃĐœŃ‹ ЎууЎлага хоĐčхоĐčĐł Đ·Ó©ĐČŃˆÓ©Ó©Ń€ĐŽÓ©Đł"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"ĐĐ¶Đ»Ń‹Đœ ĐżŃ€ĐŸŃ„Đ°ĐčĐ» руу ŃŃĐ»ĐłŃŃ…"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Єаах"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"ĐąÒŻĐłĐ¶ĐžĐłĐŽŃŃĐœ ĐŽŃĐ»ĐłŃŃ†ĐžĐčĐœ Ń‚ĐŸŃ…ĐžŃ€ĐłĐŸĐŸ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mn/tiles_states_strings.xml b/packages/SystemUI/res/values-mn/tiles_states_strings.xml
index 776c487..5cd21c1 100644
--- a/packages/SystemUI/res/values-mn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mn/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"ĐŁĐœŃ‚Ń€Đ°Đ°Đ»Ń‚Ń‚Đ°Đč"</item>
     <item msgid="5966994759929723339">"АсаалттаĐč"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 2013879..3d73e22 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"à€‘à€«à€żà€žà€žà€‚à€Źà€‚à€§à€żà€€ à€žà„à€•à„à€°à„€à€šà€¶à„‰à€Ÿ <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>
     <string name="screenrecord_name" msgid="2596401223859996572">"à€žà„à€•à„à€°à„€à€š à€°à„‡à€•à„‰à€°à„à€Ąà€°"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"à€žà„à€•à„à€°à„€à€š à€°à„‡à€•à„‰à€°à„à€Ąà€żà€‚à€— à€Șà„à€°à„‹à€žà„‡à€ž à€žà„à€°à„‚"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"à€žà„à€•à„à€°à„€à€š à€°à„‡à€•à„‰à€°à„à€Ą à€žà€€à„à€°à€Ÿà€žà€Ÿà€ à„€ à€žà„à€°à„‚ à€…à€žà€Čà„‡à€Čà„€ à€žà„‚à€šà€šà€Ÿ"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"à€šà€źà€•"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"à€•à€Čà€° à€‡à€šà„à€”à„à€čà€°à„à€œà€š"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"à€°à€‚à€— à€žà„à€§à€Ÿà€°à€Łà€Ÿ"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"à€”à€Ÿà€Șà€°à€•à€°à„à€€à„‡ à€”à„à€Żà€”à€žà„‍à€„à€Ÿà€Șà€żà€€ à€•à€°à€Ÿ"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"à€Șà„‚à€°à„à€Ł à€à€Ÿà€Čà„‡"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"à€Źà€‚à€Š à€•à€°à€Ÿ"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"à€‘à€Ÿà„‹à€źà„…à€Ÿà€żà€•"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"à€†à€”à€Ÿà€œ à€•à€żà€‚à€”à€Ÿ à€”à„à€čà€Ÿà€Żà€Źà„à€°à„‡à€¶à€š à€šà€Ÿà€čà„€"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"à€†à€”à€Ÿà€œ à€•à€żà€‚à€”à€Ÿ à€”à„à€čà€Ÿà€Żà€Źà„à€°à„‡à€¶à€š à€šà€Ÿà€čà„€ à€†à€Łà€ż à€žà€‚à€­à€Ÿà€·à€Ł à€”à€żà€­à€Ÿà€—à€Ÿà€€ à€žà€°à„à€”à€Ÿà€€ à€€à€łà€Ÿà€¶à„€ à€Šà€żà€žà€€à„‡"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"à€Ąà€żà€”à„‍à€čà€Ÿà€‡à€ž à€žà„‡à€Ÿà€żà€‚à€—à„à€œà€šà„à€žà€Ÿà€° à€°à€żà€‚à€— à€•à€żà€‚à€”à€Ÿ à€”à„à€čà€Ÿà€Żà€Źà„à€°à„‡à€Ÿ à€čà„‹à€Š à€¶à€•à€€à„‹"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"à€Ąà€żà€”à„‍à€čà€Ÿà€‡à€ž à€žà„‡à€Ÿà€żà€‚à€—à„à€œà€šà„à€žà€Ÿà€° à€°à€żà€‚à€— à€•à€żà€‚à€”à€Ÿ à€”à„à€čà€Ÿà€Żà€Źà„à€°à„‡à€Ÿ à€čà„‹à€Š à€¶à€•à€€à„‹. <xliff:g id="APP_NAME">%1$s</xliff:g> à€źà€§à„€à€Č à€žà€‚à€­à€Ÿà€·à€Łà„‡ à€Źà€Ÿà€Ż à€Ąà„€à€«à„‰à€Čà„à€Ÿ à€Źà€Źà€Č à€čà„‹à€€à€Ÿà€€."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"à€čà„€ à€žà„‚à€šà€šà€Ÿ à€źà€żà€łà€Ÿà€Čà„‍à€Żà€Ÿà€”à€° à€†à€”à€Ÿà€œ à€”à„‍à€čà€Ÿà€”à€Ÿ à€•à„€ à€”à„à€čà€Ÿà€Żà€Źà„à€°à„‡à€¶à€š à€”à„‍à€čà€Ÿà€”à„‡ à€€à„‡ à€žà€żà€žà„‍à€Ÿà€źà€źà€§à„à€Żà„‡ à€šà€źà„‚à€Š à€•à€°à€Ÿ"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;à€žà„à€„à€żà€€à„€&lt;/b&gt; à€čà„€ à€Ąà„€à€«à„‰à€Čà„à€Ÿ à€źà„à€čà€Łà„‚à€š à€Șà„à€°à€źà„‹à€Ÿ à€•à„‡à€Čà„€ à€—à„‡à€Čà„€"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;à€žà„à€„à€żà€€à„€&lt;/b&gt; à€Čà€Ÿ à€žà€Ÿà€Żà€Čà€‚à€Ÿ à€źà„à€čà€Łà„‚à€š à€Ąà„€à€źà„‹à€Ÿ à€•à„‡à€Čà„‡ à€—à„‡à€Čà„‡"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"à€žà„à€•à„à€°à„€à€š à€°à„‡à€•à„‰à€°à„à€Ąà€żà€‚à€—"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"à€¶à„€à€°à„à€·à€• à€šà€Ÿà€čà„€"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"à€žà„à€Ÿà€à€Ąà€Źà€Ÿà€Ż"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"à€źà„…à€—à„à€šà€żà€«à€żà€•à„‡à€¶à€š à€”à€żà€‚à€Ąà„‹"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"à€źà„…à€—à„à€šà€żà€«à€żà€•à„‡à€¶à€š à€”à€żà€‚à€Ąà„‹ à€šà€żà€Żà€‚à€€à„à€°à€Łà„‡"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"à€à„‚à€ź à€‡à€š à€•à€°à€Ÿ"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"à€šà€żà€Żà€‚à€€à„à€°à€Łà„‡ à€œà„‹à€Ąà€Łà„à€Żà€Ÿà€žà€Ÿà€ à„€ à„Čà€Ș à€šà€żà€”à€Ąà€Ÿ"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# à€šà€żà€Żà€‚à€€à„à€°à€Ł à€œà„‹à€Ąà€Čà„‡ à€†à€čà„‡.}other{# à€šà€żà€Żà€‚à€€à„à€°à€Łà„‡ à€œà„‹à€Ąà€Čà„€ à€†à€čà„‡à€€.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"à€•à€Ÿà€ąà„‚à€š à€Ÿà€Ÿà€•à€Čà„‡"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g> à€œà„‹à€Ąà€Ÿà€Żà€šà„‡ à€†à€čà„‡ à€•à€Ÿ?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"à€€à„à€źà„à€čà„€ <xliff:g id="APPNAME">%s</xliff:g> à€œà„‹à€Ąà€€à€Ÿ, à€€à„‡à€”à„à€čà€Ÿ à€€à„‡ à€Żà€Ÿ à€Șà„…à€šà€Čà€źà€§à„à€Żà„‡ à€šà€żà€Żà€‚à€€à„à€°à€Łà„‡ à€†à€Łà€ż à€†à€¶à€Ż à€œà„‹à€Ąà„‚ à€¶à€•à€€à„‡. à€Żà„‡à€„à„‡ à€•à„‹à€Łà€€à„€ à€šà€żà€Żà€‚à€€à„à€°à€Łà„‡ à€Šà€Ÿà€–à€”à€Ÿà€”à„€à€€ à€€à„‡ à€€à„à€źà„à€čà„€ à€•à€Ÿà€čà„€ à€…‍à„…à€Șà„à€žà€źà€§à„à€Żà„‡ à€šà€żà€”à€Ąà„‚ à€¶à€•à€€à€Ÿ."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"à€†à€”à€Ąà€Čà„‡"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"à€†à€”à€Ąà€Čà„‡, à€žà„à€„à€Ÿà€š <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"à€šà€Ÿà€”à€Ąà€Čà„‡"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> à€‰à€˜à€Ąà€Ÿ"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> à€źà€§à„à€Żà„‡ <xliff:g id="ARTIST_NAME">%2$s</xliff:g> à€šà„‡ <xliff:g id="SONG_NAME">%1$s</xliff:g> à€Șà„à€Čà„‡ à€•à€°à€Ÿ"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> à€źà€§à„à€Żà„‡ <xliff:g id="SONG_NAME">%1$s</xliff:g> à€Șà„à€Čà„‡ à€•à€°à€Ÿ"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"à€€à„à€źà€šà„à€Żà€Ÿà€žà€Ÿà€ à„€"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"à€Șà€čà€żà€Čà„à€Żà€Ÿà€žà€Ÿà€°à€–à„‡ à€•à€°à€Ÿ"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> à€”à€° à€Șà„à€Čà„‡ à€•à€°à€Łà„à€Żà€Ÿà€žà€Ÿà€ à„€ à€œà€”à€ł à€œà€Ÿ"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"à€Żà„‡à€„à„‡ à€Șà„à€Čà„‡ à€•à€°à€Łà„à€Żà€Ÿà€žà€Ÿà€ à„€, <xliff:g id="DEVICENAME">%1$s</xliff:g> à€šà„à€Żà€Ÿ à€œà€”à€ł à€œà€Ÿ"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"à€•à€Ÿà€čà„€à€€à€°à„€ à€šà„‚à€• à€à€Ÿà€Čà„€. à€Șà„à€šà„à€čà€Ÿ à€Șà„à€°à€Żà€€à„à€š à€•à€°à€Ÿ."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"à€Čà„‹à€Ą à€•à€°à€€ à€†à€čà„‡"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"à€Ÿà„…à€Źà€Čà„‡à€Ÿ"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"à€€à„à€źà€šà€Ÿ à€źà„€à€Ąà€żà€Żà€Ÿ à€•à€Ÿà€žà„à€Ÿ à€•à€°à€€ à€†à€čà„‡"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"<xliff:g id="APP_LABEL">%1$s</xliff:g> à€•à€Ÿà€žà„à€Ÿ à€•à€°à€€ à€†à€čà„‡"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"à€šà€żà€·à„à€•à„à€°à€żà€Ż, à„Čà€Ș à€€à€Șà€Ÿà€žà€Ÿ"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"à€†à€ąà€łà€Čà„‡ à€šà€Ÿà€čà„€"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"à€šà€żà€Żà€‚à€€à„à€°à€Ł à€‰à€Șà€Čà€Źà„à€§ à€šà€Ÿà€čà„€"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"à€à€°à€°, à€Șà„à€šà„à€čà€Ÿ à€Șà„à€°à€Żà€€à„à€š à€•à€°à€Ÿ"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"à€šà€żà€Żà€‚à€€à„à€°à€Łà„‡ à€œà„‹à€Ąà€Ÿ"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"à€šà€żà€Żà€‚à€€à„à€°à€Łà„‡ à€žà€‚à€Șà€Ÿà€Šà€żà€€ à€•à€°à€Ÿ"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"à€…‍à„…à€Ș à€œà„‹à€Ąà€Ÿ"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"à€†à€‰à€Ÿà€Șà„à€Ÿ à€œà„‹à€Ąà€Ÿ"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"à€—à€Ÿ"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"à€à€• à€Ąà€żà€”à„à€čà€Ÿà€‡à€ž à€šà€żà€”à€Ąà€Čà„‡"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"à€žà„à€Șà„€à€•à€° à€†à€Łà€ż à€Ąà€żà€žà„à€Șà„à€Čà„‡"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"à€žà„à€šà€”à€Čà„‡à€Čà„€ à€Ąà€żà€”à„à€čà€Ÿà€‡à€ž"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"à€Șà„à€°à„€à€źà€żà€Żà€ź à€–à€Ÿà€€à„‡ à€†à€”à€¶à„à€Żà€• à€†à€čà„‡"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"à€Źà„à€°à„‰à€Ąà€•à€Ÿà€žà„à€Ÿà€żà€‚à€— à€•à€žà„‡ à€•à€Ÿà€ź à€•à€°à€€à„‡"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"à€Źà„à€°à„‰à€Ąà€•à€Ÿà€žà„à€Ÿ à€•à€°à€Ÿ"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"à€•à€‚à€Șà„…à€Ÿà€żà€Źà€Č à€Źà„à€Čà„‚à€Ÿà„‚à€„ à€Ąà€żà€”à„‍à€čà€Ÿà€‡à€ž à€…à€žà€Čà„‡à€Čà„‡ à€€à„à€źà€šà„à€Żà€Ÿ à€œà€”à€łà€Șà€Ÿà€žà€šà„‡ à€Čà„‹à€• à€čà„‡ à€€à„à€źà„à€čà„€ à€Źà„à€°à„‰à€Ąà€•à€Ÿà€žà„à€Ÿ à€•à€°à€€ à€…à€žà€Čà„‡à€Čà€Ÿ à€źà„€à€Ąà€żà€Żà€Ÿ à€à€•à„‚ à€¶à€•à€€à€Ÿà€€"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"à€Źà„à€°à„‰à€Ąà€•à€Ÿà€žà„à€Ÿ à€•à€°à„‚ à€¶à€•à€€ à€šà€Ÿà€čà„€"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"à€žà„‡à€”à„à€č à€•à€°à„‚ à€¶à€•à€€ à€šà€Ÿà€čà„€. à€Șà„à€šà„à€čà€Ÿ à€Șà„à€°à€Żà€€à„à€š à€•à€°à€Ÿ."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"à€žà„‡à€”à„à€č à€•à€°à„‚ à€¶à€•à€€ à€šà€Ÿà€čà„€."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"à€Źà€żà€Čà„à€Ą à€šà€‚à€Źà€°"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"à€Źà€żà€Čà„à€Ą à€šà€‚à€Źà€° à€•à„à€Čà€żà€Șà€Źà„‹à€°à„à€Ąà€”à€° à€•à„‰à€Șà„€ à€•à„‡à€Čà€Ÿ."</string>
     <string name="basic_status" msgid="2315371112182658176">"à€žà€‚à€­à€Ÿà€·à€Ł à€‰à€˜à€Ąà€Ÿ"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• à€•à€żà€źà€Ÿà€š à€à€• à€Ąà€żà€”à„à€čà€Ÿà€‡à€ž à€‰à€Șà€Čà€Źà„à€§ à€•à€°à€Łà„‡"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"à€žà„à€Șà€°à„à€¶ à€•à€°à€Ÿ à€†à€Łà€ż à€§à€°à„‚à€š à€ à„‡à€”à€Ÿ à€¶à„‰à€°à„à€Ÿà€•à€Ÿ"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"à€°à€Šà„à€Š à€•à€°à€Ÿ"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"à€†à€€à€Ÿ à€«à„à€Čà€żà€Ș à€•à€°à€Ÿ"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"à€†à€Łà€–à„€ à€šà€Ÿà€‚à€—à€Čà„à€Żà€Ÿ à€žà„‡à€Čà„à€«à„€à€žà€Ÿà€ à„€ à€«à„‹à€šà€Źà€Šà„à€Šà€Č à€…à€§à€żà€• à€œà€Ÿà€Łà„‚à€š à€˜à„à€Żà€Ÿ"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"à€†à€Łà€–à„€ à€šà€Ÿà€‚à€—à€Čà„à€Żà€Ÿ à€žà„‡à€Čà„à€«à„€à€žà€Ÿà€ à„€ à€«à„à€°à€‚à€Ÿ à€Ąà€żà€žà„à€Șà„à€Čà„‡ à€”à€Ÿà€Șà€°à€Ÿà€Żà€šà€Ÿ à€•à€Ÿ?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"à€‰à€šà„à€š à€°à„‡à€à„‹à€Čà„à€Żà„‚à€¶à€š à€…à€žà€Čà„‡à€Čà„à€Żà€Ÿ à€”à€żà€žà„à€€à„ƒà€€ à€«à„‹à€Ÿà„‹à€žà€Ÿà€ à„€ à€°à„€à€…à€° à€•à„…à€źà„‡à€°à€Ÿ à€”à€Ÿà€Șà€°à€Ÿ."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ à€čà„€ à€žà„à€•à„à€°à„€à€š à€Źà€‚à€Š à€čà„‹à€ˆà€Č"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"à€«à„‹à€Čà„à€Ą à€•à€°à€€à€Ÿ à€Żà„‡à€Łà„à€Żà€Ÿà€žà€Ÿà€°à€–à„‡ à€Ąà€żà€”à„à€čà€Ÿà€‡à€ž à€…à€šà€«à„‹à€Čà„à€Ą à€•à„‡à€Čà„‡ à€œà€Ÿà€€ à€†à€čà„‡"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"à€«à„‹à€Čà„à€Ą à€•à€°à€€à€Ÿ à€Żà„‡à€Łà„à€Żà€Ÿà€žà€Ÿà€°à€–à„‡ à€Ąà€żà€”à„à€čà€Ÿà€‡à€ž à€†à€œà„‚à€Źà€Ÿà€œà„‚à€Čà€Ÿ à€«à„à€Čà€żà€Ș à€•à„‡à€Čà„‡ à€œà€Ÿà€€ à€†à€čà„‡"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> à€Źà„…à€Ÿà€°à„€ à€¶à€żà€Čà„à€Čà€• à€†à€čà„‡"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"à€€à„à€źà€šà„‡ à€žà„à€Ÿà€Ÿà€Żà€Čà€ž à€šà€Ÿà€°à„à€œà€°à€¶à„€ à€•à€šà„‡à€•à„à€Ÿ à€•à€°à€Ÿ"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"à€žà„à€Ÿà€Ÿà€Żà€Čà€ž à€Źà„…à€Ÿà€°à„€ à€•à€źà„€ à€†à€čà„‡"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"à€”à„à€čà€żà€Ąà€żà€“ à€•à„…à€źà„‡à€°à€Ÿ"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"à€Żà€Ÿ à€Șà„à€°à„‹à€«à€Ÿà€‡à€Čà€”à€°à„‚à€š à€•à„‰à€Č à€•à€°à„‚ à€¶à€•à€€ à€šà€Ÿà€čà„€"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"à€€à„à€źà€šà„‡ à€•à€Ÿà€źà€Ÿà€¶à„€ à€žà€‚à€Źà€‚à€§à€żà€€ à€§à„‹à€°à€Ł à€€à„à€źà„à€čà€Ÿà€Čà€Ÿ à€«à€•à„à€€ à€•à€Ÿà€°à„à€Ż à€Șà„à€°à„‹à€«à€Ÿà€‡à€Čà€”à€°à„‚à€š à€«à„‹à€š à€•à„‰à€Č à€•à€°à€šà„à€Żà€Ÿà€šà„€ à€…à€šà„à€źà€€à„€ à€Šà„‡à€€à„‡"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"à€•à€Ÿà€°à„à€Ż à€Șà„à€°à„‹à€«à€Ÿà€‡à€Čà€”à€° à€žà„à€”à€żà€š à€•à€°à€Ÿ"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"à€Źà€‚à€Š à€•à€°à€Ÿ"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"à€Čà„‰à€• à€žà„à€•à„à€°à„€à€š à€žà„‡à€Ÿà€żà€‚à€—à„à€œ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mr/tiles_states_strings.xml b/packages/SystemUI/res/values-mr/tiles_states_strings.xml
index f75f0d0..78560c4 100644
--- a/packages/SystemUI/res/values-mr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mr/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"à€Źà€‚à€Š à€†à€čà„‡"</item>
     <item msgid="5966994759929723339">"à€žà„à€°à„‚ à€†à€čà„‡"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 6930fac9..c3408b7 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Sempadan bawah <xliff:g id="PERCENT">%1$d</xliff:g> peratus"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Sempadan kiri <xliff:g id="PERCENT">%1$d</xliff:g> peratus"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Sempadan kanan <xliff:g id="PERCENT">%1$d</xliff:g> peratus"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Tangkapan skrin tugasan disimpan dalam apl <xliff:g id="APP">%1$s</xliff:g>"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Disimpan dalam <xliff:g id="APP">%1$s</xliff:g> dalam profil kerja"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Fail"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> telah mengesan tangkapan skrin ini."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> dan apl lain yang dibuka telah mengesan tangkapan skrin ini."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Perakam Skrin"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Memproses rakaman skrin"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Pemberitahuan breterusan untuk sesi rakaman skrin"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Kecerahan"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Penyongsangan warna"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Pembetulan warna"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Urus pengguna"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Selesai"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Tutup"</string>
@@ -775,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"rakaman skrin"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Tiada tajuk"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Tunggu sedia"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Tetingkap Pembesaran"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kawalan Tetingkap Pembesaran"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zum masuk"</string>
@@ -800,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Pilih apl untuk menambahkan kawalan"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# kawalan ditambah.}other{# kawalan ditambah.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Dialih keluar"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Tambahkan <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Apabila anda menambahkan <xliff:g id="APPNAME">%s</xliff:g>, apl ini boleh menambahkan kawalan dan kandungan pada panel ini. Dalam sesetengah apl, anda boleh memilih kawalan yang muncul di sini."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Digemari"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Digemari, kedudukan <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Dinyahgemari"</string>
@@ -850,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Buka <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Mainkan <xliff:g id="SONG_NAME">%1$s</xliff:g> oleh <xliff:g id="ARTIST_NAME">%2$s</xliff:g> daripada <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Mainkan <xliff:g id="SONG_NAME">%1$s</xliff:g> daripada <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Untuk Anda"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Buat asal"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Alihkan lebih dekat untuk bermain pada<xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Untuk bermain di sini, bergerak lebih dekat kepada <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -857,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Kesilapan telah berlaku. Cuba lagi."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Memuatkan"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Menghantar media anda"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Menghantar <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Tidak aktif, semak apl"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Tidak ditemukan"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Kawalan tidak tersedia"</string>
@@ -866,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Ralat, cuba lagi"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Tambah kawalan"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Edit kawalan"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Tambahkan apl"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Tambah output"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Kumpulan"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 peranti dipilih"</string>
@@ -881,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Pembesar Suara &amp; Paparan"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Peranti yang Dicadangkan"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Memerlukan akaun premium"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cara siaran berfungsi"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Siarkan"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Orang berdekatan anda dengan peranti Bluetooth yang serasi boleh mendengar media yang sedang anda siarkan"</string>
@@ -892,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Tidak dapat disiarkan"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Tidak dapat disimpan. Cuba lagi."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Tidak dapat disimpan."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Gunakan sekurang-kurangnya 4 aksara"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Gunakan kurang daripada 16 aksara"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Nombor binaan"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Nombor binaan disalin ke papan keratan."</string>
     <string name="basic_status" msgid="2315371112182658176">"Buka perbualan"</string>
@@ -1011,11 +1030,16 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Sekurang-kurangnya satu peranti tersedia"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Pintasan sentuh &amp; tahan"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Batal"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Balikkan sekarang"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Buka telefon untuk swafoto yang lebih baik"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Balikkan ke paparan depan utk swafoto lebih baik?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Gunakan kamera menghadap belakang untuk mendapatkan foto yang lebih luas dengan resolusi yang lebih tinggi."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Skrin ini akan dimatikan"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Peranti boleh lipat dibuka"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Peranti boleh lipat diterbalikkan"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Bateri tinggal <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
@@ -1026,4 +1050,5 @@
     <string name="call_from_work_profile_text" msgid="3458704745640229638">"Dasar kerja anda membenarkan anda membuat panggilan telefon hanya daripada profil kerja"</string>
     <string name="call_from_work_profile_action" msgid="2937701298133010724">"Tukar kepada profil kerja"</string>
     <string name="call_from_work_profile_close" msgid="7927067108901068098">"Tutup"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Tetapan skrin kunci"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ms/tiles_states_strings.xml b/packages/SystemUI/res/values-ms/tiles_states_strings.xml
index 9fa7ab5..f3dafa5 100644
--- a/packages/SystemUI/res/values-ms/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ms/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Mati"</item>
     <item msgid="5966994759929723339">"Hidup"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 3a7cf14..b74dd93 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"အလုပá€șနဟင့á€șဆိုငá€șသေဏ ဖနá€șá€žá€Źá€žá€•á€Œá€„á€șဓာတá€șပုံမျဏသကို <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>
     <string name="screenrecord_name" msgid="2596401223859996572">"ဖနá€șá€žá€Źá€žá€•á€Œá€„á€ș ရိုကá€șá€€á€°á€žá€™á€Ÿá€Ż"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"စကရငá€șရိုကá€șá€€á€°á€žá€™á€Ÿá€Ż á€Ąá€•á€Œá€źá€žá€žá€á€șနေသညá€ș"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"ဖနá€șá€žá€Źá€žá€•á€Œá€„á€ș ရိုကá€șကူသသည့á€ș စကá€șရဟငá€șá€Ąá€á€œá€€á€ș ဆကá€șတိုကá€șá€œá€Źá€”á€±á€žá€±á€Ź á€Ąá€€á€Œá€±á€Źá€„á€șá€žá€€á€Œá€Źá€žá€á€»á€€á€ș"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"အလငá€șသတေဏကá€șá€•á€™á€Ÿá€Ż"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ဥရေဏငá€șá€•á€Œá€±á€Źá€„á€șသပဌနá€șá€•á€Œá€Żá€œá€Żá€•á€șရနá€ș"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ဥရေဏငá€ș á€Ąá€™á€Ÿá€”á€șပဌငá€șခဌငá€șှ"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"á€Ąá€žá€Żá€¶á€žá€•á€Œá€Żá€žá€°á€™á€»á€Źá€ž စဟမံရနá€ș"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"á€•á€Œá€źá€žá€•á€«á€•á€Œá€ź"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"ပိတá€șရနá€ș"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"á€Ąá€œá€­á€Żá€Ąá€œá€»á€±á€Źá€€á€ș"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"ဥသံ á€žá€­á€Żá€·á€™á€Ÿá€Żá€á€ș တုနá€șá€á€«á€™á€Ÿá€Żá€™á€›á€Ÿá€­á€•á€«"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"ဥသံ á€žá€­á€Żá€·á€™á€Ÿá€Żá€á€ș တုနá€șá€á€«á€™á€Ÿá€Żá€™á€›á€Ÿá€­á€•á€«áŠ စကာှဝိုငá€șှကဏá€čဍ၏ ဥေဏကá€șပိုငá€șသတလငá€ș မဌငá€șရသညá€ș"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"စကá€șပစá€čစညá€șှ ဆကá€șတငá€șမျဏသပေါá€ș á€Ąá€á€Œá€±á€á€¶á€•á€Œá€źá€ž á€Ąá€žá€¶á€™á€Œá€Šá€șနိုငá€șသညá€ș (သို့) တုနá€șခါနိုငá€șသညá€ș"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"စကá€șပစá€čစညá€șှဆကá€șတငá€șမျဏသပေါá€ș á€Ąá€á€Œá€±á€á€¶á€•á€Œá€źá€ž á€Ąá€žá€¶á€™á€Œá€Šá€șနိုငá€șသညá€ș (သို့) တုနá€șခါနိုငá€șသညá€ș။ မူရငá€șှသတá€șမဟတá€șချကá€șá€Ąá€–á€Œá€…á€ș <xliff:g id="APP_NAME">%1$s</xliff:g> မဟ စကာှဝိုငá€șသမျဏသကို ပူဖေဏငá€șသကလကá€șဖဌင့á€ș ပဌသညá€ș။"</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"á€€á€Ąá€€á€Œá€±á€Źá€„á€șá€žá€€á€Œá€Źá€žá€á€»á€€á€șက ဥသံ á€žá€­á€Żá€·á€™á€Ÿá€Żá€á€ș တုနá€șá€á€«á€™á€Ÿá€Ż ပေသရနá€ș သင့á€ș/မသင့á€șကို စနစá€șက á€†á€Żá€¶á€žá€–á€Œá€á€șပါစေ"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;á€Ąá€á€Œá€±á€Ąá€”á€±-&lt;/b&gt; မူရငá€șသသို့ ချိနá€șá€Šá€Ÿá€­á€‘á€Źá€žá€žá€Šá€ș"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;á€Ąá€á€Œá€±á€Ąá€”á€±-&lt;/b&gt; ဥသံတိတá€șခဌငá€șသသို့ ပဌနá€șချိနá€șá€Šá€Ÿá€­á€‘á€Źá€žá€žá€Šá€ș"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"စခရငá€șရိုကá€șá€€á€°á€žá€™á€Ÿá€Ż"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"ခေါငá€șှစဉá€ș မရဟိပါ"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"ဥသင့á€șဥနေဥထဏသ"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"ဝငá€șှဒိုှ ချá€Č့ခဌငá€șှ"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"ဝငá€șှဒိုှ ထိနá€șသချုပá€șá€™á€Ÿá€Żá€™á€»á€Źá€ž ချá€Č့ခဌငá€șှ"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ဇူှမá€șဆလá€Čရနá€ș"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"ထိနá€șသချုပá€șá€™á€Ÿá€Żá€™á€»á€Źá€žá€‘á€Šá€·á€șရနá€ș အကá€șပá€șရလေသခဌငá€șှ"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{ထိနá€șသချုပá€șခလုတá€ș # ခု ထည့á€șထာှသညá€ș။}other{ထိနá€șသချုပá€șခလုတá€ș # ခု ထည့á€șထာှသညá€ș။}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"ဖယá€șá€›á€Ÿá€Źá€žá€‘á€Źá€žá€žá€Šá€ș"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g> ထည့á€șမလာှ။"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"<xliff:g id="APPNAME">%s</xliff:g> ထည့á€șသေဏဥခါ ၎ငá€șှသညá€ș ကဥကန့á€șတလငá€ș သတá€șမဟတá€șချကá€șá€™á€»á€Źá€žá€”á€Ÿá€„á€·á€ș á€Ąá€€á€Œá€±á€Źá€„á€șှအရာကို ထည့á€șနိုငá€șသညá€ș။ အကá€șပá€șá€Ąá€á€»á€­á€Żá€·áŒ မဌငá€șရမည့á€ș သတá€șမဟတá€șချကá€șမျဏသကို á€€á€”á€±á€›á€Źá€á€œá€„á€ș á€›á€œá€±á€žá€”á€­á€Żá€„á€șသညá€ș။"</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"á€Ąá€€á€Œá€­á€Żá€€á€șá€†á€Żá€¶á€žá€á€œá€„á€ș ထည့á€șထာှသညá€ș"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"á€Ąá€€á€Œá€­á€Żá€€á€șá€†á€Żá€¶á€žá€á€œá€„á€ș ထည့á€șထာှသညá€ș၊ ဥဆင့á€ș <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"á€Ąá€€á€Œá€­á€Żá€€á€șá€†á€Żá€¶á€žá€™á€Ÿ ဖယá€șá€›á€Ÿá€Źá€žá€‘á€Źá€žá€žá€Šá€ș"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> ကို ဖလင့á€șပါ"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> ၏ <xliff:g id="SONG_NAME">%1$s</xliff:g> ကို <xliff:g id="APP_LABEL">%3$s</xliff:g> တလငá€ș ဖလင့á€șပါ"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> ကို <xliff:g id="APP_LABEL">%2$s</xliff:g> တလငá€ș ဖလင့á€șပါ"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"သင့á€șá€Ąá€á€œá€€á€ș"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"နေဏကá€șပဌနá€șရနá€ș"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> တလငá€șဖလင့á€șရနá€ș á€Ąá€”á€źá€žá€žá€­á€Żá€·á€›á€œá€Ÿá€±á€·á€•á€«"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"á€€á€”á€±á€›á€Źá€á€œá€„á€ș ဖလင့á€șရနá€ș <xliff:g id="DEVICENAME">%1$s</xliff:g> ဥနဟသသို့ ရလဟေ့ပါ"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"တစá€șá€á€Żá€á€Żá€™á€Ÿá€Źá€žá€žá€œá€Źá€žá€žá€Šá€ș။ ထပá€șစမá€șသကဌည့á€șပါ။"</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"ဖလင့á€șနေသညá€ș"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"တကá€șဘလကá€ș"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"သင့á€șမြဒြယာကို ကာစá€șလုပá€șနေသညá€ș"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"<xliff:g id="APP_LABEL">%1$s</xliff:g> ကို ကာစá€șလုပá€șနေသညá€ș"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ရပá€șနေသညá€ș၊ အကá€șပá€șကို စစá€șဆေသပါ"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"မတလေ့ပါ"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"ထိနá€șသချုပá€șá€™á€Ÿá€Ż မရနိုငá€șပါ"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"á€™á€Ÿá€Źá€žá€žá€œá€Źá€žá€žá€Šá€ș၊ ပဌနá€șစမá€șသကဌည့á€șပါ"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"ထိနá€șသချုပá€șá€™á€Ÿá€Żá€™á€»á€Źá€ž ထည့á€șရနá€ș"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"ထိနá€șသချုပá€șá€™á€Ÿá€Żá€™á€»á€Źá€ž ပဌငá€șရနá€ș"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"အကá€șပá€șထည့á€șရနá€ș"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"á€™á€źá€’á€źá€šá€Źá€Ąá€‘á€œá€€á€șမျဏသ ထည့á€șရနá€ș"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"အုပá€șစု"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"စကá€șပစá€čစညá€șှ ၁ ခုကို ရလေသချယá€șထာှသညá€ș"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"á€…á€•á€źá€€á€Źá€”á€Ÿá€„á€·á€ș ဖနá€șá€žá€Źá€žá€•á€Œá€„á€șမျဏသ"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"á€Ąá€€á€Œá€¶á€•á€Œá€Żá€‘á€Źá€žá€žá€±á€Ź စကá€șပစá€čစညá€șသမျဏသ"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"ပရဟမဟယံဥကေဏင့á€ș လိုအပá€șသညá€ș"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ထုတá€șလလဟင့á€șá€™á€Ÿá€Żá€†á€±á€Źá€„á€șရလကá€șပုံ"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"ထုတá€șလလဟင့á€șခဌငá€șှ"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"á€Ąá€”á€źá€žá€›á€Ÿá€­á€á€œá€Čသုံသနိုငá€șသေဏ ဘလူှတုသá€șသုံသစကá€ș á€Ąá€žá€Żá€¶á€žá€•á€Œá€Żá€žá€°á€™á€»á€Źá€žá€€ သငá€șထုတá€șလလဟင့á€șနေသေဏ မြဒြယာကို နာှဆငá€șနိုငá€șသညá€ș"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"ထုတá€șလလဟင့á€ș၍ မရပါ"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"သိမá€șသ၍မရပါ။ ထပá€șစမá€șသကဌည့á€șပါ။"</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"သိမá€șသ၍မရပါ။"</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"တညá€șဆေဏကá€șá€™á€Ÿá€Żá€”á€¶á€•á€«á€á€ș"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"တညá€șဆေဏကá€șá€™á€Ÿá€Żá€”á€¶á€•á€«á€á€șကို ကလစá€șဘုတá€șသို့ မိတá€čá€á€°á€€á€°á€žá€•á€Œá€źá€žá€•á€«á€•á€Œá€źá‹"</string>
     <string name="basic_status" msgid="2315371112182658176">"စကာှဝိုငá€șှကို ဖလင့á€șရနá€ș"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• အနညá€șသဆုံသ စကá€șတစá€șခုသုံသနိုငá€șရမညá€ș"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"ဖဌတá€șလမá€șသလင့á€șခá€șကို á€‘á€­á€•á€Œá€źá€žá€–á€­á€‘á€Źá€žá€•á€«"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"မလုပá€șတေဏ့"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"á€šá€á€Żá€œá€Ÿá€Šá€·á€șလိုကá€șပါ"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"ပိုကေဏငá€șသသေဏ ဆယá€șလá€șá€–á€źá€Ąá€á€œá€€á€ș ဖုနá€șá€žá€€á€­á€Żá€–á€Œá€”á€·á€șလိုကá€șပါ"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"ပိုကေဏငá€șသသေဏ ဆယá€șလá€șá€–á€źá€Ąá€á€œá€€á€ș ဖနá€șá€žá€Źá€žá€•á€Œá€„á€șá€€á€­á€Żá€œá€Ÿá€Šá€·á€șမလာှ။"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"ပုံရိပá€șပဌတá€șသာှကိနá€șှ á€•á€­á€Żá€™á€Œá€„á€·á€șá€•á€Œá€źá€ž မဌငá€șကလငá€șသပိုကျယá€șသည့á€ș ဓာတá€șá€•á€Żá€¶á€Ąá€á€œá€€á€ș နေဏကá€șဘကá€șကငá€șမရာကို á€Ąá€žá€Żá€¶á€žá€•á€Œá€Żá€•á€«á‹"</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ကဖနá€șá€žá€Źá€žá€•á€Œá€„á€șကို ပိတá€șလိုကá€șမညá€ș"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ခေါကá€șနိုငá€șသေဏစကá€șကို ဖဌန့á€șလိုကá€șသညá€ș"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ခေါကá€șနိုငá€șသေဏစကá€șကို တစá€șဘကá€șသို့ လဟနá€șလိုကá€șသညá€ș"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"ဘကá€șထရြ <xliff:g id="PERCENTAGE">%s</xliff:g> ကျနá€șသေသသညá€ș"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"စတိုငá€șလပá€șစá€șကို á€Ąá€Źá€žá€žá€œá€„á€șá€žá€€á€­á€›á€­á€šá€Źá€”á€Ÿá€„á€·á€ș ချိတá€șဆကá€șခဌငá€șှ"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"စတိုငá€șလပá€șစá€ș ဘကá€șထရြ အာှနညá€șသနေသညá€ș"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"ဗြဒြယိုကငá€șမရာ"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"ကပရိုဖိုငá€șမဟ ခေါá€șဆို၍ မရပါ"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"သင့á€șအလုပá€șမူဝါဒသညá€ș သင့á€șအာှ အလုပá€șပရိုဖိုငá€șá€™á€Ÿá€žá€Ź ဖုနá€șသခေါá€șá€†á€­á€Żá€á€œá€„á€·á€ș á€•á€Œá€Żá€žá€Šá€ș"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"အလုပá€șပရိုဖိုငá€șသို့ á€•á€Œá€±á€Źá€„á€șှရနá€ș"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"ပိတá€șရနá€ș"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"á€œá€±á€Źá€·á€á€șမျကá€șá€”á€Ÿá€Źá€•á€Œá€„á€ș ဆကá€șတငá€șမျဏသ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-my/tiles_states_strings.xml b/packages/SystemUI/res/values-my/tiles_states_strings.xml
index 493a7f0..cadf009 100644
--- a/packages/SystemUI/res/values-my/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-my/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"ပိတá€ș"</item>
     <item msgid="5966994759929723339">"ဖလင့á€ș"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 5fc1884..af9cf7e 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Nedre grense <xliff:g id="PERCENT">%1$d</xliff:g> prosent"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Venstre grense <xliff:g id="PERCENT">%1$d</xliff:g> prosent"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Høyre grense <xliff:g id="PERCENT">%1$d</xliff:g> prosent"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Jobbrelaterte skjermdumper lagres i <xliff:g id="APP">%1$s</xliff:g>-appen"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Lagret i <xliff:g id="APP">%1$s</xliff:g> i jobbprofilen"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Filer"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> har registrert denne skjermdumpen."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> og andre åpne apper har registrert denne skjermdumpen."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Skjermopptaker"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Behandler skjermopptaket"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Vedvarende varsel for et skjermopptak"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Lysstyrke"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Fargeinvertering"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Fargekorrigering"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Administrer brukere"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Ferdig"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Lukk"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatisk"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Ingen lyd eller vibrering"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Ingen lyd eller vibrering, og vises lavere i samtaledelen"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Kan ringe eller vibrere basert på enhetsinnstillingene"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Kan ringe eller vibrere basert på enhetsinnstillingene. Samtaler fra <xliff:g id="APP_NAME">%1$s</xliff:g> vises som standard som bobler."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"La systemet velge om dette varselet skal lage lyd eller vibrere"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Status:&lt;/b&gt; Oppgradert til standard"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Status:&lt;/b&gt; Nedgradert til lydløst"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"skjermopptak"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Ingen tittel"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Ventemodus"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Forstørringsvindu"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kontroller for forstørringsvindu"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zoom inn"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Velg en app for å legge til kontroller"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# kontroll er lagt til.}other{# kontroller er lagt til.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Fjernet"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Vil du legge til <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Når du legger til <xliff:g id="APPNAME">%s</xliff:g>, kan den legge til kontroller og innhold i dette panelet. I noen apper kan du velge hvilke kontroller som vises her."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Favoritt"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Favoritt, posisjon <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Fjernet som favoritt"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Åpne <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Spill av <xliff:g id="SONG_NAME">%1$s</xliff:g> av <xliff:g id="ARTIST_NAME">%2$s</xliff:g> fra <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Spill av <xliff:g id="SONG_NAME">%1$s</xliff:g> fra <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"For deg"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Angre"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Flytt nærmere for å spille av på <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"For å spille av her, gå nærmere <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Noe gikk galt. Prøv på nytt."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Laster inn"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"nettbrett"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Caster mediene"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Caster <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv. Sjekk appen"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Ikke funnet"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Kontrollen er utilgjengelig"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"En feil oppsto. Prøv på nytt"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Legg til kontroller"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Endre kontroller"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Legg til app"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Legg til utenheter"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Gruppe"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 enhet er valgt"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Høyttalere og skjermer"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Foreslåtte enheter"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Krever en premium-konto"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Slik fungerer kringkasting"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Kringkasting"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Folk i nærheten med kompatible Bluetooth-enheter kan lytte til mediene du kringkaster"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Kan ikke kringkaste"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Kan ikke lagre. Prøv på nytt."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Kan ikke lagre."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Delversjonsnummer"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Delversjonsnummeret er kopiert til utklippstavlen."</string>
     <string name="basic_status" msgid="2315371112182658176">"Åpen samtale"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• minst én enhet er tilgjengelig"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Trykk på og hold inne snarveien"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Avbryt"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Vend nå"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Brett ut telefonen for å ta bedre selfier"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Vil du bytte til frontskjermen for bedre selfier?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Bruk det bakovervendte kameraet for å ta bredere bilder med høyere oppløsning."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Denne skjermen slås av"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"En sammenleggbar enhet blir brettet ut"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"En sammenleggbar enhet blir snudd"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> batteri gjenstår"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Koble pekepennen til en lader"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Det er lite batteri i pekepennen"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Kan ikke ringe fra denne profilen"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Som følge av jobbreglene dine kan du bare starte telefonanrop fra jobbprofilen."</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Bytt til jobbprofilen"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Lukk"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Innstillinger for låseskjermen"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-nb/tiles_states_strings.xml b/packages/SystemUI/res/values-nb/tiles_states_strings.xml
index 6fa902a..b465617 100644
--- a/packages/SystemUI/res/values-nb/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-nb/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Av"</item>
     <item msgid="5966994759929723339">"På"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index d74d153..ff017ab 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"à€•à€Ÿà€°à„à€Ż à€Șà„à€°à„‹à€«à€Ÿà€‡à€Č à€Șà„à€°à€Żà„‹à€— à€—à€°à„€ à€Čà€żà€‡à€à€•à€Ÿ à€žà„à€•à„à€°à€żà€šà€žà€Ÿà€čà€°à„‚ <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">"Files"</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>
     <string name="screenrecord_name" msgid="2596401223859996572">"à€žà„à€•à„à€°à€żà€š à€°à„‡à€•à€°à„à€Ąà€°"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"à€žà„à€•à„à€°à€żà€š à€°à„‡à€•à€°à„à€Ąà€żà€™à€•à„‹ à€Șà„à€°à€•à„à€°à€żà€Żà€Ÿ à€…à€˜à€ż à€Źà€ąà€Ÿà€‡à€à€Šà„ˆ"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"à€•à„à€šà„ˆ à€žà„à€•à„à€°à€żà€š à€°à„‡à€•à€°à„à€Ą à€—à€°à„à€šà„‡ à€žà€€à„à€°à€•à€Ÿ à€Čà€Ÿà€—à€ż à€šà€Čà€żà€°à€čà„‡à€•à„‹ à€žà„‚à€šà€šà€Ÿ"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"à€‰à€œà„à€Żà€Ÿà€Čà€Șà€š"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"à€•à€Čà€° à€‡à€šà„à€­à€°à„à€žà€š"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"à€•à€Čà€° à€•à€°à„‡à€•à„à€žà€š"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"à€Șà„à€°à€Żà„‹à€—à€•à€°à„à€€à€Ÿà€čà€°à„‚ à€”à„à€Żà€”à€žà„à€„à€żà€€ à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"à€­à€Żà„‹"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"à€Źà€šà„à€Š à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"à€žà„à€”à€šà€Ÿà€Čà€żà€€"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"à€Źà€œà„à€Šà„ˆà€š à€Șà€šà€ż, à€­à€Ÿà€‡à€Źà„à€°à„‡à€Ÿ à€Șà€šà€ż à€čà„à€à€Šà„ˆà€š"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"à€Źà€œà„à€Šà„ˆà€š à€Șà€šà€ż, à€­à€Ÿà€‡à€Źà„à€°à„‡à€Ÿ à€Șà€šà€ż à€čà„à€à€Šà„ˆà€š à€° à€”à€Ÿà€°à„à€€à€Ÿà€Čà€Ÿà€Ș à€–à€Łà„à€Ąà€•à„‹ à€€à€Čà€€à€żà€° à€Šà„‡à€–à€Ÿ à€Șà€°à„à€›"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"à€Ąà€żà€­à€Ÿà€‡à€žà€•à„‹ à€žà„‡à€Ÿà€żà€™à€•à€Ÿ à€†à€§à€Ÿà€°à€źà€Ÿ à€˜à€šà„à€Ÿà„€ à€Źà€œà„à€š à€”à€Ÿ à€­à€Ÿà€‡à€Źà„à€°à„‡à€Ÿ à€čà„à€š à€žà€•à„à€›"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"à€Ąà€żà€­à€Ÿà€‡à€žà€•à„‹ à€žà„‡à€Ÿà€żà€™à€•à€Ÿ à€†à€§à€Ÿà€°à€źà€Ÿ à€˜à€šà„à€Ÿà„€ à€Źà€œà„à€š à€”à€Ÿ à€•à€źà„à€Șà€š à€čà„à€š à€žà€•à„à€›à„€ <xliff:g id="APP_NAME">%1$s</xliff:g> à€źà€Ÿà€°à„à€«à€€ à€—à€°à€żà€à€•à€Ÿ à€”à€Ÿà€°à„à€€à€Ÿà€Čà€Ÿà€Șà€čà€°à„‚ à€žà„à€”à€€à€ƒ à€Źà€Źà€Čà€źà€Ÿ à€Šà„‡à€–à€żà€šà„à€›à€šà„à„€"</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"à€žà€żà€žà„à€Ÿà€źà€Čà€Ÿà€ˆ à€Żà„‹ à€žà„‚à€šà€šà€Ÿ à€†à€‰à€à€Šà€Ÿ à€§à„à€”à€šà€ż à€Źà€œà„à€šà„ à€Șà€°à„à€› à€”à€Ÿ à€•à€źà„à€Șà€š à€čà„à€šà„ à€Șà€°à„à€› à€­à€šà„à€šà„‡ à€•à„à€°à€Ÿà€•à„‹ à€šà€żà€§à„‹ à€—à€°à„à€š à€Šà€żà€šà„à€čà„‹à€žà„"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;à€žà„à€„à€żà€€à€ż:&lt;/b&gt; à€žà„‚à€šà€šà€Ÿà€Čà€Ÿà€ˆ à€źà€čà€€à„à€€à„à€”à€Șà„‚à€°à„à€Ł à€ à€Ÿà€šà„€ à€Ąà€żà€«à€Čà„à€Ÿ à€źà„‹à€Ąà€źà€Ÿ à€žà„‡à€Ÿ à€—à€°à€żà€à€•à„‹ à€›"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;à€žà„à€„à€żà€€à€ż:&lt;/b&gt; à€žà„‚à€šà€šà€Ÿà€Čà€Ÿà€ˆ à€•à€ź à€źà€čà€€à„à€€à„à€”à€Șà„‚à€°à„à€Ł à€ à€Ÿà€šà„€ à€žà€Ÿà€‡à€Čà„‡à€šà„à€Ÿ à€źà„‹à€Ąà€źà€Ÿ à€žà„‡à€Ÿ à€—à€°à€żà€à€•à„‹ à€›"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"à€žà„à€•à„à€°à€żà€š à€°à„‡à€•à€°à„à€Ąà€żà€™"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"à€¶à„€à€°à„à€·à€• à€›à„ˆà€š"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"à€žà„à€Ÿà„à€Żà€Ÿà€šà„à€Ąà€Źà€Ÿà€ˆ"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"à€źà„à€Żà€Ÿà€—à„à€šà€żà€«à€żà€•à„‡à€žà€š à€”à€żà€šà„à€Ąà„‹"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"à€źà„à€Żà€Ÿà€—à„à€šà€żà€«à€żà€•à„‡à€žà€š à€”à€żà€šà„à€Ąà„‹à€•à€Ÿ à€šà€żà€Żà€šà„à€€à„à€°à€Łà€čà€°à„‚"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"à€œà„à€ź à€‡à€š à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"à€•à€šà„à€Ÿà„à€°à„‹à€Č à€„à€Șà„à€šà„ à€Șà€°à„à€šà„‡ à€à€Ș à€›à€Ÿà€šà„à€šà„à€čà„‹à€žà„"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# à€•à€šà„à€Ÿà„à€°à„‹à€Č à€čà€Ÿà€Čà€żà€Żà„‹à„€}other{# à€”à€Ÿà€Ÿ à€•à€šà„à€Ÿà„à€°à„‹à€Č à€čà€Ÿà€Čà€żà€Żà„‹à„€}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"à€čà€Ÿà€Ÿà€‡à€à€•à„‹"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g> à€čà€Ÿà€Čà„à€šà„‡ à€čà„‹?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"à€€à€Șà€Ÿà€ˆà€‚à€Čà„‡ <xliff:g id="APPNAME">%s</xliff:g> à€čà€Ÿà€Čà„à€šà„à€­à€Żà„‹ à€­à€šà„‡ à€Żà€žà€Čà„‡ à€Żà„‹ à€Șà„à€Żà€Ÿà€šà€Čà€źà€Ÿ à€žà„‡à€Ÿà€żà€™ à€° à€žà€Ÿà€źà€—à„à€°à„€ à€čà€Ÿà€Čà„à€š à€žà€•à„à€›à„€ à€€à€Șà€Ÿà€ˆà€‚ à€•à„‡à€čà„€ à€à€Șà€čà€°à„‚à€źà€Ÿ à€Żà€čà€Ÿà€ à€•à„à€š à€•à„à€š à€žà„‡à€Ÿà€żà€™ à€Šà„‡à€–à€Ÿà€‰à€šà„‡ à€­à€šà„à€šà„‡ à€•à„à€°à€Ÿ à€›à€šà„Œà€Ÿ à€—à€°à„à€š à€žà€•à„à€šà„à€čà„à€šà„à€›à„€"</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"à€źà€šà€Șà€°à€Ÿà€‡à€à€•à„‹"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"à€źà€š à€Șà€°à€Ÿà€‡à€à€•à€Ÿ à€•à„à€°à€Ÿà€čà€°à„‚à€•à„‹ <xliff:g id="NUMBER">%d</xliff:g> à€”à€ à€žà„à€„à€Ÿà€šà€źà€Ÿ"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"à€źà€š à€Șà€°à„à€šà„‡ à€•à„à€°à€Ÿà€čà€°à„‚à€•à„‹ à€žà„‚à€šà„€à€źà€Ÿ à€šà€°à€Ÿà€–à€żà€à€•à„‹"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> à€–à„‹à€Čà„à€šà„à€čà„‹à€žà„"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> à€•à„‹ <xliff:g id="SONG_NAME">%1$s</xliff:g> à€Źà„‹à€Čà€•à„‹ à€—à„€à€€ <xliff:g id="APP_LABEL">%3$s</xliff:g> à€źà€Ÿ à€Źà€œà€Ÿà€‰à€šà„à€čà„‹à€žà„"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> à€Źà„‹à€Čà€•à„‹ à€—à„€à€€ <xliff:g id="APP_LABEL">%2$s</xliff:g> à€źà€Ÿ à€Źà€œà€Ÿà€‰à€šà„à€čà„‹à€žà„"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"à€€à€Șà€Ÿà€ˆà€‚à€•à„‹ à€Čà€Ÿà€—à€ż à€žà€żà€«à€Ÿà€°à€żà€ž à€—à€°à€żà€à€•à€Ÿ"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"à€…à€šà„à€Ąà„‚ à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> à€źà€Ÿ à€Șà„à€Čà„‡ à€—à€°à„à€š à€†à€«à„à€šà„‹ à€Ąà€żà€­à€Ÿà€‡à€ž à€šà€œà€żà€•à„ˆ à€Čà„ˆà€œà€Ÿà€šà„à€čà„‹à€žà„"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"à€Żà„‹ à€Ąà€żà€­à€Ÿà€‡à€žà€źà€Ÿ à€Șà„à€Čà„‡ à€—à€°à„à€š <xliff:g id="DEVICENAME">%1$s</xliff:g> à€•à„‹ à€…à€ à€šà€œà€żà€• à€œà€Ÿà€šà„à€čà„‹à€žà„"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"à€•à„‡à€čà„€ à€šà€żà€œ à€—à€Ąà€Źà€Ą à€­à€Żà„‹à„€ à€«à„‡à€°à€ż à€Șà„à€°à€Żà€Ÿà€ž à€—à€°à„à€šà„à€čà„‹à€žà„à„€"</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"à€Čà„‹à€Ą à€čà„à€à€Šà„ˆ à€›"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"à€Ÿà„à€Żà€Ÿà€Źà„à€Čà„‡à€Ÿ"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"à€€à€Șà€Ÿà€ˆà€‚à€•à„‹ à€źà€żà€Ąà€żà€Żà€Ÿ à€•à€Ÿà€žà„à€Ÿ à€—à€°à€żà€à€Šà„ˆ à€›"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"<xliff:g id="APP_LABEL">%1$s</xliff:g> à€•à€Ÿà€žà„à€Ÿ à€—à€°à€żà€à€Šà„ˆ à€›"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"à€šà€żà€·à„à€•à„à€°à€żà€Ż à€›, à€à€Ș à€œà€Ÿà€à€š à€—à€°à„à€šà„…"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"à€«à„‡à€Čà€Ÿ à€Șà€°à„‡à€š"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"à€šà€żà€Żà€šà„à€€à„à€°à€Ł à€‰à€Șà€Čà€Źà„à€§ à€›à„ˆà€š"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"à€€à„à€°à„à€Ÿà€ż à€­à€Żà„‹, à€«à„‡à€°à€ż à€Șà„à€°à€Żà€Ÿà€ž à€—à€°à„à€šà„…"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"à€•à€šà„à€Ÿà„à€°à„‹à€Č à€„à€Șà„à€šà„à€čà„‹à€žà„"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"à€•à€šà„à€Ÿà„à€°à„‹à€Č à€žà€źà„à€Șà€Ÿà€Šà€š à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"à€à€Ș à€čà€Ÿà€Čà„à€šà„à€čà„‹à€žà„"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"à€†à€‰à€Ÿà€Șà„à€Ÿ à€Żà€šà„à€€à„à€°à€čà€°à„‚ à€„à€Șà„à€šà„à€čà„‹à€žà„"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"à€žà€źà„‚à€č"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"à„§ à€Żà€šà„à€€à„à€° à€šà€Żà€š à€—à€°à€żà€Żà„‹"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"à€žà„à€Șà€żà€•à€° à€€à€„à€Ÿ à€Ąà€żà€žà„à€Șà„à€Čà„‡à€čà€°à„‚"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"à€žà€żà€«à€Ÿà€°à€żà€ž à€—à€°à€żà€à€•à€Ÿ à€Ąà€żà€­à€Ÿà€‡à€žà€čà€°à„‚"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"à€Șà„à€°à€żà€źà€żà€Żà€ź à€–à€Ÿà€€à€Ÿ à€šà€Ÿà€čà€żà€šà„à€›"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"à€Șà„à€°à€žà€Ÿà€°à€Ł à€—à€°à„à€šà„‡ à€žà„à€”à€żà€§à€Ÿà€Čà„‡ à€•à€žà€°à„€ à€•à€Ÿà€ź à€—à€°à„à€›"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"à€Șà„à€°à€žà€Ÿà€°à€Ł"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"à€•à€źà„à€Șà„à€Żà€Ÿà€Ÿà€żà€Źà€Č à€Źà„à€Čà„à€Ÿà„à€„ à€Ąà€żà€­à€Ÿà€‡à€ž à€­à€à€•à€Ÿ à€šà€œà€żà€•à„ˆà€•à€Ÿ à€źà€Ÿà€šà„à€›à„‡à€čà€°à„‚ à€€à€Șà€Ÿà€ˆà€‚à€Čà„‡ à€Șà„à€°à€žà€Ÿà€°à€Ł à€—à€°à€żà€°à€čà€šà„à€­à€à€•à„‹ à€źà€żà€Ąà€żà€Żà€Ÿ à€žà„à€šà„à€š à€žà€•à„à€›à€šà„"</string>
@@ -894,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"à€Șà„à€°à€žà€Ÿà€°à€Ł à€—à€°à„à€š à€žà€•à€żà€à€š"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"à€žà„‡à€­ à€—à€°à„à€š à€žà€•à€żà€à€šà„€ à€«à„‡à€°à€ż à€Șà„à€°à€Żà€Ÿà€ž à€—à€°à„à€šà„à€čà„‹à€žà„à„€"</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"à€žà„‡à€­ à€—à€°à„à€š à€žà€•à€żà€à€šà„€"</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"à€•à€źà„à€€à„€à€źà€Ÿ à„Ș à€”à€Ÿà€Ÿ à€”à€°à„à€Ł à€Șà„à€°à€Żà„‹à€— à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"à„§à„Ź à€”à€Ÿà€Ÿà€­à€šà„à€Šà€Ÿ à€•à€ź à€”à€°à„à€Ł à€Șà„à€°à€Żà„‹à€— à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"à€Źà€żà€Čà„à€Ą à€šà€źà„à€Źà€°"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"à€Źà€żà€Čà„à€Ą à€šà€źà„à€Źà€° à€•à€Șà„€ à€—à€°à„€ à€•à„à€Čà€żà€Șà€Źà„‹à€°à„à€Ąà€źà€Ÿ à€žà€Ÿà€°à€żà€Żà„‹à„€"</string>
     <string name="basic_status" msgid="2315371112182658176">"à€”à€Ÿà€°à„à€€à€Ÿà€Čà€Ÿà€Ș à€–à„‹à€Čà„à€šà„à€čà„‹à€žà„"</string>
@@ -1013,24 +1030,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• à€•à€źà„à€€à„€à€źà€Ÿ à€à€‰à€Ÿà€Ÿ à€Ąà€żà€­à€Ÿà€‡à€ž à€‰à€Șà€Čà€Źà„à€§ à€›"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"à€žà€°à„à€Ÿà€•à€Ÿ à€„à€żà€šà€żà€°à€Ÿà€–à„à€šà„à€čà„‹à€žà„"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"à€°à€Šà„à€Š à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"à€…à€čà€żà€Čà„‡ à€šà„ˆ à€«à„à€Čà€żà€Ș à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"à€…à€ à€°à€Ÿà€źà„à€°à„‹ à€žà„‡à€Čà„à€«à„€ à€–à€żà€šà„à€š à€«à„‹à€š à€…à€šà€«à„‹à€Čà„à€Ą à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"à€…à€ à€°à€Ÿà€źà„à€°à„‹ à€žà„‡à€Čà„à€«à„€ à€–à€żà€šà„à€š à€«à„à€Čà€żà€Ș à€—à€°à„€ à€…à€—à€Ÿà€Ąà€żà€Șà€Ÿà„à€Ÿà€żà€•à„‹ à€Ąà€żà€žà„à€Șà„à€Čà„‡ à€Șà„à€°à€Żà„‹à€— à€—à€°à„à€šà„‡ à€čà„‹?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"à€…à€ à€Źà€ąà„€ à€°à€żà€œà„‹à€Čà„à€Żà„à€žà€š à€­à€à€•à„‹ à€«à€°à€Ÿà€•à€żà€Čà„‹ à€«à„‹à€Ÿà„‹ à€–à€żà€šà„à€š à€Șà€›à€Ÿà€Ąà€żà€Șà€Ÿà„à€Ÿà€żà€•à„‹ à€•à„à€Żà€Ÿà€źà„‡à€°à€Ÿ à€Șà„à€°à€Żà„‹à€— à€—à€°à„à€šà„à€čà„‹à€žà„à„€"</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ à€Żà„‹ à€žà„à€•à„à€°à€żà€š à€…à€« à€čà„à€šà„‡ à€›"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"à€«à„‹à€Čà„à€Ą à€—à€°à„à€š à€źà€żà€Čà„à€šà„‡ à€Ąà€żà€­à€Ÿà€‡à€ž à€…à€šà€«à„‹à€Čà„à€Ą à€—à€°à„‡à€•à„‹ à€Šà„‡à€–à€Ÿà€‡à€à€•à„‹ à€à€šà€żà€źà„‡à€žà€š"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"à€«à„‹à€Čà„à€Ą à€—à€°à„à€š à€źà€żà€Čà„à€šà„‡ à€Ąà€żà€­à€Ÿà€‡à€ž à€Żà€€à€Ÿà€‰à€€à€Ÿ à€Șà€Čà„à€Ÿà€Ÿà€à€° à€Šà„‡à€–à€Ÿà€‡à€à€•à„‹ à€à€šà€żà€źà„‡à€žà€š"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> à€Źà„à€Żà€Ÿà€Ÿà„à€°à„€ à€Źà€Ÿà€à€•à„€ à€›"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"à€†à€«à„à€šà„‹ à€žà„à€Ÿà€Ÿà€‡à€Čà€ž à€šà€Ÿà€°à„à€œà€°à€źà€Ÿ à€•à€šà„‡à€•à„à€Ÿ à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"à€žà„à€Ÿà€Ÿà€‡à€Čà€žà€•à„‹ à€Źà„à€Żà€Ÿà€Ÿà„à€°à„€ à€Čà„‹ à€›"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"à€­à€żà€Ąà€żà€Żà„‹ à€•à„à€Żà€Ÿà€źà„‡à€°à€Ÿ"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"à€Żà„‹ à€Șà„à€°à„‹à€«à€Ÿà€‡à€Čà€Źà€Ÿà€Ÿ à€•à€Č à€—à€°à„à€š à€žà€•à€żà€à€Šà„ˆà€š"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"à€€à€Șà€Ÿà€ˆà€‚à€•à„‹ à€•à€Ÿà€źà€žà€źà„à€Źà€šà„à€§à„€ à€šà„€à€€à€żà€…à€šà„à€žà€Ÿà€° à€•à€Ÿà€°à„à€Ż à€Șà„à€°à„‹à€«à€Ÿà€‡à€Čà€Źà€Ÿà€Ÿ à€źà€Ÿà€€à„à€° à€«à„‹à€š à€•à€Č à€—à€°à„à€š à€žà€•à€żà€šà„à€›"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"à€•à€Ÿà€°à„à€Ż à€Șà„à€°à„‹à€«à€Ÿà€‡à€Č à€Șà„à€°à€Żà„‹à€— à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"à€Źà€šà„à€Š à€—à€°à„à€šà„à€čà„‹à€žà„"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"à€Čà€• à€žà„à€•à„à€°à€żà€šà€žà€źà„à€Źà€šà„à€§à„€ à€žà„‡à€Ÿà€żà€™"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ne/tiles_states_strings.xml b/packages/SystemUI/res/values-ne/tiles_states_strings.xml
index 17193ba..bbdf72a 100644
--- a/packages/SystemUI/res/values-ne/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ne/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"à€…à€«"</item>
     <item msgid="5966994759929723339">"à€…à€š"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 1b3cefd..a368fcf 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Ondergrens <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Linkergrens <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Rechtergrens <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Werkscreenshots worden opgeslagen in de <xliff:g id="APP">%1$s</xliff:g>-app"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Opgeslagen in <xliff:g id="APP">%1$s</xliff:g> in het werkprofiel."</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Bestanden"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> heeft dit screenshot waargenomen."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> en andere geopende apps hebben dit screenshot waargenomen."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Schermopname"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Schermopname verwerken"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Doorlopende melding voor een schermopname-sessie"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Helderheid"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Kleurinversie"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Kleurcorrectie"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Gebruikers beheren"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Klaar"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Sluiten"</string>
@@ -775,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"schermopname"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Geen titel"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Stand-by"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Vergrotingsvenster"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Bediening van vergrotingsvenster"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Inzoomen"</string>
@@ -800,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Kies de app waaraan je bedieningselementen wilt toevoegen"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# bedieningselement toegevoegd.}other{# bedieningselementen toegevoegd.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Verwijderd"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g> toevoegen?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Als je <xliff:g id="APPNAME">%s</xliff:g> toevoegt, kan deze app bedieningselementen en content aan dit deelvenster toevoegen. In sommige apps kun je kiezen welke bedieningselementen hier worden getoond."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Gemarkeerd als favoriet"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Gemarkeerd als favoriet, positie <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Verwijderd als favoriet"</string>
@@ -850,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> openen"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="SONG_NAME">%1$s</xliff:g> van <xliff:g id="ARTIST_NAME">%2$s</xliff:g> afspelen via <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> afspelen via <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Voor jou"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Ongedaan maken"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Houd dichter bij <xliff:g id="DEVICENAME">%1$s</xliff:g> om af te spelen"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Ga dichter naar <xliff:g id="DEVICENAME">%1$s</xliff:g> toe om hier af te spelen"</string>
@@ -857,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Er is iets misgegaan. Probeer het opnieuw."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Laden"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Je media casten"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"<xliff:g id="APP_LABEL">%1$s</xliff:g> casten"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inactief, check de app"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Niet gevonden"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Beheeroptie niet beschikbaar"</string>
@@ -866,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Fout, probeer het opnieuw"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Bedieningselementen toevoegen"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Bedieningselementen bewerken"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"App toevoegen"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Uitvoer toevoegen"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Groep"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Eén apparaat geselecteerd"</string>
@@ -881,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers en schermen"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Voorgestelde apparaten"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Je hebt een premium account nodig"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Hoe uitzenden werkt"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Uitzending"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Mensen bij jou in de buurt met geschikte bluetooth-apparaten kunnen luisteren naar de media die je uitzendt"</string>
@@ -892,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Kan niet uitzenden"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Kan niet opslaan. Probeer het opnieuw."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Kan niet opslaan."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Gebruik minstens 4 tekens"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Gebruik minder dan 16 tekens"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Buildnummer"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Buildnummer naar klembord gekopieerd."</string>
     <string name="basic_status" msgid="2315371112182658176">"Gesprek openen"</string>
@@ -1011,11 +1030,16 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Er is ten minste één apparaat beschikbaar"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Houd de sneltoets ingedrukt"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Annuleren"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Nu omkeren"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Klap de telefoon open voor een betere selfie"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Omkeren naar scherm voorkant voor een betere selfie?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Gebruik de camera aan de achterzijde voor een bredere foto met hogere resolutie."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Dit scherm gaat uit"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Opvouwbaar apparaat wordt uitgevouwen"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Opvouwbaar apparaat wordt gedraaid"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Nog <xliff:g id="PERCENTAGE">%s</xliff:g> batterijlading"</string>
@@ -1026,4 +1050,5 @@
     <string name="call_from_work_profile_text" msgid="3458704745640229638">"Op basis van je werkbeleid kun je alleen bellen vanuit het werkprofiel"</string>
     <string name="call_from_work_profile_action" msgid="2937701298133010724">"Overschakelen naar werkprofiel"</string>
     <string name="call_from_work_profile_close" msgid="7927067108901068098">"Sluiten"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Instellingen vergrendelscherm"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-nl/tiles_states_strings.xml b/packages/SystemUI/res/values-nl/tiles_states_strings.xml
index fbccd78..b51dc65 100644
--- a/packages/SystemUI/res/values-nl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-nl/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Uit"</item>
     <item msgid="5966994759929723339">"Aan"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 37b1990..0fd1d1d 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"à­±àŹŸàŹ°à­àŹ• àŹžà­àŹ•à­àŹ°àŹżàŹšàŹžàŹŸàŹ—à­àŹĄàŹŒàŹżàŹ•à­ <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>
     <string name="screenrecord_name" msgid="2596401223859996572">"àŹžà­àŹ•à­àŹ°àŹżàŹšà­ àŹ°à­‡àŹ•àŹ°à­àŹĄàŹ°à­"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"àŹžà­àŹ•à­àŹ°àŹżàŹš àŹ°à­‡àŹ•àŹ°à­àŹĄàŹżàŹ‚àŹ° àŹȘà­àŹ°àŹ•à­àŹ°àŹżà­ŸàŹŸàŹ•àŹ°àŹŁ"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"àŹàŹ• àŹžà­àŹ•à­àŹ°àŹż‍àŹšà­‍ àŹ°à­‡àŹ•àŹ°à­àŹĄà­‍ àŹžà­‡àŹžàŹšà­‍ àŹȘàŹŸàŹ‡àŹ àŹšàŹŸàŹČà­àŹ„àŹżàŹŹàŹŸ àŹŹàŹżàŹœà­àŹžàŹȘà­àŹ€àŹż"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"àŹ‰àŹœà­àŹœà­à­±àŹłàŹ€àŹŸ"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"àŹ°àŹ™à­àŹ— àŹ‡àŹšàŹ­àŹŸàŹ°à­àŹžàŹš"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"àŹ°àŹ™à­àŹ— àŹžàŹ‚àŹ¶à­‹àŹ§àŹš"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"à­Ÿà­àŹœàŹ°àŹźàŹŸàŹšàŹ™à­àŹ•à­ àŹȘàŹ°àŹżàŹšàŹŸàŹłàŹšàŹŸ àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"àŹčà­‹àŹ‡àŹ—àŹČàŹŸ"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"àŹŹàŹšà­àŹŠ àŹ•àŹ°àŹšà­àŹ€à­"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"àŹžà­à­±àŹšàŹŸàŹłàŹżàŹ€"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"àŹ•à­ŒàŹŁàŹžàŹż àŹžàŹŸàŹ‰àŹŁà­àŹĄ àŹ•àŹżàŹźà­àŹŹàŹŸ àŹ­àŹŸàŹ‡àŹŹà­àŹ°à­‡àŹžàŹšà­ àŹšàŹŸàŹčàŹżàŹ"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"àŹ•à­ŒàŹŁàŹžàŹż àŹžàŹŸàŹ‰àŹŁà­àŹĄ àŹ•àŹżàŹźà­àŹŹàŹŸ àŹ­àŹŸàŹ‡àŹŹà­àŹ°à­‡àŹžàŹšà­ àŹšàŹŸàŹčàŹżàŹ àŹàŹŹàŹ‚ àŹŹàŹŸàŹ°à­àŹ€à­àŹ€àŹŸàŹłàŹŸàŹȘ àŹŹàŹżàŹ­àŹŸàŹ—àŹ° àŹšàŹżàŹźà­àŹšàŹ°à­‡ àŹŠà­‡àŹ–àŹŸàŹŻàŹŸàŹ"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"àŹĄàŹżàŹ­àŹŸàŹ‡àŹž àŹžà­‡àŹŸàŹżàŹ‚àŹž àŹ†àŹ§àŹŸàŹ°àŹ°à­‡ àŹ°àŹżàŹ‚ àŹ•àŹżàŹźà­àŹŹàŹŸ àŹ­àŹŸàŹ‡àŹŹà­àŹ°à­‡àŹŸ àŹčà­‹àŹ‡àŹȘàŹŸàŹ°à­‡"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"àŹĄàŹżàŹ­àŹŸàŹ‡àŹž àŹžà­‡àŹŸàŹżàŹ‚àŹž àŹ†àŹ§àŹŸàŹ°àŹ°à­‡ àŹ°àŹżàŹ‚ àŹ•àŹżàŹźà­àŹŹàŹŸ àŹ­àŹŸàŹ‡àŹŹà­àŹ°à­‡àŹŸ àŹčà­‹àŹ‡àŹȘàŹŸàŹ°à­‡à„€ àŹĄàŹżàŹ«àŹČà­àŹŸ àŹ­àŹŸàŹŹàŹ°à­‡ <xliff:g id="APP_NAME">%1$s</xliff:g>àŹ°à­ àŹŹàŹŸàŹ°à­àŹ€à­àŹ€àŹŸàŹłàŹŸàŹȘàŹ—à­àŹĄàŹŒàŹżàŹ• àŹŹàŹŹàŹČ àŹ­àŹŸàŹŹà­‡ àŹŠà­‡àŹ–àŹŸàŹŻàŹŸàŹà„€"</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"àŹàŹčàŹż àŹŹàŹżàŹœà­àŹžàŹȘà­àŹ€àŹż àŹȘà­àŹ°àŹŸàŹȘà­àŹ€ àŹčà­‡àŹŹàŹŸ àŹžàŹźà­ŸàŹ°à­‡ àŹžàŹŸàŹ‰àŹŁà­àŹĄ àŹčà­‡àŹŹàŹŸ àŹ‰àŹšàŹżàŹ€ àŹšàŹŸ àŹ­àŹŸàŹ‡àŹŹà­àŹ°à­‡àŹžàŹšà­ àŹ€àŹŸàŹčàŹŸ àŹžàŹżàŹ·à­àŹŸàŹźàŹ•à­ àŹžà­àŹ„àŹżàŹ° àŹ•àŹ°àŹżàŹŹàŹŸàŹ•à­ àŹŠàŹżàŹ…àŹšà­àŹ€à­"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;àŹžà­àŹ„àŹżàŹ€àŹż:&lt;/b&gt; àŹĄàŹżàŹ«àŹČà­àŹŸàŹ•à­ àŹȘà­àŹ°àŹźà­‹àŹŸà­ àŹ•àŹ°àŹŸàŹŻàŹŸàŹ‡àŹ›àŹż"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;àŹžà­àŹ„àŹżàŹ€àŹż:&lt;/b&gt; àŹšà­€àŹ°àŹŹàŹ•à­ àŹĄàŹżàŹźà­‹àŹŸà­ àŹ•àŹ°àŹŸàŹŻàŹŸàŹ‡àŹ›àŹż"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"àŹžà­àŹ•à­àŹ°àŹżàŹš àŹ°à­‡àŹ•àŹ°à­àŹĄàŹżàŹ‚"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"àŹ•à­ŒàŹŁàŹžàŹż àŹ¶à­€àŹ°à­àŹ·àŹ• àŹšàŹŸàŹčàŹżàŹ"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"àŹ·à­àŹŸàŹŸàŹŁà­àŹĄàŹŹàŹŸàŹ"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"àŹźà­à­ŸàŹŸàŹ—à­àŹšàŹżàŹ«àŹżàŹ•à­‡àŹžàŹšà­ à­±àŹżàŹŁà­àŹĄà­‹"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"àŹźà­à­ŸàŹŸàŹ—à­àŹšàŹżàŹ«àŹżàŹ•à­‡àŹžàŹšà­ à­±àŹżàŹŁà­àŹĄà­‹ àŹšàŹżà­ŸàŹšà­àŹ€à­àŹ°àŹŁàŹ—à­àŹĄàŹŒàŹżàŹ•"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"àŹœà­àŹźà­ àŹ‡àŹšà­ àŹ•àŹ°àŹšà­àŹ€à­"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"àŹšàŹżà­ŸàŹšà­àŹ€à­àŹ°àŹŁàŹ—à­àŹĄàŹŒàŹżàŹ•à­ àŹŻà­‹àŹ— àŹ•àŹ°àŹżàŹŹàŹŸàŹ•à­ àŹ†àŹȘ୍ àŹŹàŹŸàŹ›àŹšà­àŹ€à­"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{#àŹŸàŹż àŹšàŹżà­ŸàŹšà­àŹ€à­àŹ°àŹŁ àŹŻà­‹àŹ— àŹ•àŹ°àŹŸàŹŻàŹŸàŹ‡àŹ›àŹżà„€}other{#àŹŸàŹż àŹšàŹżà­ŸàŹšà­àŹ€à­àŹ°àŹŁ àŹŻà­‹àŹ— àŹ•àŹ°àŹŸàŹŻàŹŸàŹ‡àŹ›àŹżà„€}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"àŹ•àŹŸàŹąàŹŒàŹż àŹŠàŹżàŹ†àŹŻàŹŸàŹ‡àŹ›àŹż"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g>àŹ•à­ àŹŻà­‹àŹ— àŹ•àŹ°àŹżàŹŹà­‡?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"àŹŻà­‡àŹ€à­‡àŹŹà­‡àŹłà­‡ àŹ†àŹȘàŹŁ <xliff:g id="APPNAME">%s</xliff:g>àŹ•à­ àŹŻà­‹àŹ— àŹ•àŹ°àŹšà­àŹ€àŹż, àŹžà­‡àŹ€à­‡àŹŹà­‡àŹłà­‡ àŹàŹčàŹż àŹȘà­‡àŹšà­‡àŹČàŹ°à­‡ àŹàŹčàŹŸ àŹšàŹżà­ŸàŹšà­àŹ€à­àŹ°àŹŁ àŹàŹŹàŹ‚ àŹŹàŹżàŹ·à­ŸàŹŹàŹžà­àŹ€à­ àŹŻà­‹àŹ— àŹ•àŹ°àŹżàŹȘàŹŸàŹ°àŹżàŹŹà„€ àŹ•àŹżàŹ›àŹż àŹ†àŹȘà­àŹžàŹ°à­‡, àŹàŹ àŹŸàŹ°à­‡ àŹ•à­‡àŹ‰àŹ àŹšàŹżà­ŸàŹšà­àŹ€à­àŹ°àŹŁàŹ—à­àŹĄàŹŒàŹżàŹ• àŹŠà­‡àŹ–àŹŸàŹŻàŹżàŹŹ àŹ€àŹŸàŹčàŹŸ àŹ†àŹȘàŹŁ àŹŹàŹŸàŹ›àŹżàŹȘàŹŸàŹ°àŹżàŹŹà­‡à„€"</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"àŹȘàŹžàŹšà­àŹŠ àŹ•àŹ°àŹŸàŹŻàŹŸàŹ‡àŹ›àŹż"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"àŹȘàŹžàŹšà­àŹŠ àŹ•àŹ°àŹŸàŹŻàŹŸàŹ‡àŹ›àŹż, àŹžà­àŹ„àŹżàŹ€àŹż <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"àŹšàŹŸàŹȘàŹžàŹšà­àŹŠ àŹ•àŹ°àŹŸàŹŻàŹŸàŹ‡àŹ›àŹż"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> àŹ–à­‹àŹČàŹšà­àŹ€à­"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g>àŹ°à­ <xliff:g id="ARTIST_NAME">%2$s</xliff:g>àŹ™à­àŹ• <xliff:g id="SONG_NAME">%1$s</xliff:g> àŹšàŹČàŹŸàŹšà­àŹ€à­"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g>àŹ°à­ <xliff:g id="SONG_NAME">%1$s</xliff:g> àŹšàŹČàŹŸàŹšà­àŹ€à­"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"àŹ†àŹȘàŹŁàŹ™à­àŹ• àŹȘàŹŸàŹ‡àŹ"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"àŹȘà­‚àŹ°à­àŹŹàŹŹàŹ€à­ àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g>àŹ°à­‡ àŹȘ୍àŹČେ àŹ•àŹ°àŹżàŹŹàŹŸ àŹȘàŹŸàŹ‡àŹ àŹȘàŹŸàŹ–àŹ•à­ àŹźà­àŹ­ àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"àŹàŹ àŹŸàŹ°à­‡ àŹȘ୍àŹČେ àŹ•àŹ°àŹżàŹŹàŹŸ àŹȘàŹŸàŹ‡àŹ <xliff:g id="DEVICENAME">%1$s</xliff:g> àŹȘàŹŸàŹ–àŹ•à­ àŹźà­àŹ­ àŹ•àŹ°àŹšà­àŹ€à­"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"àŹ•àŹżàŹ›àŹż àŹ€à­àŹ°à­àŹŸàŹż àŹčà­‹àŹ‡àŹ›àŹżà„€ àŹȘà­àŹŁàŹż àŹšà­‡àŹ·à­àŹŸàŹŸ àŹ•àŹ°àŹšà­àŹ€à­à„€"</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"àŹČà­‹àŹĄ àŹčà­‡àŹ‰àŹ›àŹż"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"àŹŸàŹŸàŹŹàŹČà­‡àŹŸ"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"àŹ†àŹȘàŹŁàŹ™à­àŹ• àŹźàŹżàŹĄàŹżàŹ†àŹ•à­ àŹ•àŹŸàŹ·à­àŹŸ àŹ•àŹ°àŹŸàŹŻàŹŸàŹ‰àŹ›àŹż"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"<xliff:g id="APP_LABEL">%1$s</xliff:g>àŹ•à­ àŹ•àŹŸàŹ·à­àŹŸ àŹ•àŹ°àŹŸàŹŻàŹŸàŹ‰àŹ›àŹż"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"àŹšàŹżàŹ·à­àŹ•à­àŹ°àŹżà­Ÿ àŹ…àŹ›àŹż, àŹ†àŹȘ àŹŻàŹŸàŹžà­àŹš àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"àŹźàŹżàŹłàŹżàŹČàŹŸ àŹšàŹŸàŹčàŹżàŹ"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"àŹšàŹżà­ŸàŹšà­àŹ€à­àŹ°àŹŁ àŹ‰àŹȘàŹČàŹŹà­àŹ§ àŹšàŹŸàŹčàŹżàŹ"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"àŹ€à­àŹ°à­àŹŸàŹż àŹčà­‹àŹ‡àŹ›àŹż, àŹȘà­àŹŁàŹż àŹšà­‡àŹ·à­àŹŸàŹŸ àŹ•àŹ°"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"àŹšàŹżà­ŸàŹšà­àŹ€à­àŹ°àŹŁàŹ—à­àŹĄàŹŒàŹżàŹ• àŹŻà­‹àŹ— àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"àŹšàŹżà­ŸàŹšà­àŹ€à­àŹ°àŹŁàŹ—à­àŹĄàŹŒàŹżàŹ•à­ àŹàŹĄàŹżàŹŸ àŹ•àŹ°àŹšà­àŹ€à­"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"àŹ†àŹȘ àŹŻà­‹àŹ— àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"àŹ†àŹ‰àŹŸàŹȘà­àŹŸà­ àŹŻà­‹àŹ— àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"àŹ—à­‹àŹ·à­àŹ à­€"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1àŹŸàŹż àŹĄàŹżàŹ­àŹŸàŹ‡àŹžà­ àŹšà­ŸàŹš àŹ•àŹ°àŹŸàŹŻàŹŸàŹ‡àŹ›àŹż"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"àŹžà­àŹȘàŹżàŹ•àŹ° àŹàŹŹàŹ‚ àŹĄàŹżàŹžàŹȘ୍àŹČà­‡àŹ—à­àŹĄàŹŒàŹżàŹ•"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"àŹȘà­àŹ°àŹžà­àŹ€àŹŸàŹŹàŹżàŹ€ àŹĄàŹżàŹ­àŹŸàŹ‡àŹžàŹ—à­àŹĄàŹŒàŹżàŹ•"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"àŹȘà­àŹ°àŹżàŹźàŹżà­ŸàŹź àŹ†àŹ•àŹŸàŹ‰àŹŁà­àŹŸ àŹ†àŹŹàŹ¶à­à­ŸàŹ•"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"àŹŹà­àŹ°àŹĄàŹ•àŹŸàŹ·à­àŹŸàŹżàŹ‚ àŹ•àŹżàŹȘàŹ°àŹż àŹ•àŹŸàŹź àŹ•àŹ°à­‡"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"àŹŹà­àŹ°àŹĄàŹ•àŹŸàŹ·à­àŹŸ"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"àŹ†àŹȘàŹŁàŹ™à­àŹ• àŹ†àŹ–àŹȘàŹŸàŹ–àŹ° àŹ•àŹźà­àŹȘàŹŸàŹŸàŹżàŹŹàŹČ àŹŹà­àŹČà­àŹŸà­àŹ„ àŹĄàŹżàŹ­àŹŸàŹ‡àŹž àŹ„àŹżàŹŹàŹŸ àŹČà­‹àŹ•àŹźàŹŸàŹšà­‡ àŹ†àŹȘàŹŁ àŹŹà­àŹ°àŹĄàŹ•àŹŸàŹ·à­àŹŸ àŹ•àŹ°à­àŹ„àŹżàŹŹàŹŸ àŹźàŹżàŹĄàŹżàŹ† àŹ¶à­àŹŁàŹżàŹȘàŹŸàŹ°àŹżàŹŹà­‡"</string>
@@ -894,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"àŹŹà­àŹ°àŹĄàŹ•àŹŸàŹ·à­àŹŸ àŹ•àŹ°àŹŸàŹŻàŹŸàŹ‡àŹȘàŹŸàŹ°àŹżàŹŹ àŹšàŹŸàŹčàŹżàŹ"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"àŹžà­‡àŹ­ àŹ•àŹ°àŹŸàŹŻàŹŸàŹ‡àŹȘàŹŸàŹ°àŹżàŹČàŹŸ àŹšàŹŸàŹčàŹżàŹà„€ àŹȘà­àŹŁàŹż àŹšà­‡àŹ·à­àŹŸàŹŸ àŹ•àŹ°àŹšà­àŹ€à­à„€"</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"àŹžà­‡àŹ­ àŹ•àŹ°àŹŸàŹŻàŹŸàŹ‡àŹȘàŹŸàŹ°àŹżàŹČàŹŸ àŹšàŹŸàŹčàŹżàŹà„€"</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"àŹ…àŹ€àŹżàŹ•àŹźàŹ°à­‡ 4àŹŸàŹż àŹ•à­‡àŹ°à­‡àŹ•à­àŹŸàŹ° àŹŹà­à­ŸàŹŹàŹčàŹŸàŹ° àŹ•àŹ°àŹšà­àŹ€à­"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"16àŹŸàŹżàŹ°à­ àŹ•àŹź àŹ•à­‡àŹ°à­‡àŹ•à­àŹŸàŹ° àŹŹà­à­ŸàŹŹàŹčàŹŸàŹ° àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"àŹŹàŹżàŹČà­àŹĄ àŹšàŹźà­à­±àŹ°"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"àŹ•à­àŹČàŹżàŹȘàŹŹà­‹àŹ°à­àŹĄàŹ•à­ àŹ•àŹȘàŹż àŹ•àŹ°àŹŸàŹŻàŹŸàŹ‡àŹ„àŹżàŹŹàŹŸ àŹŹàŹżàŹČà­àŹĄ àŹšàŹźà­à­±àŹ°à„€"</string>
     <string name="basic_status" msgid="2315371112182658176">"àŹŹàŹŸàŹ°à­àŹ€à­àŹ€àŹŸàŹłàŹŸàŹȘ àŹ–à­‹àŹČàŹšà­àŹ€à­"</string>
@@ -1013,24 +1030,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• àŹ…àŹ€àŹżàŹ•àŹźàŹ°à­‡ àŹ—à­‹àŹŸàŹżàŹ àŹĄàŹżàŹ­àŹŸàŹ‡àŹž àŹ‰àŹȘàŹČàŹŹà­àŹ§ àŹ…àŹ›àŹż"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"àŹžàŹ°à­àŹŸàŹ•àŹŸàŹ•à­ àŹžà­àŹȘàŹ°à­àŹ¶ àŹ•àŹ°àŹż àŹ§àŹ°àŹż àŹ°àŹ–àŹšà­àŹ€à­"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"àŹŹàŹŸàŹ€àŹżàŹČ àŹ•àŹ°àŹšà­àŹ€à­"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"àŹŹàŹ°à­àŹ€à­àŹ€àŹźàŹŸàŹš àŹ«à­àŹČàŹżàŹȘ àŹ•àŹ°àŹšà­àŹ€à­"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"àŹàŹ• àŹ‰àŹšà­àŹšàŹ€ àŹžà­‡àŹČà­àŹ«àŹż àŹȘàŹŸàŹ‡àŹ àŹ«à­‹àŹšàŹ•à­ àŹ…àŹšàŹ«à­‹àŹČà­àŹĄ àŹ•àŹ°àŹšà­àŹ€à­"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"àŹàŹ• àŹ‰àŹšà­àŹšàŹ€ àŹžà­‡àŹČà­àŹ«àŹż àŹȘàŹŸàŹ‡àŹ àŹžàŹŸàŹźà­àŹšàŹŸ àŹĄàŹżàŹžàŹȘ୍àŹČà­‡àŹ•à­ àŹ«à­àŹČàŹżàŹȘ àŹ•àŹ°àŹżàŹŹà­‡?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"àŹ‰àŹšà­àŹš àŹ°àŹżàŹœà­‹àŹČà­à­Ÿà­àŹžàŹš àŹžàŹč àŹ…àŹ§àŹżàŹ• àŹšàŹ‰àŹĄàŹŒàŹŸàŹ° àŹàŹ• àŹ«àŹŸà­‹ àŹšà­‡àŹŹàŹŸ àŹȘàŹŸàŹ‡àŹ àŹȘàŹ›-àŹȘàŹŸàŹ° àŹ•à­‡àŹźà­‡àŹ°àŹŸ àŹŹà­à­ŸàŹŹàŹčàŹŸàŹ° àŹ•àŹ°àŹšà­àŹ€à­à„€"</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ àŹàŹčàŹż àŹžà­àŹ•à­àŹ°àŹżàŹš àŹŹàŹšà­àŹŠ àŹčà­‹àŹ‡àŹŻàŹżàŹŹ"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"àŹ«à­‹àŹČà­àŹĄ àŹ•àŹ°àŹŸàŹŻàŹŸàŹ‡àŹȘàŹŸàŹ°à­àŹ„àŹżàŹŹàŹŸ àŹĄàŹżàŹ­àŹŸàŹ‡àŹžàŹ•à­ àŹ…àŹšàŹ«à­‹àŹČà­àŹĄ àŹ•àŹ°àŹŸàŹŻàŹŸàŹ‰àŹ›àŹż"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"àŹ«à­‹àŹČà­àŹĄ àŹ•àŹ°àŹŸàŹŻàŹŸàŹ‡àŹȘàŹŸàŹ°à­àŹ„àŹżàŹŹàŹŸ àŹĄàŹżàŹ­àŹŸàŹ‡àŹžàŹ•à­ àŹ«à­àŹČàŹżàŹȘ àŹ•àŹ°àŹŸàŹŻàŹŸàŹ‰àŹ›àŹż"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> àŹŹà­‡àŹŸà­‡àŹ°à­€ àŹšàŹŸàŹ°à­àŹœ àŹŹàŹŸàŹ•àŹż àŹ…àŹ›àŹż"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"àŹàŹ• àŹšàŹŸàŹ°à­àŹœàŹ° àŹžàŹč àŹ†àŹȘàŹŁàŹ™à­àŹ• àŹ·à­àŹŸàŹŸàŹ‡àŹČàŹžàŹ•à­ àŹ•àŹšà­‡àŹ•à­àŹŸ àŹ•àŹ°àŹšà­àŹ€à­"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"àŹ·à­àŹŸàŹŸàŹ‡àŹČàŹž àŹŹà­‡àŹŸà­‡àŹ°à­€àŹ° àŹšàŹŸàŹ°à­àŹœ àŹ•àŹź àŹ…àŹ›àŹż"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"àŹ­àŹżàŹĄàŹżàŹ“ àŹ•à­‡àŹźà­‡àŹ°àŹŸ"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"àŹàŹčàŹż àŹȘà­àŹ°à­‹àŹ«àŹŸàŹ‡àŹČàŹ°à­ àŹ•àŹČ àŹ•àŹ°àŹŸàŹŻàŹŸàŹ‡àŹȘàŹŸàŹ°àŹżàŹŹ àŹšàŹŸàŹčàŹżàŹ"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"àŹ†àŹȘàŹŁàŹ™à­àŹ• à­±àŹŸàŹ°à­àŹ• àŹšà­€àŹ€àŹż àŹ†àŹȘàŹŁàŹ™à­àŹ•à­ àŹ•à­‡àŹŹàŹł à­±àŹŸàŹ°à­àŹ• àŹȘà­àŹ°à­‹àŹ«àŹŸàŹ‡àŹČàŹ°à­ àŹ«à­‹àŹš àŹ•àŹČ àŹ•àŹ°àŹżàŹŹàŹŸàŹ•à­ àŹ…àŹšà­àŹźàŹ€àŹż àŹŠàŹżàŹ"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"à­±àŹŸàŹ°à­àŹ• àŹȘà­àŹ°à­‹àŹ«àŹŸàŹ‡àŹČàŹ•à­ àŹžà­à­±àŹżàŹš àŹ•àŹ°àŹšà­àŹ€à­"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"àŹŹàŹšà­àŹŠ àŹ•àŹ°àŹšà­àŹ€à­"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"àŹČàŹ• àŹžà­àŹ•à­àŹ°àŹżàŹš àŹžà­‡àŹŸàŹżàŹ‚àŹž"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-or/tiles_states_strings.xml b/packages/SystemUI/res/values-or/tiles_states_strings.xml
index acaa3fb..5e4b730 100644
--- a/packages/SystemUI/res/values-or/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-or/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"àŹŹàŹšà­àŹŠ àŹ…àŹ›àŹż"</item>
     <item msgid="5966994759929723339">"àŹšàŹŸàŹČୁ àŹ…àŹ›àŹż"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 58df165..28c6b31 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"àš•àšŸàš°àšœ àšžàš•à©àš°à©€àššàšžàšŒàšŸàšŸàšŸàš‚ àššà©‚à©° <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>
     <string name="screenrecord_name" msgid="2596401223859996572">"àšžàš•à©àš°à©€àšš àš°àšżàš•àšŸàš°àšĄàš°"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"àšžàš•à©àš°à©€àšš àš°àšżàš•àšŸàš°àšĄàšżà©°àš— àšœàšŸàš°à©€ àščੈ"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"àš•àšżàšžà©‡ àšžàš•à©àš°à©€àšš àš°àšżàš•àšŸàš°àšĄ àšžà©ˆàšžàšŒàšš àšČàšˆ àššà©±àšČ àš°àščੀ àšžà©‚àššàššàšŸ"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"àššàšźàš•"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"àš°à©°àš— àšȘàšČàšŸàššàšŸ"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"àš°à©°àš— àšžà©àš§àšŸàšˆ"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"àš”àš°àš€à©‹àš‚àš•àšŸàš°àšŸàš‚ àšŠàšŸ àšȘà©àš°àšŹà©°àš§àšš àš•àš°à©‹"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"àščੋ àš—àšżàš†"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"àšŹà©°àšŠ àš•àš°à©‹"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"àšžàš”à©ˆàššàšČàšżàš€"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"àš•à©‹àšˆ àš§à©àššà©€ àšœàšŸàš‚ àš„àš°àš„àš°àšŸàščàšŸ àššàščà©€àš‚"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"àš•à©‹àšˆ àš§à©àššà©€ àšœàšŸàš‚ àš„àš°àš„àš°àšŸàščàšŸ àššàščà©€àš‚ àš…àš€à©‡ àšžà©‚àššàššàšŸàš”àšŸàš‚ àš—à©±àšČàšŹàšŸàš€ àšžà©ˆàš•àšžàšŒàšš àš”àšżà©±àšš àščà©‡àš àšČੇ àšȘàšŸàšžà©‡ àšŠàšżàšžàšŠà©€àš†àš‚ àščàšš"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"àšĄà©€àš”àšŸàšˆàšž àšžà©ˆàšŸàšżà©°àš—àšŸàš‚ àšŠà©‡ àš†àš§àšŸàš° \'àš€à©‡ àš˜à©°àšŸà©€ àš”à©±àšœ àšžàš•àšŠà©€ àščੈ àšœàšŸàš‚ àš„àš°àš„àš°àšŸàščàšŸ àščੋ àšžàš•àšŠà©€ àščੈ"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"àšĄà©€àš”àšŸàšˆàšž àšžà©ˆàšŸàšżà©°àš—àšŸàš‚ àšŠà©‡ àš†àš§àšŸàš° \'àš€à©‡ àš˜à©°àšŸà©€ àš”à©±àšœ àšžàš•àšŠà©€ àščੈ àšœàšŸàš‚ àš„àš°àš„àš°àšŸàščàšŸ àščੋ àšžàš•àšŠà©€ àščà©ˆà„€ àšȘà©‚àš°àš”-àššàšżàš°àš§àšŸàš°àšżàš€ àš€à©Œàš° \'àš€à©‡ <xliff:g id="APP_NAME">%1$s</xliff:g> àšŹàšŹàšČ àš€à©‹àš‚ àš—à©±àšČàšŸàš‚àšŹàšŸàš€àšŸàš‚à„€"</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"àšžàšżàšžàšŸàšź àššà©‚à©° àššàšżàš°àš§àšŸàš°àš€ àš•àš°àšš àšŠàšżàš“ àš•àšż àš‡àšž àšžà©‚àššàššàšŸ àšČàšˆ àš•à©‹àšˆ àš§à©àššà©€ àš”àšœàšŸàš‰àšŁà©€ àššàšŸàščà©€àšŠà©€ àščੈ àšœàšŸàš‚ àš„àš°àš„àš°àšŸàščàšŸ àš•àš°àššà©€ àššàšŸàščà©€àšŠà©€ àščੈ"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;àšžàš„àšżàš€à©€:&lt;/b&gt; àšŠàš°àšœàšŸ àš”àš§àšŸ àš•à©‡ àšȘà©‚àš°àš”-àššàšżàš°àš§àšŸàš°àš€ \'àš€à©‡ àšžà©ˆà©±àšŸ àš•à©€àš€àšŸ àš—àšżàš†"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;àšžàš„àšżàš€à©€:&lt;/b&gt; àšŠàš°àšœàšŸ àš˜àšŸàšŸ àš•à©‡ àšžàšŒàšŸàš‚àš€ \'àš€à©‡ àšžà©ˆà©±àšŸ àš•à©€àš€àšŸ àš—àšżàš†"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"àšžàš•à©àš°à©€àšš àš°àšżàš•àšŸàš°àšĄàšżà©°àš—"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"àš•à©‹àšˆ àšžàšżàš°àšČà©‡àš– àššàščà©€àš‚"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"àšžàšŸà©ˆàš‚àšĄàšŹàšŸàšˆ"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"àš”à©±àšĄàšŠàš°àšžàšŒà©€àš•àš°àšš Window"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"àš”à©±àšĄàšŠàš°àšžàšŒà©€àš•àš°àšš Window àšŠà©‡ àš•à©°àšŸàš°à©‹àšČ"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"àšœàšŒà©‚àšź àš”àš§àšŸàš“"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"àš•à©°àšŸàš°à©‹àšČ àšžàšŒàšŸàšźàšČ àš•àš°àšš àšČàšˆ àšàšȘ àššà©àšŁà©‹"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# àš•à©°àšŸàš°à©‹àšČ àšžàšŒàšŸàšźàšČ àš•à©€àš€àšŸ àš—àšżàš†à„€}one{# àš•à©°àšŸàš°à©‹àšČ àšžàšŒàšŸàšźàšČ àš•à©€àš€àšŸ àš—àšżàš†à„€}other{# àš•à©°àšŸàš°à©‹àšČ àšžàšŒàšŸàšźàšČ àš•à©€àš€à©‡ àš—àšà„€}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"àščàšŸàšŸàš‡àš† àš—àšżàš†"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"àš•à©€ <xliff:g id="APPNAME">%s</xliff:g> àšžàšŒàšŸàšźàšČ àš•àš°àššàšŸ àščੈ?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"àšœàšŠà©‹àš‚ àš€à©àšžà©€àš‚ <xliff:g id="APPNAME">%s</xliff:g> àšžàšŒàšŸàšźàšČ àš•àš°àšŠà©‡ àščੋ, àš€àšŸàš‚ àš‡àšč àš‡àšž àšȘà©ˆàššàšČ àš”àšżà©±àšš àš•à©°àšŸàš°à©‹àšČàšŸàš‚ àš…àš€à©‡ àšžàšźà©±àš—àš°à©€ àššà©‚à©° àšžàšŒàšŸàšźàšČ àš•àš° àšžàš•àšŠà©€ àščà©ˆà„€ àš•à©àš àšàšȘàšŸàš‚ àšČàšˆ, àš€à©àšžà©€àš‚ àš‡àšč àššà©àšŁ àšžàš•àšŠà©‡ àščੋ àš•àšż àš‡à©±àš„à©‡ àš•àšżàščੜੇ àš•à©°àšŸàš°à©‹àšČ àšŠàšżàšžàšŁà„€"</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"àšźàššàšȘàšžà©°àšŠ àš”àšżà©±àšš àšžàšŒàšŸàšźàšČ àš•à©€àš€àšŸ àš—àšżàš†"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"àšźàššàšȘàšžà©°àšŠ àš”àšżà©±àšš àšžàšŒàšŸàšźàšČ àš•à©€àš€àšŸ àš—àšżàš†, àšžàš„àšŸàšš <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"àšźàššàšȘàšžà©°àšŠ àš”àšżà©±àššà©‹àš‚ àščàšŸàšŸàš‡àš† àš—àšżàš†"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> àš–à©‹àšČ੍àščੋ"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> àš€à©‹àš‚ <xliff:g id="ARTIST_NAME">%2$s</xliff:g> àšŠàšŸ <xliff:g id="SONG_NAME">%1$s</xliff:g> àššàšČàšŸàš“"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> àš€à©‹àš‚ <xliff:g id="SONG_NAME">%1$s</xliff:g> àššàšČàšŸàš“"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"àš€à©àščàšŸàšĄà©‡ àšČàšˆ"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"àš…àšŁàš•à©€àš€àšŸ àš•àš°à©‹"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> \'àš€à©‡ àššàšČàšŸàš‰àšŁ àšČàšˆ àššà©‡à©œà©‡ àšČàšżàšœàšŸàš“"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"àš‡à©±àš„à©‡ àššàšČàšŸàš‰àšŁ àšČàšˆ, <xliff:g id="DEVICENAME">%1$s</xliff:g> àšŠà©‡ àššà©‡à©œà©‡ àšČàšżàš†àš“"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"àš•à©‹àšˆ àš—à©œàšŹà©œ àščੋ àš—àšˆà„€ àšŠà©àšŹàšŸàš°àšŸ àš•à©‹àšžàšŒàšżàšžàšŒ àš•àš°à©‹à„€"</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"àšČà©‹àšĄ àš•à©€àš€à©€ àšœàšŸ àš°àščੀ àščੈ"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"àšŸà©ˆàšŹàšČà©ˆà©±àšŸ"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"àš€à©àščàšŸàšĄà©‡ àšźà©€àšĄà©€àš† àššà©‚à©° àš•àšŸàšžàšŸ àš•à©€àš€àšŸ àšœàšŸ àš°àšżàščàšŸ àščੈ"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"<xliff:g id="APP_LABEL">%1$s</xliff:g> \'àš€à©‡ àš•àšŸàšžàšŸ àš•à©€àš€àšŸ àšœàšŸ àš°àšżàščàšŸ àščੈ"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"àš…àš•àšżàš°àšżàš†àšžàšŒà©€àšČ, àšàšȘ àšŠà©€ àšœàšŸàš‚àšš àš•àš°à©‹"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"àššàščà©€àš‚ àšźàšżàšČàšżàš†"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"àš•à©°àšŸàš°à©‹àšČ àš‰àšȘàšČàšŹàš§ àššàščà©€àš‚ àščੈ"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"àš—à©œàšŹà©œ, àšŠà©àšŹàšŸàš°àšŸ àš•à©‹àšžàšŒàšżàšžàšŒ àš•àš°à©‹"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"àš•à©°àšŸàš°à©‹àšČ àšžàšŒàšŸàšźàšČ àš•àš°à©‹"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"àš•à©°àšŸàš°à©‹àšČàšŸàš‚ àšŠàšŸ àšžà©°àšȘàšŸàšŠàšš àš•àš°à©‹"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"àšàšȘ àšžàšŒàšŸàšźàšČ àš•àš°à©‹"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"àš†àšŠàšŸàšȘà©à©±àšŸ àšžàšŒàšŸàšźàšČ àš•àš°à©‹"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"àš—àš°à©à©±àšȘ"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 àšĄà©€àš”àšŸàšˆàšž àššà©‚à©° àššà©àšŁàšżàš† àš—àšżàš†"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"àšžàšȘà©€àš•àš° àš…àš€à©‡ àšĄàšżàšžàšȘàšČà©‡àš†àš‚"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"àšžà©àšàšŸàš àš—àš àšĄà©€àš”àšŸàšˆàšž"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"àšȘà©àš°à©€àšźà©€àš…àšź àš–àšŸàš€à©‡ àšŠà©€ àšČੋੜ àščੈ"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"àšȘà©àš°àšžàšŸàš°àšš àš•àšżàš”à©‡àš‚ àš•à©°àšź àš•àš°àšŠàšŸ àščੈ"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"àšȘà©àš°àšžàšŸàš°àšš"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"àš…àššà©àš°à©‚àšȘ àšŹàšČà©‚àšŸà©à©±àš„ àšĄà©€àš”àšŸàšˆàšžàšŸàš‚ àššàšŸàšČ àššàšœàšŒàšŠà©€àš•à©€ àšČà©‹àš• àš€à©àščàšŸàšĄà©‡ àš”à©±àšČà©‹àš‚ àšȘà©àš°àšžàšŸàš°àšš àš•à©€àš€à©‡ àšœàšŸ àš°àščੇ àšźà©€àšĄà©€àš† àššà©‚à©° àšžà©àšŁ àšžàš•àšŠà©‡ àščàšš"</string>
@@ -894,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"àšȘà©àš°àšžàšŸàš°àšš àššàščà©€àš‚ àš•à©€àš€àšŸ àšœàšŸ àšžàš•àšŠàšŸ"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"àš°à©±àš–àšżàš…àš€ àššàščà©€àš‚ àš•à©€àš€àšŸ àšœàšŸ àšžàš•àšŠàšŸà„€ àšŠà©àšŹàšŸàš°àšŸ àš•à©‹àšžàšŒàšżàšžàšŒ àš•àš°à©‹à„€"</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"àš°à©±àš–àšżàš…àš€ àššàščà©€àš‚ àš•à©€àš€àšŸ àšœàšŸ àšžàš•àšŠàšŸà„€"</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"àš˜à©±àšŸà©‹-àš˜à©±àšŸ 4 àš…à©±àš–àš°-àššàšżà©°àššà©àšč àš”àš°àš€à©‹"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"16 àš€à©‹àš‚ àš˜à©±àšŸ àš…à©±àš–àš°-àššàšżà©°àššà©àšč àš”àš°àš€à©‹"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"àšŹàšżàšČàšĄ àššà©°àšŹàš°"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"àšŹàšżàšČàšĄ àššà©°àšŹàš° àššà©‚à©° àš•àšČàšżà©±àšȘàšŹà©‹àš°àšĄ \'àš€à©‡ àš•àšŸàšȘੀ àš•à©€àš€àšŸ àš—àšżàš†à„€"</string>
     <string name="basic_status" msgid="2315371112182658176">"àš—à©±àšČàšŹàšŸàš€ àš–à©‹àšČ੍àščੋ"</string>
@@ -1013,24 +1030,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• àš˜à©±àšŸà©‹-àš˜à©±àšŸ àš‡à©±àš• àšĄà©€àš”àšŸàšˆàšž àš‰àšȘàšČàšŹàš§ àščੈ"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"àšžàšŒàšŸàš°àšŸàš•à©±àšŸ àššà©‚à©° àšžàšȘàš°àšžàšŒ àš•àš° àš•à©‡ àš°à©±àš–à©‹"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"àš°à©±àšŠ àš•àš°à©‹"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"àščà©àšŁà©‡ àš«àšČàšżà©±àšȘ àš•àš°à©‹"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"àšŹàšżàščàš€àš° àšžà©ˆàšČàš«àšŒà©€ àšČàšˆ àš«àšŒà©‹àšš àššà©‚à©° àš–à©‹àšČ੍àščੋ"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"àš•à©€ àšŹàšżàščàš€àš° àšžà©ˆàšČàš«àšŒà©€ àšČàšˆ àš…àš—àšČੀ àšĄàšżàšžàšȘàšČੇ \'àš€à©‡ àš«àšČàšżà©±àšȘ àš•àš°àššàšŸ àščੈ?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"àš‰à©±àšš àš°à©ˆàšœàšŒà©‹àšČàšżàšŠàšžàšŒàšš àš”àšŸàšČੀ àšœàšŒàšżàš†àšŠàšŸ àššà©Œà©œà©€ àš«àšŒà©‹àšŸà©‹ àšČàšˆ àšȘàšżàš›àšČੇ àš•à©ˆàšźàš°à©‡ àšŠà©€ àš”àš°àš€à©‹àš‚ àš•àš°à©‹à„€"</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ àš‡àšč àšžàš•à©àš°à©€àšš àšŹà©°àšŠ àščੋ àšœàšŸàš”à©‡àš—à©€"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"àšźà©‹à©œàššàšŻà©‹àš— àšĄà©€àš”àšŸàšˆàšž àššà©‚à©° àš–à©‹àšČ੍àščàšżàš† àšœàšŸ àš°àšżàščàšŸ àščੈ"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"àšźà©‹à©œàššàšŻà©‹àš— àšĄà©€àš”àšŸàšˆàšž àššà©‚à©° àš†àšČੇ-àšŠà©àš†àšČੇ àš«àšČàšżà©±àšȘ àš•à©€àš€àšŸ àšœàšŸ àš°àšżàščàšŸ àščੈ"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> àšŹà©ˆàšŸàš°à©€ àšŹàšŸàš•à©€"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"àš†àšȘàšŁà©‡ àšžàšŸàšŸàšˆàšČàšž àššà©‚à©° àššàšŸàš°àšœàš° àššàšŸàšČ àš•àššà©ˆàš•àšŸ àš•àš°à©‹"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"àšžàšŸàšŸàšˆàšČàšž àšŠà©€ àšŹà©ˆàšŸàš°à©€ àš˜à©±àšŸ àščੈ"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"àš”à©€àšĄà©€àš“ àš•à©ˆàšźàš°àšŸ"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"àš‡àšž àšȘà©àš°à©‹àš«àšŸàšˆàšČ àš€à©‹àš‚ àš•àšŸàšČ àššàščà©€àš‚ àš•à©€àš€à©€ àšœàšŸ àšžàš•àšŠà©€"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"àš€à©àščàšŸàšĄà©€ àš•àšŸàš°àšœ àššà©€àš€à©€ àš€à©àščàšŸàššà©‚à©° àšžàšżàš°àš«àšŒ àš•àšŸàš°àšœ àšȘà©àš°à©‹àš«àšŸàšˆàšČ àš€à©‹àš‚ àščੀ àš«àšŒà©‹àšš àš•àšŸàšČàšŸàš‚ àš•àš°àšš àšŠà©€ àš†àš—àšżàš† àšŠàšżà©°àšŠà©€ àščੈ"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"àš•àšŸàš°àšœ àšȘà©àš°à©‹àš«àšŸàšˆàšČ \'àš€à©‡ àšœàšŸàš“"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"àšŹà©°àšŠ àš•àš°à©‹"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"àšČàšŸàš• àšžàš•à©àš°à©€àšš àšžà©ˆàšŸàšżà©°àš—àšŸàš‚"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pa/tiles_states_strings.xml b/packages/SystemUI/res/values-pa/tiles_states_strings.xml
index 9653b92..5628a31 100644
--- a/packages/SystemUI/res/values-pa/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pa/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"àšŹà©°àšŠ"</item>
     <item msgid="5966994759929723339">"àššàšŸàšČੂ"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index a7c24f8..ed0d257 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Przycięcie dolnej krawędzi o <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Przycięcie lewej krawędzi o <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Przycięcie prawej krawędzi o <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"SƂuĆŒbowe zrzuty ekranu są zapisywane w aplikacji <xliff:g id="APP">%1$s</xliff:g>"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Zapisano w aplikacji <xliff:g id="APP">%1$s</xliff:g> w profilu sƂuĆŒbowym"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Pliki"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"Aplikacja <xliff:g id="APPNAME">%1$s</xliff:g> wykryƂa ten zrzut ekranu."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"Aplikacja <xliff:g id="APPNAME">%1$s</xliff:g> i inne aplikacje wykryƂy ten zrzut ekranu."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Nagrywanie ekranu"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Przetwarzam nagrywanie ekranu"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"StaƂe powiadomienie o sesji rejestrowania zawartoƛci ekranu"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Jasnoƛć"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Odwrócenie kolorów"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Korekcja kolorów"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Zarządzaj uĆŒytkownikami"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Gotowe"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Zamknij"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatycznie"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Bez dĆșwięku i wibracji"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Brak dĆșwięku i wibracji, wyƛwietla się niĆŒej w sekcji rozmów"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"MoĆŒe wƂączać dzwonek lub wibracje w zaleĆŒnoƛci od ustawieƄ urządzenia"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"MoĆŒe wƂączyć dzwonek lub wibracje w zaleĆŒnoƛci od ustawieƄ urządzenia. Rozmowy z aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g> są domyƛlnie wyƛwietlane jako dymki."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Pozwól systemowi decydować, czy o powiadomieniu powinien informować dĆșwięk czy wibracja"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Stan:&lt;/b&gt; zmieniony na Domyƛlny"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Stan:&lt;/b&gt; zmieniono na Ciche"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"nagrywanie ekranu"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Bez tytuƂu"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Tryb gotowoƛci"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Okno powiększenia"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Elementy sterujące okna powiększenia"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Powiększ"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Wybierz aplikację, do której chcesz dodać elementy sterujące"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Dodano # element sterujący.}few{Dodano # elementy sterujące.}many{Dodano # elementów sterujących.}other{Dodano # elementu sterującego.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Usunięto"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Dodać aplikację <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Gdy dodasz aplikację <xliff:g id="APPNAME">%s</xliff:g>, będzie ona mogƂa dodawać elementy sterujące i treƛci do tego panelu. W niektórych aplikacjach moĆŒna wybrać elementy sterujące, które się tu pojawią."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Dodano do ulubionych"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Dodano do ulubionych, pozycja <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Usunięto z ulubionych"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Otwórz aplikację <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Odtwórz utwór <xliff:g id="SONG_NAME">%1$s</xliff:g> (<xliff:g id="ARTIST_NAME">%2$s</xliff:g>) w aplikacji <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Odtwórz utwór <xliff:g id="SONG_NAME">%1$s</xliff:g> w aplikacji <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Dla Ciebie"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Cofnij"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"PrzysuƄ się bliĆŒej, aby odtwarzać na urządzeniu <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Aby zagrać tutaj, przysuƄ bliĆŒej urządzenie <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Coƛ poszƂo nie tak. Spróbuj ponownie."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Wczytuję"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"PrzesyƂanie multimediów"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"PrzesyƂanie treƛci z aplikacji <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Nieaktywny, sprawdĆș aplikację"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Nie znaleziono"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Element jest niedostępny"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"BƂąd, spróbuj ponownie"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Dodaj elementy sterujące"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Edytuj elementy sterujące"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Dodaj aplikację"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Dodaj urządzenia wyjƛciowe"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupa"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Wybrano 1 urządzenie"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"GƂoƛniki i wyƛwietlacze"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Proponowane urządzenia"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Wymagane konto premium"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Jak dziaƂa transmitowanie"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Transmisja"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Osoby w pobliĆŒu ze zgodnymi urządzeniami Bluetooth mogą sƂuchać transmitowanych przez Ciebie multimediów"</string>
@@ -894,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Nie moĆŒna przesyƂać"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Nie moĆŒna zapisać. Spróbuj ponownie."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Nie moĆŒna zapisać."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Wpisz co najmniej 4 znaki"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Wpisz mniej niĆŒ 16 znaków"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numer kompilacji"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Numer kompilacji zostaƂ skopiowany do schowka."</string>
     <string name="basic_status" msgid="2315371112182658176">"Otwarta rozmowa"</string>
@@ -1013,24 +1030,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Dostępne jest co najmniej 1 urządzenie."</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Skrót – naciƛnij i przytrzymaj"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Anuluj"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"PrzeƂącz teraz"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"RozƂóĆŒ telefon, aby uzyskać lepszej jakoƛci selfie"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"PrzeƂączyć na przedni wyƛwietlacz?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"UĆŒyj tylnego aparatu, aby zrobić szersze zdjęcie o większej rozdzielczoƛci."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"* Ekran się wyƂączy"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"SkƂadane urządzenie jest rozkƂadane"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"SkƂadane urządzenie jest obracane"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"PozostaƂo <xliff:g id="PERCENTAGE">%s</xliff:g> baterii"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"PodƂącz rysik do Ƃadowarki"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"SƂaba bateria w rysiku"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Kamera"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Nie moĆŒna nawiązać poƂączenia z tego profilu"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Zasady obowiązujące w firmie zezwalają na nawiązywanie poƂączeƄ telefonicznych tylko w profilu sƂuĆŒbowym"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"PrzeƂącz na profil sƂuĆŒbowy"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Zamknij"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Ustawienia ekranu blokady"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pl/tiles_states_strings.xml b/packages/SystemUI/res/values-pl/tiles_states_strings.xml
index 50650986..c32aa1a 100644
--- a/packages/SystemUI/res/values-pl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pl/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"WyƂączono"</item>
     <item msgid="5966994759929723339">"WƂączono"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index e56a7c7..1b9e015 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Borda inferior em <xliff:g id="PERCENT">%1$d</xliff:g> por cento"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Borda esquerda em <xliff:g id="PERCENT">%1$d</xliff:g> por cento"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Borda direita em <xliff:g id="PERCENT">%1$d</xliff:g> por cento"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Capturas de tela do trabalho são salvas no app <xliff:g id="APP">%1$s</xliff:g>"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Salva no app <xliff:g id="APP">%1$s</xliff:g> no perfil de trabalho"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Files"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"O app <xliff:g id="APPNAME">%1$s</xliff:g> detectou essa captura de tela."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> e outros apps abertos detectaram essa captura de tela."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Gravador de tela"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processando gravação de tela"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificação contínua para uma sessão de gravação de tela"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brilho"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversão de cores"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correção de cor"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Gerenciar usuários"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Concluído"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Fechar"</string>
@@ -489,7 +493,7 @@
     <string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Chamadas e notificações farão o smartphone tocar (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Sintonizador System UI"</string>
     <string name="status_bar" msgid="4357390266055077437">"Barra de status"</string>
-    <string name="demo_mode" msgid="263484519766901593">"Modo de demonstração da IU do sistema"</string>
+    <string name="demo_mode" msgid="263484519766901593">"Modo de demonstração da interface do sistema"</string>
     <string name="enable_demo_mode" msgid="3180345364745966431">"Ativar modo de demonstração"</string>
     <string name="show_demo_mode" msgid="3677956462273059726">"Mostrar modo de demonstração"</string>
     <string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Som e vibração desativados"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"O som e a vibração estão desativados, e o balão aparece na parte inferior da seção de conversa"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Pode vibrar ou tocar com base nas configurações do dispositivo"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Pode vibrar ou tocar com base nas configurações do dispositivo. As conversas do app <xliff:g id="APP_NAME">%1$s</xliff:g> aparecem em balões por padrão."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Faça com que o sistema determine se a notificação resultará em som ou vibração"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Status:&lt;/b&gt; promovida a Padrão"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Status:&lt;/b&gt; rebaixada a Silenciosa"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"gravação de tela"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Sem título"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Em espera"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Janela de ampliação"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Controles da janela de ampliação"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Aumentar zoom"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Escolha um app para adicionar controles"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# controle adicionado.}one{# controle adicionado.}many{# de controles adicionados.}other{# controles adicionados.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Removido"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Adicionar o app <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Quando você adiciona o app <xliff:g id="APPNAME">%s</xliff:g>, ele pode incluir controles e conteúdo neste painel. Em alguns casos, é possível escolher quais controles aparecem aqui."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Adicionado como favorito"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Adicionado como favorito (posição <xliff:g id="NUMBER">%d</xliff:g>)"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Removido dos favoritos"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Abrir <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Tocar <xliff:g id="SONG_NAME">%1$s</xliff:g> de <xliff:g id="ARTIST_NAME">%2$s</xliff:g> no app <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Tocar <xliff:g id="SONG_NAME">%1$s</xliff:g> no app <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Para você"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Desfazer"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Aproxime os dispositivos para tocar a mídia neste: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Para abrir aqui, aproxime-se do dispositivo <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Algo deu errado. Tente novamente."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Carregando"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Transmitindo sua mídia"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Transmitindo o app <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inativo, verifique o app"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Não encontrado"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"O controle está indisponível"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Erro. Tente novamente"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Adicionar controles"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Editar controles"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Adicionar app"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Adicionar saídas"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupo"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 dispositivo selecionado"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Alto-falantes e telas"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositivos sugeridos"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Requer uma conta premium"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funciona a transmissão"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Transmitir"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"As pessoas próximas a você com dispositivos Bluetooth compatíveis podem ouvir a mídia que você está transmitindo"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Não foi possível fazer a transmissão"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Falha ao salvar. Tente de novo."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Falha ao salvar."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número da versão"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Número da versão copiado para a área de transferência."</string>
     <string name="basic_status" msgid="2315371112182658176">"Conversa aberta"</string>
@@ -1011,26 +1030,27 @@
     <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Um app de câmera está instalado"</string>
     <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• O app está disponível"</string>
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Pelo menos um dispositivo está disponível"</string>
-    <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Mantenha o atalho pressionado"</string>
+    <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Toque e pressione o atalho"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Cancelar"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Virar agora"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Abra o smartphone para tirar uma selfie melhor"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Usar o display frontal para tirar uma selfie melhor?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Use a câmera traseira para tirar uma foto mais ampla e com maior resolução."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Esta tela vai ser desativada"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositivo dobrável sendo aberto"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositivo dobrável sendo virado"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Bateria restante: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecte sua stylus a um carregador"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria da stylus fraca"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Filmadora"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Não é possível fazer uma ligação por este perfil"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Sua política de trabalho só permite fazer ligações pelo perfil de trabalho"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Alternar para o perfil de trabalho"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Fechar"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Configurações de tela de bloqueio"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
index ebe67d8..cea4532 100644
--- a/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Desativado"</item>
     <item msgid="5966994759929723339">"Ativado"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 7f1ce90..22ddebf 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Limite inferior de <xliff:g id="PERCENT">%1$d</xliff:g> por cento"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Limite esquerdo de <xliff:g id="PERCENT">%1$d</xliff:g> por cento"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Limite direito de <xliff:g id="PERCENT">%1$d</xliff:g> por cento"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"As capturas de ecrã do trabalho são guardadas na app <xliff:g id="APP">%1$s</xliff:g>"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Guardada na app <xliff:g id="APP">%1$s</xliff:g> no perfil de trabalho"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Ficheiros"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"A app <xliff:g id="APPNAME">%1$s</xliff:g> detetou esta captura de ecrã."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"A app <xliff:g id="APPNAME">%1$s</xliff:g> e outras apps abertas detetaram esta captura de ecrã."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Gravador de ecrã"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"A processar a gravação de ecrã"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificação persistente de uma sessão de gravação de ecrã"</string>
@@ -255,6 +257,7 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brilho"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversão de cores"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correção da cor"</string>
+    <string name="quick_settings_font_scaling_label" msgid="5289001009876936768">"Tamanho do tipo de letra"</string>
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Gerir utilizadores"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Concluído"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Fechar"</string>
@@ -775,6 +778,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"gravação de ecrã"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Sem título"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Modo de espera"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Janela de ampliação"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Controlos da janela de ampliação"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Aumentar zoom"</string>
@@ -800,6 +809,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Escolha uma app para adicionar controlos"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# controlo adicionado.}many{# controlos adicionados.}other{# controlos adicionados.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Removido"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Adicionar <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Quando adicionar a app <xliff:g id="APPNAME">%s</xliff:g>, esta pode adicionar controlos e conteúdos a este painel. Em algumas apps, pode escolher que controlos são apresentados aqui."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Adicionado aos favoritos"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Adicionados aos favoritos, posição <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Removido dos favoritos"</string>
@@ -850,6 +861,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Abrir <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Reproduzir <xliff:g id="SONG_NAME">%1$s</xliff:g> de <xliff:g id="ARTIST_NAME">%2$s</xliff:g> a partir da app <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Reproduzir <xliff:g id="SONG_NAME">%1$s</xliff:g> a partir da app <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Para si"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Anular"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Aproxime-se para reproduzir no dispositivo <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Para reproduzir aqui, aproxime-se do <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -857,6 +869,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Algo correu mal. Tente novamente."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"A carregar"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"A transmitir o conteúdo multimédia"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"A transmitir a app <xliff:g id="APP_LABEL">%1$s</xliff:g>…"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inativa. Consulte a app."</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Não encontrado."</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"O controlo está indisponível"</string>
@@ -866,6 +880,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Erro. Tente novamente."</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Adicionar controlos"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Editar controlos"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Adicionar app"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Adicione saídas"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupo"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 dispositivo selecionado"</string>
@@ -881,6 +896,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Altifalantes e ecrãs"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositivos sugeridos"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Requer uma conta premium"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funciona a transmissão"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Transmissão"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"As pessoas próximas de si com dispositivos Bluetooth compatíveis podem ouvir o conteúdo multimédia que está a transmitir"</string>
@@ -892,6 +908,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Não é possível transmitir"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Não é possível guardar. Tente novamente."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Não é possível guardar."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Use, pelo menos, 4 carateres"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Use menos de 16 carateres"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número da compilação"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Número da compilação copiado para a área de transferência."</string>
     <string name="basic_status" msgid="2315371112182658176">"Abrir conversa"</string>
@@ -1011,11 +1029,11 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Está disponível, pelo menos, um dispositivo"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Toque sem soltar no atalho"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Cancelar"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Inverter agora"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Desdobre o telemóvel para uma selfie melhor"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Inverter para ecrã frontal para uma selfie melhor?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Use a câmara traseira para uma foto mais ampla com uma resolução superior."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Este ecrã vai ser desligado"</b></string>
+    <string name="rear_display_bottom_sheet_confirm" msgid="1507591562761552899">"Mudar de ecrã agora"</string>
+    <string name="rear_display_folded_bottom_sheet_title" msgid="3930008746560711990">"Desdobre o telemóvel"</string>
+    <string name="rear_display_unfolded_bottom_sheet_title" msgid="6291111173057304055">"Mudar de ecrã?"</string>
+    <string name="rear_display_folded_bottom_sheet_description" msgid="6842767125783222695">"Para uma resolução superior, use a câmara traseira"</string>
+    <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Para uma resolução superior, inverta o telemóvel"</string>
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositivo dobrável a ser desdobrado"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositivo dobrável a ser virado ao contrário"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> de bateria restante"</string>
@@ -1026,4 +1044,5 @@
     <string name="call_from_work_profile_text" msgid="3458704745640229638">"A sua Política de Trabalho só lhe permite fazer chamadas telefónicas a partir do perfil de trabalho"</string>
     <string name="call_from_work_profile_action" msgid="2937701298133010724">"Mudar para perfil de trabalho"</string>
     <string name="call_from_work_profile_close" msgid="7927067108901068098">"Fechar"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Definições do ecrã de bloqueio"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
index bda7473..b58b848 100644
--- a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
@@ -176,4 +176,9 @@
     <item msgid="8014986104355098744">"Desativado"</item>
     <item msgid="5966994759929723339">"Ativado"</item>
   </string-array>
+  <string-array name="tile_states_font_scaling">
+    <item msgid="3173069902082305985">"Indisponível"</item>
+    <item msgid="2478289035899842865">"Desativado"</item>
+    <item msgid="5137565285664080143">"Ativado"</item>
+  </string-array>
 </resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index e56a7c7..1b9e015 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Borda inferior em <xliff:g id="PERCENT">%1$d</xliff:g> por cento"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Borda esquerda em <xliff:g id="PERCENT">%1$d</xliff:g> por cento"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Borda direita em <xliff:g id="PERCENT">%1$d</xliff:g> por cento"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Capturas de tela do trabalho são salvas no app <xliff:g id="APP">%1$s</xliff:g>"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Salva no app <xliff:g id="APP">%1$s</xliff:g> no perfil de trabalho"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Files"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"O app <xliff:g id="APPNAME">%1$s</xliff:g> detectou essa captura de tela."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> e outros apps abertos detectaram essa captura de tela."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Gravador de tela"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processando gravação de tela"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificação contínua para uma sessão de gravação de tela"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brilho"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversão de cores"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correção de cor"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Gerenciar usuários"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Concluído"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Fechar"</string>
@@ -489,7 +493,7 @@
     <string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Chamadas e notificações farão o smartphone tocar (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Sintonizador System UI"</string>
     <string name="status_bar" msgid="4357390266055077437">"Barra de status"</string>
-    <string name="demo_mode" msgid="263484519766901593">"Modo de demonstração da IU do sistema"</string>
+    <string name="demo_mode" msgid="263484519766901593">"Modo de demonstração da interface do sistema"</string>
     <string name="enable_demo_mode" msgid="3180345364745966431">"Ativar modo de demonstração"</string>
     <string name="show_demo_mode" msgid="3677956462273059726">"Mostrar modo de demonstração"</string>
     <string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Som e vibração desativados"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"O som e a vibração estão desativados, e o balão aparece na parte inferior da seção de conversa"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Pode vibrar ou tocar com base nas configurações do dispositivo"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Pode vibrar ou tocar com base nas configurações do dispositivo. As conversas do app <xliff:g id="APP_NAME">%1$s</xliff:g> aparecem em balões por padrão."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Faça com que o sistema determine se a notificação resultará em som ou vibração"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Status:&lt;/b&gt; promovida a Padrão"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Status:&lt;/b&gt; rebaixada a Silenciosa"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"gravação de tela"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Sem título"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Em espera"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Janela de ampliação"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Controles da janela de ampliação"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Aumentar zoom"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Escolha um app para adicionar controles"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# controle adicionado.}one{# controle adicionado.}many{# de controles adicionados.}other{# controles adicionados.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Removido"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Adicionar o app <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Quando você adiciona o app <xliff:g id="APPNAME">%s</xliff:g>, ele pode incluir controles e conteúdo neste painel. Em alguns casos, é possível escolher quais controles aparecem aqui."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Adicionado como favorito"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Adicionado como favorito (posição <xliff:g id="NUMBER">%d</xliff:g>)"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Removido dos favoritos"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Abrir <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Tocar <xliff:g id="SONG_NAME">%1$s</xliff:g> de <xliff:g id="ARTIST_NAME">%2$s</xliff:g> no app <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Tocar <xliff:g id="SONG_NAME">%1$s</xliff:g> no app <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Para você"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Desfazer"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Aproxime os dispositivos para tocar a mídia neste: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Para abrir aqui, aproxime-se do dispositivo <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Algo deu errado. Tente novamente."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Carregando"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Transmitindo sua mídia"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Transmitindo o app <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inativo, verifique o app"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Não encontrado"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"O controle está indisponível"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Erro. Tente novamente"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Adicionar controles"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Editar controles"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Adicionar app"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Adicionar saídas"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupo"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 dispositivo selecionado"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Alto-falantes e telas"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositivos sugeridos"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Requer uma conta premium"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funciona a transmissão"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Transmitir"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"As pessoas próximas a você com dispositivos Bluetooth compatíveis podem ouvir a mídia que você está transmitindo"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Não foi possível fazer a transmissão"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Falha ao salvar. Tente de novo."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Falha ao salvar."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número da versão"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Número da versão copiado para a área de transferência."</string>
     <string name="basic_status" msgid="2315371112182658176">"Conversa aberta"</string>
@@ -1011,26 +1030,27 @@
     <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Um app de câmera está instalado"</string>
     <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• O app está disponível"</string>
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Pelo menos um dispositivo está disponível"</string>
-    <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Mantenha o atalho pressionado"</string>
+    <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Toque e pressione o atalho"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Cancelar"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Virar agora"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Abra o smartphone para tirar uma selfie melhor"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Usar o display frontal para tirar uma selfie melhor?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Use a câmera traseira para tirar uma foto mais ampla e com maior resolução."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Esta tela vai ser desativada"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispositivo dobrável sendo aberto"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispositivo dobrável sendo virado"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Bateria restante: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecte sua stylus a um carregador"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria da stylus fraca"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Filmadora"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Não é possível fazer uma ligação por este perfil"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Sua política de trabalho só permite fazer ligações pelo perfil de trabalho"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Alternar para o perfil de trabalho"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Fechar"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Configurações de tela de bloqueio"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt/tiles_states_strings.xml b/packages/SystemUI/res/values-pt/tiles_states_strings.xml
index ebe67d8..cea4532 100644
--- a/packages/SystemUI/res/values-pt/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Desativado"</item>
     <item msgid="5966994759929723339">"Ativado"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index a4c66ec..907b9c1 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Marginea de jos la <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Marginea stângă la <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Marginea dreaptă la <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Capturile de ecran pentru serviciu sunt salvate în aplicația <xliff:g id="APP">%1$s</xliff:g>"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Salvată în <xliff:g id="APP">%1$s</xliff:g> în profilul de serviciu"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Fișiere"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> a detectat această captură de ecran."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> și alte aplicații deschise au detectat această captură de ecran."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Recorder pentru ecran"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Se procesează înregistrarea"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificare în curs pentru o sesiune de înregistrare a ecranului"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Luminozitate"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversarea culorilor"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Corecția culorii"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Gestionează utilizatorii"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Terminat"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Închide"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automat"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Fără sunet sau vibrații"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Fără sunet sau vibrații și apare în partea de jos a secțiunii de conversație"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Poate să sune sau să vibreze, în funcție de setările dispozitivului"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Poate să sune sau să vibreze, în funcție de setările dispozitivului. Conversațiile din balonul <xliff:g id="APP_NAME">%1$s</xliff:g> în mod prestabilit."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Solicită-i sistemului să stabilească dacă această notificare e sonoră sau cu vibrații."</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Stare:&lt;/b&gt; promovată la prestabilită"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Stare:&lt;/b&gt; setată ca Silențioasă"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"înregistrare de ecran"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Fără titlu"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Standby"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Fereastra de mărire"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Comenzi pentru fereastra de mărire"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Mărește"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Alege aplicația pentru a adăuga comenzi"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{S-a adăugat # comandă.}few{S-au adăugat # comenzi.}other{S-au adăugat # de comenzi.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Eliminată"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Adaugi <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Când adaugi <xliff:g id="APPNAME">%s</xliff:g>, aplicația poate să adauge comenzi și conținut pe acest panou. În anumite aplicații, poți să alegi comenzile care se afișează aici."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Marcată ca preferată"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Marcată ca preferată, poziția <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"S-a anulat marcarea ca preferată"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Deschide <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Redă <xliff:g id="SONG_NAME">%1$s</xliff:g> de la <xliff:g id="ARTIST_NAME">%2$s</xliff:g> în <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Redă <xliff:g id="SONG_NAME">%1$s</xliff:g> în <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Pentru tine"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Anulează"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Apropie-te pentru a reda pe <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Pentru a reda conținutul aici, apropie-te de <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"A apărut o eroare. Încearcă din nou."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Se încarcă"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tabletă"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Se proiectează conținutul media"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Se proiectează <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inactiv, verifică aplicația"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Nu s-a găsit"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Comanda este indisponibilă"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Eroare, încearcă din nou"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Adaugă comenzi"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Editează comenzile"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Adaugă o aplicație"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Adaugă ieșiri"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Grup"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"S-a selectat un dispozitiv"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Difuzoare și afișaje"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispozitive sugerate"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Necesită un cont premium"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cum funcționează transmisia"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Transmite"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Persoanele din apropiere cu dispozitive Bluetooth compatibile pot asculta conținutul pe care îl transmiți"</string>
@@ -894,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Nu se poate transmite"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Nu se poate salva. Încearcă din nou."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Nu se poate salva."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Folosește minimum 4 caractere."</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Folosește maximum 16 caractere"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numărul versiunii"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Numărul versiunii s-a copiat în clipboard."</string>
     <string name="basic_status" msgid="2315371112182658176">"Deschide conversația"</string>
@@ -1013,24 +1030,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Este disponibil cel puțin un dispozitiv"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Atinge lung comanda rapidă"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Anulează"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Întoarce-l acum"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Desfă telefonul pentru un selfie mai bun"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Comuți la ecranul frontal pentru un selfie mai bun?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Folosește camera posterioară pentru o fotografie mai lată, cu rezoluție mai mare."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Acest ecran se va dezactiva"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Dispozitiv pliabil care este desfăcut"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Dispozitiv pliabil care este întors"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> baterie rămasă"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conectează-ți creionul la un încărcător"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Nivelul bateriei creionului este scăzut"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Cameră video"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Nu poți iniția apeluri din acest profil"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Politica privind activitatea îți permite să efectuezi apeluri telefonice numai din profilul de serviciu"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Comută la profilul de serviciu"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Închide"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Setările ecranului de blocare"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ro/tiles_states_strings.xml b/packages/SystemUI/res/values-ro/tiles_states_strings.xml
index 7b7bb3a..782afc5 100644
--- a/packages/SystemUI/res/values-ro/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ro/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Dezactivat"</item>
     <item msgid="5966994759929723339">"Activat"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index d4dddbe..2b8c776 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"ĐĄĐșŃ€ĐžĐœŃˆĐŸŃ‚Ń‹ ŃĐŸŃ…Ń€Đ°ĐœŃŃŽŃ‚ŃŃ ĐČ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐž \"<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>
     <string name="screenrecord_name" msgid="2596401223859996572">"Запось ĐČĐžĐŽĐ”ĐŸ с эĐșŃ€Đ°ĐœĐ°"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ĐžĐ±Ń€Đ°Đ±ĐŸŃ‚Đșа запОсО с эĐșŃ€Đ°ĐœĐ°…"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"йДĐșŃƒŃ‰Đ”Đ” уĐČĐ”ĐŽĐŸĐŒĐ»Đ”ĐœĐžĐ” ĐŽĐ»Ń запОсО ĐČĐžĐŽĐ”ĐŸ с эĐșŃ€Đ°ĐœĐ°"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ЯрĐșĐŸŃŃ‚ŃŒ"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Đ˜ĐœĐČĐ”Ń€ŃĐžŃ цĐČĐ”Ń‚ĐŸĐČ"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ĐšĐŸŃ€Ń€Đ”Đșцоя цĐČДта"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"УпраĐČĐ»Đ”ĐœĐžĐ” ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»ŃĐŒĐž"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Đ“ĐŸŃ‚ĐŸĐČĐŸ"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"ЗаĐșрыть"</string>
@@ -775,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"Đ·Đ°ĐżĐžŃŃŒ эĐșŃ€Đ°ĐœĐ°"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"БДз ĐœĐ°Đ·ĐČĐ°ĐœĐžŃ"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"ĐŸĐ”Ń€Đ”Ń…ĐŸĐŽ ĐČ Ń€Đ”Đ¶ĐžĐŒ ĐŸĐ¶ĐžĐŽĐ°ĐœĐžŃ"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"ОĐșĐœĐŸ уĐČĐ”Đ»ĐžŃ‡Đ”ĐœĐžŃ"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"ĐĐ°ŃŃ‚Ń€ĐŸĐčĐșĐž ĐŸĐșĐœĐ° уĐČĐ”Đ»ĐžŃ‡Đ”ĐœĐžŃ"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ĐŁĐČĐ”Đ»ĐžŃ‡ĐžŃ‚ŃŒ"</string>
@@ -800,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Đ§Ń‚ĐŸĐ±Ń‹ ĐŽĐŸĐ±Đ°ĐČоть ĐČОЎжДты упраĐČĐ»Đ”ĐœĐžŃ, ĐČыбДрОтД ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ”"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Đ”ĐŸĐ±Đ°ĐČĐ»Đ”Đœ # ŃĐ»Đ”ĐŒĐ”ĐœŃ‚ упраĐČĐ»Đ”ĐœĐžŃ.}one{Đ”ĐŸĐ±Đ°ĐČĐ»Đ”Đœ # ŃĐ»Đ”ĐŒĐ”ĐœŃ‚ упраĐČĐ»Đ”ĐœĐžŃ.}few{Đ”ĐŸĐ±Đ°ĐČĐ»Đ”ĐœĐŸ # ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Đ° упраĐČĐ»Đ”ĐœĐžŃ.}many{Đ”ĐŸĐ±Đ°ĐČĐ»Đ”ĐœĐŸ # ŃĐ»Đ”ĐŒĐ”ĐœŃ‚ĐŸĐČ ŃƒĐżŃ€Đ°ĐČĐ»Đ”ĐœĐžŃ.}other{Đ”ĐŸĐ±Đ°ĐČĐ»Đ”ĐœĐŸ # ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Đ° упраĐČĐ»Đ”ĐœĐžŃ.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"ĐŁĐŽĐ°Đ»Đ”ĐœĐŸ"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Đ”ĐŸĐ±Đ°ĐČоть ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” \"<xliff:g id="APPNAME">%s</xliff:g>\"?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"ĐŸŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” \"<xliff:g id="APPNAME">%s</xliff:g>\" ĐŒĐŸĐ¶Đ”Ń‚ ĐŽĐŸĐ±Đ°ĐČоть ĐœĐ° эту ĐżĐ°ĐœĐ”Đ»ŃŒ ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Ń‹ упраĐČĐ»Đ”ĐœĐžŃ Đž ĐșĐŸĐœŃ‚Đ”ĐœŃ‚. ĐĐ”ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ ĐżĐŸĐ·ĐČĐŸĐ»ŃŃŽŃ‚ ĐČŃ‹Đ±ĐžŃ€Đ°Ń‚ŃŒ, ĐșаĐșОД ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Ń‹ Đ±ŃƒĐŽŃƒŃ‚ Đ·ĐŽĐ”ŃŃŒ ĐżĐŸĐșĐ°Đ·Đ°ĐœŃ‹."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Đ”ĐŸĐ±Đ°ĐČĐ»Đ”ĐœĐŸ ĐČ ĐžĐ·Đ±Ń€Đ°ĐœĐœĐŸĐ”"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Đ”ĐŸĐ±Đ°ĐČĐ»Đ”ĐœĐŸ ĐČ ĐžĐ·Đ±Ń€Đ°ĐœĐœĐŸĐ” ĐœĐ° ĐżĐŸĐ·ĐžŃ†ĐžŃŽ <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"ĐĐ” ĐŽĐŸĐ±Đ°ĐČĐ»Đ”ĐœĐŸ ĐČ ĐžĐ·Đ±Ń€Đ°ĐœĐœĐŸĐ”"</string>
@@ -850,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"ОтĐșрыть ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” \"<xliff:g id="APP_LABEL">%1$s</xliff:g>\""</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Đ’ĐŸŃĐżŃ€ĐŸĐžĐ·ĐČДстО ĐŒĐ”ĐŽĐžĐ°Ń„Đ°ĐčĐ» \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" (ĐžŃĐżĐŸĐ»ĐœĐžŃ‚Đ”Đ»ŃŒ: <xliff:g id="ARTIST_NAME">%2$s</xliff:g>) Оз ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ \"<xliff:g id="APP_LABEL">%3$s</xliff:g>\""</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Đ’ĐŸŃĐżŃ€ĐŸĐžĐ·ĐČДстО ĐŒĐ”ĐŽĐžĐ°Ń„Đ°ĐčĐ» \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" Оз ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ \"<xliff:g id="APP_LABEL">%2$s</xliff:g>\""</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Đ”Đ»Ń ĐČас"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"ĐžŃ‚ĐŒĐ”ĐœĐžŃ‚ŃŒ"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Đ§Ń‚ĐŸĐ±Ń‹ ĐœĐ°Ń‡Đ°Ń‚ŃŒ Ń‚Ń€Đ°ĐœŃĐ»ŃŃ†ĐžŃŽ ĐœĐ° ŃƒŃŃ‚Ń€ĐŸĐčстĐČĐ” \"<xliff:g id="DEVICENAME">%1$s</xliff:g>\", ĐżĐŸĐŽĐŸĐčЎОтД Đș ĐœĐ”ĐŒŃƒ блОжД."</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Đ§Ń‚ĐŸĐ±Ń‹ ĐœĐ°Ń‡Đ°Ń‚ŃŒ ĐČĐŸŃĐżŃ€ĐŸĐžĐ·ĐČĐ”ĐŽĐ”ĐœĐžĐ” Đ·ĐŽĐ”ŃŃŒ, ĐżĐŸĐŽĐŸĐčЎОтД блОжД Đș ŃƒŃŃ‚Ń€ĐŸĐčстĐČу (<xliff:g id="DEVICENAME">%1$s</xliff:g>)."</string>
@@ -857,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"ĐŸŃ€ĐŸĐžĐ·ĐŸŃˆĐ»Đ° ĐŸŃˆĐžĐ±Đșа. ĐŸĐŸĐČŃ‚ĐŸŃ€ĐžŃ‚Đ” ĐżĐŸĐżŃ‹Ń‚Đșу."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Đ—Đ°ĐłŃ€ŃƒĐ·Đșа…"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ĐżĐ»Đ°ĐœŃˆĐ”Ń‚"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"ĐąŃ€Đ°ĐœŃĐ»ŃŃ†ĐžŃ ĐŒĐ”ĐŽĐžĐ°ĐșĐŸĐœŃ‚Đ”ĐœŃ‚Đ°"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"ĐąŃ€Đ°ĐœŃĐ»ŃŃ†ĐžŃ: <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ĐĐ”Ń‚ ĐŸŃ‚ĐČДта. ĐŸŃ€ĐŸĐČĐ”Ń€ŃŒŃ‚Đ” ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ”."</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"ĐĐ” ĐœĐ°ĐčĐŽĐ”ĐœĐŸ."</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"УпраĐČĐ»Đ”ĐœĐžĐ” ĐœĐ”ĐŽĐŸŃŃ‚ŃƒĐżĐœĐŸ"</string>
@@ -866,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"ĐžŃˆĐžĐ±Đșа. ĐŸĐŸĐČŃ‚ĐŸŃ€ĐžŃ‚Đ” ĐżĐŸĐżŃ‹Ń‚Đșу."</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Đ”ĐŸĐ±Đ°ĐČоть ĐČОЎжДты"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Đ˜Đ·ĐŒĐ”ĐœĐžŃ‚ŃŒ ĐČОЎжДты"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Đ”ĐŸĐ±Đ°ĐČоть ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ”"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Đ”ĐŸĐ±Đ°ĐČĐ»Đ”ĐœĐžĐ” ŃƒŃŃ‚Ń€ĐŸĐčстĐČ ĐČыĐČĐŸĐŽĐ°"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Группа"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Đ’Ń‹Đ±Ń€Đ°ĐœĐŸ 1 ŃƒŃŃ‚Ń€ĐŸĐčстĐČĐŸ"</string>
@@ -881,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ĐšĐŸĐ»ĐŸĐœĐșĐž Đž ЎОсплДО"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Đ Đ”ĐșĐŸĐŒĐ”ĐœĐŽŃƒĐ”ĐŒŃ‹Đ” ŃƒŃŃ‚Ń€ĐŸĐčстĐČа"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"ĐąŃ€Đ”Đ±ŃƒĐ”Ń‚ŃŃ ĐżŃ€Đ”ĐŒĐžŃƒĐŒ-аĐșĐșĐ°ŃƒĐœŃ‚"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"КаĐș Ń€Đ°Đ±ĐŸŃ‚Đ°ŃŽŃ‚ Ń‚Ń€Đ°ĐœŃĐ»ŃŃ†ĐžĐž"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"ĐąŃ€Đ°ĐœŃĐ»ŃŃ†ĐžŃ"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ĐĐ°Ń…ĐŸĐŽŃŃ‰ĐžĐ”ŃŃ Ń€ŃĐŽĐŸĐŒ с ĐČĐ°ĐŒĐž люЎО с ŃĐŸĐČĐŒĐ”ŃŃ‚ĐžĐŒŃ‹ĐŒĐž ŃƒŃŃ‚Ń€ĐŸĐčстĐČĐ°ĐŒĐž Bluetooth ĐŒĐŸĐłŃƒŃ‚ ŃĐ»ŃƒŃˆĐ°Ń‚ŃŒ ĐŒĐ”ĐŽĐžĐ°Ń„Đ°Đčлы, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐČы Ń‚Ń€Đ°ĐœŃĐ»ĐžŃ€ŃƒĐ”Ń‚Đ”."</string>
@@ -892,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"ĐĐ” ŃƒĐŽĐ°Đ»ĐŸŃŃŒ Đ·Đ°ĐżŃƒŃŃ‚ĐžŃ‚ŃŒ Ń‚Ń€Đ°ĐœŃĐ»ŃŃ†ĐžŃŽ"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"ĐĐ” ŃƒĐŽĐ°Đ»ĐŸŃŃŒ ŃĐŸŃ…Ń€Đ°ĐœĐžŃ‚ŃŒ. ĐŸĐŸĐČŃ‚ĐŸŃ€ĐžŃ‚Đ” ĐżĐŸĐżŃ‹Ń‚Đșу."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"ĐĐ” ŃƒĐŽĐ°Đ»ĐŸŃŃŒ ŃĐŸŃ…Ń€Đ°ĐœĐžŃ‚ŃŒ."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"ĐĐŸĐŒĐ”Ń€ ŃĐ±ĐŸŃ€ĐșĐž"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"ĐĐŸĐŒĐ”Ń€ ŃĐ±ĐŸŃ€ĐșĐž сĐșĐŸĐżĐžŃ€ĐŸĐČĐ°Đœ ĐČ Đ±ŃƒŃ„Đ”Ń€ ĐŸĐ±ĐŒĐ”ĐœĐ°."</string>
     <string name="basic_status" msgid="2315371112182658176">"ОтĐșрытыĐč чат"</string>
@@ -1011,11 +1032,16 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Đ”ĐŸŃŃ‚ŃƒĐżĐœĐŸ Ń…ĐŸŃ‚Ń бы ĐŸĐŽĐœĐŸ ŃƒŃŃ‚Ń€ĐŸĐčстĐČĐŸ."</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"ĐĐ°Đ¶ĐŒĐžŃ‚Đ” Đž ŃƒĐŽĐ”Ń€Đ¶ĐžĐČаĐčтД ŃŃ€Đ»Ń‹Đș"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"ĐžŃ‚ĐŒĐ”ĐœĐ°"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"ĐŸĐ”Ń€Đ”ĐČĐ”Ń€ĐœŃƒŃ‚ŃŒ сДĐčчас"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Đ Đ°Đ·Đ»ĐŸĐ¶ĐžŃ‚Đ” Ń‚Đ”Đ»Đ”Ń„ĐŸĐœ, Ń‡Ń‚ĐŸĐ±Ń‹ сДлфО ĐżĐŸĐ»ŃƒŃ‡ĐžĐ»ĐŸŃŃŒ Đ»ŃƒŃ‡ŃˆĐ”"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"ĐŸĐ”Ń€Đ”ĐČĐ”Ń€ĐœŃƒĐ»Đž Ń‚Đ”Đ»Đ”Ń„ĐŸĐœ ĐżĐ”Ń€Đ”ĐŽĐœĐžĐŒ эĐșŃ€Đ°ĐœĐŸĐŒ Đș сДбД?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Đ˜ŃĐżĐŸĐ»ŃŒĐ·ŃƒĐčтД ĐŸŃĐœĐŸĐČĐœŃƒŃŽ ĐșĐ°ĐŒĐ”Ń€Ńƒ с ŃˆĐžŃ€ĐŸĐșĐŸŃƒĐłĐŸĐ»ŃŒĐœŃ‹ĐŒ ĐŸĐ±ŃŠĐ”ĐșтоĐČĐŸĐŒ Đž ĐČŃ‹ŃĐŸĐșĐžĐŒ Ń€Đ°Đ·Ń€Đ”ŃˆĐ”ĐœĐžĐ”ĐŒ."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Đ­Ń‚ĐŸŃ‚ эĐșŃ€Đ°Đœ ĐŸŃ‚ĐșĐ»ŃŽŃ‡ĐžŃ‚ŃŃ"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ĐĄĐșĐ»Đ°ĐŽĐœĐŸĐ” ŃƒŃŃ‚Ń€ĐŸĐčстĐČĐŸ ĐČ Ń€Đ°Đ·Đ»ĐŸĐ¶Đ”ĐœĐœĐŸĐŒ ĐČОЎД"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ĐŸĐ”Ń€Đ”ĐČĐ”Ń€ĐœŃƒŃ‚ĐŸĐ” сĐșĐ»Đ°ĐŽĐœĐŸĐ” ŃƒŃŃ‚Ń€ĐŸĐčстĐČĐŸ"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"ĐŁŃ€ĐŸĐČĐ”ĐœŃŒ Đ·Đ°Ń€ŃĐŽĐ° батарДО: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
@@ -1026,4 +1052,5 @@
     <string name="call_from_work_profile_text" msgid="3458704745640229638">"ĐĄĐŸĐłĐ»Đ°ŃĐœĐŸ праĐČĐžĐ»Đ°ĐŒ ĐČашДĐč ĐŸŃ€ĐłĐ°ĐœĐžĐ·Đ°Ń†ĐžĐž ĐČы ĐŒĐŸĐ¶Đ”Ń‚Đ” ŃĐŸĐČĐ”Ń€ŃˆĐ°Ń‚ŃŒ Ń‚Đ”Đ»Đ”Ń„ĐŸĐœĐœŃ‹Đ” Đ·ĐČĐŸĐœĐșĐž Ń‚ĐŸĐ»ŃŒĐșĐŸ Оз Ń€Đ°Đ±ĐŸŃ‡Đ”ĐłĐŸ ĐżŃ€ĐŸŃ„ĐžĐ»Ń."</string>
     <string name="call_from_work_profile_action" msgid="2937701298133010724">"ĐŸĐ”Ń€Đ”Đčто ĐČ Ń€Đ°Đ±ĐŸŃ‡ĐžĐč ĐżŃ€ĐŸŃ„ĐžĐ»ŃŒ"</string>
     <string name="call_from_work_profile_close" msgid="7927067108901068098">"ЗаĐșрыть"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"ĐĐ°ŃŃ‚Ń€ĐŸĐčĐșĐž Đ±Đ»ĐŸĐșĐžŃ€ĐŸĐČĐșĐž эĐșŃ€Đ°ĐœĐ°"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ru/tiles_states_strings.xml b/packages/SystemUI/res/values-ru/tiles_states_strings.xml
index 6255bd8..9a4960b 100644
--- a/packages/SystemUI/res/values-ru/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ru/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"ОтĐșĐ»ŃŽŃ‡Đ”ĐœĐŸ"</item>
     <item msgid="5966994759929723339">"ВĐșĐ»ŃŽŃ‡Đ”ĐœĐŸ"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 838f4ee..8c85119 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"වැඩ තිර රූ <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>
     <string name="screenrecord_name" msgid="2596401223859996572">"තිර රෙකෝඩරà¶ș"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"තිර ඎටිගත කිරීඞ සකසඞින්"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"තිර ඎටිගත කිරීඞේ සැසිà¶șක් à·ƒà¶łà·„à· කෙරෙන à¶Żà·à¶±à·”à¶žà·Š à¶Żà·“à¶ž"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"à¶Żà·“à¶Žà·Šà¶­à·’à¶žà¶­à·Š බව"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"වර්ණ අඎවර්තනà¶ș"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"වර්ණ à¶±à·’à·€à·à¶»à¶Żà·’ කිරීඞ"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"ඎරිශීගකà¶șන් කළඞනාකරණà¶ș කරන්න"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"නිඞà¶șි"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"වසන්න"</string>
@@ -775,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"තිර ඎටිගත කිරීඞ"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"ඞාතෘකාවක් නැත"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"ඎොරොත්තු"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"විශාගන කවුළුව"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"විශාගනà¶ș කිරීඞේ කවුළු ඎාගන"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"විශාගනà¶ș වැඩි කරන්න"</string>
@@ -800,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"ඎාගන එක් කිරීඞට à¶șà·™à¶Żà·”à¶ž තෝරා ගන්න"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# ඎාගනà¶șක් එක් කර ඇත.}one{ඎාගන #ක් එක් කර ඇත.}other{ඎාගන #ක් එක් කර ඇත.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"ඉවත් කළා"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g> එක් කරන්න à¶Ż?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"ඔබ <xliff:g id="APPNAME">%s</xliff:g> එක් කළ විට, එà¶șà¶§ ඞෙඞ ඎැනගà¶șà¶§ ඎාගන සහ අන්තර්ගතà¶ș එක් කළ හැක. සඞහර à¶șà·™à¶Żà·”à¶žà·Šà·€à¶œ, ඔබට ඞෙහි ඎෙන්වන්නේ කුඞන ඎාගන à¶Ż à¶șන්න තෝරා ගැනීඞට හැක."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"à¶Žà·Š‍රිà¶șà¶­à¶ž කළා"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"à¶Žà·Š‍රිà¶șà¶­à¶ž කළා, තත්ත්ව <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"à¶Žà·Š‍රිà¶șà¶­à¶ž වෙතින් ඉවත් කළා"</string>
@@ -850,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> විවෘත කරන්න"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g>ගේ <xliff:g id="SONG_NAME">%1$s</xliff:g> <xliff:g id="APP_LABEL">%3$s</xliff:g> වෙතින් à·€à·à¶Żà¶±à¶ș කරන්න"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> <xliff:g id="APP_LABEL">%2$s</xliff:g> වෙතින් à·€à·à¶Żà¶±à¶ș කරන්න"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"ඔබට"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"ඎසුගඞනà¶ș කරන්න"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> හි à·€à·à¶Żà¶±à¶ș කිරීඞට වඩාත් ළං වන්න"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"ඞෙහි à·€à·à¶Żà¶±à¶ș කිරීඞ à·ƒà¶łà·„à·, <xliff:g id="DEVICENAME">%1$s</xliff:g> වෙත සඞීඎ වන්න"</string>
@@ -857,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"à¶șà¶žà·Š à¶Żà·™à¶șක් à·€à·à¶»à¶Żà·’à¶«à·’. නැවත උත්සාහ කරන්න."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"ඎූරණà¶ș වේ"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ටැබ්ගටà¶ș"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"ඔබේ ඞාධ්‍à¶ș විකාශà¶ș කිරීඞ"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"<xliff:g id="APP_LABEL">%1$s</xliff:g> විකාශà¶ș කරඞින්"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"අක්‍රිà¶șà¶șි, à¶șà·™à¶Żà·”à¶ž ඎරීක්ෂා කරන්න"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"à·„à¶žà·” නොවිණි"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"ඎාගනà¶ș ගබා ගත නොහැකිà¶ș"</string>
@@ -866,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"à¶Żà·à·‚à¶șකි, නැවත උත්සාහ කරන්න"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"ඎාගන එක් කරන්න"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"ඎාගන සංස්කරණà¶ș කරන්න"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"à¶șà·™à¶Żà·”à¶ž එක් කරන්න"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"à¶Žà·Š‍à¶»à¶­à·’à¶Żà·à¶± එක් කරන්න"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"සඞූහà¶ș"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"උඎාංග 1ක් තෝරන à¶œà¶Żà·“"</string>
@@ -881,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ස්ඎීකර් සහ à·ƒà¶‚à¶Żà¶»à·Šà·à¶š"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"à¶șà·à¶ąà·’à¶­ උඎාංග"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"ඎාරිතෝෂික ගිණුඞක් අවශ්‍à¶șà¶șි"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"විකාශනà¶ș ක්‍රිà¶șා කරන ආකාරà¶ș"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"විකාශනà¶ș"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ගැළඎෙන බ්ගූටූත් උඎාංග සහිත ඔබ අවට සිටින à¶Žà·”à¶Żà·Šà¶œà¶œà¶șින්ට ඔබ විකාශනà¶ș කරන ඞාධ්‍à¶șà¶șà¶§ සවන් à¶Żà·’à¶ș හැකිà¶ș"</string>
@@ -892,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"විකාශනà¶ș කළ නොහැකිà¶ș"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"සුරැකිà¶ș නොහැකිà¶ș. නැවත උත්සාහ කරන්න."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"සුරැකිà¶ș නොහැකිà¶ș."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"නිඞැවුඞ් අංකà¶ș"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"නිඞැවුඞ් අංකà¶ș ඎසුරු ඎුවරුවට à¶Žà·’à¶§à¶Žà¶­à·Š කරන à¶œà¶Żà·’."</string>
     <string name="basic_status" msgid="2315371112182658176">"à·ƒà¶‚à·€à·à¶Żà¶ș විවෘත කරන්න"</string>
@@ -1011,11 +1032,16 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• අවඞ වශà¶șෙන් එක උඎාංගà¶șක් ගැබේ"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"ස්ඎර්ශ කර අග්ගා සිටීඞේ කෙටිඞඟ"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"අවගංගු කරන්න"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"à¶Żà·à¶±à·Š ඎෙරළන්න"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"වඩා à·„à·œà¶ł සෙග්ෆිà¶șක් à·ƒà¶łà·„à· à¶Żà·”à¶»à¶šà¶źà¶±à¶ș à¶Żà·’à¶œà·„à¶»à·’à¶±à·Šà¶±"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"වඩා à·„à·œà¶ł සෙග්ෆිà¶șක් à·ƒà¶łà·„à· à¶‰à¶Żà·’à¶»à·’à¶Žà·ƒ à·ƒà¶‚à¶Żà¶»à·Šà·à¶šà¶șà¶§ ඎෙරළන්න à¶Ż?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"ඉහළ à·€à·’à¶·à·šà¶Żà¶± සහිත ඎුළුග් à¶Ąà·à¶șාරූඎà¶șක් à·ƒà¶łà·„à· ඎසුඎසට ඞුහුණගා ඇති කැඞරාව භාවිතා කරන්න."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ඞෙඞ තිරà¶ș ක්‍රිà¶șා විරහිත වනු ඇත"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"à¶Żà·’à¶œ හැරෙඞින් ඎවතින නැඞිà¶ș හැකි උඎාංගà¶ș"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"වටා ඎෙරළෙඞින් තිබෙන නැඞිà¶ș හැකි උඎාංගà¶ș"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> බැටරිà¶ș ඉතිරිව ඇත"</string>
@@ -1026,4 +1052,5 @@
     <string name="call_from_work_profile_text" msgid="3458704745640229638">"ඔබේ වැඩ à¶Žà·Š‍රතිඎත්තිà¶ș ඔබට කාර්à¶șාග ඎැතිකඩෙන් à¶Žà¶žà¶«à¶šà·Š à¶Żà·”à¶»à¶šà¶źà¶± ඇඞතුඞ් ගබා ගැනීඞට ඉඩ සගසà¶șි"</string>
     <string name="call_from_work_profile_action" msgid="2937701298133010724">"කාර්à¶șාග ඎැතිකඩ වෙත ඞාරු වන්න"</string>
     <string name="call_from_work_profile_close" msgid="7927067108901068098">"වසන්න"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"අගුළු තිර සැකසීඞ්"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-si/tiles_states_strings.xml b/packages/SystemUI/res/values-si/tiles_states_strings.xml
index 327e0b9..c9312ff 100644
--- a/packages/SystemUI/res/values-si/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-si/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"ක්‍රිà¶șාවිරහිතà¶șි"</item>
     <item msgid="5966994759929723339">"ක්‍රිà¶șාත්ඞකà¶șි"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 92448f8..262e269 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"<xliff:g id="PERCENT">%1$d</xliff:g> %% dolnej hranice"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"<xliff:g id="PERCENT">%1$d</xliff:g> %% ÄŸavej hranice"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"<xliff:g id="PERCENT">%1$d</xliff:g> %% pravej hranice"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Pracovné snímky obrazovky sú uloĆŸené v aplikácii <xliff:g id="APP">%1$s</xliff:g>"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"UloĆŸená v aplikácii <xliff:g id="APP">%1$s</xliff:g> v pracovnom profile"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Files"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"Aplikácia <xliff:g id="APPNAME">%1$s</xliff:g> zaznamenala túto snímku obrazovky."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> a Äalšie otvorené aplikácie zaznamenali túto snímku obrazovky."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Rekordér obrazovky"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Spracúva sa záznam obrazovky"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Zobrazuje sa upozornenie týkajúce sa relácie záznamu obrazovky"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Jas"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inverzia farieb"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Úprava farieb"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"SpravovaĆ„ pouĆŸívateÄŸov"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Hotovo"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"ZavrieƄ"</string>
@@ -775,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"nahrávanie obrazovky"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Bez názvu"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Pohotovostný reĆŸim"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Okno priblíĆŸenia"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Ovládacie prvky okna priblíĆŸenia"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"PriblíĆŸiĆ„"</string>
@@ -800,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Vyberte aplikáciu, ktorej ovládače si chcete pridaĆ„"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Bol pridaný # ovládací prvok.}few{Boli pridané # ovládacie prvky.}many{# controls added.}other{Bolo pridaných # ovládacích prvkov.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Odstránené"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Chcete pridaĆ„ aplikáciu <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Keď pridáte aplikáciu <xliff:g id="APPNAME">%s</xliff:g>, bude môcĆ„ pridaĆ„ ovládanie a obsah na tento panel. V prípade niektorých aplikácií môĆŸete vybraĆ„, ktoré ovládacie prvky sa tu majú zobraziĆ„."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Pridané medzi obÄŸúbené"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Pridané medzi obÄŸúbené, pozícia <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Odstránené z obÄŸúbených"</string>
@@ -850,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"OtvoriƄ <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"PrehraĆ„ skladbu <xliff:g id="SONG_NAME">%1$s</xliff:g> od interpreta <xliff:g id="ARTIST_NAME">%2$s</xliff:g> z aplikácie <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"PrehraĆ„ skladbu <xliff:g id="SONG_NAME">%1$s</xliff:g> z aplikácie <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Pre vás"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"SpäĆ„"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Ak chcete prehrávaĆ„ v zariadení <xliff:g id="DEVICENAME">%1$s</xliff:g>, priblíĆŸte sa k nemu"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Ak tu chcete prehrávaĆ„ obsah, priblíĆŸte zariadenie k zariadeniu <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -857,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Niečo sa pokazilo. Skúste to znova."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Načítava sa"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Prenášajú sa médiá"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Prenáša sa <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Neaktívne, preverte aplikáciu"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Nenájdené"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Ovládač nie je k dispozícii"</string>
@@ -866,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Chyba, skúste to znova"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"PridaĆ„ ovládače"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"UpraviĆ„ ovládače"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"PridaĆ„ aplikáciu"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Pridanie výstupov"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Skupina"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 vybrané zariadenie"</string>
@@ -881,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Reproduktory a obrazovky"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Navrhované zariadenia"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"VyĆŸaduje prémiový účet"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Ako vysielanie funguje"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Vysielanie"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Äœudia v okolí s kompatibilnými zariadeniami s rozhraním Bluetooth si môĆŸu vypočuĆ„ médiá, ktoré vysielate"</string>
@@ -892,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Nedá sa vysielaĆ„"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Nedá sa uloĆŸiĆ„. Skúste to znova."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Nedá sa uloĆŸiĆ„."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"PouĆŸite aspoƈ štyri znaky"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"PouĆŸite menej neĆŸ 16 znakov"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Číslo zostavy"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Číslo zostavy bolo skopírované do schránky."</string>
     <string name="basic_status" msgid="2315371112182658176">"Otvorená konverzácia"</string>
@@ -1011,11 +1030,16 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• K dispozícii je minimálne jedno zariadenie"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"PridrĆŸte skratku"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"ZrušiĆ„"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"PrevráĆ„te"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Ak chcete lepšie selfie, rozloĆŸte telefón"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"PrevrátiĆ„ na pred. obrazovku pre lepšie selfie?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Pomocou zadného fotoaparátu vytvorte širšiu fotku s vyšším rozlíšením."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Táto obrazovka sa vypne"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"RozloĆŸenie skladacieho zariadenia"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Prevrátenie skladacieho zariadenia"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Zostáva <xliff:g id="PERCENTAGE">%s</xliff:g> batérie"</string>
@@ -1026,4 +1050,5 @@
     <string name="call_from_work_profile_text" msgid="3458704745640229638">"Pracovné pravidlá vám umoĆŸĆˆujú telefonovaĆ„ iba v pracovnom profile"</string>
     <string name="call_from_work_profile_action" msgid="2937701298133010724">"PrepnúĆ„ na pracovný profil"</string>
     <string name="call_from_work_profile_close" msgid="7927067108901068098">"ZavrieƄ"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Nastavenia uzamknutej obrazovky"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sk/tiles_states_strings.xml b/packages/SystemUI/res/values-sk/tiles_states_strings.xml
index 3cbde1c..3540eab 100644
--- a/packages/SystemUI/res/values-sk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sk/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Vypnuté"</item>
     <item msgid="5966994759929723339">"Zapnuté"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 4658ba6..012b14f 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Meja spodaj <xliff:g id="PERCENT">%1$d</xliff:g> odstotkov"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Meja levo <xliff:g id="PERCENT">%1$d</xliff:g> odstotkov"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Meja desno <xliff:g id="PERCENT">%1$d</xliff:g> odstotkov"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Posnetki zaslona v delovnem profilu so shranjeni v aplikaciji <xliff:g id="APP">%1$s</xliff:g>."</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Shranjeno v aplikaciji <xliff:g id="APP">%1$s</xliff:g> v delovnem profilu"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Datoteke"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"Aplikacija <xliff:g id="APPNAME">%1$s</xliff:g> je zaznala ta posnetek zaslona."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> in druge odprte aplikacije so zaznale ta posnetek zaslona."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Snemalnik zaslona"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Obdelava videoposnetka zaslona"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Nenehno obvešÄanje o seji snemanja zaslona"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Svetlost"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inverzija barv"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Popravljanje barv"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Upravljanje uporabnikov"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Končano"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Zapri"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Samodejno"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Brez zvočnega opozarjanja ali vibriranja."</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Brez zvočnega opozarjanja ali vibriranja, prikaz niĆŸje v razdelku Pogovor."</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Zvonjenje ali vibriranje je omogočeno na podlagi nastavitev naprave."</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Zvonjenje ali vibriranje je omogočeno na podlagi nastavitev naprave. Pogovori v aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> so privzeto prikazani v oblačkih."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Naj sistem določi, ali ob prejemu tega obvestila naprava predvaja zvok ali zavibrira"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Stanje:&lt;/b&gt; UvršÄeno med privzeta obvestila"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Stanje:&lt;/b&gt; UvršÄeno med obvestila brez zvoka"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"snemanje zaslona"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Brez naslova"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Stanje pripravljenosti"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Povečevalno okno"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kontrolniki povečevalnega okna"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Povečaj"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Izberite aplikacijo za dodajanje kontrolnikov"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# kontrolnik je dodan.}one{# kontrolnik je dodan.}two{# kontrolnika sta dodana.}few{# kontrolniki so dodani.}other{# kontrolnikov je dodanih.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Odstranjeno"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Ćœelite dodati aplikacijo <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Ko dodate aplikacijo <xliff:g id="APPNAME">%s</xliff:g>, lahko ta doda kontrolnike in vsebino v to podokno. V nekaterih aplikacijah lahko izberete, kateri kontrolniki so prikazani tukaj."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Dodano med priljubljene"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Dodano med priljubljene, poloĆŸaj <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Odstranjeno iz priljubljenih"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Odpri aplikacijo <xliff:g id="APP_LABEL">%1$s</xliff:g>."</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Predvajaj skladbo <xliff:g id="SONG_NAME">%1$s</xliff:g> izvajalca <xliff:g id="ARTIST_NAME">%2$s</xliff:g> iz aplikacije <xliff:g id="APP_LABEL">%3$s</xliff:g>."</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Predvajaj skladbo <xliff:g id="SONG_NAME">%1$s</xliff:g> iz aplikacije <xliff:g id="APP_LABEL">%2$s</xliff:g>."</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Za vas"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Razveljavi"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Za predvajanje v napravi <xliff:g id="DEVICENAME">%1$s</xliff:g> bolj pribliĆŸajte telefon"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Če ĆŸelite predvajati tukaj, se pribliĆŸajte napravi <xliff:g id="DEVICENAME">%1$s</xliff:g>."</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Prišlo je do napake. Poskusite znova."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Nalaganje"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablični računalnik"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Predvajanje predstavnosti"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Predvajanje aplikacije <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Neaktivno, poglejte aplikacijo"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Ni mogoče najti"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Kontrolnik ni na voljo"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Napaka, poskusite znova"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Dodajte kontrolnike"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Uredite kontrolnike"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Dodaj aplikacijo"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Dodajanje izhodov"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Skupina"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Izbrana je ena naprava"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Zvočniki in zasloni"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Predlagane naprave"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Obvezen je plačljivi račun"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kako deluje oddajanje"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Oddajanje"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Osebe v bliĆŸini z zdruĆŸljivo napravo Bluetooth lahko poslušajo predstavnost, ki jo oddajate."</string>
@@ -894,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Oddajanje ni mogoče"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Ni mogoče shraniti. Poskusite znova."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Ni mogoče shraniti."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Uporabite vsaj 4 znake."</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Uporabite manj kot 16 znakov."</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Delovna različica"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Delovna različica je bila kopirana v odloĆŸišÄe."</string>
     <string name="basic_status" msgid="2315371112182658176">"Odprt pogovor"</string>
@@ -1013,24 +1030,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Na voljo mora biti vsaj ena naprava."</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"PridrĆŸite bliĆŸnjico"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Prekliči"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Obrnite"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Razprite telefon za boljši selfi"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Obrnite telefon na sprednji zaslon za boljši selfi"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Uporabite hrbtni fotoaparat, da posnamete širšo sliko višje ločljivosti."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Ta zaslon se bo izklopil."</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Razpiranje zloĆŸljive naprave"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Obračanje zloĆŸljive naprave"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Preostanek energije baterije: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"PoveĆŸite pisalo s polnilnikom."</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Skoraj prazna baterija pisala"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Ni mogoče klicati iz tega profila"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"SluĆŸbeni pravilnik dovoljuje opravljanje telefonskih klicev le iz delovnega profila."</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Preklopi na delovni profil"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Zapri"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Nastavitve zaklepanja zaslona"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sl/tiles_states_strings.xml b/packages/SystemUI/res/values-sl/tiles_states_strings.xml
index e720819..985b779 100644
--- a/packages/SystemUI/res/values-sl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sl/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Izklopljeno"</item>
     <item msgid="5966994759929723339">"Vklopljeno"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 98bff8b..a9c8784 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Kufiri i poshtëm <xliff:g id="PERCENT">%1$d</xliff:g> për qind"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Kufiri i majtë <xliff:g id="PERCENT">%1$d</xliff:g> për qind"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Kufiri i djathtë <xliff:g id="PERCENT">%1$d</xliff:g> për qind"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Pamjet e ekranit të punës janë ruajtur në aplikacionin \"<xliff:g id="APP">%1$s</xliff:g>\""</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Ruajtur në <xliff:g id="APP">%1$s</xliff:g> në profilin e punës"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Skedarë"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> zbuloi këtë pamje ekrani."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> dhe aplikacionet e tjera të hapura zbuluan këtë pamje ekrani."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Regjistruesi i ekranit"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Regjistrimi i ekranit po përpunohet"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Njoftim i vazhdueshëm për një seancë regjistrimi të ekranit"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Ndriçimi"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Anasjellja e ngjyrës"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Korrigjimi i ngjyrës"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Menaxho përdoruesit"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"U krye"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Mbyll"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatike"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Asnjë tingull ose dridhje"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Asnjë tingull ose dridhje dhe shfaqet më poshtë në seksionin e bisedave"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Mund të bjerë zilja ose të dridhet në bazë të cilësimeve të pajisjes"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Mund të bjerë zilja ose të dridhet në bazë të cilësimeve të pajisjes. Bisedat nga flluska e <xliff:g id="APP_NAME">%1$s</xliff:g> si parazgjedhje."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Kërkoji sistemit të përcaktojë nëse ky njoftim duhet të lëshojë tingull apo dridhje"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Statusi:&lt;/b&gt; Promovuar si parazgjedhje"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Statusi:&lt;/b&gt; Ulur në nivel si në heshtje"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"regjistrim i ekranit"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Pa titull"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Në gatishmëri"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Dritarja e zmadhimit"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kontrollet e dritares së zmadhimit"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zmadho"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Zgjidh aplikacionin për të shtuar kontrollet"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{U shtua # kontroll.}other{U shtuan # kontrolle.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"E hequr"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Të shtohet <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Kur shton <xliff:g id="APPNAME">%s</xliff:g>, ai mund t\'i shtojë kontrolle dhe përmbajtje këtij paneli. Në disa aplikacione, mund të zgjedhësh se cilat kontrolle shfaqen këtu."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"E shtuar te të preferuarat"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"E shtuar te të preferuarat, pozicioni <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"E hequr nga të preferuarat"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Hap <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Luaj <xliff:g id="SONG_NAME">%1$s</xliff:g> nga <xliff:g id="ARTIST_NAME">%2$s</xliff:g> nga <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Luaj <xliff:g id="SONG_NAME">%1$s</xliff:g> nga <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Për ty"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Zhbëj"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Afrohu për të luajtur në <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Për ta luajtur këtu, afrohu më shumë te <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Ndodhi një gabim. Provo përsëri."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Po ngarkohet"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Po transmeton median tënde"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Po transmeton <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Joaktive, kontrollo aplikacionin"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Nuk u gjet"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Kontrolli është i padisponueshëm"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Gabim, provo sërish"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Shto kontrollet"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Modifiko kontrollet"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Shto një aplikacion"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Shto daljet"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupi"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 pajisje e zgjedhur"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Altoparlantët dhe ekranet"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Pajisjet e sugjeruara"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Kërkon llogari premium"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Si funksionon transmetimi"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Transmetimi"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Personat në afërsi me ty me pajisje të përputhshme me Bluetooth mund të dëgjojnë median që ti po transmeton"</string>
@@ -894,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Nuk mund të transmetohet"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Nuk mund të ruhet. Provo përsëri."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Nuk mund të ruhet."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Përdor të paktën 4 karaktere"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Përdor më pak se 16 karaktere"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numri i ndërtimit"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Numri i ndërtimit u kopjua te kujtesa e fragmenteve"</string>
     <string name="basic_status" msgid="2315371112182658176">"Hap bisedën"</string>
@@ -1013,24 +1030,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Ofrohet të paktën një pajisje"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Prek dhe mbaj shtypur shkurtoren"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Anulo"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"U kthye tani"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Shpalos telefonin për një selfi më të mirë"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Të kthehet tek ekrani para për selfi më të mirë?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Përdor lenten e kamerës së pasme për një fotografi më të gjerë me rezolucion më të lartë."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Ky ekran do të fiket"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Pajisja e palosshme duke u hapur"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Pajisja e palosshme duke u rrotulluar"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Përqindja e mbetur e baterisë: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Lidhe stilolapsin me një karikues"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria e stilolapsit në nivel të ulët"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Nuk mund të telefonosh nga ky profil"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Politika jote e punës të lejon të bësh telefonata vetëm nga profili i punës"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Kalo te profili i punës"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Mbyll"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Cilësimet e ekranit të kyçjes"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sq/tiles_states_strings.xml b/packages/SystemUI/res/values-sq/tiles_states_strings.xml
index 7a09f24..5862e2e 100644
--- a/packages/SystemUI/res/values-sq/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sq/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Joaktiv"</item>
     <item msgid="5966994759929723339">"Aktiv"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 5970655..a9df97a 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"ĐĄĐœĐžĐŒŃ†Đž Đ”ĐșŃ€Đ°ĐœĐ° за ĐżĐŸŃĐ°ĐŸ сД чуĐČају у аплОĐșацојо <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>
     <string name="screenrecord_name" msgid="2596401223859996572">"ĐĄĐœĐžĐŒĐ°Ń‡ Đ”ĐșŃ€Đ°ĐœĐ°"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ĐžĐ±Ń€Đ°Ń’ŃƒŃ˜Đ”ĐŒĐŸ ĐČĐžĐŽĐ”ĐŸ ŃĐœĐžĐŒĐșа Đ”ĐșŃ€Đ°ĐœĐ°"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"ОбаĐČĐ”ŃˆŃ‚Đ”ŃšĐ” ĐŸ сДсОјО ŃĐœĐžĐŒĐ°ŃšĐ° Đ”ĐșŃ€Đ°ĐœĐ° јД аĐșтоĐČĐœĐŸ"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ОсĐČĐ”Ń‚Ń™Đ”ĐœĐŸŃŃ‚"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Đ˜ĐœĐČĐ”Ń€Đ·ĐžŃ˜Đ° Đ±ĐŸŃ˜Đ°"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ĐšĐŸŃ€Đ”Đșцоја Đ±ĐŸŃ˜Đ°"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"УпраĐČљаjтД ĐșĐŸŃ€ĐžŃĐœĐžŃ†ĐžĐŒĐ°"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Đ“ĐŸŃ‚ĐŸĐČĐŸ"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"ЗатĐČĐŸŃ€Đž"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"ĐŃƒŃ‚ĐŸĐŒĐ°Ń‚ŃĐșа"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"БДз Đ·ĐČуĐșа Đž ĐČОбрОрања"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"БДз Đ·ĐČуĐșа Đž ĐČОбрОрања Đž проĐșазујД сД у ĐœĐ°ŃŃ‚Đ°ĐČĐșу ĐŸĐŽĐ”Ń™Đșа за ĐșĐŸĐœĐČĐ”Ń€Đ·Đ°Ń†ĐžŃ˜Đ”"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"ĐœĐŸĐ¶Đ” Ўа Đ·ĐČĐŸĐœĐž ОлО ĐČОбрОра у заĐČĐžŃĐœĐŸŃŃ‚Đž ĐŸĐŽ ĐżĐŸĐŽĐ”ŃˆĐ°ĐČања ŃƒŃ€Đ”Ń’Đ°Ń˜Đ°"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"ĐœĐŸĐ¶Đ” Ўа Đ·ĐČĐŸĐœĐž ОлО ĐČОбрОра у заĐČĐžŃĐœĐŸŃŃ‚Đž ĐŸĐŽ ĐżĐŸĐŽĐ”ŃˆĐ°ĐČања ŃƒŃ€Đ”Ń’Đ°Ń˜Đ°. ĐšĐŸĐœĐČĐ”Ń€Đ·Đ°Ń†ĐžŃ˜Đ” Оз аплОĐșĐ°Ń†ĐžŃ˜Đ” <xliff:g id="APP_NAME">%1$s</xliff:g> ĐżĐŸĐŽŃ€Đ°Đ·ŃƒĐŒĐ”ĐČĐ°ĐœĐŸ сД проĐșазују у ĐŸĐ±Đ»Đ°Ń‡ĐžŃ›ĐžĐŒĐ°."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ĐĐ”Đșа ŃĐžŃŃ‚Đ”ĐŒ утĐČрЮо Ўа лО ĐŸĐČĐŸ ĐŸĐ±Đ°ĐČĐ”ŃˆŃ‚Đ”ŃšĐ” трДба Ўа Đ”ĐŒĐžŃ‚ŃƒŃ˜Đ” Đ·ĐČуĐș ОлО Ўа ĐČОбрОра"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Статус:&lt;/b&gt; ĐŁĐœĐ°ĐżŃ€Đ”Ń’Đ”ĐœĐŸ у ĐŸĐŸĐŽŃ€Đ°Đ·ŃƒĐŒĐ”ĐČĐ°ĐœĐŸ"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Статус:&lt;/b&gt; Đ”Đ”ĐłŃ€Đ°ĐŽĐžŃ€Đ°ĐœĐŸ у ĐĐ”Ń‡ŃƒŃ˜ĐœĐŸ"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"ŃĐœĐžĐŒĐ°ŃšĐ” Đ”ĐșŃ€Đ°ĐœĐ°"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"БДз ĐœĐ°ŃĐ»ĐŸĐČа"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"ХтањД пропраĐČĐœĐŸŃŃ‚Đž"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"ĐŸŃ€ĐŸĐ·ĐŸŃ€ за уĐČДћањД"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"ĐšĐŸĐœŃ‚Ń€ĐŸĐ»Đ” ĐżŃ€ĐŸĐ·ĐŸŃ€Đ° за уĐČДћањД"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ĐŁĐČĐ”Ń›Đ°Ń˜Ń‚Đ”"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"ОЎабДрОтД аплОĐșацоју за ĐŽĐŸĐŽĐ°ĐČањД ĐșĐŸĐœŃ‚Ń€ĐŸĐ»Đ°"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# ĐșĐŸĐœŃ‚Ń€ĐŸĐ»Đ° јД ĐŽĐŸĐŽĐ°Ń‚Đ°.}one{# ĐșĐŸĐœŃ‚Ń€ĐŸĐ»Đ° јД ĐŽĐŸĐŽĐ°Ń‚Đ°.}few{# ĐșĐŸĐœŃ‚Ń€ĐŸĐ»Đ” су ĐŽĐŸĐŽĐ°Ń‚Đ”.}other{# ĐșĐŸĐœŃ‚Ń€ĐŸĐ»Đ° јД ĐŽĐŸĐŽĐ°Ń‚ĐŸ.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"ĐŁĐșĐ»ĐŸŃšĐ”ĐœĐŸ"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"ЖДлОтД лО Ўа ĐŽĐŸĐŽĐ°Ń‚Đ” <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"КаЮа ĐŽĐŸĐŽĐ°Ń‚Đ” аплОĐșацоју <xliff:g id="APPNAME">%s</xliff:g>, ĐŸĐœĐ° ĐŒĐŸĐ¶Đ” Ўа ĐŽĐŸĐŽĐ°Ń˜Đ” ĐșĐŸĐœŃ‚Ń€ĐŸĐ»Đ” Đž ŃĐ°ĐŽŃ€Đ¶Đ°Ń˜ у ĐŸĐČĐŸ ĐŸĐșĐœĐŸ. ĐŁ ĐœĐ”ĐșĐžĐŒ аплОĐșĐ°Ń†ĐžŃ˜Đ°ĐŒĐ° ĐŒĐŸĐ¶Đ”Ń‚Đ” Ўа ОзабДрДтД ĐșĐŸŃ˜Đ” ћД сД ĐșĐŸĐœŃ‚Ń€ĐŸĐ»Đ” ĐŸĐČĐŽĐ” проĐșазОĐČато."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"ĐžĐ·ĐœĐ°Ń‡Đ”ĐœĐŸ јД ĐșĐ°ĐŸ ĐŸĐŒĐžŃ™Đ”ĐœĐŸ"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"ĐžĐ·ĐœĐ°Ń‡Đ”ĐœĐŸ јД ĐșĐ°ĐŸ ĐŸĐŒĐžŃ™Đ”ĐœĐŸ, <xliff:g id="NUMBER">%d</xliff:g>. ĐżĐŸĐ·ĐžŃ†ĐžŃ˜Đ°"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"ĐŁĐșĐ»ĐŸŃšĐ”ĐœĐŸ јД Оз ĐŸĐŒĐžŃ™Đ”ĐœĐžŃ…"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"ОтĐČĐŸŃ€ĐžŃ‚Đ” <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"ĐŸŃƒŃŃ‚ĐžŃ‚Đ” <xliff:g id="SONG_NAME">%1$s</xliff:g> ОзĐČĐŸŃ’Đ°Ń‡Đ° <xliff:g id="ARTIST_NAME">%2$s</xliff:g> Оз аплОĐșĐ°Ń†ĐžŃ˜Đ” <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"ĐŸŃƒŃŃ‚ĐžŃ‚Đ” <xliff:g id="SONG_NAME">%1$s</xliff:g> Оз аплОĐșĐ°Ń†ĐžŃ˜Đ” <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"За ĐČас"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"ĐžĐżĐŸĐ·ĐŸĐČĐž"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"ĐŸŃ€ĐžĐ±Đ»ĐžĐ¶ĐžŃ‚Đ” Ўа бОстД ĐżŃƒŃˆŃ‚Đ°Đ»Đž ĐŒŃƒĐ·ĐžĐșу ĐœĐ°: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Да бОстД ĐżŃƒŃˆŃ‚Đ°Đ»Đž ŃĐ°ĐŽŃ€Đ¶Đ°Ń˜ ĐŸĐČĐŽĐ”, прОблОжОтД ŃƒŃ€Đ”Ń’Đ°Ń˜Ńƒ <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Đ”ĐŸŃˆĐ»ĐŸ јД ĐŽĐŸ ĐłŃ€Đ”ŃˆĐșĐ”. ĐŸŃ€ĐŸĐ±Đ°Ń˜Ń‚Đ” ĐżĐŸĐœĐŸĐČĐŸ."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"УчотаĐČа сД"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"таблДт"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"ĐŸŃ€Đ”Đ±Đ°Ń†ĐžĐČањД ĐŒĐ”ĐŽĐžŃ˜Đ°"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"ĐŸŃ€Đ”Đ±Đ°Ń†ŃƒŃ˜Đ” сД <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ĐĐ”Đ°ĐșтоĐČĐœĐŸ. ВОЎОтД аплОĐșацоју"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"ĐĐžŃ˜Đ” ĐżŃ€ĐŸĐœĐ°Ń’Đ”ĐœĐŸ"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"ĐšĐŸĐœŃ‚Ń€ĐŸĐ»Đ° ĐœĐžŃ˜Đ” ĐŽĐŸŃŃ‚ŃƒĐżĐœĐ°"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Đ“Ń€Đ”ŃˆĐșа. ĐŸŃ€ĐŸĐ±Đ°Ń˜Ń‚Đ” ĐżĐŸĐœĐŸĐČĐŸ"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Đ”ĐŸĐŽĐ°Ń˜ ĐșĐŸĐœŃ‚Ń€ĐŸĐ»Đ”"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Đ˜Đ·ĐŒĐ”ĐœĐž ĐșĐŸĐœŃ‚Ń€ĐŸĐ»Đ”"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Đ”ĐŸĐŽĐ°Ń˜ аплОĐșацоју"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Đ”ĐŸĐŽĐ°Ń˜Ń‚Đ” ОзлазД"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Група"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Đ˜Đ·Đ°Đ±Ń€Đ°Đœ јД 1 ŃƒŃ€Đ”Ń’Đ°Ń˜"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ЗĐČŃƒŃ‡ĐœĐžŃ†Đž Đž Đ”ĐșŃ€Đ°ĐœĐž"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"ĐŸŃ€Đ”ĐŽĐ»ĐŸĐ¶Đ”ĐœĐž ŃƒŃ€Đ”Ń’Đ°Ń˜Đž"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"ЗахтДĐČа ĐżŃ€Đ”ĐŒĐžŃ˜ŃƒĐŒ ĐœĐ°Đ»ĐŸĐł"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"КаĐșĐŸ Ń„ŃƒĐœĐșŃ†ĐžĐŸĐœĐžŃˆĐ” Đ”ĐŒĐžŃ‚ĐŸĐČањД"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Đ•ĐŒĐžŃ‚ĐŸĐČањД"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ЉуЮо у Đ±Đ»ĐžĐ·ĐžĐœĐž са ĐșĐŸĐŒĐżĐ°Ń‚ĐžĐ±ĐžĐ»ĐœĐžĐŒ Bluetooth ŃƒŃ€Đ”Ń’Đ°Ń˜ĐžĐŒĐ° ĐŒĐŸĐłŃƒ Ўа слушају ĐŒĐ”ĐŽĐžŃ˜ŃĐșĐž ŃĐ°ĐŽŃ€Đ¶Đ°Ń˜ ĐșĐŸŃ˜Đž Đ”ĐŒĐžŃ‚ŃƒŃ˜Đ”Ń‚Đ”"</string>
@@ -894,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Đ•ĐŒĐžŃ‚ĐŸĐČањД ĐœĐžŃ˜Đ” ŃƒŃĐżĐ”Đ»ĐŸ"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"ЧуĐČањД ĐœĐžŃ˜Đ” ŃƒŃĐżĐ”Đ»ĐŸ. ĐŸŃ€ĐŸĐ±Đ°Ń˜Ń‚Đ” ĐżĐŸĐœĐŸĐČĐŸ."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"ЧуĐČањД ĐœĐžŃ˜Đ” ŃƒŃĐżĐ”Đ»ĐŸ."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"ĐšĐŸŃ€ĐžŃŃ‚ĐžŃ‚Đ” бар 4 Đ·ĐœĐ°Đșа"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"ĐšĐŸŃ€ĐžŃŃ‚ĐžŃ‚Đ” ĐŒĐ°ŃšĐ” ĐŸĐŽ 16 Đ·ĐœĐ°ĐșĐŸĐČа"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Đ‘Ń€ĐŸŃ˜ ĐČĐ”Ń€Đ·ĐžŃ˜Đ”"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Đ‘Ń€ĐŸŃ˜ ĐČĐ”Ń€Đ·ĐžŃ˜Đ” јД ĐșĐŸĐżĐžŃ€Đ°Đœ у проĐČŃ€Đ”ĐŒĐ”ĐœŃƒ ĐŒĐ”ĐŒĐŸŃ€ĐžŃ˜Ńƒ."</string>
     <string name="basic_status" msgid="2315371112182658176">"ОтĐČĐŸŃ€ĐžŃ‚Đ” ĐșĐŸĐœĐČĐ”Ń€Đ·Đ°Ń†ĐžŃ˜Ńƒ"</string>
@@ -1013,24 +1030,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Ўа јД ĐŽĐŸŃŃ‚ŃƒĐżĐ°Đœ Đ±Đ°Ń€Đ”ĐŒ Ń˜Đ”ĐŽĐ°Đœ ŃƒŃ€Đ”Ń’Đ°Ń˜"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Đ”ĐŸĐŽĐžŃ€ĐœĐžŃ‚Đ” Đž заЎржОтД ĐżŃ€Đ”Ń‡ĐžŃ†Ńƒ"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"ОтĐșажО"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"ĐžĐ±Ń€ĐœĐžŃ‚Đ”"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"ОтĐČĐŸŃ€ĐžŃ‚Đ” Ń‚Đ”Đ»Đ”Ń„ĐŸĐœ за Đ±ĐŸŃ™Đž сДлфО"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"ЖДлОтД Ўа ĐŸĐ±Ń€ĐœĐ”Ń‚Đ” ĐœĐ° прДЎњО Đ”ĐșŃ€Đ°Đœ за Đ±ĐŸŃ™Đž сДлфО?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"ĐšĐŸŃ€ĐžŃŃ‚ĐžŃ‚Đ” Đ·Đ°ĐŽŃšŃƒ ĐșĐ°ĐŒĐ”Ń€Ńƒ Ўа бОстД ŃĐœĐžĐŒĐžĐ»Đž шору слОĐșу са ĐČĐžŃˆĐŸĐŒ Ń€Đ”Đ·ĐŸĐ»ŃƒŃ†ĐžŃ˜ĐŸĐŒ."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ОĐČај Đ”ĐșŃ€Đ°Đœ ћД сД ОсĐșључото"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"ĐŁŃ€Đ”Ń’Đ°Ń˜ ĐœĐ° прДĐșĐ»ĐŸĐż сД ĐŸŃ‚ĐČара"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"ĐŁŃ€Đ”Ń’Đ°Ń˜ ĐœĐ° прДĐșĐ»ĐŸĐż сД ĐŸĐ±Ń€Ń›Đ”"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"ĐŸŃ€Đ”ĐŸŃŃ‚Đ°Đ»ĐŸ јД Ń˜ĐŸŃˆ<xliff:g id="PERCENTAGE">%s</xliff:g> Đ±Đ°Ń‚Đ”Ń€ĐžŃ˜Đ”"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ĐŸĐŸĐČДжОтД посаљĐșу са ĐżŃƒŃšĐ°Ń‡Đ”ĐŒ"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"ĐĐžĐ·Đ°Đș ĐœĐžĐČĐŸ Đ±Đ°Ń‚Đ”Ń€ĐžŃ˜Đ” посаљĐșĐ”"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Đ’ĐžĐŽĐ”ĐŸ ĐșĐ°ĐŒĐ”Ń€Đ°"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"ĐĐ” ĐŒĐŸĐ¶Đ”Ń‚Đ” Ўа ŃƒĐżŃƒŃ›ŃƒŃ˜Đ”Ń‚Đ” ĐżĐŸĐ·ĐžĐČĐ” са ĐŸĐČĐŸĐł ĐżŃ€ĐŸŃ„ĐžĐ»Đ°"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"ĐĄĐŒĐ”Ń€ĐœĐžŃ†Đ” за ĐżĐŸŃĐ°ĐŸ ĐČĐ°ĐŒ ĐŸĐŒĐŸĐłŃƒŃ›Đ°ĐČају Ўа Ń‚Đ”Đ»Đ”Ń„ĐŸĐœĐžŃ€Đ°Ń‚Đ” ŃĐ°ĐŒĐŸ са ĐżĐŸŃĐ»ĐŸĐČĐœĐŸĐł ĐżŃ€ĐŸŃ„ĐžĐ»Đ°"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"ĐŸŃ€Đ”Ń’Đž ĐœĐ° ĐżĐŸŃĐ»ĐŸĐČĐœĐž ĐżŃ€ĐŸŃ„ĐžĐ»"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"ЗатĐČĐŸŃ€Đž"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"ĐŸĐŸĐŽĐ”ŃˆĐ°ĐČања заĐșŃ™ŃƒŃ‡Đ°ĐœĐŸĐł Đ”ĐșŃ€Đ°ĐœĐ°"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sr/tiles_states_strings.xml b/packages/SystemUI/res/values-sr/tiles_states_strings.xml
index dace491..e817eea 100644
--- a/packages/SystemUI/res/values-sr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sr/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"ИсĐșŃ™ŃƒŃ‡Đ”ĐœĐŸ"</item>
     <item msgid="5966994759929723339">"ĐŁĐșŃ™ŃƒŃ‡Đ”ĐœĐŸ"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 097af72..d839a11 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Nedre gräns: <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Vänster gräns: <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Höger gräns: <xliff:g id="PERCENT">%1$d</xliff:g> procent"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Jobbskärmbilder sparas i <xliff:g id="APP">%1$s</xliff:g>-appen"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Sparad i <xliff:g id="APP">%1$s</xliff:g> i jobbprofilen"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Filer"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> identifierade skärmbilden."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> och andra öppna appar identifierade skärmbilden."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Skärminspelare"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Behandlar skärminspelning"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Avisering om att skärminspelning pågår"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Ljusstyrka"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Färginvertering"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Färgkorrigering"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Hantera användare"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Klart"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Stäng"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatiskt"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Inga ljud eller vibrationer"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Inga ljud eller vibrationer och visas längre ned bland konversationerna"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Kan ringa eller vibrera beroende på inställningarna på enheten"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Kan ringa eller vibrera beroende på inställningarna på enheten. Konversationer från <xliff:g id="APP_NAME">%1$s</xliff:g> visas i bubblor som standard."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Låt systemet avgöra om den här aviseringen ska låta eller vibrera"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Status:&lt;/b&gt; Ändrad till Standard"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Status:&lt;/b&gt; Ändrad till Tyst"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"skärminspelning"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Ingen titel"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Viloläge"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Förstoringsfönster"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Inställningar för förstoringsfönster"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zooma in"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Välj en app om du vill lägga till snabbkontroller"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# kontroll har lagts till.}other{# kontroller har lagts till.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Har tagits bort"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Vill du lägga till <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"När du lägger till <xliff:g id="APPNAME">%s</xliff:g> kan den lägga till kontroller och innehåll i den här panelen. I vissa appar kan du styra vilka kontroller som visas här."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Har lagts till som favorit"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Har lagts till som favorit, plats <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Har tagits bort från favoriter"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Öppna <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Spela upp <xliff:g id="SONG_NAME">%1$s</xliff:g> med <xliff:g id="ARTIST_NAME">%2$s</xliff:g> från <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Spela upp <xliff:g id="SONG_NAME">%1$s</xliff:g> från <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"För dig"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Ångra"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Flytta närmare för att spela upp på <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Kom närmare <xliff:g id="DEVICENAME">%1$s</xliff:g> om du vill spela upp här"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Något gick fel. Försök igen."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Läser in"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"surfplatta"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Castar din media"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Castar <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv, kolla appen"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Hittades inte"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Styrning är inte tillgänglig"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Fel, försök igen"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Lägg till snabbkontroller"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Redigera snabbkontroller"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Lägg till app"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Lägg till utgångar"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupp"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 enhet har valts"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Högtalare och skärmar"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Förslag på enheter"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Premiumkonto krävs"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Så fungerar utsändning"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Utsändning"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Personer i närheten med kompatibla Bluetooth-enheter kan lyssna på medieinnehåll som du sänder ut"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Det gick inte att sända ut"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Det gick inte att spara. Försök igen."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Det gick inte att spara."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Versionsnummer"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Versionsnumret har kopierats till urklipp."</string>
     <string name="basic_status" msgid="2315371112182658176">"Öppen konversation"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• minst en enhet är tillgänglig"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Tryck länge på genvägen"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Avbryt"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Vänd nu"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Vik upp telefonen för att ta en bättre selfie"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Vill du ta en bättre selfie med främre skärmen?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Använd den bakre kameran för att ta ett mer vidsträckt foto med högre upplösning."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Den här skärmen inaktiveras"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"En vikbar enhet viks upp"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"En vikbar enhet vänds"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> av batteriet återstår"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Anslut e-pennan till en laddare"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"E-pennans batterinivå är låg"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Det går inte att ringa från den här profilen"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Jobbprincipen tillåter endast att du ringer telefonsamtal från jobbprofilen"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Byt till jobbprofilen"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Stäng"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Inställningar för låsskärm"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sv/tiles_states_strings.xml b/packages/SystemUI/res/values-sv/tiles_states_strings.xml
index 9e69b00..45169aa 100644
--- a/packages/SystemUI/res/values-sv/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sv/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Av"</item>
     <item msgid="5966994759929723339">"På"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 94812c5..c303c09 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Mpaka wa sehemu ya chini wa asilimia <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Mpaka wa sehemu ya kushoto wa asilimia <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Mpaka wa sehemu ya kulia wa asilimia <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Picha ya skrini ya kazi huhifadhiwa kwenye programu ya <xliff:g id="APP">%1$s</xliff:g>"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Imehifadhiwa kwenye <xliff:g id="APP">%1$s</xliff:g> katika wasifu wa kazini"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Faili"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> imetambua picha hii ya skrini."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> na zingine zinazotumika zimetambua picha hii ya skrini."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Kinasa Skrini"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Inachakata rekodi ya skrini"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Arifa inayoendelea ya kipindi cha kurekodi skrini"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Ung\'avu"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Ugeuzaji rangi"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Usahihishaji wa rangirangi"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Dhibiti watumiaji"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Nimemaliza"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Funga"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Otomatiki"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Hakuna sauti wala mtetemo"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Hakuna sauti wala mtetemo na huonekana upande wa chini katika sehemu ya mazungumzo"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Huenda ikalia au kutetema kulingana na mipangilio ya kifaa"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Huenda ikalia au kutetema kulingana na mipangilio ya kifaa. Mazungumzo kutoka kiputo cha <xliff:g id="APP_NAME">%1$s</xliff:g> kwa chaguomsingi."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Ruhusu mfumo ubainishe iwapo arifa hii inapaswa kutoa sauti au mtetemo"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Hali:&lt;/b&gt; Imepandishwa Hadhi Kuwa Chaguomsingi"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Status:&lt;/b&gt; Imeshushwa Hadhi Kuwa Kimya"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"kurekodi skrini"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Wimbo hauna jina"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Hali tuli"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Dirisha la Ukuzaji"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Vidhibiti vya Dirisha la Ukuzaji"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Vuta karibu"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Chagua programu ili uweke vidhibiti"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Umeweka kidhibiti #.}other{Umeweka vidhibiti #.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Kimeondolewa"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Ungependa kuweka <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Unapoweka <xliff:g id="APPNAME">%s</xliff:g>, inaweza kuweka vidhibiti na maudhui kwenye kidirisha hiki. Katika baadhi ya programu, unaweza kuchagua ni vidhibiti vipi vionekane hapa."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Kimewekwa kwenye vipendwa"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Kimewekwa kwenye vipendwa, nafasi ya <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Kimeondolewa kwenye vipendwa"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Fungua <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Cheza <xliff:g id="SONG_NAME">%1$s</xliff:g> ulioimbwa na <xliff:g id="ARTIST_NAME">%2$s</xliff:g> katika <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Cheza <xliff:g id="SONG_NAME">%1$s</xliff:g> katika <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Kwa Ajili Yako"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Tendua"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Sogeza karibu ili ucheze kwenye <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Ili ucheze maudhui kwenye kifaa hiki, sogeza karibu na <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Hitilafu fulani imetokea. Jaribu tena."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Inapakia"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"kompyuta kibao"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Inatuma maudhui yako"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Inatuma <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Haitumiki, angalia programu"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Hakipatikani"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Kidhibiti hakipatikani"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Hitilafu, jaribu tena"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Weka vidhibiti"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Badilisha vidhibiti"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Weka programu"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Weka vifaa vya kutoa sauti"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Kikundi"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Umechagua kifaa 1"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Spika na Skrini"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Vifaa Vilivyopendekezwa"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Inahitaji akaunti ya kulipia"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Jinsi utangazaji unavyofanya kazi"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Tangaza"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Watu walio karibu nawe wenye vifaa oanifu vya Bluetooth wanaweza kusikiliza maudhui unayoyatangaza"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Imeshindwa kutuma arifa"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Imeshindwa kuhifadhi. Jaribu tena."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Imeshindwa kuhifadhi."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Nambari ya muundo"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Nambari ya muundo imewekwa kwenye ubao wa kunakili."</string>
     <string name="basic_status" msgid="2315371112182658176">"Fungua mazungumzo"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Angalau kifaa kimoja kinapatikana"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Gusa na ushikilie njia ya mkato"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Ghairi"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Geuza kifaa sasa"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Kunjua simu ili upige selfi iliyo bora"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Ungependa kugeuza skrini ya mbele ili upige selfi?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Tumia kamera ya nyuma ili upige picha pana iliyo na ubora wa juu."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Skrini hii itajizima"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Kifaa kinachokunjwa kikikunjuliwa"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Kifaa kinachokunjwa kikigeuzwa"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Chaji ya betri imesalia <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Unganisha stylus yako kwenye chaja"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Chaji ya betri ya Stylus imepungua"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Kamera ya kuchukulia video"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Huwezi kupiga simu kutoka kwenye wasifu huu"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Sera ya mahali pako pa kazi inakuruhusu upige simu kutoka kwenye wasifu wa kazini pekee"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Tumia wasifu wa kazini"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Funga"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Mipangilio ya skrini iliyofungwa"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sw/tiles_states_strings.xml b/packages/SystemUI/res/values-sw/tiles_states_strings.xml
index 2f765ef..aad0099 100644
--- a/packages/SystemUI/res/values-sw/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sw/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Imezimwa"</item>
     <item msgid="5966994759929723339">"Imewashwa"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index 5d78e4e..2a27b47 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -45,8 +45,6 @@
     <item name="controls_task_view_width_percentage" translatable="false" format="float" type="dimen">0.45</item>
     <dimen name="controls_task_view_right_margin">8dp</dimen>
 
-    <dimen name="status_bar_header_height_keyguard">42dp</dimen>
-
     <dimen name="lockscreen_shade_max_over_scroll_amount">32dp</dimen>
 
     <dimen name="status_view_margin_horizontal">8dp</dimen>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index db7fb48..45b137a 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -16,8 +16,9 @@
 */
 -->
 <resources>
-    <!-- Height of the status bar header bar when on Keyguard -->
-    <dimen name="status_bar_header_height_keyguard">60dp</dimen>
+    <!-- Height of the status bar header bar when on Keyguard.
+         On large screens should be the same as the regular status bar. -->
+    <dimen name="status_bar_header_height_keyguard">@dimen/status_bar_height</dimen>
 
     <!-- Size of user icon + frame in the qs user picker (incl. frame) -->
     <dimen name="qs_framed_avatar_size">60dp</dimen>
@@ -51,9 +52,6 @@
     <!-- Text size for user name in user switcher -->
     <dimen name="kg_user_switcher_text_size">18sp</dimen>
 
-    <dimen name="controls_header_bottom_margin">12dp</dimen>
-    <dimen name="controls_top_margin">24dp</dimen>
-
     <dimen name="global_actions_grid_item_layout_height">80dp</dimen>
 
     <dimen name="qs_brightness_margin_bottom">16dp</dimen>
diff --git a/packages/SystemUI/res/values-sw720dp-land/dimens.xml b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
index 122806a..9ed9360 100644
--- a/packages/SystemUI/res/values-sw720dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
@@ -17,14 +17,11 @@
 */
 -->
 <resources>
-    <dimen name="controls_padding_horizontal">205dp</dimen>
     <dimen name="split_shade_notifications_scrim_margin_bottom">24dp</dimen>
     <dimen name="notification_panel_margin_bottom">64dp</dimen>
 
     <dimen name="keyguard_split_shade_top_margin">72dp</dimen>
 
-    <dimen name="status_bar_header_height_keyguard">56dp</dimen>
-
     <dimen name="status_view_margin_horizontal">24dp</dimen>
 
     <dimen name="qs_media_session_height_expanded">184dp</dimen>
diff --git a/packages/SystemUI/res/values-sw720dp/dimens.xml b/packages/SystemUI/res/values-sw720dp/dimens.xml
index 927059a..8f59df6 100644
--- a/packages/SystemUI/res/values-sw720dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp/dimens.xml
@@ -19,7 +19,7 @@
     <!-- gap on either side of status bar notification icons -->
     <dimen name="status_bar_icon_padding">1dp</dimen>
 
-    <dimen name="controls_padding_horizontal">75dp</dimen>
+    <dimen name="controls_padding_horizontal">40dp</dimen>
 
     <dimen name="large_screen_shade_header_height">56dp</dimen>
 
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 1ca016a..e58acfa 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"àźȘàźŁàźżàź•àŻ àź•àźŁàź•àŻàź•àŻ àźžàŻàź•àźżàź°àŻ€àź©àŻàź·àźŸàźŸàŻàźŸàŻàź•àźłàŻ <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">"Files"</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>
     <string name="screenrecord_name" msgid="2596401223859996572">"àźžàŻàź•àźżàź°àŻ€àź©àŻ àź°àŻ†àź•àŻàź•àźŸàź°àŻàźŸàź°àŻ"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"àźžàŻàź•àŻàź°àŻ€àź©àŻ àź°àŻ†àź•àŻàź•àźŸàź°àŻàźŸàźżàź™àŻ àźšàŻ†àźŻàźČàźŸàź•àŻàź•àźȘàŻàźȘàźŸàŻàź•àźżàź±àź€àŻ"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"àź€àźżàź°àŻˆ àź°àŻ†àź•àŻàź•àźŸàź°àŻàźŸàźżàź™àŻ àź…àźźàź°àŻàź”àźżàź±àŻàź•àźŸàź© àź€àŻŠàźŸàź°àŻ àź…àź±àźżàź”àźżàźȘàŻàźȘàŻ"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"àź’àźłàźżàź°àŻàź”àŻ"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"àź•àźČàź°àŻ àź‡àź©àŻàź”àŻ†àź°àŻàź·àź©àŻ"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"àź•àźČàź°àŻ àź•àź°àŻ†àź•àŻ‌àź·àź©àŻ"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"àźȘàźŻàź©àź°àŻàź•àźłàŻˆ àźšàźżàź°àŻàź”àź•àźżàźŻàŻàź™àŻàź•àźłàŻ"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"àźźàŻàźŸàźżàźšàŻàź€àź€àŻ"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"àźźàŻ‚àźŸàŻàź•"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"àź€àźŸàź©àźżàźŻàź™àŻàź•àŻ"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"àź’àźČàźż / àź…àź€àźżàź°àŻàź”àŻ àź‡àźČàŻàźČàŻˆ"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"àź’àźČàźż / àź…àź€àźżàź°àŻàź”àŻ àź‡àźČàŻàźČàźŸàźźàźČàŻ àź‰àź°àŻˆàźŻàźŸàźŸàźČàŻ àźȘàźżàź°àźżàź”àźżàź©àŻ àź•àŻ€àźŽàŻàźȘàŻ àźȘàź•àŻàź€àźżàźŻàźżàźČàŻ àź€àŻ‹àź©àŻàź±àŻàźźàŻ"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"àźšàźŸàź€àź© àź…àźźàŻˆàźȘàŻàźȘàŻàź•àźłàŻˆàźȘàŻ àźȘàŻŠàź±àŻàź€àŻàź€àŻ àź’àźČàźżàź•àŻàź•àź•àŻàź•àŻ‚àźŸàŻàźźàŻ àź…àźČàŻàźČàź€àŻ àź…àź€àźżàź°àŻàź”àźŸàŻˆàźŻàź•àŻàź•àŻ‚àźŸàŻàźźàŻ"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"àźšàźŸàź€àź© àź…àźźàŻˆàźȘàŻàźȘàŻàź•àźłàŻˆàźȘàŻ àźȘàŻŠàź±àŻàź€àŻàź€àŻ àź’àźČàźżàź•àŻàź•àź•àŻàź•àŻ‚àźŸàŻàźźàŻ àź…àźČàŻàźČàź€àŻ àź…àź€àźżàź°àŻàź”àźŸàŻˆàźŻàź•àŻàź•àŻ‚àźŸàŻàźźàŻ. àź‡àźŻàźČàŻàźȘàźŸàź•, <xliff:g id="APP_NAME">%1$s</xliff:g> àź†àźȘàŻàźžàźżàźČàŻ àźȘàŻ†àź±àźȘàŻàźȘàźŸàŻàźźàŻ àź‰àź°àŻˆàźŻàźŸàźŸàźČàŻ àź…àź±àźżàź”àźżàźȘàŻàźȘàŻàź•àźłàŻ àź•àŻàźźàźżàźŽàŻàź•àźłàźŸàź•àź€àŻ àź€àŻ‹àź©àŻàź±àŻàźźàŻ."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"àź‡àźšàŻàź€ àź…àź±àźżàź”àźżàźȘàŻàźȘàŻ àź’àźČàźż àźŽàźŽàŻàźȘàŻàźȘ àź”àŻ‡àźŁàŻàźŸàŻàźźàźŸ àź…àź€àźżàź° àź”àŻ‡àźŁàŻàźŸàŻàźźàźŸ àźŽàź©àŻàźȘàź€àŻˆ àźšàźżàźžàŻàźŸàźźàŻ àź€àŻ€àź°àŻàźźàźŸàź©àźżàź•àŻàź•àŻàźźàŻ"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;àźšàźżàźČàŻˆ:&lt;/b&gt; àź‡àźŻàźČàŻàźȘàŻàźšàźżàźČàŻˆàź•àŻàź•àŻ àź‰àźŻàź°àŻàź€àŻàź€àźż àź…àźźàŻˆàź•àŻàź•àźȘàŻàźȘàźŸàŻàźŸàź€àŻ"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;àźšàźżàźČàŻˆ:&lt;/b&gt; àźšàŻˆàźČàź©àŻàźŸàŻ àźšàźżàźČàŻˆàź•àŻàź•àŻàź•àŻ àź•àŻàź±àŻˆàź€àŻàź€àŻ àź…àźźàŻˆàź•àŻàź•àźȘàŻàźȘàźŸàŻàźŸàź€àŻ"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"àźžàŻàź•àźżàź°àŻ€àź©àŻ àź°àŻ†àź•àŻàź•àźŸàź°àŻàźŸàźżàź™àŻ"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"àź€àźČàŻˆàźȘàŻàźȘàŻ àź‡àźČàŻàźČàŻˆ"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"àź‡àźŻàź•àŻàź• àźšàŻ‡àź°àźźàŻ"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"àźȘàŻ†àź°àźżàź€àźŸàź•àŻàź•àźČàŻ àźšàźŸàźłàź°àźźàŻ"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"àźȘàŻ†àź°àźżàź€àźŸàź•àŻàź•àźČàŻ àźšàźŸàźłàź°àź•àŻ àź•àźŸàŻàźŸàŻàźȘàŻàźȘàźŸàźŸàŻàź•àźłàŻ"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"àźȘàŻ†àź°àźżàź€àźŸàź•àŻàź•àŻ"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"àź•àźŸàŻàźŸàŻàźȘàŻàźȘàźŸàźŸàŻàź•àźłàŻˆàźšàŻ àźšàŻ‡àź°àŻàź•àŻàź• àź”àŻ‡àźŁàŻàźŸàźżàźŻ àź†àźȘàŻàźžàŻˆàź€àŻ àź€àŻ‡àź°àŻàźšàŻàź€àŻ†àźŸàŻàź™àŻàź•àźłàŻ"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# àź•àźŸàŻàźŸàŻàźȘàŻàźȘàźŸàźŸàŻ àźšàŻ‡àź°àŻàź•àŻàź•àźȘàŻàźȘàźŸàŻàźŸàź€àŻ.}other{# àź•àźŸàŻàźŸàŻàźȘàŻàźȘàźŸàźŸàŻàź•àźłàŻ àźšàŻ‡àź°àŻàź•àŻàź•àźȘàŻàźȘàźŸàŻàźŸàź©.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"àź…àź•àź±àŻàź±àźȘàŻàźȘàźŸàŻàźŸàź€àŻ"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g> àź†àźȘàŻàźžàŻˆàźšàŻ àźšàŻ‡àź°àŻàź•àŻàź•àź”àźŸ?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"àź‡àźšàŻàź€àźȘàŻ àźȘàŻ‡àź©àźČàźżàźČàŻ <xliff:g id="APPNAME">%s</xliff:g> àź†àźȘàŻàźžàŻˆàźšàŻ àźšàŻ‡àź°àŻàź•àŻàź•àŻàźźàŻàźȘàŻ‹àź€àŻ àź•àźŸàŻàźŸàŻàźȘàŻàźȘàźŸàźŸàŻàź•àźłàŻˆàźŻàŻàźźàŻ àź‰àźłàŻàźłàźŸàź•àŻàź•àź€àŻàź€àŻˆàźŻàŻàźźàŻ àź…àź€àŻ àźšàŻ‡àź°àŻàź•àŻàź•àźČàźŸàźźàŻ. àź‡àź°àŻàźȘàŻàźȘàźżàź©àŻàźźàŻ, àźšàźżàźČ àź†àźȘàŻàźžàźżàźČàŻ àźŽàźšàŻàź€àŻ†àźšàŻàź€àź•àŻ àź•àźŸàŻàźŸàŻàźȘàŻàźȘàźŸàźŸàŻàź•àźłàŻ àź‡àź™àŻàź•àŻ‡ àź•àźŸàźŸàŻàźŸàźȘàŻàźȘàźŸ àź”àŻ‡àźŁàŻàźŸàŻàźźàŻ àźŽàź©àŻàźȘàź€àŻˆ àźšàŻ€àź™àŻàź•àźłàŻ‡ àź€àŻ‡àź°àŻàź”àŻàźšàŻ†àźŻàŻàźŻàźČàźŸàźźàŻ."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"àźȘàźżàźŸàźżàź€àŻàź€àź”àź±àŻàź±àźżàźČàŻ àźšàŻ‡àź°àŻàź•àŻàź•àźȘàŻàźȘàźŸàŻàźŸàź€àŻ"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"àźȘàźżàźŸàźżàź€àŻàź€àź”àź±àŻàź±àźżàźČàŻ àźšàŻ‡àź°àŻàź•àŻàź•àźȘàŻàźȘàźŸàŻàźŸàź€àŻ, àźšàźżàźČàŻˆ <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"àźȘàźżàźŸàźżàź€àŻàź€àź”àź±àŻàź±àźżàźČàźżàź°àŻàźšàŻàź€àŻ àźšàŻ€àź•àŻàź•àźȘàŻàźȘàźŸàŻàźŸàź€àŻ"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> àź†àźȘàŻàźžàŻˆàź€àŻ àź€àźżàź±àź™àŻàź•àźłàŻ"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> àź‡àź©àŻ <xliff:g id="SONG_NAME">%1$s</xliff:g> àźȘàźŸàźŸàźČàŻˆ <xliff:g id="APP_LABEL">%3$s</xliff:g> àź†àźȘàŻàźžàźżàźČàŻ àźȘàźżàźłàŻ‡àźšàŻ†àźŻàŻ"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> àźȘàźŸàźŸàźČàŻˆ <xliff:g id="APP_LABEL">%2$s</xliff:g> àź†àźȘàŻàźžàźżàźČàŻ àźȘàźżàźłàŻ‡àźšàŻ†àźŻàŻ"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"àź‰àź™àŻàź•àźłàŻàź•àŻàź•àźŸàź©àź”àŻˆ"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"àźšàŻ†àźŻàźČàŻàź€àź”àźżàź°àŻ"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> àźšàźŸàź€àź©àź€àŻàź€àźżàźČàŻ àź‡àźŻàź•àŻàź• àź‰àź™àŻàź•àźłàŻ àźšàźŸàź€àź©àź€àŻàź€àŻˆ àź…àź°àŻàź•àźżàźČàŻ àźŽàźŸàŻàź€àŻàź€àŻàźšàŻ àźšàŻ†àźČàŻàźČàŻàź™àŻàź•àźłàŻ"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"àź‡àź™àŻàź•àŻ àźȘàźżàźłàŻ‡ àźšàŻ†àźŻàŻàźŻ àź‰àź™àŻàź•àźłàŻ àźšàźŸàź€àź©àź€àŻàź€àŻˆ <xliff:g id="DEVICENAME">%1$s</xliff:g>àź…àź°àŻàź•àźżàźČàŻ àźšàź•àź°àŻàź€àŻàź€àŻàź™àŻàź•àźłàŻ"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"àźàź€àŻ‹ àź€àź”àź±àźŸàź•àźżàź”àźżàźŸàŻàźŸàź€àŻ. àźźàŻ€àźŁàŻàźŸàŻàźźàŻ àźźàŻàźŻàźČàź”àŻàźźàŻ."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"àźàź±àŻàź±àŻàź•àźżàź±àź€àŻ"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"àźŸàŻ‡àźȘàŻàźČàŻ†àźŸàŻ"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"àź‰àź™àŻàź•àźłàŻ àźźàŻ€àźŸàźżàźŻàźŸ àź…àźČàŻˆàźȘàź°àźȘàŻàźȘàźȘàŻàźȘàźŸàŻàź•àźżàź±àź€àŻ"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"<xliff:g id="APP_LABEL">%1$s</xliff:g> àź†àźȘàŻàźžàŻˆ àź…àźČàŻˆàźȘàź°àźȘàŻàźȘàŻàź•àźżàź±àź€àŻ"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"àźšàŻ†àźŻàźČàźżàźČàŻ àź‡àźČàŻàźČàŻˆ , àźšàź°àźżàźȘàźŸàź°àŻàź•àŻàź•àź”àŻàźźàŻ"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"àź‡àźČàŻàźČàŻˆ"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"àź•àźŸàŻàźŸàŻàźȘàŻàźȘàźŸàźŸàŻ àź‡àźČàŻàźČàŻˆ"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"àźȘàźżàźŽàŻˆ, àźźàŻ€àźŁàŻàźŸàŻàźźàŻ àźźàŻàźŻàźČàź”àŻàźźàŻ"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"àź•àźŸàŻàźŸàŻàźȘàŻàźȘàźŸàźŸàŻàź•àźłàŻˆàźšàŻ àźšàŻ‡àź°àŻàź€àŻàź€àźČàŻ"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"àź•àźŸàŻàźŸàŻàźȘàŻàźȘàźŸàźŸàŻàź•àźłàŻˆ àźźàźŸàź±àŻàź±àŻàź€àźČàŻ"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"àź†àźȘàŻàźžàŻˆàźšàŻ àźšàŻ‡àź°àŻ"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"àź…àź”àŻàźŸàŻàźȘàŻàźŸàŻàź•àźłàŻˆàźšàŻ àźšàŻ‡àź°àŻàź€àŻàź€àźČàŻ"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"àź•àŻàźŽàŻ"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 àźšàźŸàź€àź©àźźàŻ àź€àŻ‡àź°àŻàźšàŻàź€àŻ†àźŸàŻàź•àŻàź•àźȘàŻàźȘàźŸàŻàźŸàŻàźłàŻàźłàź€àŻ"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"àźžàŻàźȘàŻ€àź•àŻàź•àź°àŻàź•àźłàŻ &amp; àźŸàźżàźžàŻàźȘàŻàźłàŻ‡àź•àŻàź•àźłàŻ"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"àźȘàź°àźżàźšàŻàź€àŻàź°àŻˆàź•àŻàź•àźȘàŻàźȘàźŸàŻàźźàŻ àźšàźŸàź€àź©àź™àŻàź•àźłàŻ"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"àźȘàźżàź°àŻ€àźźàźżàźŻàźźàŻ àź•àźŁàź•àŻàź•àŻ àź€àŻ‡àź”àŻˆ"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"àźȘàźżàź°àźŸàźŸàŻàź•àźŸàźžàŻàźŸàŻ àźŽàź”àŻàź”àźŸàź±àŻ àźšàŻ†àźŻàźČàŻàźȘàźŸàŻàź•àźżàź±àź€àŻ?"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"àźȘàźżàź°àźŸàźŸàŻàź•àźŸàźžàŻàźŸàŻ"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"àźšàŻ€àź™àŻàź•àźłàŻ àźȘàźżàź°àźŸàźŸàŻàź•àźŸàźžàŻàźŸàŻ àźšàŻ†àźŻàŻàźŻàŻàźźàŻ àźźàŻ€àźŸàźżàźŻàźŸàź”àŻˆ àź…àź°àŻàź•àźżàźČàŻàźłàŻàźłàź”àź°àŻàź•àźłàŻ àź‡àźŁàź•àŻàź•àźźàźŸàź© àźȘàŻàźłàŻ‚àźŸàŻ‚àź€àŻ àźšàźŸàź€àź©àź™àŻàź•àźłàŻ àźźàŻ‚àźČàźźàŻ àź•àŻ‡àźŸàŻàź•àźČàźŸàźźàŻ"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"àź’àźłàźżàźȘàź°àźȘàŻàźȘ àźźàŻàźŸàźżàźŻàź”àźżàźČàŻàźČàŻˆ"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"àźšàŻ‡àźźàźżàź•àŻàź• àźźàŻàźŸàźżàźŻàź”àźżàźČàŻàźČàŻˆ. àźźàŻ€àźŁàŻàźŸàŻàźźàŻ àźźàŻàźŻàźČàź”àŻàźźàŻ."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"àźšàŻ‡àźźàźżàź•àŻàź• àźźàŻàźŸàźżàźŻàź”àźżàźČàŻàźČàŻˆ."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"àźȘàź€àźżàźȘàŻàźȘàŻ àźŽàźŁàŻ"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"àźȘàź€àźżàźȘàŻàźȘàŻ àźŽàźŁàŻ àź•àźżàźłàźżàźȘàŻàźȘàŻ‹àź°àŻàźŸàŻàź•àŻàź•àŻ àźšàź•àźČàŻ†àźŸàŻàź•àŻàź•àźȘàŻàźȘàźŸàŻàźŸàź€àŻ."</string>
     <string name="basic_status" msgid="2315371112182658176">"àź€àźżàź±àźšàŻàź€àźšàźżàźČàŻˆ àź‰àź°àŻˆàźŻàźŸàźŸàźČàŻ"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• àź•àŻàź±àŻˆàźšàŻàź€àźȘàźŸàŻàźšàźźàŻ àź’àź°àŻ àźšàźŸàź€àź©àźźàźŸàź”àź€àŻ àź•àźżàźŸàŻˆàź•àŻàź• àź”àŻ‡àźŁàŻàźŸàŻàźźàŻ"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"àź·àźŸàź°àŻàźŸàŻàź•àźŸàŻàźŸàŻˆ àź€àŻŠàźŸàŻàźŸàŻàźȘàŻ àźȘàźżàźŸàźżàź•àŻàź•àź”àŻàźźàŻ"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"àź°àź€àŻàź€àŻàźšàŻ†àźŻàŻ"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"àź‡àźȘàŻàźȘàŻ‹àź€àŻ àźźàźŸàź±àŻàź±àź”àŻàźźàŻ"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"àźšàźżàź±àźšàŻàź€ àźšàŻ†àźČàŻàźƒàźȘàźżàź•àŻàź•àŻ àźźàŻŠàźȘàŻˆàźČàŻˆ àźźàźŸàź•àŻàź•àźŸàź€àŻ€àź°àŻàź•àźłàŻ"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"àźšàźżàź±àźšàŻàź€ àźšàŻ†àźČàŻàźƒàźȘàźżàź•àŻàź•àŻ àźźàŻàź©àŻàźȘàŻàź± àźŸàźżàźžàŻàźȘàźżàźłàŻ‡àź”àźżàź±àŻàź•àŻ àźźàźŸàź±àŻàź±àź”àźŸ?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"àź…àź€àźżàź•àź€àŻ àź€àŻ†àźłàźżàź”àŻàź€àŻàź€àźżàź±àź©àŻàźŸàź©àŻ àź…àź•àźČàź•àŻ àź•àŻ‹àźŁàź€àŻàź€àźżàźČàŻ àźȘàźŸàź€àŻàź€àŻˆ àźŽàźŸàŻàźȘàŻàźȘàź€àź±àŻàź•àŻàźȘàŻ àźȘàźżàź©àŻàźȘàź•àŻàź•àź•àŻ àź•àŻ‡àźźàź°àźŸàź”àŻˆàźȘàŻ àźȘàźŻàź©àŻàźȘàźŸàŻàź€àŻàź€àŻàź™àŻàź•àźłàŻ."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ àź‡àźšàŻàź€àź€àŻ àź€àźżàź°àŻˆ àź†àźƒàźȘàŻ àź†àź•àźżàź”àźżàźŸàŻàźźàŻ"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"àźźàźŸàź•àŻàź•àź€àŻàź€àź•àŻàź• àźšàźŸàź€àź©àźźàŻ àź€àźżàź±àź•àŻàź•àźȘàŻàźȘàźŸàŻàź•àźżàź±àź€àŻ"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"àźźàźŸàź•àŻàź•àź€àŻàź€àź•àŻàź• àźšàźŸàź€àź©àźźàŻ àźƒàźȘàźżàźłàźżàźȘàŻ àźšàŻ†àźŻàŻàźŻàźȘàŻàźȘàźŸàŻàźŸàŻ àź€àźżàź°àŻàźȘàŻàźȘàźȘàŻàźȘàźŸàŻàź•àźżàź±àź€àŻ"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> àźȘàŻ‡àźŸàŻàźŸàź°àźż àźźàŻ€àź€àźźàŻàźłàŻàźłàź€àŻ"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"àź‰àź™àŻàź•àźłàŻ àźžàŻàźŸàŻˆàźČàźžàŻˆàźšàŻ àźšàźŸàź°àŻàźœàź°àŻàźŸàź©àŻ àź‡àźŁàŻˆàźŻàŻàź™àŻàź•àźłàŻ"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"àźžàŻàźŸàŻˆàźČàźžàźżàź©àŻ àźȘàŻ‡àźŸàŻàźŸàź°àźż àź•àŻàź±àŻˆàź”àźŸàź• àź‰àźłàŻàźłàź€àŻ"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"àź”àŻ€àźŸàźżàźŻàŻ‹ àź•àŻ‡àźźàź°àźŸ"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"àź‡àźšàŻàź€àź•àŻ àź•àźŁàź•àŻàź•àźżàźČàźżàź°àŻàźšàŻàź€àŻ àź…àźŽàŻˆàź•àŻàź• àźźàŻàźŸàźżàźŻàźŸàź€àŻ"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"àź‰àź™àŻàź•àźłàŻ àźȘàźŁàźżàź•àŻ àź•àŻŠàźłàŻàź•àŻˆàźŻàźżàź©àŻàźȘàźŸàźż àźšàŻ€àź™àŻàź•àźłàŻ àźȘàźŁàźżàź•àŻ àź•àźŁàź•àŻàź•àźżàźČàŻ àź‡àź°àŻàźšàŻàź€àŻ àźźàźŸàŻàźŸàŻàźźàŻ‡ àźƒàźȘàŻ‹àź©àŻ àź…àźŽàŻˆàźȘàŻàźȘàŻàź•àźłàŻˆàźšàŻ àźšàŻ†àźŻàŻàźŻ àźźàŻàźŸàźżàźŻàŻàźźàŻ"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"àźȘàźŁàźżàź•àŻ àź•àźŁàź•àŻàź•àźżàź±àŻàź•àŻ àźźàźŸàź±àŻ"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"àźźàŻ‚àźŸàŻàź•"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"àźȘàŻ‚àźŸàŻàźŸàŻàź€àŻ àź€àźżàź°àŻˆ àź…àźźàŻˆàźȘàŻàźȘàŻàź•àźłàŻ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ta/tiles_states_strings.xml b/packages/SystemUI/res/values-ta/tiles_states_strings.xml
index 41f6412..1a22d9f 100644
--- a/packages/SystemUI/res/values-ta/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ta/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"àźźàŻàźŸàź•àŻàź•àźȘàŻàźȘàźŸàŻàźŸàŻàźłàŻàźłàź€àŻ"</item>
     <item msgid="5966994759929723339">"àź‡àźŻàź•àŻàź•àźȘàŻàźȘàźŸàŻàźŸàŻàźłàŻàźłàź€àŻ"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 766ebca..11f528a 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"ఔర్క్ ఞ్క్రీచ్‌షటట్‌à°Čు <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>
     <string name="screenrecord_name" msgid="2596401223859996572">"ఞ్క్రీచ్ à°°à°żà°•à°Ÿà°°à±à°Ąà°°à±"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ఞ్క్రీచ్ à°°à°żà°•à°Ÿà°°à±à°Ąà°żà°‚à°—à± à°…à°”à±à°€à±‹à°‚à°Šà°ż"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"ఞ్క్రీచ్ à°°à°żà°•à°Ÿà°°à±à°Ąà± ఞెషచ్ కోఞం ఆచ్‌à°—à±‹à°Żà°żà°‚à°—à± à°šà±‹à°Ÿà°żà°«à°żà°•à±‡à°·à°šà±"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"à°Ș్రకటశం"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"కà°Čర్ à°źà°Ÿà°°à±à°Șà°żà°Ąà°ż"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"కà°Čర్ కరెక్షచ్"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"à°Żà±‚à°œà°°à±‌à°Čచు à°źà±‡à°šà±‡à°œà± à°šà±‡à°Żà°‚à°Ąà°ż"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"à°Șà±‚à°°à±à°€à°Żà°żà°‚à°Šà°ż"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"à°źà±‚à°žà°żà°”à±‡à°Żà°ż"</string>
@@ -775,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"ఞ్క్రీచ్ à°°à°żà°•à°Ÿà°°à±à°Ąà°żà°‚à°—à±"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"à°¶à±€à°°à±à°·à°żà°• à°Čేఊు"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"à°žà±à°Ÿà°Ÿà°‚à°Ąà±‌à°Źà±ˆ"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"à°źà±à°Żà°Ÿà°—à±à°šà°żà°«à°żà°•à±‡à°·à°šà± à°”à°żà°‚à°Ąà±‹"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"à°źà±à°Żà°Ÿà°—à±à°šà°żà°«à°żà°•à±‡à°·à°šà± à°šà°żà°Żà°‚à°€à±à°°à°Łà°Č à°”à°żà°‚à°Ąà±‹"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ఊగ్గరగట à°œà±‚à°źà± à°šà±‡à°Żà°‚à°Ąà°ż"</string>
@@ -800,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"కంట్రోà°Č్ఞ్‌చు à°Żà°Ÿà°Ąà± à°šà±‡à°Żà°Ąà°Ÿà°šà°żà°•à°ż à°Żà°Ÿà°Ș్‌చు à°Žà°‚à°šà±à°•à±‹à°‚à°Ąà°ż"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# కంట్రోà°Č్ à°œà±‹à°Ąà°żà°‚à°šà°Źà°Ąà°żà°‚à°Šà°ż.}other{# కంట్రోà°Č్ఞ్ à°œà±‹à°Ąà°żà°‚à°šà°Źà°Ąà±à°Ąà°Ÿà°Żà°ż.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"à°€à±€à°žà°żà°”à±‡à°Żà°Źà°Ąà°żà°‚à°Šà°ż"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g>చు à°œà±‹à°Ąà°żà°‚à°šà°Ÿà°Čà°Ÿ?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"à°źà±€à°°à± <xliff:g id="APPNAME">%s</xliff:g>చు à°œà±‹à°Ąà°żà°‚à°šà°żà°šà°Ș్à°Șà±à°Ąà±, à°‡à°Šà°ż ఈ à°Șà±à°Żà°Ÿà°šà±†à°Č్‌కు కంట్రోà°Č్ఞ్‌à°šà°ż, కంటెంట్‌చు à°œà±‹à°Ąà°żà°‚à°šà°—à°Čఊు. à°•à±Šà°šà±à°šà°ż à°Żà°Ÿà°Ș్‌à°Čà°Čో, à°‡à°•à±à°•à°Ą à°à°Żà±‡ కంట్రోà°Č్ఞ్ à°•à°šà°żà°Șà°żà°‚à°šà°Ÿà°Čో à°źà±€à°°à± ఎంచుకోఔచ్చు."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"à°‡à°·à±à°Ÿà°źà±ˆà°šà°Šà°żà°—à°Ÿ గుర్ఀు à°Șà±†à°Ÿà±à°Ÿà°Źà°Ąà°żà°‚à°Šà°ż"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"<xliff:g id="NUMBER">%d</xliff:g>à°” ఞ్ఄటచంà°Čో à°‡à°·à±à°Ÿà°źà±ˆà°šà°Šà°żà°—à°Ÿ గుర్ఀు à°Șà±†à°Ÿà±à°Ÿà°Źà°Ąà°żà°‚à°Šà°ż"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"à°‡à°·à±à°Ÿà°źà±ˆà°šà°Šà°żà°—à°Ÿ à°Șà±†à°Ÿà±à°Ÿà°żà°š గుర్ఀు à°€à±€à°žà°żà°”à±‡à°Żà°Źà°Ąà°żà°‚à°Šà°ż"</string>
@@ -850,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g>చు à°€à±†à°°à°”à°‚à°Ąà°ż"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> à°šà±à°‚à°Ąà°ż <xliff:g id="ARTIST_NAME">%2$s</xliff:g> à°Șà°Ÿà°Ąà°żà°š <xliff:g id="SONG_NAME">%1$s</xliff:g>‌చు à°Ș్à°Čే à°šà±‡à°Żà°‚à°Ąà°ż"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> à°šà±à°‚à°Ąà°ż <xliff:g id="SONG_NAME">%1$s</xliff:g>‌చు à°Ș్à°Čే à°šà±‡à°Żà°‚à°Ąà°ż"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"à°źà±€ కోఞం"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"à°šà°°à±à°Ż రఊ్ఊు"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g>‌à°Čో à°Ș్à°Čే à°šà±‡à°Żà°Ąà°Ÿà°šà°żà°•à°ż ఊగ్గరగట à°”à±†à°łà±à°Čà°‚à°Ąà°ż"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"à°‡à°•à±à°•à°Ą à°†à°Ąà°Ÿà°Ÿà°šà°żà°•à°ż, <xliff:g id="DEVICENAME">%1$s</xliff:g>‌కు ఊగ్గరగట à°”à±†à°łà±à°Čà°‚à°Ąà°ż"</string>
@@ -857,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"ఏఊో à°€à°Ș్à°Șు à°œà°°à°żà°—à°żà°‚à°Šà°ż. à°źà°łà±à°Čీ ట్రై à°šà±‡à°Żà°‚à°Ąà°ż."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"à°Čà±‹à°Ąà± à°…à°”à±à°€à±‹à°‚à°Šà°ż"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"à°Ÿà°Ÿà°Źà±à°Čెట్"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"à°źà±€ à°źà±€à°Ąà°żà°Żà°Ÿ à°Ș్రఞటరం à°…à°”à±à°€à±‹à°‚à°Šà°ż"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"<xliff:g id="APP_LABEL">%1$s</xliff:g> à°Ș్రఞటరం à°…à°”à±à°€à±‹à°‚à°Šà°ż"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ఇచ్‌à°Żà°Ÿà°•à±à°Ÿà°żà°”à±, à°Żà°Ÿà°Ș్ చెక్ à°šà±‡à°Żà°‚à°Ąà°ż"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"à°•à°šà±à°—à±Šà°šà°Źà°Ąà°Čేఊు"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"కంట్రోà°Č్ à°…à°‚à°Šà±à°Źà°Ÿà°Ÿà±à°Čో à°Čేఊు"</string>
@@ -866,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"ఎర్రర్, à°źà°łà±à°Čీ à°Șà±à°°à°Żà°€à±à°šà°żà°‚à°šà°‚à°Ąà°ż"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"కంట్రోà°Č్ఞ్‌చు à°œà±‹à°Ąà°żà°‚à°šà°‚à°Ąà°ż"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"కంట్రోà°Č్ఞ్‌చు à°Žà°Ąà°żà°Ÿà± à°šà±‡à°Żà°‚à°Ąà°ż"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"à°Żà°Ÿà°Ș్‌చు à°œà±‹à°Ąà°żà°‚à°šà°‚à°Ąà°ż"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"అఔుట్‌à°Șుట్‌à°Čచు à°œà±‹à°Ąà°żà°‚à°šà°‚à°Ąà°ż"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"గ్రూà°Ș్"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 à°Șà°°à°żà°•à°°à°‚ à°Žà°‚à°šà±à°•à±‹à°Źà°Ąà°żà°‚à°Šà°ż"</string>
@@ -881,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ఞ్à°Șీకర్‌à°Čు &amp; à°Ąà°żà°žà±‌à°Ș్à°Čేà°Čు"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"à°žà±‚à°šà°żà°‚à°šà°Źà°Ąà°żà°š à°Șà°°à°żà°•à°°à°Ÿà°Čు"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"à°Șà±à°°à±€à°źà°żà°Żà°‚ ఖటఀట అఔఞరం"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"à°Ș్రఞటరం à°•à°Ÿà°”à°Ąà°‚ à°…à°šà±‡à°Šà°ż ఎà°Čà°Ÿ à°Șà°šà°ż à°šà±‡à°žà±à°€à±à°‚à°Šà°ż"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"à°Ș్రఞటరం"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"à°źà±€à°•à± à°žà°źà±€à°Șంà°Čో ఉచ్చ à°”à±à°Żà°•à±à°€à±à°Čు అచుకూà°Čà°€ ఉచ్చ à°Źà±à°Čూటూఀ్ à°Șà°°à°żà°•à°°à°Ÿà°Čఀో à°źà±€à°°à± à°Ș్రఞటరం చేఞ్ఀుచ్చ à°źà±€à°Ąà°żà°Żà°Ÿà°šà± à°”à°żà°šà°—à°Čరు"</string>
@@ -892,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"à°Ș్రఞటరం à°šà±‡à°Żà°Ąà°‚ à°žà°Ÿà°§à±à°Żà°Șà°Ąà°Čేఊు"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"ఞేఔ్ à°šà±‡à°Żà°Ąà°‚ à°žà°Ÿà°§à±à°Żà°Șà°Ąà°Šà±. à°źà°łà±à°Čీ ట్రై à°šà±‡à°Żà°‚à°Ąà°ż."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"ఞేఔ్ à°šà±‡à°Żà°Ąà°‚ à°žà°Ÿà°§à±à°Żà°Șà°Ąà°Šà±."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"కచీఞం 4 అక్షరటà°Čచు ఉà°Șà°Żà±‹à°—à°żà°‚à°šà°‚à°Ąà°ż"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"16 కంటే ఀక్కుఔ అక్షరటà°Čచు ఉà°Șà°Żà±‹à°—à°żà°‚à°šà°‚à°Ąà°ż"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"à°Źà°żà°Čà±à°Ąà± à°šà°‚à°Źà°°à±"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"à°Źà°żà°Čà±à°Ąà± à°šà°‚à°Źà°°à±, క్à°Čà°żà°Ș్‌à°Źà±‹à°°à±à°Ąà±‌కు à°•à°Ÿà°Șీ à°šà±‡à°Żà°Źà°Ąà°żà°‚à°Šà°ż."</string>
     <string name="basic_status" msgid="2315371112182658176">"à°žà°‚à°­à°Ÿà°·à°Łà°šà± à°€à±†à°°à°”à°‚à°Ąà°ż"</string>
@@ -1011,11 +1030,16 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• కచీఞం ఒక à°Șà°°à°żà°•à°°à°źà±ˆà°šà°Ÿ à°…à°‚à°Šà±à°Źà°Ÿà°Ÿà±à°Čో à°‰à°‚à°Šà°šà°ż"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"షటర్ట్‌కట్‌చు à°€à°Ÿà°•à°ż, à°šà±Šà°•à±à°•à°ż ఉంచు"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"రఊ్ఊు à°šà±‡à°Żà°‚à°Ąà°ż"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"ఇà°Ș్à°Șà±à°Ąà±‡ à°€à°żà°Ș్à°Șà°‚à°Ąà°ż"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"à°źà±†à°°à±à°—à±ˆà°š ఞెà°Č్ఫీ కోఞం ఫోచ్‌చు అచ్‌ఫోà°Čà±à°Ąà± à°šà±‡à°Żà°‚à°Ąà°ż"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"à°źà°‚à°šà°ż ఞెà°Č్ఫీ కోఞం à°źà±à°‚à°Šà± ఔైà°Șు à°Ąà°żà°žà±‌à°Ș్à°Čేకు à°€à°żà°Ș్à°Șà°Ÿà°Čà°Ÿ?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"à°…à°§à°żà°• à°°à°żà°œà°Čà±à°Żà±‚à°·à°šà±‌ఀో à°Șెఊ్ఊ ఫోటో కోఞం ఔెచుక ఔైà°Șుచ ఉచ్చ à°•à±†à°źà±†à°°à°Ÿà°šà± ఉà°Șà°Żà±‹à°—à°żà°‚à°šà°‚à°Ąà°ż."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ఈ ఞ్క్రీచ్ ఆఫ్ à°…à°”à±à°€à±à°‚à°Šà°ż"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"à°źà°Ąà°”à°—à°Č à°Șà°°à°żà°•à°°à°‚ à°”à°żà°Ș్à°Șà°Źà°Ąà±à°€à±‹à°‚à°Šà°ż"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"à°źà°Ąà°”à°—à°Č à°Șà°°à°żà°•à°°à°‚ చుట్టూ à°€à°żà°Ș్à°Șà°Źà°Ąà±à°€à±‹à°‚à°Šà°ż"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> à°Źà±à°Żà°Ÿà°Ÿà°°à±€ à°źà°żà°—à°żà°Čà°ż à°‰à°‚à°Šà°ż"</string>
@@ -1026,4 +1050,5 @@
     <string name="call_from_work_profile_text" msgid="3458704745640229638">"à°źà±€ ఔర్క్ à°Șà°Ÿà°Čà°žà±€, à°źà°żà°źà±à°źà°Čà±à°šà°ż ఔర్క్ à°Ș్రొఫైà°Č్ à°šà±à°‚à°Ąà°ż à°źà°Ÿà°€à±à°°à°źà±‡ ఫోచ్ à°•à°Ÿà°Č్ఞ్ à°šà±‡à°Żà°Ąà°Ÿà°šà°żà°•à°ż à°…à°šà±à°źà°€à°żà°žà±à°€à±à°‚à°Šà°ż"</string>
     <string name="call_from_work_profile_action" msgid="2937701298133010724">"ఔర్క్ à°Ș్రొఫైà°Č్‌కు à°źà°Ÿà°°à°‚à°Ąà°ż"</string>
     <string name="call_from_work_profile_close" msgid="7927067108901068098">"à°źà±‚à°žà°żà°”à±‡à°Żà°‚à°Ąà°ż"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"à°Čటక్ ఞ్క్రీచ్ à°žà±†à°Ÿà±à°Ÿà°żà°‚à°—à±‌à°Čు"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-te/tiles_states_strings.xml b/packages/SystemUI/res/values-te/tiles_states_strings.xml
index 44ba477..c5a525c 100644
--- a/packages/SystemUI/res/values-te/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-te/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"ఆఫ్"</item>
     <item msgid="5966994759929723339">"ఆచ్"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 5a58355..c3037be 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"àž àžČàžžàž«àž™àč‰àžČàžˆàž­àž‡àžČàž™àžˆàž°àžšàž±àž™àž—àž¶àžàž­àžąàžčàčˆàčƒàž™àčàž­àž› <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>
     <string name="screenrecord_name" msgid="2596401223859996572">"àč‚àž›àžŁàčàžàžŁàžĄàžšàž±àž™àž—àž¶àžàž«àž™àč‰àžČàžˆàž­"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"àžàžłàž„àž±àž‡àž›àžŁàž°àžĄàž§àž„àžœàž„àžàžČàžŁàž­àž±àž”àž«àž™àč‰àžČàžˆàž­"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"àžàžČàžŁàčàžˆàč‰àž‡àč€àž•àž·àž­àž™àž•àčˆàž­àč€àž™àž·àčˆàž­àž‡àžȘàžłàž«àžŁàž±àžšàč€àž‹àžȘàžŠàž±àž™àžàžČàžŁàžšàž±àž™àž—àž¶àžàž«àž™àč‰àžČàžˆàž­"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"àž„àž§àžČàžĄàžȘàž§àčˆàžČàž‡"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"àžàžČàžŁàžàž„àž±àžšàžȘàž”"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"àžàžČàžŁàčàžàč‰àžȘàž”"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"àžˆàž±àž”àžàžČàžŁàžœàžčàč‰àčƒàžŠàč‰"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"àč€àžȘàžŁàč‡àžˆàžȘàžŽàč‰àž™"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"àž›àžŽàž”"</string>
@@ -775,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"àžàžČàžŁàžšàž±àž™àž—àž¶àžàž«àž™àč‰àžČàžˆàž­"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"àč„àžĄàčˆàžĄàž”àžŠàž·àčˆàž­"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"àžȘàčàž•àž™àž”àčŒàžšàžČàžą"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"àž«àž™àč‰àžČàž•àčˆàžČàž‡àžàžČàžŁàž‚àžąàžČàžą"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"àžàžČàžŁàž„àž§àžšàž„àžžàžĄàž«àž™àč‰àžČàž•àčˆàžČàž‡àžàžČàžŁàž‚àžąàžČàžą"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"àž‹àžčàžĄàč€àž‚àč‰àžČ"</string>
@@ -800,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"àč€àž„àž·àž­àžàčàž­àž›àč€àžžàž·àčˆàž­àč€àžžàžŽàčˆàžĄàž•àž±àž§àž„àž§àžšàž„àžžàžĄ"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{àč€àžžàžŽàčˆàžĄàž•àž±àž§àž„àž§àžšàž„àžžàžĄ # àž•àž±àž§àčàž„àč‰àž§}other{àč€àžžàžŽàčˆàžĄàž•àž±àž§àž„àž§àžšàž„àžžàžĄ # àž•àž±àž§àčàž„àč‰àž§}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"àž™àžłàž­àž­àžàčàž„àč‰àž§"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"àč€àžžàžŽàčˆàžĄ <xliff:g id="APPNAME">%s</xliff:g> àč„àž«àžĄ"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"àč€àžĄàž·àčˆàž­àč€àžžàžŽàčˆàžĄ <xliff:g id="APPNAME">%s</xliff:g> àž„àžžàž“àžˆàž°àč€àžžàžŽàčˆàžĄàžàžČàžŁàž„àž§àžšàž„àžžàžĄàčàž„àž°àč€àž™àž·àč‰àž­àž«àžČàč„àž›àžąàž±àž‡àčàžœàž‡àž™àž”àč‰àč„àž”àč‰ àčƒàž™àžšàžČàž‡àčàž­àž› àž„àžžàž“àč€àž„àž·àž­àžàč„àž”àč‰àž§àčˆàžČàž•àč‰àž­àž‡àžàžČàžŁàčƒàž«àč‰àžàžČàžŁàž„àž§àžšàž„àžžàžĄàčƒàž”àž›àžŁàžČàžàžàž‚àž¶àč‰àž™àž—àž”àčˆàž™àž”àčˆ"</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"àž•àž±àč‰àž‡àč€àž›àč‡àž™àžŁàžČàžąàžàžČàžŁàč‚àž›àžŁàž”àčàž„àč‰àž§"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"àž•àž±àč‰àž‡àč€àž›àč‡àž™àžŁàžČàžąàžàžČàžŁàč‚àž›àžŁàž”àčàž„àč‰àž§ àč‚àž”àžąàž­àžąàžčàčˆàž„àžłàž”àž±àžšàž—àž”àčˆ <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"àž™àžłàž­àž­àžàžˆàžČàžàžŁàžČàžąàžàžČàžŁàč‚àž›àžŁàž”àčàž„àč‰àž§"</string>
@@ -850,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"àč€àž›àžŽàž” <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"àč€àž›àžŽàž”àč€àžžàž„àž‡ <xliff:g id="SONG_NAME">%1$s</xliff:g> àž‚àž­àž‡ <xliff:g id="ARTIST_NAME">%2$s</xliff:g> àžˆàžČàž <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"àč€àž›àžŽàž”àč€àžžàž„àž‡ <xliff:g id="SONG_NAME">%1$s</xliff:g> àžˆàžČàž <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"àžȘàžłàž«àžŁàž±àžšàž„àžžàž“"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"àč€àž„àžŽàžàž—àžł"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"àž‚àžąàž±àžšàč„àž›àčƒàžàž„àč‰àžĄàžČàžàž‚àž¶àč‰àž™àč€àžžàž·àčˆàž­àč€àž„àčˆàž™àčƒàž™ <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"àž‚àžąàž±àžšàč„àž›àčƒàžàž„àč‰ <xliff:g id="DEVICENAME">%1$s</xliff:g> àžĄàžČàžàž‚àž¶àč‰àž™àč€àžžàž·àčˆàž­àč€àž„àčˆàž™àž—àž”àčˆàž™àž”àčˆ"</string>
@@ -857,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"àč€àžàžŽàž”àž‚àč‰àž­àžœàžŽàž”àžžàž„àžČàž” àč‚àž›àžŁàž”àž„àž­àž‡àž­àž”àžàž„àžŁàž±àč‰àž‡"</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"àžàžłàž„àž±àž‡àč‚àž«àž„àž”"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"àčàž—àč‡àžšàč€àž„àč‡àž•"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"àžàžłàž„àž±àž‡àčàž„àžȘàž•àčŒàžȘàž·àčˆàž­"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"àžàžłàž„àž±àž‡àčàž„àžȘàž•àčŒ <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"àč„àžĄàčˆàžĄàž”àžàžČàžŁàčƒàžŠàč‰àž‡àžČàž™ àč‚àž›àžŁàž”àž•àžŁàž§àžˆàžȘàž­àžšàčàž­àž›"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"àč„àžĄàčˆàžžàžš"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"àčƒàžŠàč‰àžàžČàžŁàž„àž§àžšàž„àžžàžĄàč„àžĄàčˆàč„àž”àč‰"</string>
@@ -866,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"àžžàžšàž‚àč‰àž­àžœàžŽàž”àžžàž„àžČàž” àč‚àž›àžŁàž”àž„àž­àž‡àž­àž”àžàž„àžŁàž±àč‰àž‡"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"àč€àžžàžŽàčˆàžĄàž•àž±àž§àž„àž§àžšàž„àžžàžĄ"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"àčàžàč‰àč„àž‚àž•àž±àž§àž„àž§àžšàž„àžžàžĄ"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"àč€àžžàžŽàčˆàžĄàčàž­àž›"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"àč€àžžàžŽàčˆàžĄàč€àž­àžČàž•àčŒàžžàžžàž•"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"àžàž„àžžàčˆàžĄ"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"àč€àž„àž·àž­àžàž­àžžàž›àžàžŁàž“àčŒàč„àž§àč‰ 1 àžŁàžČàžąàžàžČàžŁ"</string>
@@ -881,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"àž„àžłàč‚àžžàž‡àčàž„àž°àžˆàž­àčàžȘàž”àž‡àžœàž„"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"àž­àžžàž›àžàžŁàž“àčŒàž—àž”àčˆàčàž™àž°àž™àžł"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"àž•àč‰àž­àž‡àčƒàžŠàč‰àžšàž±àžàžŠàž” Premium"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"àž§àžŽàž˜àž”àžàžČàžŁàž—àžłàž‡àžČàž™àž‚àž­àž‡àžàžČàžŁàž­àž­àžàž­àžČàžàžČàžš"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"àž›àžŁàž°àžàžČàžš"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"àžœàžčàč‰àž—àž”àčˆàž­àžąàžčàčˆàčƒàžàž„àč‰àž„àžžàž“àčàž„àž°àžĄàž”àž­àžžàž›àžàžŁàž“àčŒàžšàž„àžčàž—àžčàž˜àž—àž”àčˆàžŁàž­àž‡àžŁàž±àžšàžȘàžČàžĄàžČàžŁàž–àžŁàž±àžšàžŸàž±àž‡àžȘàž·àčˆàž­àž—àž”àčˆàž„àžžàž“àžàžłàž„àž±àž‡àž­àž­àžàž­àžČàžàžČàžšàč„àž”àč‰"</string>
@@ -892,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"àž­àž­àžàž­àžČàžàžČàžšàč„àžĄàčˆàč„àž”àč‰"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"àžšàž±àž™àž—àž¶àžàč„àžĄàčˆàč„àž”àč‰ àč‚àž›àžŁàž”àž„àž­àž‡àž­àž”àžàž„àžŁàž±àč‰àž‡"</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"àžšàž±àž™àž—àž¶àžàč„àžĄàčˆàč„àž”àč‰"</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"àčƒàžŠàč‰àž­àž±àžàž‚àžŁàž°àž­àžąàčˆàžČàž‡àž™àč‰àž­àžą 4 àž•àž±àž§"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"àčƒàžŠàč‰àž­àž±àžàž‚àžŁàž°àč„àžĄàčˆàč€àžàžŽàž™ 16 àž•àž±àž§"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"àž«àžĄàžČàžąàč€àž„àž‚àžšàžŽàž„àž”àčŒ"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"àž„àž±àž”àž„àž­àžàž«àžĄàžČàžąàč€àž„àž‚àžšàžŽàž„àž”àčŒàč„àž›àžąàž±àž‡àž„àž„àžŽàž›àžšàž­àžŁàčŒàž”àčàž„àč‰àž§"</string>
     <string name="basic_status" msgid="2315371112182658176">"àč€àž›àžŽàž”àžàžČàžŁàžȘàž™àž—àž™àžČ"</string>
@@ -1011,11 +1030,16 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• àžĄàž”àž­àžžàž›àžàžŁàž“àčŒàžžàžŁàč‰àž­àžĄàčƒàžŠàč‰àž‡àžČàž™àž­àžąàčˆàžČàž‡àž™àč‰àž­àžą 1 àžŁàžČàžąàžàžČàžŁ"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"àčàž•àž°àčàž›àč‰àž™àžžàžŽàžĄàžžàčŒàž„àž±àž”àž„àč‰àžČàž‡àč„àž§àč‰"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"àžąàžàč€àž„àžŽàž"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"àžžàž„àžŽàžàč€àž„àžą"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"àžàžČàž‡àč‚àž—àžŁàžšàž±àžžàž—àčŒàč€àžžàž·àčˆàž­àč€àž‹àž„àžŸàž”àž—àž”àčˆàž”àž”àžąàžŽàčˆàž‡àž‚àž¶àč‰àž™"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"àžžàž„àžŽàžàč€àž›àč‡àž™àž«àž™àč‰àžČàžˆàž­àž”àč‰àžČàž™àž«àž™àč‰àžČàč€àžžàž·àčˆàž­àž àžČàžžàč€àž‹àž„àžŸàž”àž—àž”àčˆàž”àž”àž‚àž¶àč‰àž™àč„àž«àžĄ"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"àčƒàžŠàč‰àžàž„àč‰àž­àž‡àž«àž„àž±àž‡àč€àžžàž·àčˆàž­àž–àčˆàžČàžąàž àžČàžžàžàž§àč‰àžČàž‡àž‚àž¶àč‰àž™àž”àč‰àž§àžąàž„àž§àžČàžĄàž„àž°àč€àž­àž”àžąàž”àžȘàžčàž‡àž‚àž¶àč‰àž™"</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ àž«àž™àč‰àžČàžˆàž­àž™àž”àč‰àžˆàž°àž›àžŽàž”àč„àž›"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"àž­àžžàž›àžàžŁàž“àčŒàž—àž”àčˆàžžàž±àžšàč„àž”àč‰àžàžłàž„àž±àž‡àžàžČàž‡àž­àž­àž"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"àž­àžžàž›àžàžŁàž“àčŒàž—àž”àčˆàžžàž±àžšàč„àž”àč‰àžàžłàž„àž±àž‡àžžàž„àžŽàžàč„àž›àžĄàžČ"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"àč€àž«àž„àž·àž­àčàžšàž•àč€àž•àž­àžŁàž”àčˆ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
@@ -1026,4 +1050,5 @@
     <string name="call_from_work_profile_text" msgid="3458704745640229638">"àž™àč‚àžąàžšàžČàžąàžàžČàžŁàž—àžłàž‡àžČàž™àž­àž™àžžàžàžČàž•àčƒàž«àč‰àž„àžžàž“àč‚àž—àžŁàž­àž­àžàč„àž”àč‰àžˆàžČàžàč‚àž›àžŁàč„àžŸàž„àčŒàž‡àžČàž™àč€àž—àčˆàžČàž™àž±àč‰àž™"</string>
     <string name="call_from_work_profile_action" msgid="2937701298133010724">"àžȘàž„àž±àžšàč„àž›àčƒàžŠàč‰àč‚àž›àžŁàč„àžŸàž„àčŒàž‡àžČàž™"</string>
     <string name="call_from_work_profile_close" msgid="7927067108901068098">"àž›àžŽàž”"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"àžàžČàžŁàž•àž±àč‰àž‡àž„àčˆàžČàž«àž™àč‰àžČàžˆàž­àž„àč‡àž­àž"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-th/tiles_states_strings.xml b/packages/SystemUI/res/values-th/tiles_states_strings.xml
index 9cd060f..61e0fe6 100644
--- a/packages/SystemUI/res/values-th/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-th/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"àž›àžŽàž”"</item>
     <item msgid="5966994759929723339">"àč€àž›àžŽàž”"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index b07ad7b..c991c82 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"<xliff:g id="PERCENT">%1$d</xliff:g> (na) porsyento sa hangganan sa ibaba"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"<xliff:g id="PERCENT">%1$d</xliff:g> (na) porsyento sa hangganan sa kaliwa"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"<xliff:g id="PERCENT">%1$d</xliff:g> (na) porsyento sa hangganan sa kanan"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Naka-save ang mga screenshot sa trabaho sa <xliff:g id="APP">%1$s</xliff:g> app"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Na-save sa <xliff:g id="APP">%1$s</xliff:g> sa profile sa trabaho"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Mga File"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"Na-detect ng <xliff:g id="APPNAME">%1$s</xliff:g> ang screenshot. na ito"</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"Na-detect ng <xliff:g id="APPNAME">%1$s</xliff:g> at ng iba pang bukas na app ang screenshot na ito."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Recorder ng Screen"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Pinoproseso screen recording"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Kasalukuyang notification para sa session ng pag-record ng screen"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brightness"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Pag-invert ng kulay"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Pagtatama ng kulay"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Pamahalaan ang mga user"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Tapos na"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Isara"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Awtomatiko"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Walang tunog o pag-vibrate"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Walang tunog o pag-vibrate at lumalabas nang mas mababa sa seksyon ng pag-uusap"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Puwedeng mag-ring o mag-vibrate batay sa mga setting ng device"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Puwedeng mag-ring o mag-vibrate batay sa mga setting ng device. Mga pag-uusap mula sa <xliff:g id="APP_NAME">%1$s</xliff:g> bubble bilang default."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Ipatukoy sa system kung dapat gumawa ng tunog o pag-vibrate ang notification na ito"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Status:&lt;/b&gt; Na-promote sa Default"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Status:&lt;/b&gt; Na-demote sa Naka-silent"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"pag-record ng screen"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Walang pamagat"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Naka-standby"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Window ng Pag-magnify"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Mga Kontrol sa Pag-magnify ng Window"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Mag-zoom in"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Pumili ng app para magdagdag ng mga kontrol"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Nagdagdag ng # kontrol.}one{Nagdagdag ng # kontrol.}other{Nagdagdag ng # na kontrol.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Inalis"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Idagdag ang <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Kapag idinagdag mo ang <xliff:g id="APPNAME">%s</xliff:g>, puwede itong magdagdag ng mga kontrol at content sa panel na ito. Sa ilang app, puwede mong piliin kung aling mga kontrol ang lalabas dito."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Ginawang paborito"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Ginawang paborito, posisyon <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Inalis sa paborito"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Buksan ang <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"I-play ang <xliff:g id="SONG_NAME">%1$s</xliff:g> ni/ng <xliff:g id="ARTIST_NAME">%2$s</xliff:g> mula sa <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"I-play ang <xliff:g id="SONG_NAME">%1$s</xliff:g> mula sa <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Para sa Iyo"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"I-undo"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Lumapit pa para mag-play sa <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Para mag-play dito, lumapit sa <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Nagkaproblema. Subukan ulit."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Naglo-load"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Pag-cast ng iyong media"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Kina-cast ang <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Hindi aktibo, tingnan ang app"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Hindi nahanap"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Hindi available ang kontrol"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Nagka-error, subukan ulit"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Magdagdag ng mga kontrol"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Mag-edit ng mga kontrol"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Magdagdag ng app"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Magdagdag ng mga output"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupo"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 device ang napili"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Mga Speaker at Display"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Mga Iminumungkahing Device"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Nangangailangan ng premium account"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Paano gumagana ang pag-broadcast"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Broadcast"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Makakapakinig ang mga taong malapit sa iyo na may mga compatible na Bluetooth device sa media na bino-broadcast mo"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Hindi makapag-broadcast"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Hindi ma-save. Subukan ulit."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Hindi ma-save."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numero ng build"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Nakopya sa clipboard ang numero ng build."</string>
     <string name="basic_status" msgid="2315371112182658176">"Buksan ang pag-uusap"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• May kahit isang device na available"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Pindutin nang matagal: shortcut"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Kanselahin"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"I-flip na ngayon"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"I-unfold ang telepono para sa mas magandang selfie"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"I-flip sa front display para sa magandang selfie?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Gamitin ang camera sa harap para sa mas malawak na larawan na may mas mataas na resolution."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Mag-o-off ang screen na ito"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Ina-unfold na foldable na device"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Fini-flip na foldable na device"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> baterya na lang ang natitira"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Ikonekta sa charger ang iyong stylus"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Paubos na ang baterya ng stylus"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Video camera"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Hindi puwedeng tumawag mula sa profile na ito"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Pinapayagan ka ng iyong patakaran sa trabaho na tumawag lang mula sa profile sa trabaho"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Lumipat sa profile sa trabaho"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Isara"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Mga setting ng lock screen"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-tl/tiles_states_strings.xml b/packages/SystemUI/res/values-tl/tiles_states_strings.xml
index cd7dcf5..4522945 100644
--- a/packages/SystemUI/res/values-tl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-tl/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Naka-off"</item>
     <item msgid="5966994759929723339">"Naka-on"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 07cec1a..d8569b3 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Alt sınır yüzde <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Sol sınır yüzde <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Sağ sınır yüzde <xliff:g id="PERCENT">%1$d</xliff:g>"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"İß profilindeki ekran görüntüleri <xliff:g id="APP">%1$s</xliff:g> uygulamasına kaydedilir"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"İß profilinde <xliff:g id="APP">%1$s</xliff:g> uygulamasına kaydedildi"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Dosyalar"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> bu ekran görüntüsünü algıladı."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> ve diğer açık uygulamalar bu ekran görüntüsünü algıladı."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Ekran Kaydedicisi"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekran kaydı ißleniyor"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ekran kaydı oturumu için devam eden bildirim"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Parlaklık"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Rengi ters çevirme"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Renk düzeltme"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Kullanıcıları yönet"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Bitti"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Kapat"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Otomatik"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Sessiz veya titreßim yok"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Ses veya titreßim yok, görüßme bölümünün altında görünür"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Cihaz ayarlarına bağlı olarak zili çalabilir veya titreyebilir"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Cihaz ayarlarına bağlı olarak zili çalabilir veya titreyebilir <xliff:g id="APP_NAME">%1$s</xliff:g> adlı uygulamadan görüßmeler varsayılan olarak baloncukla gösterilir."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Bu bildirimin ses çıkarması veya titreßmesi gerekip gerekmediğine sistem karar versin"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Durum:&lt;/b&gt; Varsayılana yükseltildi"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Durum:&lt;/b&gt; Sessize Düßürüldü"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"ekran kaydı"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Baßlıksız"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Beklemeye alınıyor"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Büyütme Penceresi"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Büyütme Penceresi Kontrolleri"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Yakınlaßtır"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Denetim eklemek için uygulama seçin"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# kontrol eklendi.}other{# kontrol eklendi.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Kaldırıldı"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g> eklensin mi?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"<xliff:g id="APPNAME">%s</xliff:g> uygulamasını eklerseniz bu panele kontrol ve içerik ekleyebilir. Bazı uygulamalarda, burada hangi kontrollerin görüneceğini seçebilirsiniz."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Favoriler listesine eklendi"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Favorilere eklendi, konum: <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Favorilerden kaldırıldı"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> uygulamasını aç"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> uygulamasından <xliff:g id="ARTIST_NAME">%2$s</xliff:g>, <xliff:g id="SONG_NAME">%1$s</xliff:g> ßarkısını çal"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> uygulamasından <xliff:g id="SONG_NAME">%1$s</xliff:g> ßarkısını çal"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Sizin İçin"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Geri al"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> cihazında oynatmak için yaklaßın"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Burada oynatmak için <xliff:g id="DEVICENAME">%1$s</xliff:g> cihazına yaklaßın"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Bir hata olußtu. Tekrar deneyin."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Yükleme"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"tablet"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Medyanız yayınlanıyor"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"<xliff:g id="APP_LABEL">%1$s</xliff:g> yayınlanıyor"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Devre dıßı, uygulamaya bakın"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Bulunamadı"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Kontrol kullanılamıyor"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Hata, yeniden deneyin"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Denetim ekle"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Denetimleri düzenle"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Uygulama ekle"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Çıkıßlar ekleyin"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Grup"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 cihaz seçildi"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"%%<xliff:g id="PERCENTAGE">%1$d</xliff:g>"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Hoparlörler ve Ekranlar"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Önerilen Cihazlar"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Premium hesap gerekiyor"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Yayınlamanın ißleyiß ßekli"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Anons"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Yakınınızda ve uyumlu Bluetooth cihazları olan kißiler yayınladığınız medya içeriğini dinleyebilir"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Yayınlanamıyor"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Kaydedilemiyor. Tekrar deneyin."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Kaydedilemiyor."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Derleme numarası"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Derleme numarası panoya kopyalandı."</string>
     <string name="basic_status" msgid="2315371112182658176">"Görüßmeyi aç"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• En az bir cihaz mevcut olmalıdır"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Kısayola dokunup basılı tutun"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"İptal"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"ƞimdi çevirin"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Daha iyi selfie çekmek için telefonu açın"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Daha iyi bir selfie için ön ekrana geçilsin mi?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Daha yüksek çözünürlüğe sahip daha büyük bir fotoğraf için arka yüz kamerasını kullanın."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ * Bu ekran kapatılacak"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Katlanabilir cihaz açılıyor"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Katlanabilir cihaz döndürülüyor"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> pil kaldı"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Ekran kaleminizi bir ßarj cihazına bağlayın"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Ekran kaleminin pil seviyesi düßük"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Video kamera"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Bu profilden telefon araması yapılamıyor"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"İßletme politikanız yalnızca iß profilinden telefon araması yapmanıza izin veriyor"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"İß profiline geç"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Kapat"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Kilit ekranı ayarları"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-tr/tiles_states_strings.xml b/packages/SystemUI/res/values-tr/tiles_states_strings.xml
index 28ba7dc..5d6a7f4 100644
--- a/packages/SystemUI/res/values-tr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-tr/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Kapalı"</item>
     <item msgid="5966994759929723339">"Açık"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 804d2c1..df1d27a 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"Đ ĐŸĐ±ĐŸŃ‡Ń– Đ·ĐœŃ–ĐŒĐșĐž Đ”ĐșŃ€Đ°ĐœĐ° Đ·Đ±Đ”Ń€Ń–ĐłĐ°ŃŽŃ‚ŃŒŃŃ ĐČ ĐŽĐŸĐŽĐ°Ń‚Đșу <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>
     <string name="screenrecord_name" msgid="2596401223859996572">"Запос ĐČŃ–ĐŽĐ”ĐŸ Đ· Đ”ĐșŃ€Đ°ĐœĐ°"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ĐžĐ±Ń€ĐŸĐ±Đșа запОсуĐČĐ°ĐœĐœŃ Đ”ĐșŃ€Đ°ĐœĐ°"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"ĐĄĐżĐŸĐČŃ–Ń‰Đ”ĐœĐœŃ ĐżŃ€ĐŸ ŃĐ”Đ°ĐœŃ запОсу Đ”ĐșŃ€Đ°ĐœĐ°"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ЯсĐșраĐČість"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Đ†ĐœĐČĐ”Ń€ŃŃ–Ń ĐșĐŸĐ»ŃŒĐŸŃ€Ń–ĐČ"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ĐšĐŸŃ€Đ”Đșція ĐșĐŸĐ»ŃŒĐŸŃ€Ńƒ"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"ĐšĐ”Ń€ŃƒĐČато ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČĐ°Ń‡Đ°ĐŒĐž"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Đ“ĐŸŃ‚ĐŸĐČĐŸ"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"ЗаĐșрото"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"АĐČŃ‚ĐŸĐŒĐ°Ń‚ĐžŃ‡ĐœĐŸ"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"БДз Đ·ĐČуĐșу чо ĐČібрації"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"БДз Đ·ĐČуĐșу чо ĐČібрації, Đ·\'яĐČĐ»ŃŃ”Ń‚ŃŒŃŃ ĐœĐžĐ¶Ń‡Đ” ĐČ Ń€ĐŸĐ·ĐŽŃ–Đ»Ń– Ń€ĐŸĐ·ĐŒĐŸĐČ"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"ДзĐČŃ–ĐœĐŸĐș Đ°Đ±ĐŸ ĐČŃ–Đ±Ń€Đ°Ń†Ń–Ń Đ·Đ°Đ»Đ”Đ¶ĐœĐŸ ĐČіЮ ĐœĐ°Đ»Đ°ŃˆŃ‚ŃƒĐČĐ°ĐœŃŒ ĐżŃ€ĐžŃŃ‚Ń€ĐŸŃŽ"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"ĐœĐŸĐ¶Đ” ĐŽĐ·ĐČĐŸĐœĐžŃ‚Đž Đ°Đ±ĐŸ ĐČŃ–Đ±Ń€ŃƒĐČато Đ·Đ°Đ»Đ”Đ¶ĐœĐŸ ĐČіЮ ĐœĐ°Đ»Đ°ŃˆŃ‚ŃƒĐČĐ°ĐœŃŒ ĐżŃ€ĐžŃŃ‚Ń€ĐŸŃŽ. ĐŸĐŸĐșĐ°Đ·ŃƒŃ” сплОĐČаючі Ń€ĐŸĐ·ĐŒĐŸĐČĐž Đ· ĐŽĐŸĐŽĐ°Ń‚Đșа <xliff:g id="APP_NAME">%1$s</xliff:g> за ŃƒĐŒĐŸĐČŃ‡Đ°ĐœĐœŃĐŒ."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Đ”ĐŸĐ·ĐČĐŸĐ»ĐžŃ‚Đž ŃĐžŃŃ‚Đ”ĐŒŃ– ĐČĐžĐ·ĐœĐ°Ń‡Đ°Ń‚Đž, чо ĐŒĐ°Ń” ŃĐżĐŸĐČŃ–Ń‰Đ”ĐœĐœŃ ŃŃƒĐżŃ€ĐŸĐČĐŸĐŽĐ¶ŃƒĐČатося Đ·ĐČуĐșĐŸĐŒ Đ°Đ±ĐŸ ĐČібрацією"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Статус&lt;/b&gt;: піЮĐČĐžŃ‰Đ”ĐœĐŸ ĐŽĐŸ \"За ŃƒĐŒĐŸĐČŃ‡Đ°ĐœĐœŃĐŒ\""</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Статус&lt;/b&gt;: Đ·ĐœĐžĐ¶Đ”ĐœĐŸ ĐŽĐŸ \"БДз Đ·ĐČуĐșу\""</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"запОс ĐČŃ–ĐŽĐ”ĐŸ Đ· Đ”ĐșŃ€Đ°ĐœĐ°"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"БДз ĐœĐ°Đ·ĐČĐž"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Đ Đ”Đ¶ĐžĐŒ ĐŸŃ‡Ń–ĐșуĐČĐ°ĐœĐœŃ"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"ВіĐșĐœĐŸ Đ·Đ±Ń–Đ»ŃŒŃˆĐ”ĐœĐœŃ"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Đ•Đ»Đ”ĐŒĐ”ĐœŃ‚Đž ĐșĐ”Ń€ŃƒĐČĐ°ĐœĐœŃ ĐČіĐșĐœĐ° Đ·Đ±Ń–Đ»ŃŒŃˆĐ”ĐœĐœŃ"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ĐĐ°Đ±Đ»ĐžĐ·ĐžŃ‚Đž"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Đ’ĐžĐ±Đ”Ń€Ń–Ń‚ŃŒ, ĐŽĐ»Ń яĐșĐŸĐłĐŸ ĐŽĐŸĐŽĐ°Ń‚Đșа ĐœĐ°Đ»Đ°ŃˆŃ‚ŃƒĐČато Đ”Đ»Đ”ĐŒĐ”ĐœŃ‚Đž ĐșĐ”Ń€ŃƒĐČĐ°ĐœĐœŃ"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Đ”ĐŸĐŽĐ°ĐœĐŸ # Đ”Đ»Đ”ĐŒĐ”ĐœŃ‚ ĐșĐ”Ń€ŃƒĐČĐ°ĐœĐœŃ.}one{Đ”ĐŸĐŽĐ°ĐœĐŸ # Đ”Đ»Đ”ĐŒĐ”ĐœŃ‚ ĐșĐ”Ń€ŃƒĐČĐ°ĐœĐœŃ.}few{Đ”ĐŸĐŽĐ°ĐœĐŸ # Đ”Đ»Đ”ĐŒĐ”ĐœŃ‚Đž ĐșĐ”Ń€ŃƒĐČĐ°ĐœĐœŃ.}many{Đ”ĐŸĐŽĐ°ĐœĐŸ # Đ”Đ»Đ”ĐŒĐ”ĐœŃ‚Ń–ĐČ ĐșĐ”Ń€ŃƒĐČĐ°ĐœĐœŃ.}other{Đ”ĐŸĐŽĐ°ĐœĐŸ # Đ”Đ»Đ”ĐŒĐ”ĐœŃ‚Đ° ĐșĐ”Ń€ŃƒĐČĐ°ĐœĐœŃ.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Đ’ĐžĐ»ŃƒŃ‡Đ”ĐœĐŸ"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Đ”ĐŸĐ»ŃƒŃ‡ĐžŃ‚Đž ĐŽĐŸĐŽĐ°Ń‚ĐŸĐș <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"ĐŻĐșŃ‰ĐŸ ĐČĐž ĐŽĐŸĐ»ŃƒŃ‡ĐžŃ‚Đ” ĐŽĐŸĐŽĐ°Ń‚ĐŸĐș <xliff:g id="APPNAME">%s</xliff:g>, ĐČŃ–Đœ Đ·ĐŒĐŸĐ¶Đ” Ń€ĐŸĐ·ĐŒŃ–Ń‰ŃƒĐČато Đ”Đ»Đ”ĐŒĐ”ĐœŃ‚Đž ĐșĐ”Ń€ŃƒĐČĐ°ĐœĐœŃ Đč ĐșĐŸĐœŃ‚Đ”ĐœŃ‚ ĐœĐ° ціĐč ĐżĐ°ĐœĐ”Đ»Ń–. ĐŁ ĐŽĐ”ŃĐșох ĐŽĐŸĐŽĐ°Ń‚Đșах ĐŒĐŸĐ¶ĐœĐ° ĐČОбратО, яĐșі Đ”Đ»Đ”ĐŒĐ”ĐœŃ‚Đž ĐșĐ”Ń€ŃƒĐČĐ°ĐœĐœŃ тут ĐČŃ–ĐŽĐŸĐ±Ń€Đ°Đ¶Đ°Ń‚ĐžĐŒŃƒŃ‚ŃŒŃŃ."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Đ”ĐŸĐŽĐ°ĐœĐŸ у ĐČĐžĐ±Ń€Đ°ĐœĐ”"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Đ”ĐŸĐŽĐ°ĐœĐŸ у ĐČĐžĐ±Ń€Đ°ĐœĐ”, ĐżĐŸĐ·ĐžŃ†Ń–Ń <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Đ’ĐžĐŽĐ°Đ»Đ”ĐœĐŸ Đ· ĐČĐžĐ±Ń€Đ°ĐœĐŸĐłĐŸ"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"ВіЮĐșрото ĐŽĐŸĐŽĐ°Ń‚ĐŸĐș <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"ĐŁĐČŃ–ĐŒĐșĐœŃƒŃ‚Đž ĐżŃ–ŃĐœŃŽ \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\", яĐșу ĐČĐžĐșĐŸĐœŃƒŃ” <xliff:g id="ARTIST_NAME">%2$s</xliff:g>, у ĐŽĐŸĐŽĐ°Ń‚Đșу <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"ĐŁĐČŃ–ĐŒĐșĐœŃƒŃ‚Đž ĐżŃ–ŃĐœŃŽ \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" у ĐŽĐŸĐŽĐ°Ń‚Đșу <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Đ”Đ»Ń ĐČас"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Đ’Ń–ĐŽĐŒŃ–ĐœĐžŃ‚Đž"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Đ©ĐŸĐ± ĐČіЮтĐČĐŸŃ€ĐžŃ‚Đž ĐșĐŸĐœŃ‚Đ”ĐœŃ‚ ĐœĐ° ĐżŃ€ĐžŃŃ‚Ń€ĐŸŃ— <xliff:g id="DEVICENAME">%1$s</xliff:g>, ĐœĐ°Đ±Đ»ĐžĐ·ŃŒŃ‚Đ”ŃŃ ĐŽĐŸ ĐœŃŒĐŸĐłĐŸ"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Đ©ĐŸĐ± ĐČіЮтĐČĐŸŃ€ĐžŃ‚Đž ĐœĐ° Ń†ŃŒĐŸĐŒŃƒ ĐżŃ€ĐžŃŃ‚Ń€ĐŸŃ—, ĐżĐ”Ń€Đ”ĐŒŃ–ŃŃ‚Ń–Ń‚ŃŒ ĐčĐŸĐłĐŸ блОжчД ĐŽĐŸ ĐżŃ€ĐžŃŃ‚Ń€ĐŸŃŽ \"<xliff:g id="DEVICENAME">%1$s</xliff:g>\""</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"ĐĄŃ‚Đ°Đ»Đ°ŃŃ ĐżĐŸĐŒĐžĐ»Đșа. ĐŸĐŸĐČŃ‚ĐŸŃ€Ń–Ń‚ŃŒ ŃĐżŃ€ĐŸĐ±Ńƒ."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"ЗаĐČĐ°ĐœŃ‚Đ°Đ¶Đ”ĐœĐœŃ"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ĐżĐ»Đ°ĐœŃˆĐ”Ń‚"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"ĐąŃ€Đ°ĐœŃĐ»ŃŃ†Ń–Ń ĐŒĐ”ĐŽŃ–Đ°ĐșĐŸĐœŃ‚Đ”ĐœŃ‚Ńƒ"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"ĐąŃ€Đ°ĐœŃĐ»ŃŃ†Ń–Ń ĐŽĐŸĐŽĐ°Ń‚Đșа <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ĐĐ”Đ°ĐșтоĐČĐœĐŸ, пДрДĐčЮіть у ĐŽĐŸĐŽĐ°Ń‚ĐŸĐș"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"ĐĐ” Đ·ĐœĐ°ĐčĐŽĐ”ĐœĐŸ"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Đ•Đ»Đ”ĐŒĐ”ĐœŃ‚ ĐșĐ”Ń€ŃƒĐČĐ°ĐœĐœŃ ĐœĐ”ĐŽĐŸŃŃ‚ŃƒĐżĐœĐžĐč"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"ĐŸĐŸĐŒĐžĐ»Đșа. ĐĄĐżŃ€ĐŸĐ±ŃƒĐčтД Đ·ĐœĐŸĐČу"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Đ”ĐŸĐŽĐ°Ń‚Đž Đ”Đ»Đ”ĐŒĐ”ĐœŃ‚Đž ĐșĐ”Ń€ŃƒĐČĐ°ĐœĐœŃ"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Đ—ĐŒŃ–ĐœĐžŃ‚Đž Đ”Đ»Đ”ĐŒĐ”ĐœŃ‚Đž ĐșĐ”Ń€ŃƒĐČĐ°ĐœĐœŃ"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Đ”ĐŸĐ»ŃƒŃ‡ĐžŃ‚Đž ĐŽĐŸĐŽĐ°Ń‚ĐŸĐș"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Đ”ĐŸĐŽĐ°Ń‚Đž ĐżŃ€ĐžŃŃ‚Ń€ĐŸŃ— ĐČĐžĐČĐŸĐŽŃƒ"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Група"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Đ’ĐžĐ±Ń€Đ°ĐœĐŸ 1 простріĐč"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ĐšĐŸĐ»ĐŸĐœĐșĐž Đč Đ”ĐșŃ€Đ°ĐœĐž"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"ĐŸŃ€ĐŸĐżĐŸĐœĐŸĐČĐ°ĐœŃ– ĐżŃ€ĐžŃŃ‚Ń€ĐŸŃ—"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"ĐŸĐŸŃ‚Ń€Ń–Đ±Đ”Đœ ĐżĐ»Đ°Ń‚ĐœĐžĐč ĐŸĐ±Đ»Ń–ĐșĐŸĐČĐžĐč запОс"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ĐŻĐș працює Ń‚Ń€Đ°ĐœŃĐ»ŃŃ†Ń–Ń"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"ĐąŃ€Đ°ĐœŃĐ»ŃŃ†Ń–Ń"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ЛюЮо ĐżĐŸĐ±Đ»ĐžĐ·Ńƒ, яĐșі ĐŒĐ°ŃŽŃ‚ŃŒ ŃŃƒĐŒŃ–ŃĐœŃ– ĐżŃ€ĐžŃŃ‚Ń€ĐŸŃ— Đ· Bluetooth, ĐŒĐŸĐ¶ŃƒŃ‚ŃŒ ŃĐ»ŃƒŃ…Đ°Ń‚Đž ĐŒĐ”ĐŽŃ–Đ°ĐșĐŸĐœŃ‚Đ”ĐœŃ‚, яĐșĐžĐč ĐČĐž Ń‚Ń€Đ°ĐœŃĐ»ŃŽŃ”Ń‚Đ”."</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"ĐĐ”ĐŒĐŸĐ¶Đ»ĐžĐČĐŸ Ń‚Ń€Đ°ĐœŃĐ»ŃŽĐČато"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"ĐĐ” ĐČĐŽĐ°Đ»ĐŸŃŃ збДрДгтО. ĐŸĐŸĐČŃ‚ĐŸŃ€Ń–Ń‚ŃŒ ŃĐżŃ€ĐŸĐ±Ńƒ."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"ĐĐ” ĐČĐŽĐ°Đ»ĐŸŃŃ збДрДгтО."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"ĐĐŸĐŒĐ”Ń€ сĐșĐ»Đ°ĐŽĐ°ĐœĐœŃ"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"ĐĐŸĐŒĐ”Ń€ сĐșĐ»Đ°ĐŽĐ°ĐœĐœŃ сĐșĐŸĐżŃ–ĐčĐŸĐČĐ°ĐœĐŸ ĐČ Đ±ŃƒŃ„Đ”Ń€ ĐŸĐ±ĐŒŃ–ĐœŃƒ."</string>
     <string name="basic_status" msgid="2315371112182658176">"ВіЮĐșрота Ń€ĐŸĐ·ĐŒĐŸĐČа"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• ĐŸŃ€ĐžĐœĐ°ĐčĐŒĐœŃ– ĐŸĐŽĐžĐœ простріĐč ĐŽĐŸŃŃ‚ŃƒĐżĐœĐžĐč"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"ĐĐ°Ń‚ĐžŃĐœŃ–Ń‚ŃŒ і ŃƒŃ‚Ń€ĐžĐŒŃƒĐčтД ŃŃ€Đ»ĐžĐș"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"ĐĄĐșасуĐČато"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"ĐŸĐ”Ń€Đ”ĐČĐ”Ń€ĐœŃƒŃ‚Đž"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Đ ĐŸĐ·ĐłĐŸŃ€ĐœŃ–Ń‚ŃŒ Ń‚Đ”Đ»Đ”Ń„ĐŸĐœ, Ń‰ĐŸĐ± Đ·Ń€ĐŸĐ±ĐžŃ‚Đž ĐșращД сДлфі"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"ĐŸĐ”Ń€Đ”ĐŒĐșĐœŃƒŃ‚Đž ĐœĐ° Ń„Ń€ĐŸĐœŃ‚Đ°Đ»ŃŒĐœŃƒ ĐșĐ°ĐŒĐ”Ń€Ńƒ ĐŽĐ»Ń ĐșŃ€Đ°Ń‰ĐŸĐłĐŸ сДлфі?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"ВоĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČуĐčтД ĐșĐ°ĐŒĐ”Ń€Ńƒ ĐœĐ° Đ·Đ°ĐŽĐœŃ–Đč ĐżĐ°ĐœĐ”Đ»Ń–, Ń‰ĐŸĐ± Đ·Ń€ĐŸĐ±ĐžŃ‚Đž Đ·ĐœŃ–ĐŒĐŸĐș Ń–Đ· ŃˆĐžŃ€ŃˆĐžĐŒ ĐșŃƒŃ‚ĐŸĐŒ і ĐČĐžŃ‰ĐŸŃŽ Ń€ĐŸĐ·ĐŽŃ–Đ»ŃŒĐœĐŸŃŽ Đ·ĐŽĐ°Ń‚ĐœŃ–ŃŃ‚ŃŽ."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ĐŠĐ”Đč Đ”ĐșŃ€Đ°Đœ ĐČĐžĐŒĐșĐœĐ”Ń‚ŃŒŃŃ"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Đ ĐŸĐ·ĐșĐ»Đ°ĐŽĐœĐžĐč простріĐč у Ń€ĐŸĐ·ĐșĐ»Đ°ĐŽĐ”ĐœĐŸĐŒŃƒ ŃŃ‚Đ°ĐœŃ–"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Đ ĐŸĐ·ĐșĐ»Đ°ĐŽĐœĐžĐč простріĐč ĐŸĐ±Đ”Ń€Ń‚Đ°Ń”Ń‚ŃŒŃŃ"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"ЗаряЮ аĐșŃƒĐŒŃƒĐ»ŃŃ‚ĐŸŃ€Đ°: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ПіЮĐșĐ»ŃŽŃ‡Ń–Ń‚ŃŒ ŃŃ‚ĐžĐ»ŃƒŃ ĐŽĐŸ Đ·Đ°Ń€ŃĐŽĐœĐŸĐłĐŸ ĐżŃ€ĐžŃŃ‚Ń€ĐŸŃŽ"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"ĐĐžĐ·ŃŒĐșĐžĐč Đ·Đ°Ń€ŃĐŽ аĐșŃƒĐŒŃƒĐ»ŃŃ‚ĐŸŃ€Đ° ŃŃ‚ĐžĐ»ŃƒŃĐ°"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Đ’Ń–ĐŽĐ”ĐŸĐșĐ°ĐŒĐ”Ń€Đ°"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"ĐĐ”ĐŒĐŸĐ¶Đ»ĐžĐČĐŸ Ń‚Đ”Đ»Đ”Ń„ĐŸĐœŃƒĐČато Đ· Ń†ŃŒĐŸĐłĐŸ ĐżŃ€ĐŸŃ„Ń–Đ»ŃŽ"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Đ’Ń–ĐŽĐżĐŸĐČŃ–ĐŽĐœĐŸ ĐŽĐŸ праĐČОл ĐŸŃ€ĐłĐ°ĐœŃ–Đ·Đ°Ń†Ń–Ń— ĐČĐž ĐŒĐŸĐ¶Đ”Ń‚Đ” Ń‚Đ”Đ»Đ”Ń„ĐŸĐœŃƒĐČато лОшД Đ· Ń€ĐŸĐ±ĐŸŃ‡ĐŸĐłĐŸ ĐżŃ€ĐŸŃ„Ń–Đ»ŃŽ"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"ĐŸĐ”Ń€Đ”Đčто ĐČ Ń€ĐŸĐ±ĐŸŃ‡ĐžĐč ĐżŃ€ĐŸŃ„Ń–Đ»ŃŒ"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"ЗаĐșрото"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"ĐŸĐ°Ń€Đ°ĐŒĐ”Ń‚Ń€Đž Đ·Đ°Đ±Đ»ĐŸĐșĐŸĐČĐ°ĐœĐŸĐłĐŸ Đ”ĐșŃ€Đ°ĐœĐ°"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-uk/tiles_states_strings.xml b/packages/SystemUI/res/values-uk/tiles_states_strings.xml
index 3f6ca46..a0cc1c1 100644
--- a/packages/SystemUI/res/values-uk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-uk/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Đ’ĐžĐŒĐșĐœĐ”ĐœĐŸ"</item>
     <item msgid="5966994759929723339">"ĐŁĐČŃ–ĐŒĐșĐœĐ”ĐœĐŸ"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index dde5323..089e4b4 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"ŰŻÙŰȘŰ±ÛŒ Ű§ŰłÚ©Ű±ÛŒÙ† ێۧÙčŰł کو <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>
     <string name="screenrecord_name" msgid="2596401223859996572">"Ű§ŰłÚ©Ű±ÛŒÙ† Ű±ÛŒÚ©Ű§Ű±ÚˆŰ±"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ŰłÚ©Ű±ÛŒÙ† Ű±ÛŒÚ©Ű§Ű±ÚˆÙ†ÚŻ ÙŸŰ±ÙˆŰłÛŒŰł ÛÙˆŰ±ÛÛŒ ہے"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ű§ŰłÚ©Ű±ÛŒÙ† Ű±ÛŒÚ©Ű§Ű±Úˆ ŰłÛŒŰŽÙ† Ú©ÛŒÙ„ŰŠÛ’ ŰŹŰ§Ű±ÛŒ Ű§Ű·Ù„Ű§Űč"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Ú†Ù…Ú©ÛŒÙ„Ű§ ٟن"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Ű±Ù†ÚŻÙˆÚș کی ŰȘÙ‚Ù„ÛŒŰš"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Ű±Ù†ÚŻ کی Ű§Ű”Ù„Ű§Ű­"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Ű”Ű§Ű±ÙÛŒÙ† کۧ Ù†ŰžÙ… Ú©Ű±ÛŒÚș"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"ہو ÚŻÛŒŰ§"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"ŰšÙ†ŰŻ Ú©Ű±ÛŒÚș"</string>
@@ -775,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"Ű§ŰłÚ©Ű±ÛŒÙ† Ű±ÛŒÚ©Ű§Ű±ÚˆÙ†ÚŻ"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Ú©ÙˆŰŠÛŒ ŰčÙ†ÙˆŰ§Ù† نہیÚș ہے"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"ۧ۳Ùčینڈ ŰšŰ§ŰŠÛŒ"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Ù…ÛŒÚŻÙ†ÛŒÙÚ©ÛŒŰŽÙ† ونڈو"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Ù…ÛŒÚŻÙ†ÛŒÙÚ©ÛŒŰŽÙ† ونڈو کنÙčŰ±ÙˆÙ„ŰČ"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ŰČوم Ű§Ù† Ú©Ű±ÛŒÚș"</string>
@@ -800,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"کنÙčŰ±ÙˆÙ„ŰČ ŰŽŰ§Ù…Ù„ Ú©Ű±Ù†Û’ کے لیے Ű§ÛŒÙŸ منŰȘ۟ۚ Ú©Ű±ÛŒÚș"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# کنÙčŰ±ÙˆÙ„ کو ŰŽŰ§Ù…Ù„ Ú©ÛŒŰ§ ÚŻÛŒŰ§Û”}other{# کنÙčŰ±ÙˆÙ„ŰČ Ú©Ùˆ ŰŽŰ§Ù…Ù„ Ú©ÛŒŰ§ ÚŻÛŒŰ§Û”}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"ہÙčۧ ŰŻÛŒŰ§ ÚŻÛŒŰ§"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g> کو ŰŽŰ§Ù…Ù„ Ú©Ű±ÛŒÚș۟"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"ŰŹŰš ŰąÙŸ <xliff:g id="APPNAME">%s</xliff:g> کو ŰŽŰ§Ù…Ù„ ک۱ŰȘے ہیÚșی ŰȘو یہ ۧ۳ ٟینل میÚș کنÙčŰ±ÙˆÙ„ŰČ Ű§ÙˆŰ± Ù…ÙˆŰ§ŰŻ کو ŰŽŰ§Ù…Ù„ ک۱ ŰłÚ©ŰȘۧ ہے۔ Ú©Ú†ÚŸ Ű§ÛŒÙŸŰł میÚșی ŰąÙŸ یہ منŰȘ۟ۚ ک۱ ŰłÚ©ŰȘے ہیÚș کہ کون ŰłÛ’ کنÙčŰ±ÙˆÙ„ŰČ ÛŒÛŰ§Úș ŰžŰ§ÛŰ± ہوÚș۔"</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"ÙŸŰłÙ†ŰŻ Ú©Ű±ŰŻÛ"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"ÙŸŰłÙ†ŰŻ Ú©Ű±ŰŻÛŰŒ ٟوŰČÛŒŰŽÙ† <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Ù†Ű§ÙŸŰłÙ†ŰŻ Ú©Ű±ŰŻÛ"</string>
@@ -850,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> کڟولیÚș"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> ŰłÛ’ <xliff:g id="ARTIST_NAME">%2$s</xliff:g> کۧ <xliff:g id="SONG_NAME">%1$s</xliff:g> Ú†Ù„Ű§ŰŠÛŒÚș"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> ŰłÛ’ <xliff:g id="SONG_NAME">%1$s</xliff:g> Ú†Ù„Ű§ŰŠÛŒÚș"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"ŰąÙŸ Ú©ÛŒÙ„ŰŠÛ’"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Ú©Ű§Ù„ŰčŰŻÙ… Ú©Ű±ÛŒÚș"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"‫<xliff:g id="DEVICENAME">%1$s</xliff:g> ÙŸŰ± Ú†Ù„Ű§Ù†Û’ کے لیے Ù‚Ű±ÛŒŰš Ú©Ű±ÛŒÚș"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"ÛŒÛŰ§Úș Ú†Ù„Ű§Ù†Û’ کے Ù„ÛŒÛ’ŰŒ <xliff:g id="DEVICENAME">%1$s</xliff:g> کے ŰČÛŒŰ§ŰŻÛ ŰŹŰ§ŰŠÛŒÚș"</string>
@@ -857,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Ú©Ú†ÚŸ ŰșÙ„Ű· ÛÙˆÚŻÛŒŰ§Û” ÙŸÚŸŰ± Ú©ÙˆŰŽŰŽ Ú©Ű±ÛŒÚș۔"</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"لوڈ ہو Ű±ÛŰ§ ہے"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ÙčÛŒŰšÙ„ÛŒÙč"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"ŰąÙŸ کۧ Ù…ÛŒÚˆÛŒŰ§ کۧ۳Ùč ہو Ű±ÛŰ§ ہے"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"<xliff:g id="APP_LABEL">%1$s</xliff:g> کۧ۳Ùč ہو Ű±ÛŰ§ ہے"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ŰșÛŒŰ± فŰčŰ§Ù„ŰŒ Ű§ÛŒÙŸ چیک Ú©Ű±ÛŒÚș"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"نہیÚș Ù…Ù„Ű§"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"کنÙčŰ±ÙˆÙ„ ŰŻŰłŰȘÛŒŰ§Űš نہیÚș ہے"</string>
@@ -866,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"ŰźŰ±Ű§ŰšÛŒŰŒ ŰŻÙˆŰšŰ§Ű±Û Ú©ÙˆŰŽŰŽ Ú©Ű±ÛŒÚș"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"کنÙčŰ±ÙˆÙ„ŰČ ŰŽŰ§Ù…Ù„ Ú©Ű±ÛŒÚș"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"کنÙčŰ±ÙˆÙ„ŰČ Ù…ÛŒÚș ŰȘŰ±Ù…ÛŒÙ… Ú©Ű±ÛŒÚș"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Ű§ÛŒÙŸ ŰŽŰ§Ù…Ù„ Ú©Ű±ÛŒÚș"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"۹ۀÙč ÙŸÙčŰł ŰŽŰ§Ù…Ù„ Ú©Ű±ÛŒÚș"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"ÚŻŰ±ÙˆÙŸ"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 ŰąÙ„Û منŰȘ۟ۚ Ú©ÛŒŰ§ ÚŻÛŒŰ§"</string>
@@ -881,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"%%<xliff:g id="PERCENTAGE">%1$d</xliff:g>"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Ű§ŰłÙŸÛŒÚ©Ű±ŰČ Ű§ÙˆŰ± ÚˆŰłÙŸÙ„ÛŒŰČ"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"ŰȘŰŹÙˆÛŒŰČ Ú©Ű±ŰŻÛ ŰąÙ„Ű§ŰȘ"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"ÙŸŰ±ÛŒÙ…ŰŠÛŒÙ… Ű§Ú©Ű§Ű€Ù†Ùč ۯ۱کۧ۱ ہے"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ۚ۱ۧڈکۧ۳ÙčÙ†ÚŻ Ú©ÛŒŰłÛ’ Ú©Ű§Ù… ک۱ŰȘۧ ہے"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"ۚ۱ۧڈکۧ۳Ùč"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Ù…ÙˆŰ§ÙÙ‚ ŰšÙ„ÙˆÙčوŰȘÚŸ ŰąÙ„Ű§ŰȘ کے ۳ۧŰȘÚŸ ŰąÙŸ کے Ù‚Ű±ÛŒŰšÛŒ Ù„ÙˆÚŻ ŰąÙŸ کے Ù†ŰŽŰ± Ú©Ű±ŰŻÛ Ù…ÛŒÚˆÛŒŰ§ کو ŰłÙ† ŰłÚ©ŰȘے ہیÚș"</string>
@@ -892,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"ۚ۱ۧڈکۧ۳Ùč نہیÚș Ú©ÛŒŰ§ ۏۧ ŰłÚ©ŰȘۧ"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Ù…Ű­ÙÙˆŰž نہیÚș Ú©ÛŒŰ§ ۏۧ ŰłÚ©Ű§Û” ÙŸÚŸŰ± Ú©ÙˆŰŽŰŽ Ú©Ű±ÛŒÚș۔"</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Ù…Ű­ÙÙˆŰž نہیÚș Ú©ÛŒŰ§ ۏۧ ŰłÚ©Ű§Û”"</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"ŰšÙ„Úˆ Ù†Ù…ŰšŰ±"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"ŰšÙ„Úˆ Ù†Ù…ŰšŰ± کلٟ ŰšÙˆŰ±Úˆ میÚș Ú©Ű§ÙŸÛŒ ہو ÚŻÛŒŰ§Û”"</string>
     <string name="basic_status" msgid="2315371112182658176">"ÚŻÙŰȘÚŻÙˆ کڟولیÚș"</string>
@@ -1011,11 +1032,16 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• کم ۧŰČ Ú©Ù… Ű§ÛŒÚ© ŰąÙ„Û ŰŻŰłŰȘÛŒŰ§Űš ہے"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"ێۧ۱Ùč Ú©Ùč Ùčچ Ú©Ű±ÛŒÚș Ű§ÙˆŰ± ŰŻŰšŰ§ŰŠÛ’ Ű±Ú©ÚŸÛŒÚș"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Ù…Ù†ŰłÙˆŰź Ú©Ű±ÛŒÚș"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"ۧۚ ٟلÙčŰ§ŰŠÛŒÚș"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"ŰšÛŰȘ۱ ŰłÛŒÙ„ÙÛŒ کے لیے فون کڟولیÚș"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"ŰšÛŰȘ۱ ŰłÛŒÙ„ÙÛŒ کے لیے ŰłŰ§Ù…Ù†Û’ ÙˆŰ§Ù„Û’ ÚˆŰłÙŸÙ„Û’ ÙŸŰ± ٟلÙčŰ§ŰŠÛŒÚș۟"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"ۧŰčلی Ű±ÛŒŰČÙˆÙ„ÛŒÙˆŰŽÙ† ÙˆŰ§Ù„ÛŒ ÙˆŰłÛŒŰč ŰȘŰ”ÙˆÛŒŰ± کے لیے ییچڟے ÙˆŰ§Ù„Ű§ Ú©ÛŒÙ…Ű±Ű§ ۧ۳ŰȘŰčÙ…Ű§Ù„ Ú©Ű±ÛŒÚș۔"</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ یہ Ű§ŰłÚ©Ű±ÛŒÙ† ŰąÙ ہو ŰŹŰ§ŰŠÛ’ ÚŻÛŒ"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"فولڈ ہونے ÙˆŰ§Ù„Û’ ŰąÙ„Û’ کو Ú©ÚŸÙˆÙ„Ű§ ۏۧ Ű±ÛŰ§ ہے"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"فولڈ ہونے ÙˆŰ§Ù„Û’ ŰąÙ„Û’ کو ÚŻÚŸÙ…Ű§ÛŒŰ§ ۏۧ Ű±ÛŰ§ ہے"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> ŰšÛŒÙčŰ±ÛŒ ŰšŰ§Ù‚ÛŒ ہے"</string>
@@ -1026,4 +1052,5 @@
     <string name="call_from_work_profile_text" msgid="3458704745640229638">"ŰąÙŸ کے Ú©Ű§Ù… ŰłÛ’ مŰȘŰčلق ÙŸŰ§Ù„ÛŒŰłÛŒ ŰąÙŸ کو Ű”Ű±Ù ŰŻÙŰȘŰ±ÛŒ ÙŸŰ±ÙˆÙŰ§ŰŠÙ„ ŰłÛ’ فون Ú©Ű§Ù„ŰČ Ú©Ű±Ù†Û’ کی ۧۏۧŰČŰȘ ŰŻÛŒŰȘی ہے"</string>
     <string name="call_from_work_profile_action" msgid="2937701298133010724">"ŰŻÙŰȘŰ±ÛŒ ÙŸŰ±ÙˆÙŰ§ŰŠÙ„ ÙŸŰ± ŰłÙˆŰŠÚ† Ú©Ű±ÛŒÚș"</string>
     <string name="call_from_work_profile_close" msgid="7927067108901068098">"ŰšÙ†ŰŻ Ú©Ű±ÛŒÚș"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"مقفل Ű§ŰłÚ©Ű±ÛŒÙ† کی ŰȘ۱ŰȘÛŒŰšŰ§ŰȘ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ur/tiles_states_strings.xml b/packages/SystemUI/res/values-ur/tiles_states_strings.xml
index 05aa4e9..5e2b073 100644
--- a/packages/SystemUI/res/values-ur/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ur/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"ŰąÙ"</item>
     <item msgid="5966994759929723339">"ŰąÙ†"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 5b60765..7df2aed 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Quyi chegara <xliff:g id="PERCENT">%1$d</xliff:g> foiz"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Chap chegara <xliff:g id="PERCENT">%1$d</xliff:g> foiz"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Oʻng chegara <xliff:g id="PERCENT">%1$d</xliff:g> foiz"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Ish skrinshotlari <xliff:g id="APP">%1$s</xliff:g> ilovasida saqlanadi"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Ish profilidagi <xliff:g id="APP">%1$s</xliff:g> ilovasiga saqlandi"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Fayllar"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"Bu skrinshotda <xliff:g id="APPNAME">%1$s</xliff:g> aniqlandi."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"Bu skrinshotda <xliff:g id="APPNAME">%1$s</xliff:g> va boshqa ochiq ilovalar aniqlandi"</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Ekrandan yozib olish"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekran yozib olinmoqda"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ekrandan yozib olish seansi uchun joriy bildirishnoma"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Yorqinlik"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Ranglarni akslantirish"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Ranglarni tuzatish"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Foydalanuvchilarni boshqarish"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Tayyor"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Yopish"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Avtomatik"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Tovush yoki tebranishsiz"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Tovush yoki tebranishsiz hamda suhbatlar ruknining pastida chiqadi"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Qurilma sozlamalari asosida jiringlashi yoki tebranishi mumkin"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Qurilma sozlamalari asosida jiringlashi yoki tebranishi mumkin. <xliff:g id="APP_NAME">%1$s</xliff:g> suhbatlari standart holatda bulutcha shaklida chiqadi."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Bu bildirishnoma jiringlashi yoki tebranishini hal qilsin"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Holati:&lt;/b&gt; Birlamchi darajaga chiqarildi"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Holati:&lt;/b&gt; Sokin darajaga tushirildi"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"ekranni yozuvi"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Nomsiz"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Kutib turing"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Kattalashtirish oynasi"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kattalashtirish oynasi sozlamalari"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Yaqinlashtirish"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Boshqaruv elementlarini kiritish uchun ilovani tanlang"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# ta boshqaruv elementi kiritildi.}other{# ta boshqaruv elementi kiritildi.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Olib tashlandi"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g> qoʻshilsinmi?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"<xliff:g id="APPNAME">%s</xliff:g> bu panelga boshqaruv elementlari va kontent qoʻshishi mumkin. Ayrim ilovalarda bu yerda qaysi elementlar chiqishini tanlashingiz mumkin."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Saralanganlarga kiritilgan"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Saralanganlarga kiritilgan, <xliff:g id="NUMBER">%d</xliff:g>-joy"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Saralanganlardan olib tashlangan"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> ilovasini ochish"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> ilovasida ijro etish: <xliff:g id="SONG_NAME">%1$s</xliff:g> – <xliff:g id="ARTIST_NAME">%2$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> ilovasida ijro etilmoqda: <xliff:g id="SONG_NAME">%1$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Siz uchun"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Qaytarish"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"<xliff:g id="DEVICENAME">%1$s</xliff:g> qurilmasida ijro qilish uchun unga yaqinlashing"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Shu yerda ijro qilish uchun <xliff:g id="DEVICENAME">%1$s</xliff:g>ga yaqinroq suring"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Xatolik yuz berdi. Qayta urining."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Yuklanmoqda"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"planshet"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Mediani translatsiya qilish"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Translatsiya qilinmoqda: <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Nofaol. Ilovani tekshiring"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Topilmadi"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Boshqarish imkonsiz"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Xato, qayta urining"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Element kiritish"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Elementlarni tahrirlash"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Ilova kiritish"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Chiquvchi qurilmani kiritish"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Guruh"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 ta qurilma tanlandi"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Karnaylar va displeylar"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Taklif qilingan qurilmalar"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Premium hisob talab etiladi"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Translatsiya qanday ishlaydi"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Translatsiya"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Atrofingizdagi mos Bluetooth qurilmasiga ega foydalanuvchilar siz translatsiya qilayotgan mediani tinglay olishadi"</string>
@@ -894,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Uzatilmadi"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Saqlanmadi. Qayta urining."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Saqlanmadi."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Parolga kamida 4 ta belgi kiriting."</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Kiritiladigan belgilar 16 tadan oshmasin"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Nashr raqami"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Nashr raqami vaqtinchalik xotiraga nusxalandi."</string>
     <string name="basic_status" msgid="2315371112182658176">"Suhbatni ochish"</string>
@@ -1013,24 +1030,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Kamida bitta qurilma mavjud"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Bosib turish yorligʻi"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Bekor qilish"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Hozir aylantirish"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Yaxshiroq selfi olish uchun telefonni yoying"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Old ekran sizga qaragan holda aylantirdingizmi?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Keng va yuqori tiniqlikdagi suratga olish uchun orqa kameradan foydalaning."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Bu ekran oʻchiriladi"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Buklanadigan qurilma ochilmoqda"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Buklanadigan qurilma aylantirilmoqda"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Batareya quvvati: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Stilusni quvvat manbaiga ulang"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Stilus batareyasi kam"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Bu profildan chaqiruv qilish imkonsiz"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Ishga oid siyosatingiz faqat ish profilidan telefon chaqiruvlarini amalga oshirish imkonini beradi"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Ish profiliga almashish"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Yopish"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Qulflangan ekran sozlamalari"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-uz/tiles_states_strings.xml b/packages/SystemUI/res/values-uz/tiles_states_strings.xml
index a84f769..6d55fc1 100644
--- a/packages/SystemUI/res/values-uz/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-uz/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Oʻchiq"</item>
     <item msgid="5966994759929723339">"Yoniq"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index d7ce218..caeb44c 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"CáșĄnh dưới cùng <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"CáșĄnh trái <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"CáșĄnh pháșŁi <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"áșąnh chỄp màn hình công việc Ä‘Æ°á»Łc lưu trong ứng dỄng <xliff:g id="APP">%1$s</xliff:g>"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Đã lưu vào <xliff:g id="APP">%1$s</xliff:g> trong hồ sÆĄ công việc"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Files"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> đã phát hiện tháș„y áșŁnh chỄp màn hình này."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> và các ứng dỄng đang mở khác đã phát hiện tháș„y áșŁnh chỄp màn hình này."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Trình ghi màn hình"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Đang xá»­ lý video ghi màn hình"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Thông báo đang diễn ra về phiên ghi màn hình"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Độ sáng"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"ĐáșŁo màu"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Chỉnh màu"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"QuáșŁn lý người dùng"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Xong"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Đóng"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Tự động"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Không phát âm thanh hoáș·c rung"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Không phát âm thanh hoáș·c rung và xuáș„t hiện phía dưới trong pháș§n cuộc trò chuyện"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Có thể đổ chuông hoáș·c rung tuỳ theo cháșż độ cài đáș·t trên thiáșżt bị"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Có thể đổ chuông hoáș·c rung tuỳ theo cháșż độ cài đáș·t trên thiáșżt bị. Theo máș·c định, các cuộc trò chuyện từ <xliff:g id="APP_NAME">%1$s</xliff:g> sáșœ hiển thị dưới dáșĄng bong bóng."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Cho phép hệ thống quyáșżt định xem thông báo này phát âm thanh hay rung"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;TráșĄng thái:&lt;/b&gt; Đã thay đổi thành Máș·c định"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;TráșĄng thái:&lt;/b&gt; Đã thay đổi thành Im láș·ng"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"ghi màn hình"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Không có tiêu đề"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Cháșż độ chờ"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Cá»­a sổ phóng to"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Các tùy chọn điều khiển cá»­a sổ phóng to"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Phóng to"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Chọn ứng dỄng để thêm các tùy chọn điều khiển"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Đã thêm # cháșż độ điều khiển.}other{Đã thêm # cháșż độ điều khiển.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Đã xóa"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Thêm <xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Khi báșĄn thêm <xliff:g id="APPNAME">%s</xliff:g>, ứng dỄng có thể bổ sung các cháșż độ điều khiển và nội dung vào báșŁng điều khiển này. Trong một số ứng dỄng, báșĄn có thể chọn cháșż độ điều khiển nào sáșœ hiển thị táșĄi đây."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"ÄÆ°á»Łc yêu thích"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"ÄÆ°á»Łc yêu thích, vị trí số <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Không Ä‘Æ°á»Łc yêu thích"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Mở <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Phát <xliff:g id="SONG_NAME">%1$s</xliff:g> cá»§a <xliff:g id="ARTIST_NAME">%2$s</xliff:g> trên <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Phát <xliff:g id="SONG_NAME">%1$s</xliff:g> trên <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Dành cho báșĄn"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Há»§y"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Đưa thiáșżt bị đáșżn gáș§n hÆĄn để phát trên <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Để phát ở đây, vui lòng láșĄi gáș§n <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Đã xáșŁy ra lỗi. Hãy thá»­ láșĄi."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Đang táșŁi"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"máy tính báșŁng"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Truyền nội dung đa phÆ°ÆĄng tiện"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Đang truyền <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Không hoáșĄt động, hãy kiểm tra ứng dỄng"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Không tìm tháș„y"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Không có chức năng điều khiển"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Lỗi, hãy thá»­ láșĄi"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Thêm các tùy chọn điều khiển"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Chỉnh sá»­a tùy chọn điều khiển"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Thêm ứng dỄng"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Thêm thiáșżt bị đáș§u ra"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Nhóm"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Đã chọn 1 thiáșżt bị"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Loa và màn hình"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Thiáșżt bị Ä‘Æ°á»Łc đề xuáș„t"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Yêu cáș§u tài khoáșŁn tráșŁ phí"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cách tính năng truyền hoáșĄt động"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Truyền"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Những người ở gáș§n có thiáșżt bị Bluetooth tÆ°ÆĄng thích có thể nghe nội dung nghe nhìn báșĄn đang truyền"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Không thể truyền"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Không lưu Ä‘Æ°á»Łc. Hãy thá»­ láșĄi."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Không lưu Ä‘Æ°á»Łc."</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Số báșŁn dá»±ng"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Đã sao chép số báșŁn dá»±ng vào báșŁng nhớ táșĄm."</string>
     <string name="basic_status" msgid="2315371112182658176">"Mở cuộc trò chuyện"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Có ít nháș„t một thiáșżt bị đang hoáșĄt động"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"CháșĄm và giữ phím táșŻt"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Huá»·"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Láș­t ngay"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Mở điện thoáșĄi ra để tá»± chỄp áșŁnh chân dung đáșčp hÆĄn"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Láș­t sang màn hình ngoài để tá»± chỄp áșŁnh chân dung đáșčp hÆĄn?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Sá»­ dỄng máy áșŁnh sau để chỄp áșŁnh góc rộng hÆĄn với độ phân giáșŁi cao hÆĄn."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Màn hình này sáșœ táșŻt"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Thiáșżt bị có thể gáș­p láșĄi đang Ä‘Æ°á»Łc mở ra"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Thiáșżt bị có thể gáș­p láșĄi đang Ä‘Æ°á»Łc láș­t ngÆ°á»Łc"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Còn <xliff:g id="PERCENTAGE">%s</xliff:g> pin"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Hãy káșżt nối bút cáșŁm ứng với bộ sáșĄc"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Bút cáșŁm ứng bị yáșżu pin"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Máy quay video"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Không thể gọi điện từ hồ sÆĄ này"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Chính sách cá»§a nÆĄi làm việc chỉ cho phép báșĄn gọi điện thoáșĄi từ hồ sÆĄ công việc"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Chuyển sang hồ sÆĄ công việc"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Đóng"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Cài đáș·t màn hình khoá"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-vi/tiles_states_strings.xml b/packages/SystemUI/res/values-vi/tiles_states_strings.xml
index 482a32f..5b816fb 100644
--- a/packages/SystemUI/res/values-vi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-vi/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"TáșŻt"</item>
     <item msgid="5966994759929723339">"Đang báș­t"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index a94c411..f2ddb99 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"ć·„äœœć±ć蕿ˆȘć›Ÿäżć­˜ćœš“<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>
     <string name="screenrecord_name" msgid="2596401223859996572">"ć±ćč•ćœ•ćˆ¶ć™š"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"æ­Łćœšć€„ç†ć±ćč•ćœ•ćˆ¶è§†éą‘"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"持续星ç€șć±ćč•ćœ•ćˆ¶äŒšèŻé€šçŸ„"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"äșźćșŠ"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"鱜è‰ČćèœŹ"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"è‰Čćœ©æ Ąæ­Ł"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"çźĄç†ç”šæˆ·"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"ćźŒæˆ"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"慳闭"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"è‡Ș抹"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"侍揑ć‡ș提ç€șéŸłïŒŒäčŸäžæŒŻćŠš"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"侍揑ć‡ș提ç€șéŸłïŒŒäčŸäžæŒŻćŠšïŒ›æ˜Ÿç€ș朹ćŻčèŻéƒšćˆ†çš„é äž‹äœçœź"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"ćŻèƒœäŒšć“é“ƒæˆ–æŒŻćŠšïŒŒć–ć†łäșŽèźŸć€‡èźŸçœź"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"ćŻèƒœäŒšć“é“ƒæˆ–æŒŻćŠšïŒŒć–ć†łäșŽèźŸć€‡èźŸçœźă€‚默èꀿƒ…憔䞋杄è‡Ș<xliff:g id="APP_NAME">%1$s</xliff:g>的ćŻčèŻäŒšä»„ćŻčèŻæłĄçš„ćœąćŒæ˜Ÿç€ș。"</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"èź©çł»ç»Ÿć†łćźšæ˜ŻćŠćș”èź©èźŸć€‡ćœšæ”¶ćˆ°æ­€é€šçŸ„æ—¶ć‘ć‡ș提ç€șéŸłæˆ–æŒŻćŠš"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;状态&lt;/b&gt;ć·Čæć‡äžș“é»˜èź€”"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;状态&lt;/b&gt;ć·Č降䜎äžș“静音”"</string>
@@ -652,7 +654,7 @@
     <string name="right_keycode" msgid="2480715509844798438">"搑揳锼码"</string>
     <string name="left_icon" msgid="5036278531966897006">"ć‘ć·Šć›Ÿæ ‡"</string>
     <string name="right_icon" msgid="1103955040645237425">"ć‘ćłć›Ÿæ ‡"</string>
-    <string name="drag_to_add_tiles" msgid="8933270127508303672">"按䜏ćč¶æ‹–ćŠšćłćŻæ·»ćŠ ć›Ÿć—"</string>
+    <string name="drag_to_add_tiles" msgid="8933270127508303672">"按䜏ćč¶æ‹–ćŠšćłćŻæ·»ćŠ ćŠŸèƒœć—"</string>
     <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"按䜏ćč¶æ‹–ćŠšćłćŻé‡æ–°æŽ’ćˆ—ć›Ÿć—"</string>
     <string name="drag_to_remove_tiles" msgid="4682194717573850385">"æ‹–ćŠšćˆ°æ­€ć€„ćłćŻç§»é™€"</string>
     <string name="drag_to_remove_disabled" msgid="933046987838658850">"æ‚šè‡łć°‘éœ€èŠ <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> äžȘ捡片"</string>
@@ -670,15 +672,15 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"星ç€șäœŽäŒ˜ć…ˆçș§çš„é€šçŸ„ć›Ÿæ ‡"</string>
     <string name="other" msgid="429768510980739978">"ć…¶ä»–"</string>
-    <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ç§»é™€ć›Ÿć—"</string>
-    <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ć°†ć›Ÿć—æ·»ćŠ ćˆ°æœ«ć°Ÿ"</string>
-    <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ç§»ćŠšć›Ÿć—"</string>
-    <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"æ·»ćŠ ć›Ÿć—"</string>
+    <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ç§»é™€ćŠŸèƒœć—"</string>
+    <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ć°†ćŠŸèƒœć—æ·»ćŠ ćˆ°æœ«ć°Ÿ"</string>
+    <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ç§»ćŠšćŠŸèƒœć—"</string>
+    <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"æ·»ćŠ ćŠŸèƒœć—"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ç§»è‡ł <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"æ·»ćŠ ćˆ°äœçœź <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"äœçœź <xliff:g id="POSITION">%1$d</xliff:g>"</string>
-    <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"ć·Čæ·»ćŠ ćĄç‰‡"</string>
-    <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"ć·Čç§»é™€ćĄç‰‡"</string>
+    <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"ć·Čæ·»ćŠ ćŠŸèƒœć—"</string>
+    <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"ć·Čç§»é™€ćŠŸèƒœć—"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ćż«æ·èźŸçœźçŒ–èŸ‘ć™šă€‚"</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>通矄<xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"æ‰“ćŒ€èźŸçœźă€‚"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"ć±ćč•ćœ•ćˆ¶"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"æ— æ ‡éą˜"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"ćŸ…æœș"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"æ”Ÿć€§çȘ—揣"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"æ”Ÿć€§çȘ—ćŁæŽ§ä»¶"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"æ”Ÿć€§"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"é€‰æ‹©èŠæ·»ćŠ æŽ§ćˆ¶ć™šçš„ćș”甚"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{ć·Čæ·»ćŠ  # äžȘæŽ§ä»¶ă€‚}other{ć·Čæ·»ćŠ  # äžȘæŽ§ä»¶ă€‚}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"ć·Č移陀"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"æ·»ćŠ “<xliff:g id="APPNAME">%s</xliff:g>”"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"ćœ“æ‚šæ·»ćŠ “<xliff:g id="APPNAME">%s</xliff:g>”ćŽïŒŒćźƒćŻä»„ć°†æŽ§ä»¶ć’Œć†…ćźčæ·»ćŠ ćˆ°æ­€éąæżă€‚ćœšæŸäș›ćș”ç”šäž­ïŒŒæ‚šćŻä»„é€‰æ‹©ćœšæ­€ć€„æ˜Ÿç€șć“Șäș›æŽ§ä»¶ă€‚"</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"ć·Č收藏"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"ć·Čæ”¶è—ïŒŒäœçœźïŒš<xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"ć·Čć–æ¶ˆæ”¶è—"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"æ‰“ćŒ€<xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"é€šèż‡<xliff:g id="APP_LABEL">%3$s</xliff:g>播攟<xliff:g id="ARTIST_NAME">%2$s</xliff:g>的《<xliff:g id="SONG_NAME">%1$s</xliff:g>》"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"é€šèż‡<xliff:g id="APP_LABEL">%2$s</xliff:g>æ’­æ”Ÿă€Š<xliff:g id="SONG_NAME">%1$s</xliff:g>》"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"äžș悚掚荐"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"撀消"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"è‹„èŠćœš“<xliff:g id="DEVICENAME">%1$s</xliff:g>”äžŠæ’­æ”ŸïŒŒèŻ·é èż‘èż™ć°èźŸć€‡"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"è‹„èŠćœšæ­€èźŸć€‡äžŠæ’­æ”ŸïŒŒèŻ·ć†é èż‘<xliff:g id="DEVICENAME">%1$s</xliff:g>侀ç‚č"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"ć‡șäș†ç‚čé—źéą˜ïŒŒèŻ·é‡èŻ•ă€‚"</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"æ­ŁćœšćŠ èœœ"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ćčłæżç””脑"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"投攟悚的ćȘ’䜓"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"投攟 <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"æ— æ•ˆïŒŒèŻ·æŁ€æŸ„ćș”甚"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"æœȘæ‰Ÿćˆ°"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"æŽ§ä»¶äžćŻç”š"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"ć‡șçŽ°é”™èŻŻïŒŒèŻ·é‡èŻ•"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"æ·»ćŠ æŽ§ćˆ¶ć™š"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"äżźæ”čæŽ§ćˆ¶ć™š"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"æ·»ćŠ ćș”甚"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"æ·»ćŠ èŸ“ć‡șèźŸć€‡"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"矀组"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"ć·Č选择 1 äžȘèźŸć€‡"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"éŸłçź±ć’Œæ˜Ÿç€șć±"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"ć»șèźźçš„èźŸć€‡"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"éœ€èŠäœżç”šä»˜èŽčćžć·"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ćčżæ’­çš„èżäœœæ–čćŒ"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"ćčżæ’­"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"é™„èż‘äœżç”šć…Œćźčè“ç‰™èźŸć€‡çš„ç”šæˆ·ćŻä»„æ”¶ćŹæ‚šćčżæ’­çš„ćȘ’äœ“ć†…ćźč"</string>
@@ -894,6 +909,10 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"æ— æł•ćčżæ’­"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"æ— æł•äżć­˜ïŒŒèŻ·é‡èŻ•ă€‚"</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"æ— æł•äżć­˜ă€‚"</string>
+    <!-- no translation found for media_output_broadcast_code_hint_no_less_than_min (4663836092607696185) -->
+    <skip />
+    <!-- no translation found for media_output_broadcast_code_hint_no_more_than_max (9181869364856175638) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"ç‰ˆæœŹć·"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"ć·Čć°†ç‰ˆæœŹć·ć€ćˆ¶ćˆ°ć‰ȘèŽŽæżă€‚"</string>
     <string name="basic_status" msgid="2315371112182658176">"ćŒ€æ”ŸćŒćŻčèŻ"</string>
@@ -960,9 +979,9 @@
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ćŠ‚èŠćˆ‡æąçœ‘ç»œïŒŒèŻ·æ–­ćŒ€ä»„ć€Șçœ‘èżžæŽ„"</string>
     <string name="wifi_scan_notify_message" msgid="3753839537448621794">"äžșäș†æć‡èźŸć€‡çš„äœżç”šäœ“éȘŒïŒŒćłäœż WLAN ć·Čć…łé—­ïŒŒćș”ç”šć’ŒæœćŠĄä»ćŻä»„随时扫描 WLAN çœ‘ç»œă€‚æ‚šćŻä»„ćœš WLAN æ‰«æèźŸçœźäž­æ›Žæ”čæ­€èźŸçœźă€‚"<annotation id="link">"曎æ”č"</annotation></string>
     <string name="turn_off_airplane_mode" msgid="8425587763226548579">"ć…łé—­éŁžèĄŒæšĄćŒ"</string>
-    <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"“<xliff:g id="APPNAME">%1$s</xliff:g>”ćžŒæœ›ć°†ä»„äž‹ć›Ÿć—æ·»ćŠ ćˆ°“ćż«æ·èźŸçœź”"</string>
-    <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"æ·»ćŠ ć›Ÿć—"</string>
-    <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"äžæ·»ćŠ ć›Ÿć—"</string>
+    <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"“<xliff:g id="APPNAME">%1$s</xliff:g>”ćžŒæœ›ć°†ä»„äž‹ćŠŸèƒœć—æ·»ćŠ ćˆ°“ćż«æ·èźŸçœź”"</string>
+    <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"æ·»ćŠ ćŠŸèƒœć—"</string>
+    <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"äžæ·»ćŠ ćŠŸèƒœć—"</string>
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"选择甚户"</string>
     <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# äžȘćș”甚怄äșŽæŽ»ćŠšçŠ¶æ€}other{# äžȘćș”甚怄äșŽæŽ»ćŠšçŠ¶æ€}}"</string>
     <string name="fgs_dot_content_description" msgid="2865071539464777240">"æ–°äżĄæŻ"</string>
@@ -1013,24 +1032,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• è‡łć°‘æœ‰äž€ć°èźŸć€‡ćŻç”š"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"蜻觊ćč¶æŒ‰äœćż«æ·æ–čćŒ"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"ć–æ¶ˆ"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"ç«‹ćłçż»èœŹ"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"ć±•ćŒ€æ‰‹æœșćŻæ‹ć‡șæ›Žć„œçš„è‡Ș拍照"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"èŠçż»èœŹćˆ°ć€–ć±ä»„æ‹ć‡șæ›Žć„œçš„è‡Șæ‹ç…§ć—ïŒŸ"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"æ‚šćŻä»„äœżç”šćŽçœźæ‘„ćƒć€Žæ‹æ‘„è§†è§’æ›Žćčżă€ćˆ†èŸšçŽ‡æ›Žé«˜çš„ç…§ç‰‡ă€‚"</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ æ­€ć±ćč•ć°†äŒšć…łé—­"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"æ­Łćœšć±•ćŒ€ćŻæŠ˜ć èźŸć€‡"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"æ­Łćœšçż»èœŹćŻæŠ˜ć èźŸć€‡"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"ç””æ± èż˜ć‰© <xliff:g id="PERCENTAGE">%s</xliff:g> 的甔量"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"èŻ·ć°†è§ŠæŽ§çŹ”èżžæŽ„ć……ç””ć™š"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"è§ŠæŽ§çŹ”ç””æ± ç””é‡äœŽ"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"æ‘„ćƒæœș"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"æ— æł•é€šèż‡èż™ä»œè”„æ–™æ‹šæ‰“ç””èŻ"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"æ čæźæ‚šçš„ć·„äœœæ”żç­–ïŒŒæ‚šćȘèƒœé€šèż‡ć·„äœœè”„æ–™æ‹šæ‰“ç””èŻ"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"ćˆ‡æąćˆ°ć·„äœœè”„æ–™"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"慳闭"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"é”ć±èźŸçœź"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
index 6ce948d..5d7d02f 100644
--- a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"慳闭"</item>
     <item msgid="5966994759929723339">"ć·ČćŒ€ćŻ"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index c0fb5b1..1898f8c 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"ć·„äœœçš„èžąć蕿ˆȘćœ–æœƒć„Č歘朹「<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>
     <string name="screenrecord_name" msgid="2596401223859996572">"èžąćč•ç•«éąéŒ„ćœ±ć·„ć…·"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"æ­Łćœšè™•ç†èžąćč•éŒ„ćœ±ć…§ćźč"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"持çșŒéĄŻç€șéŒ„ćœ±ç•«éąć·„äœœéšŽæź”é€šçŸ„"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"äșźćșŠ"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"è‰Čćœ©ćèœ‰"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"è‰Čćœ©æ Ąæ­Ł"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"çźĄç†äœżç”šè€…"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"ćźŒæˆ"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"關閉"</string>
@@ -532,8 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"è‡Ș拕"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"ç„ĄéŸłæ•ˆæˆ–éœ‡ć‹•"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"ç„ĄéŸłæ•ˆæˆ–éœ‡ć‹•ïŒŒäžŠćœšć°è©±éƒšćˆ†çš„èŒƒäœŽäœçœźéĄŻç€ș"</string>
-    <string name="notification_channel_summary_default" msgid="777294388712200605">"æ čæ“šèŁçœźçš„èš­ćźšéŸżéˆŽæˆ–éœ‡ć‹•"</string>
-    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"æ čæ“šèŁçœźçš„èš­ćźšéŸżéˆŽæˆ–éœ‡ć‹•ă€‚æ č據預蚭䟆è‡Ș「<xliff:g id="APP_NAME">%1$s</xliff:g>ă€çš„ć°è©±æœƒä»„ć°è©±æĄ†ćœąćŒéĄŻç€ș。"</string>
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"ćŻèƒœæœƒæ čæ“šèŁçœźèš­ćźšç™Œć‡ș鈎èČæˆ–éœ‡ć‹•"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"ćŻèƒœæœƒæ čæ“šèŁçœźèš­ćźšç™Œć‡ș鈎èČæˆ–éœ‡ć‹•ă€‚æ č據預蚭䟆è‡Ș <xliff:g id="APP_NAME">%1$s</xliff:g> çš„ć°è©±æœƒä»„ć°è©±æ°ŁæłĄéĄŻç€ș。"</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ç”±çł»ç”±ćˆ€æ–·æ˜ŻćŠèŠèź“æ­€é€šçŸ„ç™Œć‡șéŸłæ•ˆæˆ–éœ‡ć‹•"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;狀態&lt;/b&gt;ć·Čæć‡ç‚ș預蚭"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;狀態&lt;/b&gt;ć·Č降䜎ç‚ș靜音"</string>
@@ -775,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"éŒ„èŁœèžąćč•ç•«éą"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"ç„Ąæš™éĄŒ"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"ćŸ…æ©Ÿ"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"æ”Ÿć€§èŠ–çȘ—"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"æ”Ÿć€§èŠ–çȘ—æŽ§ćˆ¶é …"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"æ”Ÿć€§"</string>
@@ -800,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"éžæ“‡èŠæ–°ćąžæŽ§ćˆ¶é …çš„æ‡‰ç”šçš‹ćŒ"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{ć·Čæ–°ćąž # ć€‹æŽ§ćˆ¶é …ă€‚}other{ć·Čæ–°ćąž # ć€‹æŽ§ćˆ¶é …ă€‚}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"ć·Č移陀"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"èŠæ–°ćąžă€Œ<xliff:g id="APPNAME">%s</xliff:g>ă€ć—ŽïŒŸ"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"ćŠ‚æžœæ‚šæ–°ćąžă€Œ<xliff:g id="APPNAME">%s</xliff:g>ă€ïŒŒæ‡‰ç”šçš‹ćŒćŻć°‡æŽ§ćˆ¶é …ć’Œć…§ćźčæ–°ćąžè‡łæ­€éąæżă€‚éƒšä»œæ‡‰ç”šçš‹ćŒćŻèź“æ‚šéžæ“‡èŠćœšé€™èŁĄéĄŻç€șçš„æŽ§ćˆ¶é …ă€‚"</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"ć·ČćŠ ć…„æ”¶è—"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"ć·ČćŠ ć…„è‡łæ”¶è—äœçœź <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"ć·Čć–æ¶ˆæ”¶è—"</string>
@@ -850,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"開敟 <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"朹 <xliff:g id="APP_LABEL">%3$s</xliff:g> 播攟 <xliff:g id="ARTIST_NAME">%2$s</xliff:g> 的《<xliff:g id="SONG_NAME">%1$s</xliff:g>》"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"朹 <xliff:g id="APP_LABEL">%2$s</xliff:g> æ’­æ”Ÿă€Š<xliff:g id="SONG_NAME">%1$s</xliff:g>》"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"ç‚ș悚掚薊"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"ćŸ©ćŽŸ"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"ćŠ‚èŠćœšă€Œ<xliff:g id="DEVICENAME">%1$s</xliff:g>ă€äžŠæ’­æ”ŸïŒŒè«‹é èż‘äž€é»ž"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"ćŠ‚èŠćœšé€™éƒšèŁçœźæ’­æ”ŸïŒŒè«‹é èż‘ă€Œ<xliff:g id="DEVICENAME">%1$s</xliff:g>ă€äž€é»ž"</string>
@@ -857,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"ç™Œç”ŸéŒŻèȘ€ïŒŒè«‹ć†è©Šäž€æŹĄă€‚"</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"æ­ŁćœšèŒ‰ć…„"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ćčłæżé›»è…Š"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"投攟ćȘ’é«”"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"投攟 <xliff:g id="APP_LABEL">%1$s</xliff:g> 慧ćźč"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ć·Čćœç”šïŒŒè«‹æȘ࿟„æ‡‰ç”šçš‹ćŒ"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"æ‰Ÿäžćˆ°"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"ç„Ąæł•äœżç”šæŽ§ćˆ¶ćŠŸèƒœ"</string>
@@ -866,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"ç™Œç”ŸéŒŻèȘ€ïŒŒè«‹é‡è©Š"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"æ–°ćąžæŽ§ćˆ¶é …"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"ç·šèŒŻæŽ§ćˆ¶é …"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"æ–°ćąžæ‡‰ç”šçš‹ćŒ"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"æ–°ćąžèŒžć‡șèŁçœź"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"矀甄"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"ć·Č遾揖 1 éƒšèŁçœź"</string>
@@ -881,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ć–‡ć­ć’Œèžąćč•"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"ć»șè­°çš„èŁçœź"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"需芁付èČ»ćžłæˆ¶"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ć»Łæ’­é‹äœœæ–čćŒ"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"ć»Łæ’­"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"é™„èż‘æœ‰ć…Œćźčè—ç‰™èŁçœźçš„äșșćŻæ”¶èœæ‚šæ­Łćœšć»Łæ’­çš„ćȘ’體慧ćźč"</string>
@@ -892,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"ç„Ąæł•ć»Łæ’­"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"ç„Ąæł•ć„Čć­˜ïŒŒè«‹ć†è©Šäž€æŹĄă€‚"</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"ç„Ąæł•ć„Č歘。"</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"è‡łć°‘èŠæœ‰ 4 ć€‹ćŠćœąć­—ć…ƒ"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"äžćŸ—è¶…éŽ 16 ć€‹ćŠćœąć­—ć…ƒ"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"ç‰ˆæœŹè™ŸçąŒ"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"ç‰ˆæœŹè™ŸçąŒć·Čè€‡èŁœćˆ°ć‰ȘèČŒç°żă€‚"</string>
     <string name="basic_status" msgid="2315371112182658176">"é–‹ć•Ÿć°è©±"</string>
@@ -1011,19 +1030,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• è‡łć°‘äž€éƒšèŁçœźćŻç”š"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"èŒ•è§žäžŠæŒ‰äœćż«é€Ÿé”"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"ć–æ¶ˆ"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"ç«‹ćłçż»èœ‰"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"æ‰“é–‹æ‰‹æ©ŸïŒŒćłćŻæ‹æ”æ›Žć‡șè‰Č的è‡Ș拍"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"èŠçż»èœ‰è‡łć‰æ–čèžąć蕿‹æ”æ›Žć‡șè‰Č的è‡Șæ‹ć—ŽïŒŸ"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"äœżç”šćŸŒçœźéĄé ­ïŒŒæ‹æ”æ›Žć»Łè§’ă€è§ŁćƒćșŠæ›Žé«˜çš„盞片。"</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ æ­€èžąćč•ć°‡é—œé–‰"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"æ­Łćœšć±•é–‹æŠ˜ç–ŠćŒèŁçœź"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"æ­Łćœšçż»èœ‰æŠ˜ç–ŠćŒèŁçœź"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"ć‰©é€˜é›»é‡ïŒš<xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ć°‡è§žæŽ§ç­†é€ŁæŽ„ć……é›»ć™š"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"è§žæŽ§ç­†é›»é‡äžè¶ł"</string>
     <string name="video_camera" msgid="7654002575156149298">"æ”ćœ±æ©Ÿ"</string>
-    <string name="call_from_work_profile_title" msgid="6991157106804289643">"ç„Ąæł•é€éŽé€™ć€‹èł‡æ–™ć€Ÿæ’„æ‰“é›»è©±"</string>
-    <string name="call_from_work_profile_text" msgid="3458704745640229638">"èČŽć…Źćžæ”żç­–ćƒ…ć…èš±é€éŽć·„äœœèł‡æ–™ć€Ÿæ’„æ‰“é›»è©±"</string>
-    <string name="call_from_work_profile_action" msgid="2937701298133010724">"ćˆ‡æ›è‡łć·„äœœèł‡æ–™ć€Ÿ"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"ç„Ąæł•é€éŽæ­€èš­ćźšæȘ”æ’„打電話"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"æ‚šçš„ć…Źćžæ”żç­–ćȘć…èš±é€éŽć·„äœœèš­ćźšæȘ”æ’„打電話"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"ćˆ‡æ›è‡łć·„äœœèš­ćźšæȘ”"</string>
     <string name="call_from_work_profile_close" msgid="7927067108901068098">"關閉"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"äžŠéŽ–ç•«éąèš­ćźš"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
index ab8e961a..2e51f92 100644
--- a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"ć·Č關閉"</item>
     <item msgid="5966994759929723339">"ć·Č開敟"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 48dc5bf..a129650 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -91,8 +91,10 @@
     <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="2812417845875653929">"ć·„äœœçš„èžąć蕿ˆȘćœ–æœƒć„Č歘朹「<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>
     <string name="screenrecord_name" msgid="2596401223859996572">"èžąćč•éŒ„ćœ±ć™š"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"è™•ç†èžąćč•éŒ„ćœ±ć…§ćźč"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"持çșŒéĄŻç€șèžąćč•ç•«éąéŒ„èŁœć·„äœœéšŽæź”é€šçŸ„"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"äșźćșŠ"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"è‰Čćœ©ćèœ‰"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"è‰Čćœ©æ Ąæ­Ł"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"çźĄç†äœżç”šè€…"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"ćźŒæˆ"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"關閉"</string>
@@ -775,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"éŒ„èŁœèžąćč•ç•«éą"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"ç„Ąæš™éĄŒ"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"ćŸ…æ©Ÿ"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"æ”Ÿć€§èŠ–çȘ—"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"æ”Ÿć€§èŠ–çȘ—æŽ§ćˆ¶é …"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"æ”Ÿć€§"</string>
@@ -800,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"éžæ“‡æ‡‰ç”šçš‹ćŒä»„æ–°ćąžæŽ§ćˆ¶é …"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{ć·Čæ–°ćąž # ć€‹æŽ§ćˆ¶é …ă€‚}other{ć·Čæ–°ćąž # ć€‹æŽ§ćˆ¶é …ă€‚}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"ć·Č移陀"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"èŠæ–°ćąžă€Œ<xliff:g id="APPNAME">%s</xliff:g>ă€ć—ŽïŒŸ"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"ç•¶äœ æ–°ćąžă€Œ<xliff:g id="APPNAME">%s</xliff:g>ă€æ™‚ïŒŒæ‡‰ç”šçš‹ćŒäčŸćŻć°‡æŽ§ćˆ¶éžé …ćŠć…§ćźčæ–°ćąžćˆ°é€™ć€‹éąæżă€‚æŸäș›æ‡‰ç”šçš‹ćŒćŻèź“äœ éžæ“‡èŠéĄŻç€șćœšé€™èŁĄçš„æŽ§ćˆ¶éžé …ă€‚"</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"ć·ČćŠ ć…„æ”¶è—"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"ć·ČćŠ ć…„æ”¶è—ïŒŒäœçœź <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"ćŸžæ”¶è—äž­ç§»é™€"</string>
@@ -850,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"開敟「<xliff:g id="APP_LABEL">%1$s</xliff:g>」"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"透過「<xliff:g id="APP_LABEL">%3$s</xliff:g>ă€æ’­æ”Ÿ<xliff:g id="ARTIST_NAME">%2$s</xliff:g>的〈<xliff:g id="SONG_NAME">%1$s</xliff:g>〉"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"透過「<xliff:g id="APP_LABEL">%2$s</xliff:g>ă€æ’­æ”Ÿă€ˆ<xliff:g id="SONG_NAME">%1$s</xliff:g>〉"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"ç‚ș䜠掚薊"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"ćŸ©ćŽŸ"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"ćŠ‚èŠćœšă€Œ<xliff:g id="DEVICENAME">%1$s</xliff:g>ă€äžŠæ’­æ”ŸïŒŒè«‹é èż‘äž€é»ž"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"ćŠ‚èŠćœšé€™éƒšèŁçœźæ’­æ”ŸïŒŒè«‹ç§»ćˆ°æ›Žé èż‘ă€Œ<xliff:g id="DEVICENAME">%1$s</xliff:g>ă€çš„äœçœź"</string>
@@ -857,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"ç™Œç”ŸéŒŻèȘ€ïŒŒè«‹ć†è©Šäž€æŹĄă€‚"</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"èŒ‰ć…„äž­"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ćčłæżé›»è…Š"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"投攟ćȘ’é«”"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"æŠ•æ”Ÿă€Œ<xliff:g id="APP_LABEL">%1$s</xliff:g>」的慧ćźč"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"ç„Ąæ•ˆïŒŒè«‹æŸ„çœ‹æ‡‰ç”šçš‹ćŒ"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"æ‰Ÿäžćˆ°æŽ§ćˆ¶é …"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"ç„Ąæł•äœżç”šæŽ§ćˆ¶é …"</string>
@@ -866,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"ç™Œç”ŸéŒŻèȘ€ïŒŒè«‹ć†è©Šäž€æŹĄ"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"æ–°ćąžæŽ§ćˆ¶é …"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"ç·šèŒŻæŽ§ćˆ¶é …"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"æ–°ćąžæ‡‰ç”šçš‹ćŒ"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"æ–°ćąžèŒžć‡șèŁçœź"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"矀甄"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"ć·Č遾揖 1 éƒšèŁçœź"</string>
@@ -881,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ć–‡ć­ć’Œèžąćč•"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"ć»șè­°çš„èŁçœź"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"ćż…é ˆæœ‰ä»˜èČ»ćžłæˆ¶"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ć»Łæ’­ćŠŸèƒœçš„é‹äœœæ–čćŒ"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"ć»Łæ’­"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"ćŠ‚æžœé™„èż‘çš„äșș有盞ćźčçš„è—ç‰™èŁçœźïŒŒć°±ćŻä»„èœćˆ°äœ æ­Łćœšć»Łæ’­çš„ćȘ’體慧ćźč"</string>
@@ -892,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"ç„Ąæł•ć»Łæ’­"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"ç„Ąæł•ć„Čć­˜ïŒŒè«‹ć†è©Šäž€æŹĄă€‚"</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"ç„Ąæł•ć„Č歘。"</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"è‡łć°‘èŠæœ‰ 4 ć€‹ćŠćœąć­—ć…ƒ"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"äžćŸ—è¶…éŽ 16 ć€‹ćŠćœąć­—ć…ƒ"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"ç‰ˆæœŹè™ŸçąŒ"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"ć·Čć°‡ç‰ˆæœŹè™ŸçąŒè€‡èŁœćˆ°ć‰ȘèČŒç°żă€‚"</string>
     <string name="basic_status" msgid="2315371112182658176">"é–‹æ”ŸćŒć°è©±"</string>
@@ -1011,11 +1030,16 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• è‡łć°‘èŠæœ‰äž€éƒšćŻç”šèŁçœź"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"æŒ‰äœćż«é€Ÿé”"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"ć–æ¶ˆ"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"ç«‹ćłçż»èœ‰"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"打開手機è‡Șæ‹æ•ˆæžœèŒƒäœł"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"èŠçż»èœ‰ćˆ°ć‰èžąć蕿‹æ”æ›Žć„ȘèłȘ的è‡Șæ‹ç…§ć—ŽïŒŸ"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"äœżç”šćŸŒçœźéĄé ­ćŻæ‹æ”èŠ–è§’èŒƒćŻŹć»Łă€è§ŁæžćșŠèŒƒé«˜çš„盞片。"</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ 這éșŒćšæœƒé—œé–‰é€™ć€‹èžąćč•"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"æ­Łćœšć±•é–‹çš„æŠ˜ç–ŠćŒèŁçœź"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"æ­Łćœšçż»èœ‰æŠ˜ç–ŠćŒèŁçœź"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"ć‰©é€˜é›»é‡ïŒš<xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
@@ -1026,4 +1050,5 @@
     <string name="call_from_work_profile_text" msgid="3458704745640229638">"èČŽć…Źćžæ”żç­–ćƒ…ć…èš±é€éŽć·„äœœèł‡æ–™ć€Ÿæ’„æ‰“é›»è©±"</string>
     <string name="call_from_work_profile_action" msgid="2937701298133010724">"ćˆ‡æ›è‡łć·„äœœèł‡æ–™ć€Ÿ"</string>
     <string name="call_from_work_profile_close" msgid="7927067108901068098">"關閉"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"èžąćč•éŽ–ćźšèš­ćźš"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
index 3d6a546..f1f0185 100644
--- a/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"ć·Č關閉"</item>
     <item msgid="5966994759929723339">"ć·Č開敟"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 88c00a8..16f184a 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -91,8 +91,10 @@
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Iphesenti elingu-<xliff:g id="PERCENT">%1$d</xliff:g> lomngcele ophansi"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Iphesenti elingu-<xliff:g id="PERCENT">%1$d</xliff:g> lomngcele ongakwesobunxele"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Iphesenti elingu-<xliff:g id="PERCENT">%1$d</xliff:g> lomngcele ongakwesokudla"</string>
-    <string name="screenshot_work_profile_notification" msgid="2812417845875653929">"Izithombe-skrini zomsebenzi zigcinwa ku-app ye-<xliff:g id="APP">%1$s</xliff:g>"</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Kulondolozwe ku-<xliff:g id="APP">%1$s</xliff:g> kuphrofayela yomsebenzi"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Amafayela"</string>
+    <string name="screenshot_detected_template" msgid="7940376642921719915">"I-<xliff:g id="APPNAME">%1$s</xliff:g> ithole lesi sithombe-skrini."</string>
+    <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"I-<xliff:g id="APPNAME">%1$s</xliff:g> namanye ama-app avuliwe athole lesi sithombe-skrini."</string>
     <string name="screenrecord_name" msgid="2596401223859996572">"Irekhoda yesikrini"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Icubungula okokuqopha iskrini"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Isaziso esiqhubekayo seseshini yokurekhoda isikrini"</string>
@@ -255,6 +257,8 @@
     <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Ukugqama"</string>
     <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Ukuguqulwa kombala"</string>
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Ukulungiswa kombala"</string>
+    <!-- no translation found for quick_settings_font_scaling_label (5289001009876936768) -->
+    <skip />
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Phatha abasebenzisi"</string>
     <string name="quick_settings_done" msgid="2163641301648855793">"Kwenziwe"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Vala"</string>
@@ -532,10 +536,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Okuzenzekelayo"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Awukho umsindo noma ukudlidliza"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Awukho umsindo noma ukudlidliza futhi ivela ngezansi esigabeni sengxoxo"</string>
-    <!-- no translation found for notification_channel_summary_default (777294388712200605) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_default_with_bubbles (3482483084451555344) -->
-    <skip />
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Ingase ikhale noma idlidlize ngokusekelwe kumasethingi edivayisi"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Ingase ikhale noma idlidlize kuya ngamasethingi wedivayisi. Izingxoxo ezivela ku-<xliff:g id="APP_NAME">%1$s</xliff:g> ziba yibhamuza ngokuzenzakalela."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Vumela isistimu inqume uma lesi saziso kufanele senze umsindo noma sidlidlize"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Isimo:&lt;/b&gt; Siphromothelwe Kokuzenzakalelayo"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Isimo:&lt;/b&gt; Sehliselwe Kokuthulile"</string>
@@ -777,6 +779,12 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"ukurekhoda isikrini"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Asikho isihloko"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Ilindile"</string>
+    <!-- no translation found for font_scaling_dialog_title (6273107303850248375) -->
+    <skip />
+    <!-- no translation found for font_scaling_smaller (1012032217622008232) -->
+    <skip />
+    <!-- no translation found for font_scaling_larger (5476242157436806760) -->
+    <skip />
     <string name="magnification_window_title" msgid="4863914360847258333">"Iwindi Lesikhulisi"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Izilawuli Zewindi Lesikhulisi"</string>
     <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Sondeza"</string>
@@ -802,6 +810,8 @@
     <string name="controls_providers_title" msgid="6879775889857085056">"Khetha uhlelo lokusebenza ukwengeza izilawuli"</string>
     <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{ulawulo olu-# olwengeziwe.}one{ukulawulwa okungu-# okwengeziwe.}other{ukulawulwa okungu-# okwengeziwe.}}"</string>
     <string name="controls_removed" msgid="3731789252222856959">"Isusiwe"</string>
+    <string name="controls_panel_authorization_title" msgid="267429338785864842">"Engeza i-<xliff:g id="APPNAME">%s</xliff:g>?"</string>
+    <string name="controls_panel_authorization" msgid="4540047176861801815">"Uma wengeza i-<xliff:g id="APPNAME">%s</xliff:g>, ingangeza izilawuli nokuqukethwe kuleli phaneli. Kwamanye ama-app, ungakhetha ukuthi yiziphi izilawuli eziboniswa lapha."</string>
     <string name="accessibility_control_favorite" msgid="8694362691985545985">"Kwenziwe intandokazi"</string>
     <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Kwenziwe intandokazi, isimo esiyi-<xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Akwenziwanga intandokazi"</string>
@@ -852,6 +862,7 @@
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Vula i-<xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Dlala i-<xliff:g id="SONG_NAME">%1$s</xliff:g> ka-<xliff:g id="ARTIST_NAME">%2$s</xliff:g> kusuka ku-<xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Dlala i-<xliff:g id="SONG_NAME">%1$s</xliff:g> kusuka ku-<xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+    <string name="controls_media_smartspace_rec_header" msgid="5053461390357112834">"Okwenzelwe wena"</string>
     <string name="media_transfer_undo" msgid="1895606387620728736">"Hlehlisa"</string>
     <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Sondeza eduze ukudlala ku-<xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
     <string name="media_move_closer_to_end_cast" msgid="7302555909119374738">"Ukuze udlale lapha, sondela ku-<xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
@@ -859,6 +870,8 @@
     <string name="media_transfer_failed" msgid="7955354964610603723">"Kukhona okungahambanga kahle. Zama futhi."</string>
     <string name="media_transfer_loading" msgid="5544017127027152422">"Iyalayisha"</string>
     <string name="media_ttt_default_device_type" msgid="4457646436153370169">"ithebulethi"</string>
+    <string name="media_transfer_receiver_content_description_unknown_app" msgid="7381771464846263667">"Isakaza imidiya yakho"</string>
+    <string name="media_transfer_receiver_content_description_with_app_name" msgid="8555975056850659389">"Isakaza i-<xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_error_timeout" msgid="794197289772728958">"Akusebenzi, hlola uhlelo lokusebenza"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"Ayitholakali"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"Ukulawula akutholakali"</string>
@@ -868,6 +881,7 @@
     <string name="controls_error_failed" msgid="960228639198558525">"Iphutha, zama futhi"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"Engeza Izilawuli"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"Hlela izilawuli"</string>
+    <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Engeza i-app"</string>
     <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Engeza okukhiphayo"</string>
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Iqembu"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"idivayisi ekhethiwe e-1"</string>
@@ -883,6 +897,7 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Izipikha Neziboniso"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Amadivayisi Aphakanyisiwe"</string>
+    <string name="media_output_status_require_premium" msgid="5691200962588753380">"Idinga i-akhawunti ye-premium"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Indlela ukusakaza okusebenza ngayo"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Sakaza"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Abantu abaseduze nawe abanamadivayisi e-Bluetooth ahambisanayo bangalalela imidiya oyisakazayo"</string>
@@ -894,6 +909,8 @@
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Ayikwazi ukusakaza"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Ayikwazi ukulondoloza. Zama futhi."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Ayikwazi ukulondoloza."</string>
+    <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Sebenzisa okungenani izinhlamvu ezi-4"</string>
+    <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Sebenzisa isinhlamvu ezimbalwa kuneziyi-16"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Yakha inombolo"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Yakha inombolo ekopishelwe kubhodi yokunamathisela."</string>
     <string name="basic_status" msgid="2315371112182658176">"Vula ingxoxo"</string>
@@ -1013,24 +1030,25 @@
     <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Okungenani idivayisi eyodwa iyatholakala"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"Thinta futhi ubambe isinqamuleli"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Khansela"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Phendula manje"</string>
-    <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Vula ifoni ukuze ube nesithombe ozishuthe sona esingcono"</string>
-    <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Phendulela kwisibonisi sangaphambili ukuba nesithombe ozishuthe sona esingcono?"</string>
-    <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Sebenzisa ikhamera ebheke ngemuva ukuze uthole isithombe esibanzi esinokucaca okuphezulu."</string>
-    <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Lesi sikrini sizovala"</b></string>
+    <!-- no translation found for rear_display_bottom_sheet_confirm (1507591562761552899) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_title (3930008746560711990) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_title (6291111173057304055) -->
+    <skip />
+    <!-- no translation found for rear_display_folded_bottom_sheet_description (6842767125783222695) -->
+    <skip />
+    <!-- no translation found for rear_display_unfolded_bottom_sheet_description (7229961336309960201) -->
+    <skip />
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"Idivayisi egoqekayo iyembulwa"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"Idivayisi egoqekayo iphendulwa nxazonke"</string>
     <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> ibhethri elisele"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Xhuma i-stylus yakho kushaja"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Ibhethri le-stylus liphansi"</string>
-    <!-- no translation found for video_camera (7654002575156149298) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_title (6991157106804289643) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_text (3458704745640229638) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_action (2937701298133010724) -->
-    <skip />
-    <!-- no translation found for call_from_work_profile_close (7927067108901068098) -->
-    <skip />
+    <string name="video_camera" msgid="7654002575156149298">"Ikhamera yevidiyo"</string>
+    <string name="call_from_work_profile_title" msgid="6991157106804289643">"Ayikwazi ukufonela le phrofayela"</string>
+    <string name="call_from_work_profile_text" msgid="3458704745640229638">"Inqubomgomo yakho yomsebenzi ikuvumela ukuthi wenze amakholi wefoni kuphela ngephrofayela yomsebenzi"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"Shintshela kuphrofayela yomsebenzi"</string>
+    <string name="call_from_work_profile_close" msgid="7927067108901068098">"Vala"</string>
+    <string name="lock_screen_settings" msgid="9197175446592718435">"Amasethingi okukhiya isikrini"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zu/tiles_states_strings.xml b/packages/SystemUI/res/values-zu/tiles_states_strings.xml
index 81c4636..a8ccbb5 100644
--- a/packages/SystemUI/res/values-zu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zu/tiles_states_strings.xml
@@ -176,4 +176,7 @@
     <item msgid="8014986104355098744">"Valiwe"</item>
     <item msgid="5966994759929723339">"Vuliwe"</item>
   </string-array>
+    <!-- no translation found for tile_states_font_scaling:0 (3173069902082305985) -->
+    <!-- no translation found for tile_states_font_scaling:1 (2478289035899842865) -->
+    <!-- no translation found for tile_states_font_scaling:2 (5137565285664080143) -->
 </resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index f46266b..e346fe4 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -214,5 +214,12 @@
         <attr name="biometricsEnrollProgressHelp" format="reference|color" />
         <attr name="biometricsEnrollProgressHelpWithTalkback" format="reference|color" />
     </declare-styleable>
+
+    <declare-styleable name="SeekBarWithIconButtonsView_Layout">
+        <attr name="max" format="integer" />
+        <attr name="progress" format="integer" />
+        <attr name="iconStartContentDescription" format="reference" />
+        <attr name="iconEndContentDescription" format="reference" />
+    </declare-styleable>
 </resources>
 
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index e8a5e7e..3f84ddb 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -81,7 +81,7 @@
 
     <!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
     <string name="quick_settings_tiles_stock" translatable="false">
-        internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,color_correction,dream
+        internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,color_correction,dream,font_scaling
     </string>
 
     <!-- The tiles to display in QuickSettings -->
@@ -450,7 +450,7 @@
     <integer name="watch_heap_limit">256000</integer>
 
     <!-- SystemUI Plugins that can be loaded on user builds. -->
-    <string-array name="config_pluginWhitelist" translatable="false">
+    <string-array name="config_pluginAllowlist" translatable="false">
         <item>com.android.systemui</item>
     </string-array>
 
@@ -654,7 +654,7 @@
         <item>26</item> <!-- MOUTH_COVERING_DETECTED -->
     </integer-array>
 
-    <!-- Which device wake-ups will trigger face auth. These values correspond with
+    <!-- Which device wake-ups will trigger passive auth. These values correspond with
          PowerManager#WakeReason. -->
     <integer-array name="config_face_auth_wake_up_triggers">
         <item>1</item> <!-- WAKE_REASON_POWER_BUTTON -->
@@ -663,6 +663,7 @@
         <item>7</item> <!-- WAKE_REASON_WAKE_MOTION -->
         <item>9</item> <!-- WAKE_REASON_LID -->
         <item>10</item> <!-- WAKE_REASON_DISPLAY_GROUP_ADDED -->
+        <item>12</item> <!-- WAKE_REASON_UNFOLD_DEVICE -->
         <item>15</item> <!-- WAKE_REASON_TAP -->
         <item>16</item> <!-- WAKE_REASON_LIFT -->
         <item>17</item> <!-- WAKE_REASON_BIOMETRIC -->
@@ -824,4 +825,12 @@
         <item>bottom_end:wallet</item>
     </string-array>
 
+    <!-- Package name for the app that implements the wallpaper picker. -->
+    <string name="config_wallpaperPickerPackage" translatable="false">
+        com.android.wallpaper
+    </string>
+
+    <!-- Whether the floating rotation button should be on the left/right in the device's natural
+         orientation -->
+    <bool name="floating_rotation_button_position_left">true</bool>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 1ef0206..d48ea214 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -43,12 +43,18 @@
     <dimen name="navigation_edge_panel_height">268dp</dimen>
     <!-- The threshold to drag to trigger the edge action -->
     <dimen name="navigation_edge_action_drag_threshold">16dp</dimen>
+    <!-- The drag distance to consider evaluating gesture -->
+    <dimen name="navigation_edge_action_min_distance_to_start_animation">24dp</dimen>
     <!-- The threshold to progress back animation for edge swipe -->
     <dimen name="navigation_edge_action_progress_threshold">412dp</dimen>
     <!-- The minimum display position of the arrow on the screen -->
     <dimen name="navigation_edge_arrow_min_y">64dp</dimen>
     <!-- The amount by which the arrow is shifted to avoid the finger-->
     <dimen name="navigation_edge_finger_offset">64dp</dimen>
+    <!-- The threshold to dynamically activate the edge action -->
+    <dimen name="navigation_edge_action_reactivation_drag_threshold">32dp</dimen>
+    <!-- The threshold to dynamically deactivate the edge action -->
+    <dimen name="navigation_edge_action_deactivation_drag_threshold">32dp</dimen>
 
     <!-- The thickness of the arrow -->
     <dimen name="navigation_edge_arrow_thickness">4dp</dimen>
@@ -56,37 +62,61 @@
     <dimen name="navigation_edge_minimum_x_delta_for_switch">32dp</dimen>
 
     <!-- entry state -->
+    <item name="navigation_edge_entry_scale" format="float" type="dimen">0.98</item>
     <dimen name="navigation_edge_entry_margin">4dp</dimen>
-    <dimen name="navigation_edge_entry_background_width">8dp</dimen>
-    <dimen name="navigation_edge_entry_background_height">60dp</dimen>
-    <dimen name="navigation_edge_entry_edge_corners">30dp</dimen>
-    <dimen name="navigation_edge_entry_far_corners">30dp</dimen>
-    <dimen name="navigation_edge_entry_arrow_length">10dp</dimen>
-    <dimen name="navigation_edge_entry_arrow_height">7dp</dimen>
+    <item name="navigation_edge_entry_background_alpha" format="float" type="dimen">1.0</item>
+    <dimen name="navigation_edge_entry_background_width">0dp</dimen>
+    <dimen name="navigation_edge_entry_background_height">48dp</dimen>
+    <dimen name="navigation_edge_entry_edge_corners">6dp</dimen>
+    <dimen name="navigation_edge_entry_far_corners">6dp</dimen>
+    <item name="navigation_edge_entry_arrow_alpha" format="float" type="dimen">0.0</item>
+    <dimen name="navigation_edge_entry_arrow_length">8.6dp</dimen>
+    <dimen name="navigation_edge_entry_arrow_height">5dp</dimen>
 
     <!-- pre-threshold -->
     <dimen name="navigation_edge_pre_threshold_margin">4dp</dimen>
-    <dimen name="navigation_edge_pre_threshold_background_width">64dp</dimen>
-    <dimen name="navigation_edge_pre_threshold_background_height">60dp</dimen>
-    <dimen name="navigation_edge_pre_threshold_edge_corners">22dp</dimen>
-    <dimen name="navigation_edge_pre_threshold_far_corners">26dp</dimen>
+    <item name="navigation_edge_pre_threshold_background_alpha" format="float" type="dimen">1.0
+    </item>
+    <item name="navigation_edge_pre_threshold_scale" format="float" type="dimen">0.98</item>
+    <dimen name="navigation_edge_pre_threshold_background_width">51dp</dimen>
+    <dimen name="navigation_edge_pre_threshold_background_height">46dp</dimen>
+    <dimen name="navigation_edge_pre_threshold_edge_corners">16dp</dimen>
+    <dimen name="navigation_edge_pre_threshold_far_corners">20dp</dimen>
+    <item name="navigation_edge_pre_threshold_arrow_alpha" format="float" type="dimen">1.0</item>
+    <dimen name="navigation_edge_pre_threshold_arrow_length">8dp</dimen>
+    <dimen name="navigation_edge_pre_threshold_arrow_height">5.6dp</dimen>
 
-    <!-- post-threshold / active -->
+    <!-- active (post-threshold) -->
+    <item name="navigation_edge_active_scale" format="float" type="dimen">1.0</item>
     <dimen name="navigation_edge_active_margin">14dp</dimen>
-    <dimen name="navigation_edge_active_background_width">60dp</dimen>
-    <dimen name="navigation_edge_active_background_height">60dp</dimen>
-    <dimen name="navigation_edge_active_edge_corners">30dp</dimen>
-    <dimen name="navigation_edge_active_far_corners">30dp</dimen>
-    <dimen name="navigation_edge_active_arrow_length">8dp</dimen>
-    <dimen name="navigation_edge_active_arrow_height">9dp</dimen>
+    <item name="navigation_edge_active_background_alpha" format="float" type="dimen">1.0</item>
+    <dimen name="navigation_edge_active_background_width">48dp</dimen>
+    <dimen name="navigation_edge_active_background_height">48dp</dimen>
+    <dimen name="navigation_edge_active_edge_corners">24dp</dimen>
+    <dimen name="navigation_edge_active_far_corners">24dp</dimen>
+    <item name="navigation_edge_active_arrow_alpha" format="float" type="dimen">1.0</item>
+    <dimen name="navigation_edge_active_arrow_length">6.4dp</dimen>
+    <dimen name="navigation_edge_active_arrow_height">7.2dp</dimen>
 
+    <!-- committed -->
+    <item name="navigation_edge_committed_scale" format="float" type="dimen">0.85</item>
+    <item name="navigation_edge_committed_alpha" format="float" type="dimen">0</item>
+
+    <!-- cancelled -->
+    <dimen name="navigation_edge_cancelled_background_width">0dp</dimen>
+
+    <item name="navigation_edge_stretch_scale" format="float" type="dimen">1.0</item>
     <dimen name="navigation_edge_stretch_margin">18dp</dimen>
-    <dimen name="navigation_edge_stretch_background_width">74dp</dimen>
-    <dimen name="navigation_edge_stretch_background_height">60dp</dimen>
-    <dimen name="navigation_edge_stretch_edge_corners">30dp</dimen>
-    <dimen name="navigation_edge_stretch_far_corners">30dp</dimen>
-    <dimen name="navigation_edge_stretched_arrow_length">7dp</dimen>
-    <dimen name="navigation_edge_stretched_arrow_height">10dp</dimen>
+    <dimen name="navigation_edge_stretch_background_width">60dp</dimen>
+    <item name="navigation_edge_stretch_background_alpha" format="float" type="dimen">
+        @dimen/navigation_edge_entry_background_alpha
+    </item>
+    <dimen name="navigation_edge_stretch_background_height">48dp</dimen>
+    <dimen name="navigation_edge_stretch_edge_corners">24dp</dimen>
+    <dimen name="navigation_edge_stretch_far_corners">24dp</dimen>
+    <item name="navigation_edge_strech_arrow_alpha" format="float" type="dimen">1.0</item>
+    <dimen name="navigation_edge_stretched_arrow_length">5.6dp</dimen>
+    <dimen name="navigation_edge_stretched_arrow_height">8dp</dimen>
 
     <dimen name="navigation_edge_cancelled_arrow_length">12dp</dimen>
     <dimen name="navigation_edge_cancelled_arrow_height">0dp</dimen>
@@ -154,6 +184,15 @@
     <!-- Height of a small notification in the status bar-->
     <dimen name="notification_min_height">@*android:dimen/notification_min_height</dimen>
 
+    <!-- Minimum allowed height of notifications -->
+    <dimen name="notification_validation_minimum_allowed_height">10dp</dimen>
+
+    <!-- Minimum height for displaying notification content. -->
+    <dimen name="notification_content_min_height">48dp</dimen>
+
+    <!-- Reference width used when validating notification layouts -->
+    <dimen name="notification_validation_reference_width">320dp</dimen>
+
     <!-- Increased height of a small notification in the status bar -->
     <dimen name="notification_min_height_increased">146dp</dimen>
 
@@ -337,7 +376,7 @@
     <!-- Used for both start and bottom margin of the preview, relative to the action container -->
     <dimen name="overlay_preview_container_margin">8dp</dimen>
     <dimen name="overlay_action_container_margin_horizontal">8dp</dimen>
-    <dimen name="overlay_action_container_margin_bottom">4dp</dimen>
+    <dimen name="overlay_action_container_margin_bottom">6dp</dimen>
     <dimen name="overlay_bg_protection_height">242dp</dimen>
     <dimen name="overlay_action_container_corner_radius">18dp</dimen>
     <dimen name="overlay_action_container_padding_vertical">4dp</dimen>
@@ -1068,8 +1107,13 @@
     <!-- Size of Smartspace media recommendations cards in the QSPanel carousel -->
     <dimen name="qs_media_rec_icon_top_margin">16dp</dimen>
     <dimen name="qs_media_rec_album_size">88dp</dimen>
+    <dimen name="qs_media_rec_album_width">110dp</dimen>
+    <dimen name="qs_media_rec_album_height_expanded">108dp</dimen>
+    <dimen name="qs_media_rec_album_height_collapsed">77dp</dimen>
     <dimen name="qs_media_rec_album_side_margin">16dp</dimen>
     <dimen name="qs_media_rec_album_bottom_margin">8dp</dimen>
+    <dimen name="qs_media_rec_album_title_bottom_margin">22dp</dimen>
+    <dimen name="qs_media_rec_album_subtitle_height">12dp</dimen>
 
     <!-- Media tap-to-transfer chip for sender device -->
     <dimen name="media_ttt_chip_outer_padding">16dp</dimen>
@@ -1110,7 +1154,7 @@
 
     <!-- Home Controls -->
     <dimen name="controls_header_menu_size">48dp</dimen>
-    <dimen name="controls_header_bottom_margin">24dp</dimen>
+    <dimen name="controls_header_bottom_margin">16dp</dimen>
     <dimen name="controls_header_app_icon_size">24dp</dimen>
     <dimen name="controls_top_margin">48dp</dimen>
     <dimen name="controls_padding_horizontal">0dp</dimen>
@@ -1365,6 +1409,9 @@
     <dimen name="padding_above_predefined_icon_for_small">4dp</dimen>
     <dimen name="padding_between_suppressed_layout_items">8dp</dimen>
 
+    <!-- Seekbar with icon buttons -->
+    <dimen name="seekbar_icon_size">24dp</dimen>
+
     <!-- Accessibility floating menu -->
     <dimen name="accessibility_floating_menu_elevation">3dp</dimen>
     <dimen name="accessibility_floating_menu_stroke_width">1dp</dimen>
@@ -1648,4 +1695,10 @@
     <dimen name="rear_display_animation_height">200dp</dimen>
     <dimen name="rear_display_title_top_padding">24dp</dimen>
     <dimen name="rear_display_title_bottom_padding">16dp</dimen>
+
+    <!--
+    Vertical distance between the pointer and the popup menu that shows up on the lock screen when
+    it is long-pressed.
+    -->
+    <dimen name="keyguard_long_press_settings_popup_vertical_offset">96dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index c5ffc94..6354752 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -33,8 +33,6 @@
     <!-- Whether to show chipbar UI whenever the device is unlocked by ActiveUnlock. -->
     <bool name="flag_active_unlock_chipbar">true</bool>
 
-    <bool name="flag_smartspace">false</bool>
-
     <!--  Whether the user switcher chip shows in the status bar. When true, the multi user
       avatar will no longer show on the lockscreen -->
     <bool name="flag_user_switcher_chip">false</bool>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 066b185..464ce03 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -240,9 +240,13 @@
     <!-- Content description for the right boundary of the screenshot being cropped, with the current position as a percentage. [CHAR LIMIT=NONE] -->
     <string name="screenshot_right_boundary_pct">Right boundary <xliff:g id="percent" example="50">%1$d</xliff:g> percent</string>
     <!-- Notification displayed when a screenshot is saved in a work profile. [CHAR LIMIT=NONE] -->
-    <string name="screenshot_work_profile_notification">Work screenshots are saved in the <xliff:g id="app" example="Work Files">%1$s</xliff:g> app</string>
+    <string name="screenshot_work_profile_notification">Saved in <xliff:g id="app" example="Files">%1$s</xliff:g> in the work profile</string>
     <!-- Default name referring to the app on the device that lets the user browse stored files. [CHAR LIMIT=NONE] -->
     <string name="screenshot_default_files_app_name">Files</string>
+    <!-- A notice shown to the user to indicate that an app has detected the screenshot that the user has just taken. [CHAR LIMIT=75] -->
+    <string name="screenshot_detected_template"><xliff:g id="appName" example="Google Chrome">%1$s</xliff:g> detected this screenshot.</string>
+    <!-- A notice shown to the user to indicate that multiple apps have detected the screenshot that the user has just taken. [CHAR LIMIT=75] -->
+    <string name="screenshot_detected_multiple_template"><xliff:g id="appName" example="Google Chrome">%1$s</xliff:g> and other open apps detected this screenshot.</string>
 
     <!-- Notification title displayed for screen recording [CHAR LIMIT=50]-->
     <string name="screenrecord_name">Screen Recorder</string>
@@ -475,6 +479,8 @@
     <string name="accessibility_desc_notification_shade">Notification shade.</string>
     <!-- Content description for the quick settings panel (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_desc_quick_settings">Quick settings.</string>
+    <!-- Content description for the split notification shade that also includes QS (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_desc_qs_notification_shade">Quick settings and Notification shade.</string>
     <!-- Content description for the lock screen (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_desc_lock_screen">Lock screen.</string>
     <!-- Content description for the work profile lock screen. This prevents work profile apps from being used, but personal apps can be used as normal (not shown on the screen). [CHAR LIMIT=NONE] -->
@@ -656,6 +662,8 @@
     <string name="quick_settings_inversion_label">Color inversion</string>
     <!-- QuickSettings: Label for the toggle that controls whether display color correction is enabled. [CHAR LIMIT=NONE] -->
     <string name="quick_settings_color_correction_label">Color correction</string>
+    <!-- QuickSettings: Label for font size scaling. [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_font_scaling_label">Font size</string>
     <!-- QuickSettings: Control panel: Label for button that navigates to user settings. [CHAR LIMIT=NONE] -->
     <string name="quick_settings_more_user_settings">Manage users</string>
     <!-- QuickSettings: Control panel: Label for button that dismisses control panel. [CHAR LIMIT=NONE] -->
@@ -2177,6 +2185,14 @@
     <!-- Title of the overlay warning the user to interact with the device or it will go to sleep. [CHAR LIMIT=25] -->
     <string name="inattentive_sleep_warning_title">Standby</string>
 
+    <!-- Font scaling -->
+    <!-- Font scaling: Quick Settings dialog title [CHAR LIMIT=30] -->
+    <string name="font_scaling_dialog_title">Font Size</string>
+    <!-- Content Description for the icon button to make fonts smaller. [CHAR LIMIT=30] -->
+    <string name="font_scaling_smaller">Make smaller</string>
+    <!-- Content Description for the icon button to make fonts larger. [CHAR LIMIT=30] -->
+    <string name="font_scaling_larger">Make larger</string>
+
     <!-- Window Magnification strings -->
     <!-- Title for Magnification Window [CHAR LIMIT=NONE] -->
     <string name="magnification_window_title">Magnification Window</string>
@@ -2238,6 +2254,14 @@
     <!-- Removed control in management screen [CHAR LIMIT=20] -->
     <string name="controls_removed">Removed</string>
 
+    <!-- Title for the dialog presented to the user to authorize this app to display a Device
+         controls panel (embedded activity) instead of controls rendered by SystemUI [CHAR LIMIT=30] -->
+    <string name="controls_panel_authorization_title">Add <xliff:g id="appName" example="My app">%s</xliff:g>?</string>
+
+    <!-- Shows in a dialog presented to the user to authorize this app to display a Device controls
+         panel (embedded activity) instead of controls rendered by SystemUI [CHAR LIMIT=NONE] -->
+    <string name="controls_panel_authorization">When you add <xliff:g id="appName" example="My app">%s</xliff:g>, it can add controls and content to this panel. In some apps, you can choose which controls show up here.</string>
+
     <!-- a11y state description for a control that is currently favorited [CHAR LIMIT=NONE] -->
     <string name="accessibility_control_favorite">Favorited</string>
     <!-- a11y state description for a control that is currently favorited with its position [CHAR LIMIT=NONE] -->
@@ -2352,6 +2376,8 @@
     <string name="controls_media_smartspace_rec_item_description">Play <xliff:g id="song_name" example="Daily mix">%1$s</xliff:g> by <xliff:g id="artist_name" example="Various artists">%2$s</xliff:g> from <xliff:g id="app_label" example="Spotify">%3$s</xliff:g></string>
     <!-- Description for Smartspace recommendation's media item which doesn't have artist info, including information for the media's title and the source app [CHAR LIMIT=NONE]-->
     <string name="controls_media_smartspace_rec_item_no_artist_description">Play <xliff:g id="song_name" example="Daily mix">%1$s</xliff:g> from <xliff:g id="app_label" example="Spotify">%2$s</xliff:g></string>
+    <!-- Header title for Smartspace recommendation card within media controls. [CHAR_LIMIT=30] -->
+    <string name="controls_media_smartspace_rec_header">For You</string>
 
     <!--- ****** Media tap-to-transfer ****** -->
     <!-- Text for a button to undo the media transfer. [CHAR LIMIT=20] -->
@@ -2368,6 +2394,10 @@
     <string name="media_transfer_loading">Loading</string>
     <!-- Default name of the device. [CHAR LIMIT=30] -->
     <string name="media_ttt_default_device_type">tablet</string>
+    <!-- Description of media transfer icon of unknown app appears in receiver devices. [CHAR LIMIT=NONE]-->
+    <string name="media_transfer_receiver_content_description_unknown_app">Casting your media</string>
+    <!-- Description of media transfer icon appears in receiver devices. [CHAR LIMIT=NONE]-->
+    <string name="media_transfer_receiver_content_description_with_app_name">Casting <xliff:g id="app_label" example="Spotify">%1$s</xliff:g></string>
 
     <!-- Error message indicating that a control timed out while waiting for an update [CHAR_LIMIT=30] -->
     <string name="controls_error_timeout">Inactive, check app</string>
@@ -2387,6 +2417,8 @@
     <string name="controls_menu_add">Add controls</string>
     <!-- Controls menu, edit [CHAR_LIMIT=30] -->
     <string name="controls_menu_edit">Edit controls</string>
+    <!-- Controls menu, add another app [CHAR LIMIT=30] -->
+    <string name="controls_menu_add_another_app">Add app</string>
 
     <!-- Title for the media output dialog with media related devices [CHAR LIMIT=50] -->
     <string name="media_output_dialog_add_output">Add outputs</string>
@@ -2418,6 +2450,8 @@
     <string name="media_output_group_title_speakers_and_displays">Speakers &amp; Displays</string>
     <!-- Title for Suggested Devices group. [CHAR LIMIT=NONE] -->
     <string name="media_output_group_title_suggested_device">Suggested Devices</string>
+    <!-- Sub status indicates device need premium account. [CHAR LIMIT=NONE] -->
+    <string name="media_output_status_require_premium">Requires premium account</string>
 
     <!-- Media Output Broadcast Dialog -->
     <!-- Title for Broadcast First Notify Dialog [CHAR LIMIT=60] -->
@@ -2442,7 +2476,10 @@
     <string name="media_output_broadcast_update_error">Can\u2019t save. Try again.</string>
     <!-- The error message when Broadcast name/code update failed and can't change again[CHAR LIMIT=60] -->
     <string name="media_output_broadcast_last_update_error">Can\u2019t save.</string>
-
+    <!-- The hint message when Broadcast code is less than 4 characters [CHAR LIMIT=60] -->
+    <string name="media_output_broadcast_code_hint_no_less_than_min">Use at least 4 characters</string>
+    <!-- The hint message when Broadcast code is more than 16 characters [CHAR LIMIT=60] -->
+    <string name="media_output_broadcast_code_hint_no_more_than_max">Use fewer than 16 characters</string>
 
     <!-- Label for clip data when copying the build number off QS [CHAR LIMIT=NONE]-->
     <string name="build_number_clip_data_label">Build number</string>
@@ -2758,15 +2795,15 @@
     <!-- Text for education page of cancel button to hide the page. [CHAR_LIMIT=NONE] -->
     <string name="rear_display_bottom_sheet_cancel">Cancel</string>
     <!-- Text for the user to confirm they flipped the device around. [CHAR_LIMIT=NONE] -->
-    <string name="rear_display_bottom_sheet_confirm">Flip now</string>
+    <string name="rear_display_bottom_sheet_confirm">Switch screens now</string>
     <!-- Text for education page title to guide user to unfold phone. [CHAR_LIMIT=50] -->
-    <string name="rear_display_fold_bottom_sheet_title">Unfold phone for a better selfie</string>
-    <!-- Text for education page title to guide user to flip to the front display. [CHAR_LIMIT=50] -->
-    <string name="rear_display_unfold_bottom_sheet_title">Flip to front display for a better selfie?</string>
+    <string name="rear_display_folded_bottom_sheet_title">Unfold phone</string>
+    <!-- Text for education page title to guide user to switch to the front display. [CHAR_LIMIT=50] -->
+    <string name="rear_display_unfolded_bottom_sheet_title">Switch screens?</string>
     <!-- Text for education page description to suggest user to use rear selfie capture. [CHAR_LIMIT=NONE] -->
-    <string name="rear_display_bottom_sheet_description">Use the rear-facing camera for a wider photo with higher resolution.</string>
-    <!-- Text for education page description to warn user that the display will turn off if the button is clicked. [CHAR_LIMIT=NONE] -->
-    <string name="rear_display_bottom_sheet_warning"><b>&#x2731; This screen will turn off</b></string>
+    <string name="rear_display_folded_bottom_sheet_description">For higher resolution, use the rear camera</string>
+    <!-- Text for unfolded education page description to suggest user to use rear selfie capture. [CHAR_LIMIT=NONE] -->
+    <string name="rear_display_unfolded_bottom_sheet_description">For higher resolution, flip the phone</string>
     <!-- Text for education page content description for folded animation. [CHAR_LIMIT=NONE] -->
     <string name="rear_display_accessibility_folded_animation">Foldable device being unfolded</string>
     <!-- Text for education page content description for unfolded animation. [CHAR_LIMIT=NONE] -->
@@ -2798,4 +2835,28 @@
     <!-- Label for the close button on switch to work profile dialog. Switch to work profile dialog guide users to make call from work
     profile dialer app as it's not possible to make call from current profile due to an admin policy.[CHAR LIMIT=60] -->
     <string name="call_from_work_profile_close">Close</string>
+
+    <!--
+    Label for a menu item in a menu that is shown when the user wishes to configure the lock screen.
+    Clicking on this menu item takes the user to a screen where they can modify the settings of the
+    lock screen.
+
+    [CHAR LIMIT=32]
+    -->
+    <string name="lock_screen_settings">Lock screen settings</string>
+
+    <!-- Content description for Wi-Fi not available icon on dream [CHAR LIMIT=NONE]-->
+    <string name="wifi_unavailable_dream_overlay_content_description">Wi-Fi not available</string>
+
+    <!-- Content description for camera blocked icon on dream [CHAR LIMIT=NONE] -->
+    <string name="camera_blocked_dream_overlay_content_description">Camera blocked</string>
+
+    <!-- Content description for camera and microphone blocked icon on dream [CHAR LIMIT=NONE] -->
+    <string name="camera_and_microphone_blocked_dream_overlay_content_description">Camera and microphone blocked</string>
+
+    <!-- Content description for camera and microphone disabled icon on dream [CHAR LIMIT=NONE] -->
+    <string name="microphone_blocked_dream_overlay_content_description">Microphone blocked</string>
+
+    <!-- Content description for priority mode icon on dream [CHAR LIMIT=NONE] -->
+    <string name="priority_mode_dream_overlay_content_description">Priority mode on</string>
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 9846fc2..58b0234 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -678,6 +678,17 @@
 
     <style name="MediaPlayer.Recommendation"/>
 
+    <style name="MediaPlayer.Recommendation.Header">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_marginTop">@dimen/qs_media_padding</item>
+        <item name="android:layout_marginStart">@dimen/qs_media_padding</item>
+        <item name="android:fontFamily">=@*android:string/config_headlineFontFamilyMedium</item>
+        <item name="android:singleLine">true</item>
+        <item name="android:textSize">14sp</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+
     <style name="MediaPlayer.Recommendation.AlbumContainer">
         <item name="android:layout_width">@dimen/qs_media_rec_album_size</item>
         <item name="android:layout_height">@dimen/qs_media_rec_album_size</item>
@@ -686,6 +697,12 @@
         <item name="android:layout_marginBottom">@dimen/qs_media_rec_album_bottom_margin</item>
     </style>
 
+    <style name="MediaPlayer.Recommendation.AlbumContainer.Updated">
+        <item name="android:layout_width">@dimen/qs_media_rec_album_width</item>
+        <item name="android:background">@drawable/qs_media_light_source</item>
+        <item name="android:layout_marginTop">@dimen/qs_media_info_spacing</item>
+    </style>
+
     <style name="MediaPlayer.Recommendation.Album">
         <item name="android:backgroundTint">@color/media_player_album_bg</item>
     </style>
diff --git a/packages/SystemUI/res/values/tiles_states_strings.xml b/packages/SystemUI/res/values/tiles_states_strings.xml
index c809551..7020d54 100644
--- a/packages/SystemUI/res/values/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values/tiles_states_strings.xml
@@ -318,4 +318,14 @@
         <item>Off</item>
         <item>On</item>
     </string-array>
+
+    <!-- State names for font scaling tile: unavailable, off, on.
+         This subtitle is shown when the tile is in that particular state but does not set its own
+         subtitle, so some of these may never appear on screen. They should still be translated as
+         if they could appear. [CHAR LIMIT=32] -->
+    <string-array name="tile_states_font_scaling">
+        <item>Unavailable</item>
+        <item>Off</item>
+        <item>On</item>
+    </string-array>
 </resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/xml/media_recommendations_view_collapsed.xml b/packages/SystemUI/res/xml/media_recommendations_view_collapsed.xml
new file mode 100644
index 0000000..d3be3c7
--- /dev/null
+++ b/packages/SystemUI/res/xml/media_recommendations_view_collapsed.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<ConstraintSet
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    >
+
+    <Constraint
+        android:id="@+id/sizing_view"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/qs_media_session_height_collapsed"
+        />
+
+    <Constraint
+        android:id="@+id/media_rec_title"
+        style="@style/MediaPlayer.Recommendation.Header"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"/>
+
+    <Constraint
+        android:id="@+id/media_cover1_container"
+        style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated"
+        android:layout_height="@dimen/qs_media_rec_album_height_collapsed"
+        android:layout_marginEnd="@dimen/qs_media_info_spacing"
+        android:layout_marginStart="@dimen/qs_media_padding"
+        app:layout_constraintTop_toBottomOf="@+id/media_rec_title"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toStartOf="@id/media_cover2_container"/>
+
+
+    <Constraint
+        android:id="@+id/media_cover2_container"
+        style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated"
+        android:layout_height="@dimen/qs_media_rec_album_height_collapsed"
+        android:layout_marginEnd="@dimen/qs_media_info_spacing"
+        app:layout_constraintTop_toBottomOf="@+id/media_rec_title"
+        app:layout_constraintStart_toEndOf="@id/media_cover1_container"
+        app:layout_constraintEnd_toStartOf="@id/media_cover3_container"/>
+
+    <Constraint
+        android:id="@+id/media_cover3_container"
+        style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated"
+        android:layout_height="@dimen/qs_media_rec_album_height_collapsed"
+        android:layout_marginEnd="@dimen/qs_media_padding"
+        app:layout_constraintTop_toBottomOf="@+id/media_rec_title"
+        app:layout_constraintStart_toEndOf="@id/media_cover2_container"
+        app:layout_constraintEnd_toEndOf="parent"/>
+
+
+</ConstraintSet>
diff --git a/packages/SystemUI/res/xml/media_recommendations_view_expanded.xml b/packages/SystemUI/res/xml/media_recommendations_view_expanded.xml
new file mode 100644
index 0000000..88c7055
--- /dev/null
+++ b/packages/SystemUI/res/xml/media_recommendations_view_expanded.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<ConstraintSet
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    >
+
+    <Constraint
+        android:id="@+id/sizing_view"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/qs_media_session_height_expanded"
+        />
+
+    <Constraint
+        android:id="@+id/media_rec_title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/qs_media_padding"
+        android:layout_marginStart="@dimen/qs_media_padding"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+        android:singleLine="true"
+        android:textSize="14sp"
+        android:textColor="@color/notification_primary_text_color"/>
+
+    <Constraint
+        android:id="@+id/media_cover1_container"
+        style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated"
+        android:layout_height="@dimen/qs_media_rec_album_height_expanded"
+        android:layout_marginEnd="@dimen/qs_media_info_spacing"
+        android:layout_marginStart="@dimen/qs_media_padding"
+        app:layout_constraintTop_toBottomOf="@+id/media_rec_title"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toStartOf="@id/media_cover2_container"/>
+
+
+    <Constraint
+        android:id="@+id/media_cover2_container"
+        style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated"
+        android:layout_height="@dimen/qs_media_rec_album_height_expanded"
+        android:layout_marginEnd="@dimen/qs_media_info_spacing"
+        app:layout_constraintTop_toBottomOf="@+id/media_rec_title"
+        app:layout_constraintStart_toEndOf="@id/media_cover1_container"
+        app:layout_constraintEnd_toStartOf="@id/media_cover3_container"/>
+
+    <Constraint
+        android:id="@+id/media_cover3_container"
+        style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated"
+        android:layout_height="@dimen/qs_media_rec_album_height_expanded"
+        android:layout_marginEnd="@dimen/qs_media_padding"
+        app:layout_constraintTop_toBottomOf="@+id/media_rec_title"
+        app:layout_constraintStart_toEndOf="@id/media_cover2_container"
+        app:layout_constraintEnd_toEndOf="parent"/>
+
+
+</ConstraintSet>
diff --git a/packages/SystemUI/res/xml/media_session_collapsed.xml b/packages/SystemUI/res/xml/media_session_collapsed.xml
index d9c81af..5129fc0 100644
--- a/packages/SystemUI/res/xml/media_session_collapsed.xml
+++ b/packages/SystemUI/res/xml/media_session_collapsed.xml
@@ -73,11 +73,12 @@
         android:layout_height="wrap_content"
         android:layout_marginEnd="@dimen/qs_media_info_spacing"
         android:layout_marginBottom="@dimen/qs_media_padding"
-        android:layout_marginTop="0dp"
+        android:layout_marginTop="@dimen/qs_media_icon_offset"
         app:layout_constraintStart_toStartOf="@id/header_title"
         app:layout_constraintEnd_toStartOf="@id/header_artist"
         app:layout_constraintTop_toTopOf="@id/header_artist"
-        app:layout_constraintBottom_toTopOf="@id/media_action_barrier_top"
+        app:layout_constraintBottom_toBottomOf="@id/header_artist"
+        app:layout_constraintVertical_bias="0"
         app:layout_constraintHorizontal_bias="0"
         app:layout_constraintHorizontal_chainStyle="packed" />
 
diff --git a/packages/SystemUI/res/xml/qqs_header.xml b/packages/SystemUI/res/xml/qqs_header.xml
index e56e5d5..00a0444 100644
--- a/packages/SystemUI/res/xml/qqs_header.xml
+++ b/packages/SystemUI/res/xml/qqs_header.xml
@@ -48,8 +48,7 @@
             app:layout_constrainedWidth="true"
             app:layout_constraintStart_toEndOf="@id/clock"
             app:layout_constraintEnd_toStartOf="@id/barrier"
-            app:layout_constraintTop_toTopOf="parent"
-            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintBaseline_toBaselineOf="@id/clock"
             app:layout_constraintHorizontal_bias="0"
         />
     </Constraint>
diff --git a/packages/SystemUI/res/xml/qs_header.xml b/packages/SystemUI/res/xml/qs_header.xml
index d97031f..52a98984 100644
--- a/packages/SystemUI/res/xml/qs_header.xml
+++ b/packages/SystemUI/res/xml/qs_header.xml
@@ -41,9 +41,6 @@
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toBottomOf="@id/privacy_container"
             app:layout_constraintBottom_toBottomOf="@id/carrier_group"
-            app:layout_constraintEnd_toStartOf="@id/carrier_group"
-            app:layout_constraintHorizontal_bias="0"
-            app:layout_constraintHorizontal_chainStyle="spread_inside"
         />
         <Transform
             android:scaleX="2.57"
@@ -62,18 +59,18 @@
         />
     </Constraint>
 
+    <!-- LargeScreenShadeHeaderController helps with managing clock width to layout this view -->
     <Constraint
         android:id="@+id/carrier_group">
         <Layout
-            app:layout_constraintWidth_min="48dp"
-            android:layout_width="wrap_content"
+            android:layout_width="0dp"
             android:layout_height="@dimen/large_screen_shade_header_min_height"
-            app:layout_constraintStart_toEndOf="@id/clock"
+            app:layout_constraintWidth_min="48dp"
+            app:layout_constraintWidth_default="wrap"
+            app:layout_constraintStart_toStartOf="@id/clock"
             app:layout_constraintTop_toBottomOf="@id/privacy_container"
             app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintHorizontal_bias="1"
             app:layout_constraintBottom_toTopOf="@id/batteryRemainingIcon"
-            app:layout_constraintHorizontal_chainStyle="spread_inside"
             />
         <PropertySet
             android:alpha="1"
diff --git a/packages/SystemUI/scripts/token_alignment/.eslintrc.json b/packages/SystemUI/scripts/token_alignment/.eslintrc.json
new file mode 100644
index 0000000..69dc00e
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/.eslintrc.json
@@ -0,0 +1,31 @@
+{
+    "env": {
+        "es2021": true,
+        "node": true
+    },
+    "parserOptions": {
+        "ecmaVersion": "latest",
+        "sourceType": "module"
+    },
+    "plugins": ["prettier", "@typescript-eslint", "eslint-plugin-simple-import-sort", "import"],
+    "extends": ["prettier", "eslint:recommended", "plugin:@typescript-eslint/recommended"],
+    "rules": {
+        "prettier/prettier": ["error"],
+        "no-unused-vars": "off",
+        "@typescript-eslint/no-unused-vars": [
+            "warn",
+            {
+                "argsIgnorePattern": "^_",
+                "varsIgnorePattern": "^_",
+                "caughtErrorsIgnorePattern": "^_"
+            }
+        ],
+        "no-multiple-empty-lines": ["error", { "max": 2 }],
+        "no-multi-spaces": "error",
+        "simple-import-sort/imports": "error",
+        "simple-import-sort/exports": "error",
+        "import/first": "error",
+        "import/newline-after-import": "error",
+        "import/no-duplicates": "error"
+    }
+}
diff --git a/packages/SystemUI/scripts/token_alignment/.gitignore b/packages/SystemUI/scripts/token_alignment/.gitignore
new file mode 100644
index 0000000..96ce14f
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/.gitignore
@@ -0,0 +1,2 @@
+vscode
+node_modules
\ No newline at end of file
diff --git a/packages/SystemUI/scripts/token_alignment/.prettierrc b/packages/SystemUI/scripts/token_alignment/.prettierrc
new file mode 100644
index 0000000..20f02f9
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/.prettierrc
@@ -0,0 +1,9 @@
+{
+   "tabWidth": 4,
+   "printWidth": 100,
+   "semi": true,
+   "singleQuote": true,
+    "bracketSameLine": true,
+    "bracketSpacing": true,
+    "arrowParens": "always"
+}
\ No newline at end of file
diff --git a/packages/SystemUI/scripts/token_alignment/helpers/DOMFuncs.ts b/packages/SystemUI/scripts/token_alignment/helpers/DOMFuncs.ts
new file mode 100644
index 0000000..80e075c
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/helpers/DOMFuncs.ts
@@ -0,0 +1,297 @@
+// Copyright 2022 Google LLC
+
+// 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.
+
+type IElementComment =
+    | { commentNode: undefined; textContent: undefined; hidden: undefined }
+    | { commentNode: Node; textContent: string; hidden: boolean };
+
+interface ITag {
+    attrs?: Record<string, string | number>;
+    tagName: string;
+}
+
+export interface INewTag extends ITag {
+    content?: string | number;
+    comment?: string;
+}
+
+export type IUpdateTag = Partial<Omit<INewTag, 'tagName'>>;
+
+export default class DOM {
+    static addEntry(containerElement: Element, tagOptions: INewTag) {
+        const doc = containerElement.ownerDocument;
+        const exists = this.alreadyHasEntry(containerElement, tagOptions);
+
+        if (exists) {
+            console.log('Ignored adding entry already available: ', exists.outerHTML);
+            return;
+        }
+
+        let insertPoint: Node | null = containerElement.lastElementChild; //.childNodes[containerElement.childNodes.length - 1];
+
+        if (!insertPoint) {
+            console.log('Ignored adding entry in empity parent: ', containerElement.outerHTML);
+            return;
+        }
+
+        const { attrs, comment, content, tagName } = tagOptions;
+
+        if (comment) {
+            const commentNode = doc.createComment(comment);
+            this.insertAfterIdented(commentNode, insertPoint);
+            insertPoint = commentNode;
+        }
+
+        const newEl = doc.createElement(tagName);
+        if (content) newEl.innerHTML = content.toString();
+        if (attrs)
+            Object.entries(attrs).forEach(([attr, value]) =>
+                newEl.setAttribute(attr, value.toString())
+            );
+        this.insertAfterIdented(newEl, insertPoint);
+
+        return true;
+    }
+
+    static insertBeforeIndented(newNode: Node, referenceNode: Node) {
+        const paddingNode = referenceNode.previousSibling;
+        const ownerDoc = referenceNode.ownerDocument;
+        const containerNode = referenceNode.parentNode;
+
+        if (!paddingNode || !ownerDoc || !containerNode) return;
+
+        const currentPadding = paddingNode.textContent || '';
+        const textNode = referenceNode.ownerDocument.createTextNode(currentPadding);
+
+        containerNode.insertBefore(newNode, referenceNode);
+        containerNode.insertBefore(textNode, newNode);
+    }
+
+    static insertAfterIdented(newNode: Node, referenceNode: Node) {
+        const paddingNode = referenceNode.previousSibling;
+        const ownerDoc = referenceNode.ownerDocument;
+        const containerNode = referenceNode.parentNode;
+
+        if (!paddingNode || !ownerDoc || !containerNode) return;
+
+        const currentPadding = paddingNode.textContent || '';
+        const textNode = ownerDoc.createTextNode(currentPadding);
+
+        containerNode.insertBefore(newNode, referenceNode.nextSibling);
+        containerNode.insertBefore(textNode, newNode);
+    }
+
+    static getElementComment(el: Element): IElementComment {
+        const commentNode = el.previousSibling?.previousSibling;
+
+        const out = { commentNode: undefined, textContent: undefined, hidden: undefined };
+
+        if (!commentNode) return out;
+
+        const textContent = commentNode.textContent || '';
+        const hidden = textContent.substring(textContent.length - 6) == '@hide ';
+
+        if (!(commentNode && commentNode.nodeName == '#comment')) return out;
+
+        return { commentNode, textContent, hidden: hidden };
+    }
+
+    static duplicateEntryWithChange(
+        templateElement: Element,
+        options: Omit<IUpdateTag, 'content'>
+    ) {
+        const exists = this.futureEntryAlreadyExist(templateElement, options);
+        if (exists) {
+            console.log('Ignored duplicating entry already available: ', exists.outerHTML);
+            return;
+        }
+
+        const { commentNode } = this.getElementComment(templateElement);
+        let insertPoint: Node = templateElement;
+
+        if (commentNode) {
+            const newComment = commentNode.cloneNode();
+            this.insertAfterIdented(newComment, insertPoint);
+            insertPoint = newComment;
+        }
+
+        const newEl = templateElement.cloneNode(true) as Element;
+        this.insertAfterIdented(newEl, insertPoint);
+
+        this.updateElement(newEl, options);
+        return true;
+    }
+
+    static replaceStringInAttributeValueOnQueried(
+        root: Element,
+        query: string,
+        attrArray: string[],
+        replaceMap: Map<string, string>
+    ): boolean {
+        let updated = false;
+        const queried = [...Array.from(root.querySelectorAll(query)), root];
+
+        queried.forEach((el) => {
+            attrArray.forEach((attr) => {
+                if (el.hasAttribute(attr)) {
+                    const currentAttrValue = el.getAttribute(attr);
+
+                    if (!currentAttrValue) return;
+
+                    [...replaceMap.entries()].some(([oldStr, newStr]) => {
+                        if (
+                            currentAttrValue.length >= oldStr.length &&
+                            currentAttrValue.indexOf(oldStr) ==
+                                currentAttrValue.length - oldStr.length
+                        ) {
+                            el.setAttribute(attr, currentAttrValue.replace(oldStr, newStr));
+                            updated = true;
+                            return true;
+                        }
+                        return false;
+                    });
+                }
+            });
+        });
+
+        return updated;
+    }
+
+    static updateElement(el: Element, updateOptions: IUpdateTag) {
+        const exists = this.futureEntryAlreadyExist(el, updateOptions);
+        if (exists) {
+            console.log('Ignored updating entry already available: ', exists.outerHTML);
+            return;
+        }
+
+        const { comment, attrs, content } = updateOptions;
+
+        if (comment) {
+            const { commentNode } = this.getElementComment(el);
+            if (commentNode) {
+                commentNode.textContent = comment;
+            }
+        }
+
+        if (attrs) {
+            for (const attr in attrs) {
+                const value = attrs[attr];
+
+                if (value != undefined) {
+                    el.setAttribute(attr, `${value}`);
+                } else {
+                    el.removeAttribute(attr);
+                }
+            }
+        }
+
+        if (content != undefined) {
+            el.innerHTML = `${content}`;
+        }
+
+        return true;
+    }
+
+    static elementToOptions(el: Element): ITag {
+        return {
+            attrs: this.getAllElementAttributes(el),
+            tagName: el.tagName,
+        };
+    }
+
+    static getAllElementAttributes(el: Element): Record<string, string> {
+        return el
+            .getAttributeNames()
+            .reduce(
+                (acc, attr) => ({ ...acc, [attr]: el.getAttribute(attr) || '' }),
+                {} as Record<string, string>
+            );
+    }
+
+    static futureEntryAlreadyExist(el: Element, updateOptions: IUpdateTag) {
+        const currentElOptions = this.elementToOptions(el);
+
+        if (!el.parentElement) {
+            console.log('Checked el has no parent');
+            process.exit();
+        }
+
+        return this.alreadyHasEntry(el.parentElement, {
+            ...currentElOptions,
+            ...updateOptions,
+            attrs: { ...currentElOptions.attrs, ...updateOptions.attrs },
+        });
+    }
+
+    static alreadyHasEntry(
+        containerElement: Element,
+        { attrs, tagName }: Pick<INewTag, 'attrs' | 'tagName'>
+    ) {
+        const qAttrs = attrs
+            ? Object.entries(attrs)
+                  .map(([a, v]) => `[${a}="${v}"]`)
+                  .join('')
+            : '';
+
+        return containerElement.querySelector(tagName + qAttrs);
+    }
+
+    static replaceContentTextOnQueried(
+        root: Element,
+        query: string,
+        replacePairs: Array<[string, string]>
+    ) {
+        let updated = false;
+        let queried = Array.from(root.querySelectorAll(query));
+
+        if (queried.length == 0) queried = [...Array.from(root.querySelectorAll(query)), root];
+
+        queried.forEach((el) => {
+            replacePairs.forEach(([oldStr, newStr]) => {
+                if (el.innerHTML == oldStr) {
+                    el.innerHTML = newStr;
+                    updated = true;
+                }
+            });
+        });
+
+        return updated;
+    }
+
+    static XMLDocToString(doc: XMLDocument) {
+        let str = '';
+
+        doc.childNodes.forEach((node) => {
+            switch (node.nodeType) {
+                case 8: // comment
+                    str += `<!--${node.nodeValue}-->\n`;
+                    break;
+
+                case 3: // text
+                    str += node.textContent;
+                    break;
+
+                case 1: // element
+                    str += (node as Element).outerHTML;
+                    break;
+
+                default:
+                    console.log('Unhandled node type: ' + node.nodeType);
+                    break;
+            }
+        });
+
+        return str;
+    }
+}
diff --git a/packages/SystemUI/scripts/token_alignment/helpers/FileIO.ts b/packages/SystemUI/scripts/token_alignment/helpers/FileIO.ts
new file mode 100644
index 0000000..359e3ab
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/helpers/FileIO.ts
@@ -0,0 +1,112 @@
+// Copyright 2022 Google LLC
+
+// 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.import { exec } from 'child_process';
+
+import { exec } from 'child_process';
+import { parse } from 'csv-parse';
+import { promises as fs } from 'fs';
+import jsdom from 'jsdom';
+
+const DOMParser = new jsdom.JSDOM('').window.DOMParser as typeof window.DOMParser;
+
+type TFileList = string[];
+
+export type TCSVRecord = Array<string | boolean | number>;
+
+class _FileIO {
+    public parser = new DOMParser();
+    public saved: string[] = [];
+
+    public loadXML = async (path: string): Promise<XMLDocument> => {
+        try {
+            const src = await this.loadFileAsText(path);
+            return this.parser.parseFromString(src, 'text/xml') as XMLDocument;
+        } catch (error) {
+            console.log(`Failed to parse XML file '${path}'.`, error);
+            process.exit();
+        }
+    };
+
+    public loadFileAsText = async (path: string): Promise<string> => {
+        try {
+            return await fs.readFile(path, { encoding: 'utf8' });
+        } catch (error) {
+            console.log(`Failed to read file '${path}'.`, error);
+            process.exit();
+        }
+    };
+
+    public saveFile = async (data: string, path: string) => {
+        try {
+            await fs.writeFile(path, data, { encoding: 'utf8' });
+            this.saved.push(path);
+        } catch (error) {
+            console.log(error);
+            console.log(`Failed to write file '${path}'.`);
+            process.exit();
+        }
+    };
+
+    public loadFileList = async (path: string): Promise<TFileList> => {
+        const src = await this.loadFileAsText(path);
+
+        try {
+            return JSON.parse(src) as TFileList;
+        } catch (error) {
+            console.log(error);
+            console.log(`Failed to parse JSON file '${path}'.`);
+            process.exit();
+        }
+    };
+
+    public loadCSV = (path: string): Promise<Array<TCSVRecord>> => {
+        return new Promise((resolve, reject) => {
+            this.loadFileAsText(path).then((src) => {
+                parse(
+                    src,
+                    {
+                        delimiter: '	',
+                    },
+                    (err, records) => {
+                        if (err) {
+                            reject(err);
+                            return;
+                        }
+
+                        resolve(records);
+                    }
+                );
+            });
+        });
+    };
+
+    formatSaved = () => {
+        const cmd = `idea format ${this.saved.join(' ')}`;
+
+        exec(cmd, (error, out, stderr) => {
+            if (error) {
+                console.log(error.message);
+                return;
+            }
+
+            if (stderr) {
+                console.log(stderr);
+                return;
+            }
+
+            console.log(out);
+        });
+    };
+}
+
+export const FileIO = new _FileIO();
diff --git a/packages/SystemUI/scripts/token_alignment/helpers/migrationList.ts b/packages/SystemUI/scripts/token_alignment/helpers/migrationList.ts
new file mode 100644
index 0000000..8d50644
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/helpers/migrationList.ts
@@ -0,0 +1,70 @@
+// Copyright 2022 Google LLC
+
+// 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.import { exec } from 'child_process';
+
+import { FileIO, TCSVRecord } from './FileIO';
+import ProcessArgs from './processArgs';
+
+interface IInputMigItem {
+    migrationToken: string;
+    materialToken: string;
+    newDefaultValue?: string;
+    newComment?: string;
+}
+
+interface IAditionalKeys {
+    step: ('update' | 'duplicate' | 'add' | 'ignore')[];
+    isHidden: boolean;
+    replaceToken: string;
+}
+
+export type IMigItem = Omit<IInputMigItem, 'materialToken' | 'migrationToken'> & IAditionalKeys;
+
+export type IMigrationMap = Map<string, IMigItem>;
+
+function isMigrationRecord(record: TCSVRecord): record is string[] {
+    return !record.some((value) => typeof value != 'string') || record.length != 5;
+}
+
+export const loadMIgrationList = async function (): Promise<IMigrationMap> {
+    const out: IMigrationMap = new Map();
+    const csv = await FileIO.loadCSV('resources/migrationList.csv');
+
+    csv.forEach((record, i) => {
+        if (i == 0) return; // header
+
+        if (typeof record[0] != 'string') return;
+
+        if (!isMigrationRecord(record)) {
+            console.log(`Failed to validade CSV record as string[5].`, record);
+            process.exit();
+        }
+
+        const [originalToken, materialToken, newDefaultValue, newComment, migrationToken] = record;
+
+        if (out.has(originalToken)) {
+            console.log('Duplicated entry on Migration CSV file: ', originalToken);
+            return;
+        }
+
+        out.set(originalToken, {
+            replaceToken: ProcessArgs.isDebug ? migrationToken : materialToken,
+            ...(!!newDefaultValue && { newDefaultValue }),
+            ...(!!newComment && { newComment }),
+            step: [],
+            isHidden: false,
+        });
+    });
+
+    return new Map([...out].sort((a, b) => b[0].length - a[0].length));
+};
diff --git a/packages/SystemUI/scripts/token_alignment/helpers/processArgs.ts b/packages/SystemUI/scripts/token_alignment/helpers/processArgs.ts
new file mode 100644
index 0000000..be0e232
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/helpers/processArgs.ts
@@ -0,0 +1,21 @@
+// Copyright 2022 Google LLC
+
+// 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.import { exec } from 'child_process';
+
+const myArgs = process.argv.slice(2);
+
+const ProcessArgs = {
+    isDebug: myArgs.includes('debug'),
+};
+
+export default ProcessArgs;
diff --git a/packages/SystemUI/scripts/token_alignment/helpers/processXML.ts b/packages/SystemUI/scripts/token_alignment/helpers/processXML.ts
new file mode 100644
index 0000000..368d4cb
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/helpers/processXML.ts
@@ -0,0 +1,102 @@
+// Copyright 2022 Google LLC
+
+// 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.import { exec } from 'child_process';
+
+import DOM, { INewTag, IUpdateTag } from './DOMFuncs';
+import { FileIO } from './FileIO';
+import { IMigItem, IMigrationMap } from './migrationList';
+
+export type TResultExistingEval = ['update' | 'duplicate', IUpdateTag] | void;
+export type TResultMissingEval = INewTag | void;
+
+interface IProcessXML {
+    attr?: string;
+    containerQuery?: string;
+    evalExistingEntry?: TEvalExistingEntry;
+    evalMissingEntry?: TEvalMissingEntry;
+    hidable?: boolean;
+    path: string;
+    step: number;
+    tagName: string;
+}
+
+export type TEvalExistingEntry = (
+    attrname: string,
+    migItem: IMigItem,
+    qItem: Element
+) => TResultExistingEval;
+
+export type TEvalMissingEntry = (originalToken: string, migItem: IMigItem) => TResultMissingEval;
+
+export async function processQueriedEntries(
+    migrationMap: IMigrationMap,
+    {
+        attr = 'name',
+        containerQuery = '*',
+        evalExistingEntry,
+        path,
+        step,
+        tagName,
+        evalMissingEntry,
+    }: IProcessXML
+) {
+    const doc = await FileIO.loadXML(path);
+
+    const containerElement =
+        (containerQuery && doc.querySelector(containerQuery)) || doc.documentElement;
+
+    migrationMap.forEach((migItem, originalToken) => {
+        migItem.step[step] = 'ignore';
+
+        const queryTiems = containerElement.querySelectorAll(
+            `${tagName}[${attr}="${originalToken}"]`
+        );
+
+        if (evalMissingEntry) {
+            const addinOptions = evalMissingEntry(originalToken, migItem);
+
+            if (queryTiems.length == 0 && containerElement && addinOptions) {
+                DOM.addEntry(containerElement, addinOptions);
+                migItem.step[step] = 'add';
+                return;
+            }
+        }
+
+        if (evalExistingEntry)
+            queryTiems.forEach((qEl) => {
+                const attrName = qEl.getAttribute(attr);
+                const migItem = migrationMap.get(attrName || '');
+
+                if (!attrName || !migItem) return;
+
+                const updateOptions = evalExistingEntry(attrName, migItem, qEl);
+
+                if (!updateOptions) return;
+
+                const [processType, processOptions] = updateOptions;
+
+                switch (processType) {
+                    case 'update':
+                        if (DOM.updateElement(qEl, processOptions)) migItem.step[step] = 'update';
+                        break;
+
+                    case 'duplicate':
+                        if (DOM.duplicateEntryWithChange(qEl, processOptions))
+                            migItem.step[step] = 'duplicate';
+                        break;
+                }
+            });
+    });
+
+    await FileIO.saveFile(doc.documentElement.outerHTML, path);
+}
diff --git a/packages/SystemUI/scripts/token_alignment/helpers/rootPath.ts b/packages/SystemUI/scripts/token_alignment/helpers/rootPath.ts
new file mode 100644
index 0000000..2c6f632
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/helpers/rootPath.ts
@@ -0,0 +1,21 @@
+// Copyright 2022 Google LLC
+
+// 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.import { exec } from 'child_process';
+
+if (!process?.env?.ANDROID_BUILD_TOP) {
+    console.log(
+        "Error: Couldn't find 'ANDROID_BUILD_TOP' environment variable. Make sure to run 'lunch' in this terminal"
+    );
+}
+
+export const repoPath = process?.env?.ANDROID_BUILD_TOP;
diff --git a/packages/SystemUI/scripts/token_alignment/helpers/textFuncs.ts b/packages/SystemUI/scripts/token_alignment/helpers/textFuncs.ts
new file mode 100644
index 0000000..6679c5a
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/helpers/textFuncs.ts
@@ -0,0 +1,27 @@
+// Copyright 2022 Google LLC
+
+// 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.import { exec } from 'child_process';
+
+export function groupReplace(src: string, replaceMap: Map<string, string>, pattern: string) {
+    const fullPattern = pattern.replace('#group#', [...replaceMap.keys()].join('|'));
+
+    const regEx = new RegExp(fullPattern, 'g');
+
+    ''.replace;
+
+    return src.replace(regEx, (...args) => {
+        //match, ...matches, offset, string, groups
+        const [match, key] = args as string[];
+        return match.replace(key, replaceMap.get(key) || '');
+    });
+}
diff --git a/packages/SystemUI/scripts/token_alignment/index.ts b/packages/SystemUI/scripts/token_alignment/index.ts
new file mode 100644
index 0000000..1b15e48
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/index.ts
@@ -0,0 +1,240 @@
+// Copyright 2022 Google LLC
+
+// 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.import { exec } from 'child_process';
+
+import DOM from './helpers/DOMFuncs';
+import { FileIO } from './helpers/FileIO';
+import { loadMIgrationList } from './helpers/migrationList';
+import { processQueriedEntries, TEvalExistingEntry } from './helpers/processXML';
+import { repoPath } from './helpers/rootPath';
+import { groupReplace } from './helpers/textFuncs';
+
+async function init() {
+    const migrationMap = await loadMIgrationList();
+    const basePath = `${repoPath}/../tm-qpr-dev/frameworks/base/core/res/res/values/`;
+
+    await processQueriedEntries(migrationMap, {
+        containerQuery: 'declare-styleable[name="Theme"]',
+        hidable: true,
+        path: `${basePath}attrs.xml`,
+        step: 0,
+        tagName: 'attr',
+        evalExistingEntry: (_attrValue, migItem, qItem) => {
+            const { hidden, textContent: currentComment } = DOM.getElementComment(qItem);
+
+            if (hidden) migItem.isHidden = hidden;
+
+            const { newComment } = migItem;
+            return [
+                hidden ? 'update' : 'duplicate',
+                {
+                    attrs: { name: migItem.replaceToken },
+                    ...(newComment
+                        ? { comment: `${newComment} @hide ` }
+                        : currentComment
+                        ? { comment: hidden ? currentComment : `${currentComment} @hide ` }
+                        : {}),
+                },
+            ];
+        },
+        evalMissingEntry: (_originalToken, { replaceToken, newComment }) => {
+            return {
+                tagName: 'attr',
+                attrs: {
+                    name: replaceToken,
+                    format: 'color',
+                },
+                comment: `${newComment} @hide `,
+            };
+        },
+    });
+
+    // only update all existing entries
+    await processQueriedEntries(migrationMap, {
+        tagName: 'item',
+        path: `${basePath}themes_device_defaults.xml`,
+        containerQuery: 'resources',
+        step: 2,
+        evalExistingEntry: (_attrValue, { isHidden, replaceToken, step }, _qItem) => {
+            if (step[0] != 'ignore')
+                return [
+                    isHidden ? 'update' : 'duplicate',
+                    {
+                        attrs: { name: replaceToken },
+                    },
+                ];
+        },
+    });
+
+    // add missing entries on specific container
+    await processQueriedEntries(migrationMap, {
+        tagName: 'item',
+        path: `${basePath}themes_device_defaults.xml`,
+        containerQuery: 'resources style[parent="Theme.Material"]',
+        step: 3,
+        evalMissingEntry: (originalToken, { newDefaultValue, replaceToken }) => {
+            return {
+                tagName: 'item',
+                content: newDefaultValue,
+                attrs: {
+                    name: replaceToken,
+                },
+            };
+        },
+    });
+
+    const evalExistingEntry: TEvalExistingEntry = (_attrValue, { replaceToken, step }, _qItem) => {
+        if (step[0] == 'update')
+            return [
+                'update',
+                {
+                    attrs: { name: replaceToken },
+                },
+            ];
+    };
+
+    await processQueriedEntries(migrationMap, {
+        tagName: 'item',
+        containerQuery: 'resources',
+        path: `${basePath}../values-night/themes_device_defaults.xml`,
+        step: 4,
+        evalExistingEntry,
+    });
+
+    await processQueriedEntries(migrationMap, {
+        tagName: 'java-symbol',
+        path: `${basePath}symbols.xml`,
+        containerQuery: 'resources',
+        step: 5,
+        evalExistingEntry,
+    });
+
+    // update attributes on tracked XML files
+    {
+        const searchAttrs = [
+            'android:color',
+            'android:indeterminateTint',
+            'app:tint',
+            'app:backgroundTint',
+            'android:background',
+            'android:tint',
+            'android:drawableTint',
+            'android:textColor',
+            'android:fillColor',
+            'android:startColor',
+            'android:endColor',
+            'name',
+            'ns1:color',
+        ];
+
+        const filtered = new Map(
+            [...migrationMap]
+                .filter(([_originalToken, { step }]) => step[0] == 'update')
+                .map(([originalToken, { replaceToken }]) => [originalToken, replaceToken])
+        );
+
+        const query =
+            searchAttrs.map((str) => `*[${str}]`).join(',') +
+            [...filtered.keys()].map((originalToken) => `item[name*="${originalToken}"]`).join(',');
+
+        const trackedFiles = await FileIO.loadFileList(
+            `${__dirname}/resources/whitelist/xmls1.json`
+        );
+
+        const promises = trackedFiles.map(async (locaFilePath) => {
+            const filePath = `${repoPath}/${locaFilePath}`;
+
+            const doc = await FileIO.loadXML(filePath);
+            const docUpdated = DOM.replaceStringInAttributeValueOnQueried(
+                doc.documentElement,
+                query,
+                searchAttrs,
+                filtered
+            );
+            if (docUpdated) {
+                await FileIO.saveFile(DOM.XMLDocToString(doc), filePath);
+            } else {
+                console.warn(`Failed to update tracked file: '${locaFilePath}'`);
+            }
+        });
+        await Promise.all(promises);
+    }
+
+    // updates tag content on tracked files
+    {
+        const searchPrefixes = ['?android:attr/', '?androidprv:attr/'];
+        const filtered = searchPrefixes
+            .reduce<Array<[string, string]>>((acc, prefix) => {
+                return [
+                    ...acc,
+                    ...[...migrationMap.entries()]
+                        .filter(([_originalToken, { step }]) => step[0] == 'update')
+                        .map(
+                            ([originalToken, { replaceToken }]) =>
+                                [`${prefix}${originalToken}`, `${prefix}${replaceToken}`] as [
+                                    string,
+                                    string
+                                ]
+                        ),
+                ];
+            }, [])
+            .sort((a, b) => b[0].length - a[0].length);
+
+        const trackedFiles = await FileIO.loadFileList(
+            `${__dirname}/resources/whitelist/xmls2.json`
+        );
+
+        const promises = trackedFiles.map(async (locaFilePath) => {
+            const filePath = `${repoPath}/${locaFilePath}`;
+            const doc = await FileIO.loadXML(filePath);
+            const docUpdated = DOM.replaceContentTextOnQueried(
+                doc.documentElement,
+                'item, color',
+                filtered
+            );
+            if (docUpdated) {
+                await FileIO.saveFile(DOM.XMLDocToString(doc), filePath);
+            } else {
+                console.warn(`Failed to update tracked file: '${locaFilePath}'`);
+            }
+        });
+        await Promise.all(promises);
+    }
+
+    // replace imports on Java / Kotlin
+    {
+        const replaceMap = new Map(
+            [...migrationMap.entries()]
+                .filter(([_originalToken, { step }]) => step[0] == 'update')
+                .map(
+                    ([originalToken, { replaceToken }]) =>
+                        [originalToken, replaceToken] as [string, string]
+                )
+                .sort((a, b) => b[0].length - a[0].length)
+        );
+
+        const trackedFiles = await FileIO.loadFileList(
+            `${__dirname}/resources/whitelist/java.json`
+        );
+
+        const promises = trackedFiles.map(async (locaFilePath) => {
+            const filePath = `${repoPath}/${locaFilePath}`;
+            const fileContent = await FileIO.loadFileAsText(filePath);
+            const str = groupReplace(fileContent, replaceMap, 'R.attr.(#group#)(?![a-zA-Z])');
+            await FileIO.saveFile(str, filePath);
+        });
+        await Promise.all(promises);
+    }
+}
+
+init();
diff --git a/packages/SystemUI/scripts/token_alignment/package-lock.json b/packages/SystemUI/scripts/token_alignment/package-lock.json
new file mode 100644
index 0000000..da9edb3
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/package-lock.json
@@ -0,0 +1,3356 @@
+{
+    "name": "token_alignment",
+    "lockfileVersion": 3,
+    "requires": true,
+    "packages": {
+        "": {
+            "dependencies": {
+                "csv-parse": "^5.3.3",
+                "high5": "^1.0.0",
+                "jsdom": "^20.0.3"
+            },
+            "devDependencies": {
+                "@types/jsdom": "^20.0.1",
+                "@types/node": "^18.11.18",
+                "@typescript-eslint/eslint-plugin": "^5.48.0",
+                "eslint-config-prettier": "^8.6.0",
+                "eslint-plugin-import": "^2.26.0",
+                "eslint-plugin-prettier": "^4.2.1",
+                "eslint-plugin-simple-import-sort": "^8.0.0",
+                "ts-node": "^10.9.1",
+                "typescript": "^4.9.4"
+            }
+        },
+        "node_modules/@cspotcode/source-map-support": {
+            "version": "0.8.1",
+            "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
+            "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
+            "dev": true,
+            "dependencies": {
+                "@jridgewell/trace-mapping": "0.3.9"
+            },
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/@eslint/eslintrc": {
+            "version": "1.4.1",
+            "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz",
+            "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "ajv": "^6.12.4",
+                "debug": "^4.3.2",
+                "espree": "^9.4.0",
+                "globals": "^13.19.0",
+                "ignore": "^5.2.0",
+                "import-fresh": "^3.2.1",
+                "js-yaml": "^4.1.0",
+                "minimatch": "^3.1.2",
+                "strip-json-comments": "^3.1.1"
+            },
+            "engines": {
+                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+            },
+            "funding": {
+                "url": "https://opencollective.com/eslint"
+            }
+        },
+        "node_modules/@humanwhocodes/config-array": {
+            "version": "0.11.8",
+            "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
+            "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "@humanwhocodes/object-schema": "^1.2.1",
+                "debug": "^4.1.1",
+                "minimatch": "^3.0.5"
+            },
+            "engines": {
+                "node": ">=10.10.0"
+            }
+        },
+        "node_modules/@humanwhocodes/module-importer": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+            "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+            "dev": true,
+            "peer": true,
+            "engines": {
+                "node": ">=12.22"
+            },
+            "funding": {
+                "type": "github",
+                "url": "https://github.com/sponsors/nzakas"
+            }
+        },
+        "node_modules/@humanwhocodes/object-schema": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
+            "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
+            "dev": true,
+            "peer": true
+        },
+        "node_modules/@jridgewell/resolve-uri": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+            "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+            "dev": true,
+            "engines": {
+                "node": ">=6.0.0"
+            }
+        },
+        "node_modules/@jridgewell/sourcemap-codec": {
+            "version": "1.4.14",
+            "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
+            "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
+            "dev": true
+        },
+        "node_modules/@jridgewell/trace-mapping": {
+            "version": "0.3.9",
+            "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
+            "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
+            "dev": true,
+            "dependencies": {
+                "@jridgewell/resolve-uri": "^3.0.3",
+                "@jridgewell/sourcemap-codec": "^1.4.10"
+            }
+        },
+        "node_modules/@nodelib/fs.scandir": {
+            "version": "2.1.5",
+            "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+            "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+            "dev": true,
+            "dependencies": {
+                "@nodelib/fs.stat": "2.0.5",
+                "run-parallel": "^1.1.9"
+            },
+            "engines": {
+                "node": ">= 8"
+            }
+        },
+        "node_modules/@nodelib/fs.stat": {
+            "version": "2.0.5",
+            "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+            "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+            "dev": true,
+            "engines": {
+                "node": ">= 8"
+            }
+        },
+        "node_modules/@nodelib/fs.walk": {
+            "version": "1.2.8",
+            "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+            "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+            "dev": true,
+            "dependencies": {
+                "@nodelib/fs.scandir": "2.1.5",
+                "fastq": "^1.6.0"
+            },
+            "engines": {
+                "node": ">= 8"
+            }
+        },
+        "node_modules/@tootallnate/once": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
+            "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
+            "engines": {
+                "node": ">= 10"
+            }
+        },
+        "node_modules/@tsconfig/node10": {
+            "version": "1.0.9",
+            "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
+            "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
+            "dev": true
+        },
+        "node_modules/@tsconfig/node12": {
+            "version": "1.0.11",
+            "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
+            "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
+            "dev": true
+        },
+        "node_modules/@tsconfig/node14": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
+            "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
+            "dev": true
+        },
+        "node_modules/@tsconfig/node16": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
+            "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
+            "dev": true
+        },
+        "node_modules/@types/jsdom": {
+            "version": "20.0.1",
+            "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz",
+            "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==",
+            "dev": true,
+            "dependencies": {
+                "@types/node": "*",
+                "@types/tough-cookie": "*",
+                "parse5": "^7.0.0"
+            }
+        },
+        "node_modules/@types/json-schema": {
+            "version": "7.0.11",
+            "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
+            "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
+            "dev": true
+        },
+        "node_modules/@types/json5": {
+            "version": "0.0.29",
+            "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+            "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
+            "dev": true
+        },
+        "node_modules/@types/node": {
+            "version": "18.11.18",
+            "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz",
+            "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==",
+            "dev": true
+        },
+        "node_modules/@types/semver": {
+            "version": "7.3.13",
+            "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz",
+            "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==",
+            "dev": true
+        },
+        "node_modules/@types/tough-cookie": {
+            "version": "4.0.2",
+            "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz",
+            "integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==",
+            "dev": true
+        },
+        "node_modules/@typescript-eslint/eslint-plugin": {
+            "version": "5.48.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.48.0.tgz",
+            "integrity": "sha512-SVLafp0NXpoJY7ut6VFVUU9I+YeFsDzeQwtK0WZ+xbRN3mtxJ08je+6Oi2N89qDn087COdO0u3blKZNv9VetRQ==",
+            "dev": true,
+            "dependencies": {
+                "@typescript-eslint/scope-manager": "5.48.0",
+                "@typescript-eslint/type-utils": "5.48.0",
+                "@typescript-eslint/utils": "5.48.0",
+                "debug": "^4.3.4",
+                "ignore": "^5.2.0",
+                "natural-compare-lite": "^1.4.0",
+                "regexpp": "^3.2.0",
+                "semver": "^7.3.7",
+                "tsutils": "^3.21.0"
+            },
+            "engines": {
+                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/typescript-eslint"
+            },
+            "peerDependencies": {
+                "@typescript-eslint/parser": "^5.0.0",
+                "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+            },
+            "peerDependenciesMeta": {
+                "typescript": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/@typescript-eslint/parser": {
+            "version": "5.48.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.48.0.tgz",
+            "integrity": "sha512-1mxNA8qfgxX8kBvRDIHEzrRGrKHQfQlbW6iHyfHYS0Q4X1af+S6mkLNtgCOsGVl8+/LUPrqdHMssAemkrQ01qg==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "@typescript-eslint/scope-manager": "5.48.0",
+                "@typescript-eslint/types": "5.48.0",
+                "@typescript-eslint/typescript-estree": "5.48.0",
+                "debug": "^4.3.4"
+            },
+            "engines": {
+                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/typescript-eslint"
+            },
+            "peerDependencies": {
+                "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+            },
+            "peerDependenciesMeta": {
+                "typescript": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/@typescript-eslint/scope-manager": {
+            "version": "5.48.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.48.0.tgz",
+            "integrity": "sha512-0AA4LviDtVtZqlyUQnZMVHydDATpD9SAX/RC5qh6cBd3xmyWvmXYF+WT1oOmxkeMnWDlUVTwdODeucUnjz3gow==",
+            "dev": true,
+            "dependencies": {
+                "@typescript-eslint/types": "5.48.0",
+                "@typescript-eslint/visitor-keys": "5.48.0"
+            },
+            "engines": {
+                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/typescript-eslint"
+            }
+        },
+        "node_modules/@typescript-eslint/type-utils": {
+            "version": "5.48.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.48.0.tgz",
+            "integrity": "sha512-vbtPO5sJyFjtHkGlGK4Sthmta0Bbls4Onv0bEqOGm7hP9h8UpRsHJwsrCiWtCUndTRNQO/qe6Ijz9rnT/DB+7g==",
+            "dev": true,
+            "dependencies": {
+                "@typescript-eslint/typescript-estree": "5.48.0",
+                "@typescript-eslint/utils": "5.48.0",
+                "debug": "^4.3.4",
+                "tsutils": "^3.21.0"
+            },
+            "engines": {
+                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/typescript-eslint"
+            },
+            "peerDependencies": {
+                "eslint": "*"
+            },
+            "peerDependenciesMeta": {
+                "typescript": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/@typescript-eslint/types": {
+            "version": "5.48.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.48.0.tgz",
+            "integrity": "sha512-UTe67B0Ypius0fnEE518NB2N8gGutIlTojeTg4nt0GQvikReVkurqxd2LvYa9q9M5MQ6rtpNyWTBxdscw40Xhw==",
+            "dev": true,
+            "engines": {
+                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/typescript-eslint"
+            }
+        },
+        "node_modules/@typescript-eslint/typescript-estree": {
+            "version": "5.48.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.0.tgz",
+            "integrity": "sha512-7pjd94vvIjI1zTz6aq/5wwE/YrfIyEPLtGJmRfyNR9NYIW+rOvzzUv3Cmq2hRKpvt6e9vpvPUQ7puzX7VSmsEw==",
+            "dev": true,
+            "dependencies": {
+                "@typescript-eslint/types": "5.48.0",
+                "@typescript-eslint/visitor-keys": "5.48.0",
+                "debug": "^4.3.4",
+                "globby": "^11.1.0",
+                "is-glob": "^4.0.3",
+                "semver": "^7.3.7",
+                "tsutils": "^3.21.0"
+            },
+            "engines": {
+                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/typescript-eslint"
+            },
+            "peerDependenciesMeta": {
+                "typescript": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/@typescript-eslint/utils": {
+            "version": "5.48.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.48.0.tgz",
+            "integrity": "sha512-x2jrMcPaMfsHRRIkL+x96++xdzvrdBCnYRd5QiW5Wgo1OB4kDYPbC1XjWP/TNqlfK93K/lUL92erq5zPLgFScQ==",
+            "dev": true,
+            "dependencies": {
+                "@types/json-schema": "^7.0.9",
+                "@types/semver": "^7.3.12",
+                "@typescript-eslint/scope-manager": "5.48.0",
+                "@typescript-eslint/types": "5.48.0",
+                "@typescript-eslint/typescript-estree": "5.48.0",
+                "eslint-scope": "^5.1.1",
+                "eslint-utils": "^3.0.0",
+                "semver": "^7.3.7"
+            },
+            "engines": {
+                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/typescript-eslint"
+            },
+            "peerDependencies": {
+                "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+            }
+        },
+        "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": {
+            "version": "5.1.1",
+            "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+            "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+            "dev": true,
+            "dependencies": {
+                "esrecurse": "^4.3.0",
+                "estraverse": "^4.1.1"
+            },
+            "engines": {
+                "node": ">=8.0.0"
+            }
+        },
+        "node_modules/@typescript-eslint/utils/node_modules/estraverse": {
+            "version": "4.3.0",
+            "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+            "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+            "dev": true,
+            "engines": {
+                "node": ">=4.0"
+            }
+        },
+        "node_modules/@typescript-eslint/visitor-keys": {
+            "version": "5.48.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.0.tgz",
+            "integrity": "sha512-5motVPz5EgxQ0bHjut3chzBkJ3Z3sheYVcSwS5BpHZpLqSptSmELNtGixmgj65+rIfhvtQTz5i9OP2vtzdDH7Q==",
+            "dev": true,
+            "dependencies": {
+                "@typescript-eslint/types": "5.48.0",
+                "eslint-visitor-keys": "^3.3.0"
+            },
+            "engines": {
+                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/typescript-eslint"
+            }
+        },
+        "node_modules/abab": {
+            "version": "2.0.6",
+            "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
+            "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA=="
+        },
+        "node_modules/acorn": {
+            "version": "8.8.1",
+            "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz",
+            "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==",
+            "bin": {
+                "acorn": "bin/acorn"
+            },
+            "engines": {
+                "node": ">=0.4.0"
+            }
+        },
+        "node_modules/acorn-globals": {
+            "version": "7.0.1",
+            "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz",
+            "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==",
+            "dependencies": {
+                "acorn": "^8.1.0",
+                "acorn-walk": "^8.0.2"
+            }
+        },
+        "node_modules/acorn-jsx": {
+            "version": "5.3.2",
+            "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+            "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+            "dev": true,
+            "peer": true,
+            "peerDependencies": {
+                "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+            }
+        },
+        "node_modules/acorn-walk": {
+            "version": "8.2.0",
+            "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
+            "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
+            "engines": {
+                "node": ">=0.4.0"
+            }
+        },
+        "node_modules/agent-base": {
+            "version": "6.0.2",
+            "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+            "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+            "dependencies": {
+                "debug": "4"
+            },
+            "engines": {
+                "node": ">= 6.0.0"
+            }
+        },
+        "node_modules/ajv": {
+            "version": "6.12.6",
+            "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+            "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "fast-deep-equal": "^3.1.1",
+                "fast-json-stable-stringify": "^2.0.0",
+                "json-schema-traverse": "^0.4.1",
+                "uri-js": "^4.2.2"
+            },
+            "funding": {
+                "type": "github",
+                "url": "https://github.com/sponsors/epoberezkin"
+            }
+        },
+        "node_modules/ansi-regex": {
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+            "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+            "dev": true,
+            "peer": true,
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/ansi-styles": {
+            "version": "4.3.0",
+            "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+            "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "color-convert": "^2.0.1"
+            },
+            "engines": {
+                "node": ">=8"
+            },
+            "funding": {
+                "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+            }
+        },
+        "node_modules/arg": {
+            "version": "4.1.3",
+            "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+            "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+            "dev": true
+        },
+        "node_modules/argparse": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+            "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+            "dev": true,
+            "peer": true
+        },
+        "node_modules/array-includes": {
+            "version": "3.1.6",
+            "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz",
+            "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==",
+            "dev": true,
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.4",
+                "es-abstract": "^1.20.4",
+                "get-intrinsic": "^1.1.3",
+                "is-string": "^1.0.7"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/array-union": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+            "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+            "dev": true,
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/array.prototype.flat": {
+            "version": "1.3.1",
+            "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz",
+            "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==",
+            "dev": true,
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.4",
+                "es-abstract": "^1.20.4",
+                "es-shim-unscopables": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/asynckit": {
+            "version": "0.4.0",
+            "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+            "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+        },
+        "node_modules/available-typed-arrays": {
+            "version": "1.0.5",
+            "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
+            "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==",
+            "dev": true,
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/balanced-match": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+            "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+            "dev": true
+        },
+        "node_modules/brace-expansion": {
+            "version": "1.1.11",
+            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+            "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+            "dev": true,
+            "dependencies": {
+                "balanced-match": "^1.0.0",
+                "concat-map": "0.0.1"
+            }
+        },
+        "node_modules/braces": {
+            "version": "3.0.2",
+            "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+            "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+            "dev": true,
+            "dependencies": {
+                "fill-range": "^7.0.1"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/call-bind": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+            "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+            "dev": true,
+            "dependencies": {
+                "function-bind": "^1.1.1",
+                "get-intrinsic": "^1.0.2"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/callsites": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+            "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+            "dev": true,
+            "peer": true,
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/chalk": {
+            "version": "4.1.2",
+            "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+            "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "ansi-styles": "^4.1.0",
+                "supports-color": "^7.1.0"
+            },
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/chalk/chalk?sponsor=1"
+            }
+        },
+        "node_modules/color-convert": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+            "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "color-name": "~1.1.4"
+            },
+            "engines": {
+                "node": ">=7.0.0"
+            }
+        },
+        "node_modules/color-name": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+            "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+            "dev": true,
+            "peer": true
+        },
+        "node_modules/combined-stream": {
+            "version": "1.0.8",
+            "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+            "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+            "dependencies": {
+                "delayed-stream": "~1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.8"
+            }
+        },
+        "node_modules/concat-map": {
+            "version": "0.0.1",
+            "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+            "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+            "dev": true
+        },
+        "node_modules/create-require": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+            "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
+            "dev": true
+        },
+        "node_modules/cross-spawn": {
+            "version": "7.0.3",
+            "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+            "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "path-key": "^3.1.0",
+                "shebang-command": "^2.0.0",
+                "which": "^2.0.1"
+            },
+            "engines": {
+                "node": ">= 8"
+            }
+        },
+        "node_modules/cssom": {
+            "version": "0.5.0",
+            "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz",
+            "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw=="
+        },
+        "node_modules/cssstyle": {
+            "version": "2.3.0",
+            "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz",
+            "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==",
+            "dependencies": {
+                "cssom": "~0.3.6"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/cssstyle/node_modules/cssom": {
+            "version": "0.3.8",
+            "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
+            "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg=="
+        },
+        "node_modules/csv-parse": {
+            "version": "5.3.3",
+            "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-5.3.3.tgz",
+            "integrity": "sha512-kEWkAPleNEdhFNkHQpFHu9RYPogsFj3dx6bCxL847fsiLgidzWg0z/O0B1kVWMJUc5ky64zGp18LX2T3DQrOfw=="
+        },
+        "node_modules/data-urls": {
+            "version": "3.0.2",
+            "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz",
+            "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==",
+            "dependencies": {
+                "abab": "^2.0.6",
+                "whatwg-mimetype": "^3.0.0",
+                "whatwg-url": "^11.0.0"
+            },
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/debug": {
+            "version": "4.3.4",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+            "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+            "dependencies": {
+                "ms": "2.1.2"
+            },
+            "engines": {
+                "node": ">=6.0"
+            },
+            "peerDependenciesMeta": {
+                "supports-color": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/decimal.js": {
+            "version": "10.4.3",
+            "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz",
+            "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA=="
+        },
+        "node_modules/deep-is": {
+            "version": "0.1.4",
+            "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+            "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="
+        },
+        "node_modules/define-properties": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
+            "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==",
+            "dev": true,
+            "dependencies": {
+                "has-property-descriptors": "^1.0.0",
+                "object-keys": "^1.1.1"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/delayed-stream": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+            "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+            "engines": {
+                "node": ">=0.4.0"
+            }
+        },
+        "node_modules/diff": {
+            "version": "4.0.2",
+            "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+            "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+            "dev": true,
+            "engines": {
+                "node": ">=0.3.1"
+            }
+        },
+        "node_modules/dir-glob": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+            "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+            "dev": true,
+            "dependencies": {
+                "path-type": "^4.0.0"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/doctrine": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+            "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "esutils": "^2.0.2"
+            },
+            "engines": {
+                "node": ">=6.0.0"
+            }
+        },
+        "node_modules/domexception": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz",
+            "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==",
+            "dependencies": {
+                "webidl-conversions": "^7.0.0"
+            },
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/entities": {
+            "version": "4.4.0",
+            "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz",
+            "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==",
+            "engines": {
+                "node": ">=0.12"
+            },
+            "funding": {
+                "url": "https://github.com/fb55/entities?sponsor=1"
+            }
+        },
+        "node_modules/es-abstract": {
+            "version": "1.21.0",
+            "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.0.tgz",
+            "integrity": "sha512-GUGtW7eXQay0c+PRq0sGIKSdaBorfVqsCMhGHo4elP7YVqZu9nCZS4UkK4gv71gOWNMra/PaSKD3ao1oWExO0g==",
+            "dev": true,
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "es-set-tostringtag": "^2.0.0",
+                "es-to-primitive": "^1.2.1",
+                "function-bind": "^1.1.1",
+                "function.prototype.name": "^1.1.5",
+                "get-intrinsic": "^1.1.3",
+                "get-symbol-description": "^1.0.0",
+                "globalthis": "^1.0.3",
+                "gopd": "^1.0.1",
+                "has": "^1.0.3",
+                "has-property-descriptors": "^1.0.0",
+                "has-proto": "^1.0.1",
+                "has-symbols": "^1.0.3",
+                "internal-slot": "^1.0.4",
+                "is-array-buffer": "^3.0.0",
+                "is-callable": "^1.2.7",
+                "is-negative-zero": "^2.0.2",
+                "is-regex": "^1.1.4",
+                "is-shared-array-buffer": "^1.0.2",
+                "is-string": "^1.0.7",
+                "is-typed-array": "^1.1.10",
+                "is-weakref": "^1.0.2",
+                "object-inspect": "^1.12.2",
+                "object-keys": "^1.1.1",
+                "object.assign": "^4.1.4",
+                "regexp.prototype.flags": "^1.4.3",
+                "safe-regex-test": "^1.0.0",
+                "string.prototype.trimend": "^1.0.6",
+                "string.prototype.trimstart": "^1.0.6",
+                "typed-array-length": "^1.0.4",
+                "unbox-primitive": "^1.0.2",
+                "which-typed-array": "^1.1.9"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/es-set-tostringtag": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz",
+            "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==",
+            "dev": true,
+            "dependencies": {
+                "get-intrinsic": "^1.1.3",
+                "has": "^1.0.3",
+                "has-tostringtag": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/es-shim-unscopables": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz",
+            "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==",
+            "dev": true,
+            "dependencies": {
+                "has": "^1.0.3"
+            }
+        },
+        "node_modules/es-to-primitive": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+            "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+            "dev": true,
+            "dependencies": {
+                "is-callable": "^1.1.4",
+                "is-date-object": "^1.0.1",
+                "is-symbol": "^1.0.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/escape-string-regexp": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+            "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+            "dev": true,
+            "peer": true,
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/escodegen": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz",
+            "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==",
+            "dependencies": {
+                "esprima": "^4.0.1",
+                "estraverse": "^5.2.0",
+                "esutils": "^2.0.2",
+                "optionator": "^0.8.1"
+            },
+            "bin": {
+                "escodegen": "bin/escodegen.js",
+                "esgenerate": "bin/esgenerate.js"
+            },
+            "engines": {
+                "node": ">=6.0"
+            },
+            "optionalDependencies": {
+                "source-map": "~0.6.1"
+            }
+        },
+        "node_modules/eslint": {
+            "version": "8.31.0",
+            "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.31.0.tgz",
+            "integrity": "sha512-0tQQEVdmPZ1UtUKXjX7EMm9BlgJ08G90IhWh0PKDCb3ZLsgAOHI8fYSIzYVZej92zsgq+ft0FGsxhJ3xo2tbuA==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "@eslint/eslintrc": "^1.4.1",
+                "@humanwhocodes/config-array": "^0.11.8",
+                "@humanwhocodes/module-importer": "^1.0.1",
+                "@nodelib/fs.walk": "^1.2.8",
+                "ajv": "^6.10.0",
+                "chalk": "^4.0.0",
+                "cross-spawn": "^7.0.2",
+                "debug": "^4.3.2",
+                "doctrine": "^3.0.0",
+                "escape-string-regexp": "^4.0.0",
+                "eslint-scope": "^7.1.1",
+                "eslint-utils": "^3.0.0",
+                "eslint-visitor-keys": "^3.3.0",
+                "espree": "^9.4.0",
+                "esquery": "^1.4.0",
+                "esutils": "^2.0.2",
+                "fast-deep-equal": "^3.1.3",
+                "file-entry-cache": "^6.0.1",
+                "find-up": "^5.0.0",
+                "glob-parent": "^6.0.2",
+                "globals": "^13.19.0",
+                "grapheme-splitter": "^1.0.4",
+                "ignore": "^5.2.0",
+                "import-fresh": "^3.0.0",
+                "imurmurhash": "^0.1.4",
+                "is-glob": "^4.0.0",
+                "is-path-inside": "^3.0.3",
+                "js-sdsl": "^4.1.4",
+                "js-yaml": "^4.1.0",
+                "json-stable-stringify-without-jsonify": "^1.0.1",
+                "levn": "^0.4.1",
+                "lodash.merge": "^4.6.2",
+                "minimatch": "^3.1.2",
+                "natural-compare": "^1.4.0",
+                "optionator": "^0.9.1",
+                "regexpp": "^3.2.0",
+                "strip-ansi": "^6.0.1",
+                "strip-json-comments": "^3.1.0",
+                "text-table": "^0.2.0"
+            },
+            "bin": {
+                "eslint": "bin/eslint.js"
+            },
+            "engines": {
+                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+            },
+            "funding": {
+                "url": "https://opencollective.com/eslint"
+            }
+        },
+        "node_modules/eslint-config-prettier": {
+            "version": "8.6.0",
+            "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz",
+            "integrity": "sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA==",
+            "dev": true,
+            "bin": {
+                "eslint-config-prettier": "bin/cli.js"
+            },
+            "peerDependencies": {
+                "eslint": ">=7.0.0"
+            }
+        },
+        "node_modules/eslint-import-resolver-node": {
+            "version": "0.3.6",
+            "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz",
+            "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==",
+            "dev": true,
+            "dependencies": {
+                "debug": "^3.2.7",
+                "resolve": "^1.20.0"
+            }
+        },
+        "node_modules/eslint-import-resolver-node/node_modules/debug": {
+            "version": "3.2.7",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+            "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+            "dev": true,
+            "dependencies": {
+                "ms": "^2.1.1"
+            }
+        },
+        "node_modules/eslint-module-utils": {
+            "version": "2.7.4",
+            "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz",
+            "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==",
+            "dev": true,
+            "dependencies": {
+                "debug": "^3.2.7"
+            },
+            "engines": {
+                "node": ">=4"
+            },
+            "peerDependenciesMeta": {
+                "eslint": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/eslint-module-utils/node_modules/debug": {
+            "version": "3.2.7",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+            "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+            "dev": true,
+            "dependencies": {
+                "ms": "^2.1.1"
+            }
+        },
+        "node_modules/eslint-plugin-import": {
+            "version": "2.26.0",
+            "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz",
+            "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==",
+            "dev": true,
+            "dependencies": {
+                "array-includes": "^3.1.4",
+                "array.prototype.flat": "^1.2.5",
+                "debug": "^2.6.9",
+                "doctrine": "^2.1.0",
+                "eslint-import-resolver-node": "^0.3.6",
+                "eslint-module-utils": "^2.7.3",
+                "has": "^1.0.3",
+                "is-core-module": "^2.8.1",
+                "is-glob": "^4.0.3",
+                "minimatch": "^3.1.2",
+                "object.values": "^1.1.5",
+                "resolve": "^1.22.0",
+                "tsconfig-paths": "^3.14.1"
+            },
+            "engines": {
+                "node": ">=4"
+            },
+            "peerDependencies": {
+                "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8"
+            }
+        },
+        "node_modules/eslint-plugin-import/node_modules/debug": {
+            "version": "2.6.9",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+            "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+            "dev": true,
+            "dependencies": {
+                "ms": "2.0.0"
+            }
+        },
+        "node_modules/eslint-plugin-import/node_modules/doctrine": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+            "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+            "dev": true,
+            "dependencies": {
+                "esutils": "^2.0.2"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/eslint-plugin-import/node_modules/ms": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+            "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+            "dev": true
+        },
+        "node_modules/eslint-plugin-prettier": {
+            "version": "4.2.1",
+            "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz",
+            "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==",
+            "dev": true,
+            "dependencies": {
+                "prettier-linter-helpers": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=12.0.0"
+            },
+            "peerDependencies": {
+                "eslint": ">=7.28.0",
+                "prettier": ">=2.0.0"
+            },
+            "peerDependenciesMeta": {
+                "eslint-config-prettier": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/eslint-plugin-simple-import-sort": {
+            "version": "8.0.0",
+            "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-8.0.0.tgz",
+            "integrity": "sha512-bXgJQ+lqhtQBCuWY/FUWdB27j4+lqcvXv5rUARkzbeWLwea+S5eBZEQrhnO+WgX3ZoJHVj0cn943iyXwByHHQw==",
+            "dev": true,
+            "peerDependencies": {
+                "eslint": ">=5.0.0"
+            }
+        },
+        "node_modules/eslint-scope": {
+            "version": "7.1.1",
+            "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
+            "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "esrecurse": "^4.3.0",
+                "estraverse": "^5.2.0"
+            },
+            "engines": {
+                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+            }
+        },
+        "node_modules/eslint-utils": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
+            "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
+            "dev": true,
+            "dependencies": {
+                "eslint-visitor-keys": "^2.0.0"
+            },
+            "engines": {
+                "node": "^10.0.0 || ^12.0.0 || >= 14.0.0"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/mysticatea"
+            },
+            "peerDependencies": {
+                "eslint": ">=5"
+            }
+        },
+        "node_modules/eslint-utils/node_modules/eslint-visitor-keys": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+            "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
+            "dev": true,
+            "engines": {
+                "node": ">=10"
+            }
+        },
+        "node_modules/eslint-visitor-keys": {
+            "version": "3.3.0",
+            "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz",
+            "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==",
+            "dev": true,
+            "engines": {
+                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+            }
+        },
+        "node_modules/eslint/node_modules/levn": {
+            "version": "0.4.1",
+            "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+            "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "prelude-ls": "^1.2.1",
+                "type-check": "~0.4.0"
+            },
+            "engines": {
+                "node": ">= 0.8.0"
+            }
+        },
+        "node_modules/eslint/node_modules/optionator": {
+            "version": "0.9.1",
+            "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
+            "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "deep-is": "^0.1.3",
+                "fast-levenshtein": "^2.0.6",
+                "levn": "^0.4.1",
+                "prelude-ls": "^1.2.1",
+                "type-check": "^0.4.0",
+                "word-wrap": "^1.2.3"
+            },
+            "engines": {
+                "node": ">= 0.8.0"
+            }
+        },
+        "node_modules/eslint/node_modules/prelude-ls": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+            "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+            "dev": true,
+            "peer": true,
+            "engines": {
+                "node": ">= 0.8.0"
+            }
+        },
+        "node_modules/eslint/node_modules/type-check": {
+            "version": "0.4.0",
+            "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+            "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "prelude-ls": "^1.2.1"
+            },
+            "engines": {
+                "node": ">= 0.8.0"
+            }
+        },
+        "node_modules/espree": {
+            "version": "9.4.1",
+            "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz",
+            "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "acorn": "^8.8.0",
+                "acorn-jsx": "^5.3.2",
+                "eslint-visitor-keys": "^3.3.0"
+            },
+            "engines": {
+                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+            },
+            "funding": {
+                "url": "https://opencollective.com/eslint"
+            }
+        },
+        "node_modules/esprima": {
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+            "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+            "bin": {
+                "esparse": "bin/esparse.js",
+                "esvalidate": "bin/esvalidate.js"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/esquery": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
+            "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "estraverse": "^5.1.0"
+            },
+            "engines": {
+                "node": ">=0.10"
+            }
+        },
+        "node_modules/esrecurse": {
+            "version": "4.3.0",
+            "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+            "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+            "dev": true,
+            "dependencies": {
+                "estraverse": "^5.2.0"
+            },
+            "engines": {
+                "node": ">=4.0"
+            }
+        },
+        "node_modules/estraverse": {
+            "version": "5.3.0",
+            "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+            "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+            "engines": {
+                "node": ">=4.0"
+            }
+        },
+        "node_modules/esutils": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+            "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/fast-deep-equal": {
+            "version": "3.1.3",
+            "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+            "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+            "dev": true,
+            "peer": true
+        },
+        "node_modules/fast-diff": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
+            "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
+            "dev": true
+        },
+        "node_modules/fast-glob": {
+            "version": "3.2.12",
+            "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
+            "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
+            "dev": true,
+            "dependencies": {
+                "@nodelib/fs.stat": "^2.0.2",
+                "@nodelib/fs.walk": "^1.2.3",
+                "glob-parent": "^5.1.2",
+                "merge2": "^1.3.0",
+                "micromatch": "^4.0.4"
+            },
+            "engines": {
+                "node": ">=8.6.0"
+            }
+        },
+        "node_modules/fast-glob/node_modules/glob-parent": {
+            "version": "5.1.2",
+            "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+            "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+            "dev": true,
+            "dependencies": {
+                "is-glob": "^4.0.1"
+            },
+            "engines": {
+                "node": ">= 6"
+            }
+        },
+        "node_modules/fast-json-stable-stringify": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+            "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+            "dev": true,
+            "peer": true
+        },
+        "node_modules/fast-levenshtein": {
+            "version": "2.0.6",
+            "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+            "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="
+        },
+        "node_modules/fastq": {
+            "version": "1.15.0",
+            "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
+            "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
+            "dev": true,
+            "dependencies": {
+                "reusify": "^1.0.4"
+            }
+        },
+        "node_modules/file-entry-cache": {
+            "version": "6.0.1",
+            "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+            "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "flat-cache": "^3.0.4"
+            },
+            "engines": {
+                "node": "^10.12.0 || >=12.0.0"
+            }
+        },
+        "node_modules/fill-range": {
+            "version": "7.0.1",
+            "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+            "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+            "dev": true,
+            "dependencies": {
+                "to-regex-range": "^5.0.1"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/find-up": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+            "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "locate-path": "^6.0.0",
+                "path-exists": "^4.0.0"
+            },
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/flat-cache": {
+            "version": "3.0.4",
+            "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
+            "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "flatted": "^3.1.0",
+                "rimraf": "^3.0.2"
+            },
+            "engines": {
+                "node": "^10.12.0 || >=12.0.0"
+            }
+        },
+        "node_modules/flatted": {
+            "version": "3.2.7",
+            "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
+            "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
+            "dev": true,
+            "peer": true
+        },
+        "node_modules/for-each": {
+            "version": "0.3.3",
+            "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
+            "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
+            "dev": true,
+            "dependencies": {
+                "is-callable": "^1.1.3"
+            }
+        },
+        "node_modules/form-data": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+            "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+            "dependencies": {
+                "asynckit": "^0.4.0",
+                "combined-stream": "^1.0.8",
+                "mime-types": "^2.1.12"
+            },
+            "engines": {
+                "node": ">= 6"
+            }
+        },
+        "node_modules/fs.realpath": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+            "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+            "dev": true,
+            "peer": true
+        },
+        "node_modules/function-bind": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+            "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+            "dev": true
+        },
+        "node_modules/function.prototype.name": {
+            "version": "1.1.5",
+            "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz",
+            "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==",
+            "dev": true,
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.19.0",
+                "functions-have-names": "^1.2.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/functions-have-names": {
+            "version": "1.2.3",
+            "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+            "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
+            "dev": true,
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/get-intrinsic": {
+            "version": "1.1.3",
+            "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
+            "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==",
+            "dev": true,
+            "dependencies": {
+                "function-bind": "^1.1.1",
+                "has": "^1.0.3",
+                "has-symbols": "^1.0.3"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/get-symbol-description": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
+            "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==",
+            "dev": true,
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "get-intrinsic": "^1.1.1"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/glob": {
+            "version": "7.2.3",
+            "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+            "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "fs.realpath": "^1.0.0",
+                "inflight": "^1.0.4",
+                "inherits": "2",
+                "minimatch": "^3.1.1",
+                "once": "^1.3.0",
+                "path-is-absolute": "^1.0.0"
+            },
+            "engines": {
+                "node": "*"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
+            }
+        },
+        "node_modules/glob-parent": {
+            "version": "6.0.2",
+            "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+            "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "is-glob": "^4.0.3"
+            },
+            "engines": {
+                "node": ">=10.13.0"
+            }
+        },
+        "node_modules/globals": {
+            "version": "13.19.0",
+            "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz",
+            "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "type-fest": "^0.20.2"
+            },
+            "engines": {
+                "node": ">=8"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/globalthis": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz",
+            "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==",
+            "dev": true,
+            "dependencies": {
+                "define-properties": "^1.1.3"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/globby": {
+            "version": "11.1.0",
+            "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+            "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+            "dev": true,
+            "dependencies": {
+                "array-union": "^2.1.0",
+                "dir-glob": "^3.0.1",
+                "fast-glob": "^3.2.9",
+                "ignore": "^5.2.0",
+                "merge2": "^1.4.1",
+                "slash": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/gopd": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+            "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+            "dev": true,
+            "dependencies": {
+                "get-intrinsic": "^1.1.3"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/grapheme-splitter": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
+            "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
+            "dev": true,
+            "peer": true
+        },
+        "node_modules/has": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+            "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+            "dev": true,
+            "dependencies": {
+                "function-bind": "^1.1.1"
+            },
+            "engines": {
+                "node": ">= 0.4.0"
+            }
+        },
+        "node_modules/has-bigints": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
+            "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==",
+            "dev": true,
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/has-flag": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+            "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+            "dev": true,
+            "peer": true,
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/has-property-descriptors": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
+            "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
+            "dev": true,
+            "dependencies": {
+                "get-intrinsic": "^1.1.1"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/has-proto": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
+            "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
+            "dev": true,
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/has-symbols": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+            "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+            "dev": true,
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/has-tostringtag": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
+            "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
+            "dev": true,
+            "dependencies": {
+                "has-symbols": "^1.0.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/high5": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/high5/-/high5-1.0.0.tgz",
+            "integrity": "sha512-xucW/5M1hd+p6bj530wtRSKwqUQrgiIgOWepi4Di9abkonZaxhTDf0zrqqraxfZSXBcFSuc1/WVGBIlqSe1Hdw==",
+            "dependencies": {
+                "entities": "1.0"
+            }
+        },
+        "node_modules/high5/node_modules/entities": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz",
+            "integrity": "sha512-LbLqfXgJMmy81t+7c14mnulFHJ170cM6E+0vMXR9k/ZiZwgX8i5pNgjTCX3SO4VeUsFLV+8InixoretwU+MjBQ=="
+        },
+        "node_modules/html-encoding-sniffer": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz",
+            "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==",
+            "dependencies": {
+                "whatwg-encoding": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/http-proxy-agent": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
+            "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
+            "dependencies": {
+                "@tootallnate/once": "2",
+                "agent-base": "6",
+                "debug": "4"
+            },
+            "engines": {
+                "node": ">= 6"
+            }
+        },
+        "node_modules/https-proxy-agent": {
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+            "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+            "dependencies": {
+                "agent-base": "6",
+                "debug": "4"
+            },
+            "engines": {
+                "node": ">= 6"
+            }
+        },
+        "node_modules/iconv-lite": {
+            "version": "0.6.3",
+            "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+            "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+            "dependencies": {
+                "safer-buffer": ">= 2.1.2 < 3.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/ignore": {
+            "version": "5.2.4",
+            "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
+            "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
+            "dev": true,
+            "engines": {
+                "node": ">= 4"
+            }
+        },
+        "node_modules/import-fresh": {
+            "version": "3.3.0",
+            "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+            "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "parent-module": "^1.0.0",
+                "resolve-from": "^4.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/imurmurhash": {
+            "version": "0.1.4",
+            "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+            "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+            "dev": true,
+            "peer": true,
+            "engines": {
+                "node": ">=0.8.19"
+            }
+        },
+        "node_modules/inflight": {
+            "version": "1.0.6",
+            "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+            "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "once": "^1.3.0",
+                "wrappy": "1"
+            }
+        },
+        "node_modules/inherits": {
+            "version": "2.0.4",
+            "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+            "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+            "dev": true,
+            "peer": true
+        },
+        "node_modules/internal-slot": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz",
+            "integrity": "sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==",
+            "dev": true,
+            "dependencies": {
+                "get-intrinsic": "^1.1.3",
+                "has": "^1.0.3",
+                "side-channel": "^1.0.4"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/is-array-buffer": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.1.tgz",
+            "integrity": "sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==",
+            "dev": true,
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "get-intrinsic": "^1.1.3",
+                "is-typed-array": "^1.1.10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-bigint": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
+            "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
+            "dev": true,
+            "dependencies": {
+                "has-bigints": "^1.0.1"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-boolean-object": {
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
+            "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
+            "dev": true,
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "has-tostringtag": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-callable": {
+            "version": "1.2.7",
+            "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+            "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+            "dev": true,
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-core-module": {
+            "version": "2.11.0",
+            "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
+            "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
+            "dev": true,
+            "dependencies": {
+                "has": "^1.0.3"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-date-object": {
+            "version": "1.0.5",
+            "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
+            "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
+            "dev": true,
+            "dependencies": {
+                "has-tostringtag": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-extglob": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+            "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+            "dev": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/is-glob": {
+            "version": "4.0.3",
+            "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+            "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+            "dev": true,
+            "dependencies": {
+                "is-extglob": "^2.1.1"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/is-negative-zero": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
+            "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==",
+            "dev": true,
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-number": {
+            "version": "7.0.0",
+            "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+            "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+            "dev": true,
+            "engines": {
+                "node": ">=0.12.0"
+            }
+        },
+        "node_modules/is-number-object": {
+            "version": "1.0.7",
+            "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
+            "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
+            "dev": true,
+            "dependencies": {
+                "has-tostringtag": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-path-inside": {
+            "version": "3.0.3",
+            "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+            "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+            "dev": true,
+            "peer": true,
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/is-potential-custom-element-name": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
+            "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="
+        },
+        "node_modules/is-regex": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
+            "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
+            "dev": true,
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "has-tostringtag": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-shared-array-buffer": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
+            "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==",
+            "dev": true,
+            "dependencies": {
+                "call-bind": "^1.0.2"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-string": {
+            "version": "1.0.7",
+            "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
+            "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
+            "dev": true,
+            "dependencies": {
+                "has-tostringtag": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-symbol": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
+            "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
+            "dev": true,
+            "dependencies": {
+                "has-symbols": "^1.0.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-typed-array": {
+            "version": "1.1.10",
+            "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz",
+            "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==",
+            "dev": true,
+            "dependencies": {
+                "available-typed-arrays": "^1.0.5",
+                "call-bind": "^1.0.2",
+                "for-each": "^0.3.3",
+                "gopd": "^1.0.1",
+                "has-tostringtag": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-weakref": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
+            "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==",
+            "dev": true,
+            "dependencies": {
+                "call-bind": "^1.0.2"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/isexe": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+            "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+            "dev": true,
+            "peer": true
+        },
+        "node_modules/js-sdsl": {
+            "version": "4.2.0",
+            "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz",
+            "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==",
+            "dev": true,
+            "peer": true,
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/js-sdsl"
+            }
+        },
+        "node_modules/js-yaml": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+            "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "argparse": "^2.0.1"
+            },
+            "bin": {
+                "js-yaml": "bin/js-yaml.js"
+            }
+        },
+        "node_modules/jsdom": {
+            "version": "20.0.3",
+            "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz",
+            "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==",
+            "dependencies": {
+                "abab": "^2.0.6",
+                "acorn": "^8.8.1",
+                "acorn-globals": "^7.0.0",
+                "cssom": "^0.5.0",
+                "cssstyle": "^2.3.0",
+                "data-urls": "^3.0.2",
+                "decimal.js": "^10.4.2",
+                "domexception": "^4.0.0",
+                "escodegen": "^2.0.0",
+                "form-data": "^4.0.0",
+                "html-encoding-sniffer": "^3.0.0",
+                "http-proxy-agent": "^5.0.0",
+                "https-proxy-agent": "^5.0.1",
+                "is-potential-custom-element-name": "^1.0.1",
+                "nwsapi": "^2.2.2",
+                "parse5": "^7.1.1",
+                "saxes": "^6.0.0",
+                "symbol-tree": "^3.2.4",
+                "tough-cookie": "^4.1.2",
+                "w3c-xmlserializer": "^4.0.0",
+                "webidl-conversions": "^7.0.0",
+                "whatwg-encoding": "^2.0.0",
+                "whatwg-mimetype": "^3.0.0",
+                "whatwg-url": "^11.0.0",
+                "ws": "^8.11.0",
+                "xml-name-validator": "^4.0.0"
+            },
+            "engines": {
+                "node": ">=14"
+            },
+            "peerDependencies": {
+                "canvas": "^2.5.0"
+            },
+            "peerDependenciesMeta": {
+                "canvas": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/json-schema-traverse": {
+            "version": "0.4.1",
+            "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+            "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+            "dev": true,
+            "peer": true
+        },
+        "node_modules/json-stable-stringify-without-jsonify": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+            "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+            "dev": true,
+            "peer": true
+        },
+        "node_modules/json5": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+            "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
+            "dev": true,
+            "dependencies": {
+                "minimist": "^1.2.0"
+            },
+            "bin": {
+                "json5": "lib/cli.js"
+            }
+        },
+        "node_modules/levn": {
+            "version": "0.3.0",
+            "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+            "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==",
+            "dependencies": {
+                "prelude-ls": "~1.1.2",
+                "type-check": "~0.3.2"
+            },
+            "engines": {
+                "node": ">= 0.8.0"
+            }
+        },
+        "node_modules/locate-path": {
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+            "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "p-locate": "^5.0.0"
+            },
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/lodash.merge": {
+            "version": "4.6.2",
+            "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+            "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+            "dev": true,
+            "peer": true
+        },
+        "node_modules/lru-cache": {
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+            "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+            "dev": true,
+            "dependencies": {
+                "yallist": "^4.0.0"
+            },
+            "engines": {
+                "node": ">=10"
+            }
+        },
+        "node_modules/make-error": {
+            "version": "1.3.6",
+            "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+            "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+            "dev": true
+        },
+        "node_modules/merge2": {
+            "version": "1.4.1",
+            "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+            "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+            "dev": true,
+            "engines": {
+                "node": ">= 8"
+            }
+        },
+        "node_modules/micromatch": {
+            "version": "4.0.5",
+            "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+            "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+            "dev": true,
+            "dependencies": {
+                "braces": "^3.0.2",
+                "picomatch": "^2.3.1"
+            },
+            "engines": {
+                "node": ">=8.6"
+            }
+        },
+        "node_modules/mime-db": {
+            "version": "1.52.0",
+            "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+            "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/mime-types": {
+            "version": "2.1.35",
+            "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+            "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+            "dependencies": {
+                "mime-db": "1.52.0"
+            },
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/minimatch": {
+            "version": "3.1.2",
+            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+            "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+            "dev": true,
+            "dependencies": {
+                "brace-expansion": "^1.1.7"
+            },
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/minimist": {
+            "version": "1.2.7",
+            "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
+            "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==",
+            "dev": true,
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/ms": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+            "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+        },
+        "node_modules/natural-compare": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+            "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+            "dev": true,
+            "peer": true
+        },
+        "node_modules/natural-compare-lite": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz",
+            "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==",
+            "dev": true
+        },
+        "node_modules/nwsapi": {
+            "version": "2.2.2",
+            "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz",
+            "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw=="
+        },
+        "node_modules/object-inspect": {
+            "version": "1.12.2",
+            "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
+            "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==",
+            "dev": true,
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/object-keys": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+            "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+            "dev": true,
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/object.assign": {
+            "version": "4.1.4",
+            "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz",
+            "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==",
+            "dev": true,
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.4",
+                "has-symbols": "^1.0.3",
+                "object-keys": "^1.1.1"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/object.values": {
+            "version": "1.1.6",
+            "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz",
+            "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==",
+            "dev": true,
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.4",
+                "es-abstract": "^1.20.4"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/once": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+            "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "wrappy": "1"
+            }
+        },
+        "node_modules/optionator": {
+            "version": "0.8.3",
+            "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
+            "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
+            "dependencies": {
+                "deep-is": "~0.1.3",
+                "fast-levenshtein": "~2.0.6",
+                "levn": "~0.3.0",
+                "prelude-ls": "~1.1.2",
+                "type-check": "~0.3.2",
+                "word-wrap": "~1.2.3"
+            },
+            "engines": {
+                "node": ">= 0.8.0"
+            }
+        },
+        "node_modules/p-limit": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+            "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "yocto-queue": "^0.1.0"
+            },
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/p-locate": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+            "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "p-limit": "^3.0.2"
+            },
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/parent-module": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+            "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "callsites": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/parse5": {
+            "version": "7.1.2",
+            "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz",
+            "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==",
+            "dependencies": {
+                "entities": "^4.4.0"
+            },
+            "funding": {
+                "url": "https://github.com/inikulin/parse5?sponsor=1"
+            }
+        },
+        "node_modules/path-exists": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+            "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+            "dev": true,
+            "peer": true,
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/path-is-absolute": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+            "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+            "dev": true,
+            "peer": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/path-key": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+            "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+            "dev": true,
+            "peer": true,
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/path-parse": {
+            "version": "1.0.7",
+            "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+            "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+            "dev": true
+        },
+        "node_modules/path-type": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+            "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+            "dev": true,
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/picomatch": {
+            "version": "2.3.1",
+            "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+            "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+            "dev": true,
+            "engines": {
+                "node": ">=8.6"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/jonschlinkert"
+            }
+        },
+        "node_modules/prelude-ls": {
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+            "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==",
+            "engines": {
+                "node": ">= 0.8.0"
+            }
+        },
+        "node_modules/prettier": {
+            "version": "2.8.2",
+            "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.2.tgz",
+            "integrity": "sha512-BtRV9BcncDyI2tsuS19zzhzoxD8Dh8LiCx7j7tHzrkz8GFXAexeWFdi22mjE1d16dftH2qNaytVxqiRTGlMfpw==",
+            "dev": true,
+            "peer": true,
+            "bin": {
+                "prettier": "bin-prettier.js"
+            },
+            "engines": {
+                "node": ">=10.13.0"
+            },
+            "funding": {
+                "url": "https://github.com/prettier/prettier?sponsor=1"
+            }
+        },
+        "node_modules/prettier-linter-helpers": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+            "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+            "dev": true,
+            "dependencies": {
+                "fast-diff": "^1.1.2"
+            },
+            "engines": {
+                "node": ">=6.0.0"
+            }
+        },
+        "node_modules/psl": {
+            "version": "1.9.0",
+            "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
+            "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag=="
+        },
+        "node_modules/punycode": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+            "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/querystringify": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
+            "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
+        },
+        "node_modules/queue-microtask": {
+            "version": "1.2.3",
+            "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+            "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+            "dev": true,
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/feross"
+                },
+                {
+                    "type": "patreon",
+                    "url": "https://www.patreon.com/feross"
+                },
+                {
+                    "type": "consulting",
+                    "url": "https://feross.org/support"
+                }
+            ]
+        },
+        "node_modules/regexp.prototype.flags": {
+            "version": "1.4.3",
+            "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
+            "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==",
+            "dev": true,
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.3",
+                "functions-have-names": "^1.2.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/regexpp": {
+            "version": "3.2.0",
+            "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
+            "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
+            "dev": true,
+            "engines": {
+                "node": ">=8"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/mysticatea"
+            }
+        },
+        "node_modules/requires-port": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+            "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
+        },
+        "node_modules/resolve": {
+            "version": "1.22.1",
+            "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
+            "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
+            "dev": true,
+            "dependencies": {
+                "is-core-module": "^2.9.0",
+                "path-parse": "^1.0.7",
+                "supports-preserve-symlinks-flag": "^1.0.0"
+            },
+            "bin": {
+                "resolve": "bin/resolve"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/resolve-from": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+            "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+            "dev": true,
+            "peer": true,
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/reusify": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+            "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+            "dev": true,
+            "engines": {
+                "iojs": ">=1.0.0",
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/rimraf": {
+            "version": "3.0.2",
+            "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+            "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "glob": "^7.1.3"
+            },
+            "bin": {
+                "rimraf": "bin.js"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
+            }
+        },
+        "node_modules/run-parallel": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+            "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+            "dev": true,
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/feross"
+                },
+                {
+                    "type": "patreon",
+                    "url": "https://www.patreon.com/feross"
+                },
+                {
+                    "type": "consulting",
+                    "url": "https://feross.org/support"
+                }
+            ],
+            "dependencies": {
+                "queue-microtask": "^1.2.2"
+            }
+        },
+        "node_modules/safe-regex-test": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz",
+            "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==",
+            "dev": true,
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "get-intrinsic": "^1.1.3",
+                "is-regex": "^1.1.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/safer-buffer": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+            "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+        },
+        "node_modules/saxes": {
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
+            "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
+            "dependencies": {
+                "xmlchars": "^2.2.0"
+            },
+            "engines": {
+                "node": ">=v12.22.7"
+            }
+        },
+        "node_modules/semver": {
+            "version": "7.3.8",
+            "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
+            "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+            "dev": true,
+            "dependencies": {
+                "lru-cache": "^6.0.0"
+            },
+            "bin": {
+                "semver": "bin/semver.js"
+            },
+            "engines": {
+                "node": ">=10"
+            }
+        },
+        "node_modules/shebang-command": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+            "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "shebang-regex": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/shebang-regex": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+            "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+            "dev": true,
+            "peer": true,
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/side-channel": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
+            "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+            "dev": true,
+            "dependencies": {
+                "call-bind": "^1.0.0",
+                "get-intrinsic": "^1.0.2",
+                "object-inspect": "^1.9.0"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/slash": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+            "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+            "dev": true,
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/source-map": {
+            "version": "0.6.1",
+            "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+            "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+            "optional": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/string.prototype.trimend": {
+            "version": "1.0.6",
+            "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz",
+            "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==",
+            "dev": true,
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.4",
+                "es-abstract": "^1.20.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/string.prototype.trimstart": {
+            "version": "1.0.6",
+            "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz",
+            "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==",
+            "dev": true,
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.4",
+                "es-abstract": "^1.20.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/strip-ansi": {
+            "version": "6.0.1",
+            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+            "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "ansi-regex": "^5.0.1"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/strip-bom": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+            "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+            "dev": true,
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/strip-json-comments": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+            "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+            "dev": true,
+            "peer": true,
+            "engines": {
+                "node": ">=8"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/supports-color": {
+            "version": "7.2.0",
+            "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+            "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "has-flag": "^4.0.0"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/supports-preserve-symlinks-flag": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+            "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+            "dev": true,
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/symbol-tree": {
+            "version": "3.2.4",
+            "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
+            "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="
+        },
+        "node_modules/text-table": {
+            "version": "0.2.0",
+            "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+            "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+            "dev": true,
+            "peer": true
+        },
+        "node_modules/to-regex-range": {
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+            "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+            "dev": true,
+            "dependencies": {
+                "is-number": "^7.0.0"
+            },
+            "engines": {
+                "node": ">=8.0"
+            }
+        },
+        "node_modules/tough-cookie": {
+            "version": "4.1.2",
+            "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz",
+            "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==",
+            "dependencies": {
+                "psl": "^1.1.33",
+                "punycode": "^2.1.1",
+                "universalify": "^0.2.0",
+                "url-parse": "^1.5.3"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/tr46": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz",
+            "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==",
+            "dependencies": {
+                "punycode": "^2.1.1"
+            },
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/ts-node": {
+            "version": "10.9.1",
+            "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
+            "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
+            "dev": true,
+            "dependencies": {
+                "@cspotcode/source-map-support": "^0.8.0",
+                "@tsconfig/node10": "^1.0.7",
+                "@tsconfig/node12": "^1.0.7",
+                "@tsconfig/node14": "^1.0.0",
+                "@tsconfig/node16": "^1.0.2",
+                "acorn": "^8.4.1",
+                "acorn-walk": "^8.1.1",
+                "arg": "^4.1.0",
+                "create-require": "^1.1.0",
+                "diff": "^4.0.1",
+                "make-error": "^1.1.1",
+                "v8-compile-cache-lib": "^3.0.1",
+                "yn": "3.1.1"
+            },
+            "bin": {
+                "ts-node": "dist/bin.js",
+                "ts-node-cwd": "dist/bin-cwd.js",
+                "ts-node-esm": "dist/bin-esm.js",
+                "ts-node-script": "dist/bin-script.js",
+                "ts-node-transpile-only": "dist/bin-transpile.js",
+                "ts-script": "dist/bin-script-deprecated.js"
+            },
+            "peerDependencies": {
+                "@swc/core": ">=1.2.50",
+                "@swc/wasm": ">=1.2.50",
+                "@types/node": "*",
+                "typescript": ">=2.7"
+            },
+            "peerDependenciesMeta": {
+                "@swc/core": {
+                    "optional": true
+                },
+                "@swc/wasm": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/tsconfig-paths": {
+            "version": "3.14.1",
+            "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz",
+            "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==",
+            "dev": true,
+            "dependencies": {
+                "@types/json5": "^0.0.29",
+                "json5": "^1.0.1",
+                "minimist": "^1.2.6",
+                "strip-bom": "^3.0.0"
+            }
+        },
+        "node_modules/tslib": {
+            "version": "1.14.1",
+            "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+            "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+            "dev": true
+        },
+        "node_modules/tsutils": {
+            "version": "3.21.0",
+            "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
+            "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
+            "dev": true,
+            "dependencies": {
+                "tslib": "^1.8.1"
+            },
+            "engines": {
+                "node": ">= 6"
+            },
+            "peerDependencies": {
+                "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta"
+            }
+        },
+        "node_modules/type-check": {
+            "version": "0.3.2",
+            "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+            "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==",
+            "dependencies": {
+                "prelude-ls": "~1.1.2"
+            },
+            "engines": {
+                "node": ">= 0.8.0"
+            }
+        },
+        "node_modules/type-fest": {
+            "version": "0.20.2",
+            "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+            "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+            "dev": true,
+            "peer": true,
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/typed-array-length": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz",
+            "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==",
+            "dev": true,
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "for-each": "^0.3.3",
+                "is-typed-array": "^1.1.9"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/typescript": {
+            "version": "4.9.4",
+            "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz",
+            "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==",
+            "dev": true,
+            "bin": {
+                "tsc": "bin/tsc",
+                "tsserver": "bin/tsserver"
+            },
+            "engines": {
+                "node": ">=4.2.0"
+            }
+        },
+        "node_modules/unbox-primitive": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
+            "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==",
+            "dev": true,
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "has-bigints": "^1.0.2",
+                "has-symbols": "^1.0.3",
+                "which-boxed-primitive": "^1.0.2"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/universalify": {
+            "version": "0.2.0",
+            "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
+            "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
+            "engines": {
+                "node": ">= 4.0.0"
+            }
+        },
+        "node_modules/uri-js": {
+            "version": "4.4.1",
+            "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+            "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "punycode": "^2.1.0"
+            }
+        },
+        "node_modules/url-parse": {
+            "version": "1.5.10",
+            "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
+            "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
+            "dependencies": {
+                "querystringify": "^2.1.1",
+                "requires-port": "^1.0.0"
+            }
+        },
+        "node_modules/v8-compile-cache-lib": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
+            "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
+            "dev": true
+        },
+        "node_modules/w3c-xmlserializer": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz",
+            "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==",
+            "dependencies": {
+                "xml-name-validator": "^4.0.0"
+            },
+            "engines": {
+                "node": ">=14"
+            }
+        },
+        "node_modules/webidl-conversions": {
+            "version": "7.0.0",
+            "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
+            "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/whatwg-encoding": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz",
+            "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==",
+            "dependencies": {
+                "iconv-lite": "0.6.3"
+            },
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/whatwg-mimetype": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz",
+            "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==",
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/whatwg-url": {
+            "version": "11.0.0",
+            "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz",
+            "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==",
+            "dependencies": {
+                "tr46": "^3.0.0",
+                "webidl-conversions": "^7.0.0"
+            },
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/which": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+            "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+            "dev": true,
+            "peer": true,
+            "dependencies": {
+                "isexe": "^2.0.0"
+            },
+            "bin": {
+                "node-which": "bin/node-which"
+            },
+            "engines": {
+                "node": ">= 8"
+            }
+        },
+        "node_modules/which-boxed-primitive": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
+            "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
+            "dev": true,
+            "dependencies": {
+                "is-bigint": "^1.0.1",
+                "is-boolean-object": "^1.1.0",
+                "is-number-object": "^1.0.4",
+                "is-string": "^1.0.5",
+                "is-symbol": "^1.0.3"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/which-typed-array": {
+            "version": "1.1.9",
+            "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz",
+            "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==",
+            "dev": true,
+            "dependencies": {
+                "available-typed-arrays": "^1.0.5",
+                "call-bind": "^1.0.2",
+                "for-each": "^0.3.3",
+                "gopd": "^1.0.1",
+                "has-tostringtag": "^1.0.0",
+                "is-typed-array": "^1.1.10"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/word-wrap": {
+            "version": "1.2.3",
+            "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+            "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/wrappy": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+            "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+            "dev": true,
+            "peer": true
+        },
+        "node_modules/ws": {
+            "version": "8.11.0",
+            "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
+            "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
+            "engines": {
+                "node": ">=10.0.0"
+            },
+            "peerDependencies": {
+                "bufferutil": "^4.0.1",
+                "utf-8-validate": "^5.0.2"
+            },
+            "peerDependenciesMeta": {
+                "bufferutil": {
+                    "optional": true
+                },
+                "utf-8-validate": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/xml-name-validator": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
+            "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==",
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/xmlchars": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+            "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
+        },
+        "node_modules/yallist": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+            "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+            "dev": true
+        },
+        "node_modules/yn": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+            "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+            "dev": true,
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/yocto-queue": {
+            "version": "0.1.0",
+            "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+            "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+            "dev": true,
+            "peer": true,
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/scripts/token_alignment/package.json b/packages/SystemUI/scripts/token_alignment/package.json
new file mode 100644
index 0000000..2e63668
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/package.json
@@ -0,0 +1,22 @@
+{
+    "dependencies": {
+        "csv-parse": "^5.3.3",
+        "high5": "^1.0.0",
+        "jsdom": "^20.0.3"
+    },
+    "devDependencies": {
+        "@types/jsdom": "^20.0.1",
+        "@types/node": "^18.11.18",
+        "@typescript-eslint/eslint-plugin": "^5.48.0",
+        "eslint-config-prettier": "^8.6.0",
+        "eslint-plugin-import": "^2.26.0",
+        "eslint-plugin-prettier": "^4.2.1",
+        "eslint-plugin-simple-import-sort": "^8.0.0",
+        "ts-node": "^10.9.1",
+        "typescript": "^4.9.4"
+    },
+    "scripts": {
+        "main": "ts-node ./index.ts",
+        "resetRepo": "repo forall -j100  -c 'git restore .'"
+    }
+}
diff --git a/packages/SystemUI/scripts/token_alignment/resources/migrationList.csv b/packages/SystemUI/scripts/token_alignment/resources/migrationList.csv
new file mode 100644
index 0000000..4111bb3
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/resources/migrationList.csv
@@ -0,0 +1,16 @@
+android	material	newDefaultValue	newComment	migrationToken
+colorAccentPrimaryVariant	colorPrimaryContainer			MigTok02
+colorAccentSecondaryVariant	colorSecondaryContainer			MigTok04
+colorAccentTertiary	colorTertiary			MigTok05
+colorAccentTertiaryVariant	colorTertiaryContainer			MigTok06
+colorBackground	colorSurfaceContainer			MigTok07
+colorSurface	colorSurfaceContainer			MigTok08
+colorSurfaceHeader	colorSurfaceContainerHighest			MigTok09
+colorSurfaceHighlight	colorSurfaceBright			MigTok10
+colorSurfaceVariant	colorSurfaceContainerHigh			MigTok11
+textColorOnAccent	colorOnPrimary			MigTok12
+textColorPrimary	colorOnSurface			MigTok13
+textColorPrimaryInverse	colorOnShadeInactive			MigTok14
+textColorSecondary	colorOnSurfaceVariant			MigTok15
+textColorSecondaryInverse	colorOnShadeInactiveVariant			MigTok16
+textColorTertiary	colorOutline			MigTok17
\ No newline at end of file
diff --git a/packages/SystemUI/scripts/token_alignment/resources/whitelist/java.json b/packages/SystemUI/scripts/token_alignment/resources/whitelist/java.json
new file mode 100644
index 0000000..7f55b2d
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/resources/whitelist/java.json
@@ -0,0 +1,30 @@
+[
+    "frameworks/base/core/java/android/app/Notification.java",
+    "packages/apps/Settings/src/com/android/settings/dashboard/profileselector/UserAdapter.java",
+    "packages/apps/Settings/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java",
+    "frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt",
+    "frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt",
+    "packages/apps/WallpaperPicker2/src/com/android/wallpaper/picker/PreviewFragment.java",
+    "packages/apps/WallpaperPicker2/src/com/android/wallpaper/picker/CategorySelectorFragment.java",
+    "packages/apps/Launcher3/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt",
+    "frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java",
+    "vendor/unbundled_google/packages/NexusLauncher/src/com/google/android/apps/nexuslauncher/customize/WallpaperCarouselView.java",
+    "vendor/unbundled_google/packages/NexusLauncher/src/com/google/android/apps/nexuslauncher/quickstep/TaskOverlayFactoryImpl.java",
+    "frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt",
+    "frameworks/base/packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt",
+    "frameworks/base/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt",
+    "frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt",
+    "frameworks/base/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserViewBinder.kt",
+    "frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java",
+    "frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java",
+    "frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java",
+    "frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java",
+    "frameworks/base/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java",
+    "frameworks/base/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipView.java",
+    "frameworks/base/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java",
+    "frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java",
+    "frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java",
+    "frameworks/base/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java",
+    "frameworks/base/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java",
+    "frameworks/base/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java"
+]
\ No newline at end of file
diff --git a/packages/SystemUI/scripts/token_alignment/resources/whitelist/xmls1.json b/packages/SystemUI/scripts/token_alignment/resources/whitelist/xmls1.json
new file mode 100644
index 0000000..1e59773
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/resources/whitelist/xmls1.json
@@ -0,0 +1,184 @@
+[
+    "frameworks/base/core/res/res/color/resolver_profile_tab_selected_bg.xml",
+    "frameworks/base/core/res/res/color-night/resolver_profile_tab_selected_bg.xml",
+    "frameworks/base/core/res/res/drawable/autofill_bottomsheet_background.xml",
+    "frameworks/base/core/res/res/drawable/btn_outlined.xml",
+    "frameworks/base/core/res/res/drawable/btn_tonal.xml",
+    "frameworks/base/core/res/res/drawable/chooser_action_button_bg.xml",
+    "frameworks/base/core/res/res/drawable/chooser_row_layer_list.xml",
+    "frameworks/base/core/res/res/drawable/resolver_outlined_button_bg.xml",
+    "frameworks/base/core/res/res/drawable/resolver_profile_tab_bg.xml",
+    "frameworks/base/core/res/res/drawable/toast_frame.xml",
+    "frameworks/base/core/res/res/drawable/work_widget_mask_view_background.xml",
+    "frameworks/base/core/res/res/layout/app_language_picker_current_locale_item.xml",
+    "frameworks/base/core/res/res/layout/app_language_picker_system_current.xml",
+    "frameworks/base/core/res/res/layout/autofill_save.xml",
+    "frameworks/base/core/res/res/layout/chooser_grid.xml",
+    "frameworks/base/core/res/res/layout/user_switching_dialog.xml",
+    "frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml",
+    "frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml",
+    "frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml",
+    "frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/square_bk.xml",
+    "packages/modules/IntentResolver/java/res/drawable/chooser_action_button_bg.xml",
+    "packages/modules/IntentResolver/java/res/drawable/chooser_row_layer_list.xml",
+    "packages/modules/IntentResolver/java/res/drawable/resolver_outlined_button_bg.xml",
+    "packages/modules/IntentResolver/java/res/drawable/resolver_profile_tab_bg.xml",
+    "packages/modules/IntentResolver/java/res/layout/chooser_grid.xml",
+    "frameworks/base/libs/WindowManager/Shell/res/color/one_handed_tutorial_background_color.xml",
+    "frameworks/base/libs/WindowManager/Shell/res/drawable/bubble_manage_menu_bg.xml",
+    "frameworks/base/libs/WindowManager/Shell/res/drawable/bubble_stack_user_education_bg.xml",
+    "frameworks/base/libs/WindowManager/Shell/res/drawable/bubble_stack_user_education_bg_rtl.xml",
+    "vendor/unbundled_google/packages/SystemUIGoogle/bcsmartspace/res/drawable/bg_smartspace_combination_sub_card.xml",
+    "vendor/unbundled_google/packages/SystemUIGoogle/bcsmartspace/res/layout/smartspace_combination_sub_card.xml",
+    "packages/apps/Launcher3/res/drawable/bg_rounded_corner_bottom_sheet_handle.xml",
+    "packages/apps/Launcher3/res/drawable/rounded_action_button.xml",
+    "packages/apps/Launcher3/res/drawable/work_card.xml",
+    "packages/apps/Nfc/res/color/nfc_icon.xml",
+    "packages/apps/Nfc/res/color-night/nfc_icon.xml",
+    "packages/apps/Launcher3/quickstep/res/drawable/bg_overview_clear_all_button.xml",
+    "packages/apps/Launcher3/quickstep/res/drawable/bg_sandbox_feedback.xml",
+    "packages/apps/Launcher3/quickstep/res/drawable/bg_wellbeing_toast.xml",
+    "packages/apps/Launcher3/quickstep/res/drawable/button_taskbar_edu_bordered.xml",
+    "packages/apps/Launcher3/quickstep/res/drawable/button_taskbar_edu_colored.xml",
+    "packages/apps/Launcher3/quickstep/res/drawable/split_instructions_background.xml",
+    "packages/apps/Launcher3/quickstep/res/drawable/task_menu_item_bg.xml",
+    "packages/apps/Launcher3/quickstep/res/layout/digital_wellbeing_toast.xml",
+    "packages/apps/Launcher3/quickstep/res/layout/split_instructions_view.xml",
+    "packages/apps/Launcher3/quickstep/res/layout/taskbar_edu.xml",
+    "frameworks/base/packages/SettingsLib/res/drawable/broadcast_dialog_btn_bg.xml",
+    "vendor/unbundled_google/packages/NexusLauncher/res/color/share_target_text.xml",
+    "vendor/unbundled_google/packages/NexusLauncher/res/color-v31/all_apps_tab_background_selected.xml",
+    "vendor/unbundled_google/packages/NexusLauncher/res/color-v31/all_apps_tabs_background.xml",
+    "vendor/unbundled_google/packages/NexusLauncher/res/color-v31/arrow_tip_view_bg.xml",
+    "vendor/unbundled_google/packages/NexusLauncher/res/color-v31/button_bg.xml",
+    "vendor/unbundled_google/packages/NexusLauncher/res/color-v31/shortcut_halo.xml",
+    "vendor/unbundled_google/packages/NexusLauncher/res/color-v31/surface.xml",
+    "vendor/unbundled_google/packages/NexusLauncher/res/color-night-v31/all_apps_tab_background_selected.xml",
+    "vendor/unbundled_google/packages/NexusLauncher/res/color-night-v31/surface.xml",
+    "vendor/unbundled_google/packages/NexusLauncher/res/drawable/bg_pin_keyboard_snackbar_accept_button.xml",
+    "vendor/unbundled_google/packages/NexusLauncher/res/drawable/bg_search_edu_preferences_button.xml",
+    "vendor/unbundled_google/packages/NexusLauncher/res/drawable/circle_accentprimary_32dp.xml",
+    "vendor/unbundled_google/packages/NexusLauncher/res/drawable/ic_search.xml",
+    "vendor/unbundled_google/packages/NexusLauncher/res/drawable/ic_suggest_icon_background.xml",
+    "vendor/unbundled_google/packages/NexusLauncher/res/drawable/share_target_background.xml",
+    "vendor/unbundled_google/packages/NexusLauncher/res/drawable/sticky_snackbar_accept_btn_background.xml",
+    "vendor/unbundled_google/packages/NexusLauncher/res/drawable/sticky_snackbar_background.xml",
+    "vendor/unbundled_google/packages/NexusLauncher/res/drawable/sticky_snackbar_dismiss_btn_background.xml",
+    "vendor/unbundled_google/packages/NexusLauncher/res/drawable/tall_card_btn_background.xml",
+    "vendor/unbundled_google/packages/NexusLauncher/res/layout/section_header.xml",
+    "packages/apps/Settings/res/color/dream_card_color_state_list.xml",
+    "packages/apps/Settings/res/color/dream_card_icon_color_state_list.xml",
+    "packages/apps/Settings/res/color/dream_card_text_color_state_list.xml",
+    "packages/apps/Settings/res/drawable/accessibility_text_reading_preview.xml",
+    "packages/apps/Settings/res/drawable/broadcast_button_outline.xml",
+    "packages/apps/Settings/res/drawable/button_border_selected.xml",
+    "packages/apps/Settings/res/drawable/dream_preview_rounded_bg.xml",
+    "packages/apps/Settings/res/drawable/rounded_bg.xml",
+    "packages/apps/Settings/res/drawable/sim_confirm_dialog_btn_outline.xml",
+    "packages/apps/Settings/res/drawable/user_select_background.xml",
+    "packages/apps/Settings/res/drawable/volume_dialog_button_background_outline.xml",
+    "packages/apps/Settings/res/drawable/volume_dialog_button_background_solid.xml",
+    "packages/apps/Settings/res/layout/dream_preview_button.xml",
+    "packages/apps/Settings/res/layout/qrcode_scanner_fragment.xml",
+    "frameworks/base/packages/SystemUI/res/values-television/styles.xml",
+    "frameworks/base/packages/SystemUI/res/color/media_player_album_bg.xml",
+    "frameworks/base/packages/SystemUI/res/color/media_player_outline_button_bg.xml",
+    "frameworks/base/packages/SystemUI/res/color/media_player_solid_button_bg.xml",
+    "frameworks/base/packages/SystemUI/res-keyguard/color/numpad_key_color_secondary.xml",
+    "frameworks/base/packages/SystemUI/res/color/settingslib_state_on.xml",
+    "frameworks/base/packages/SystemUI/res/color/settingslib_track_off.xml",
+    "frameworks/base/packages/SystemUI/res/color/settingslib_track_on.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/accessibility_floating_tooltip_background.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/action_chip_background.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/action_chip_container_background.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/availability_dot_10dp.xml",
+    "frameworks/base/packages/SystemUI/res-keyguard/drawable/bouncer_user_switcher_header_bg.xml",
+    "frameworks/base/packages/SystemUI/res-keyguard/drawable/bouncer_user_switcher_item_selected_bg.xml",
+    "frameworks/base/packages/SystemUI/res-keyguard/drawable/bouncer_user_switcher_popup_bg.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/broadcast_dialog_btn_bg.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/fgs_dot.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/fingerprint_bg.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/ic_avatar_with_badge.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml",
+    "frameworks/base/packages/SystemUI/res-keyguard/drawable/kg_bouncer_secondary_button.xml",
+    "frameworks/base/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/logout_button_background.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/media_ttt_chip_background.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/media_ttt_chip_background_receiver.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/media_ttt_undo_background.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/notif_footer_btn_background.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/notification_guts_bg.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/notification_material_bg.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/overlay_badge_background.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/overlay_border.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/overlay_button_background.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/overlay_cancel.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/people_space_messages_count_background.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/people_tile_status_scrim.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/people_tile_suppressed_background.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/qs_dialog_btn_filled.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/qs_dialog_btn_filled_large.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/qs_media_outline_button.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/qs_media_solid_button.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/rounded_bg_full.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/rounded_bg_full_large_radius.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/screenrecord_button_background_solid.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/screenrecord_options_spinner_popup_background.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/screenrecord_spinner_background.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/screenshot_edit_background.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/user_switcher_fullscreen_button_bg.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/vector_drawable_progress_indeterminate_horizontal_trimmed.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/volume_background_bottom.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/volume_background_top.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/volume_background_top_rounded.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/volume_row_rounded_background.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/volume_row_seekbar.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/volume_row_seekbar_progress.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/wallet_action_button_bg.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/wallet_app_button_bg.xml",
+    "frameworks/base/packages/SystemUI/res/drawable/wallet_empty_state_bg.xml",
+    "frameworks/base/packages/SystemUI/res/layout/alert_dialog_title_systemui.xml",
+    "frameworks/base/packages/SystemUI/res/layout/chipbar.xml",
+    "frameworks/base/packages/SystemUI/res/layout/chipbar.xml",
+    "frameworks/base/packages/SystemUI/res/layout/clipboard_overlay.xml",
+    "frameworks/base/packages/SystemUI/res/layout/clipboard_overlay.xml",
+    "frameworks/base/packages/SystemUI/res/layout/clipboard_overlay_legacy.xml",
+    "frameworks/base/packages/SystemUI/res/layout/clipboard_overlay_legacy.xml",
+    "frameworks/base/packages/SystemUI/res/layout/internet_connectivity_dialog.xml",
+    "frameworks/base/packages/SystemUI/res/layout/notification_snooze.xml",
+    "frameworks/base/packages/SystemUI/res/layout/people_space_activity_no_conversations.xml",
+    "frameworks/base/packages/SystemUI/res/layout/people_space_activity_with_conversations.xml",
+    "frameworks/base/packages/SystemUI/res/layout/people_space_activity_with_conversations.xml",
+    "frameworks/base/packages/SystemUI/res/layout/people_space_tile_view.xml",
+    "frameworks/base/packages/SystemUI/res/layout/people_tile_large_with_content.xml",
+    "frameworks/base/packages/SystemUI/res/layout/people_tile_medium_with_content.xml",
+    "frameworks/base/packages/SystemUI/res/layout/people_tile_punctuation_background_large.xml",
+    "frameworks/base/packages/SystemUI/res/layout/people_tile_punctuation_background_large.xml",
+    "frameworks/base/packages/SystemUI/res/layout/people_tile_punctuation_background_large.xml",
+    "frameworks/base/packages/SystemUI/res/layout/chipbar.xml",
+    "frameworks/base/packages/SystemUI/res/layout/notification_snooze.xml",
+    "frameworks/base/packages/SystemUI/res/layout/people_tile_punctuation_background_medium.xml",
+    "frameworks/base/packages/SystemUI/res/layout/people_tile_punctuation_background_medium.xml",
+    "frameworks/base/packages/SystemUI/res/layout/people_tile_punctuation_background_medium.xml",
+    "frameworks/base/packages/SystemUI/res/layout/people_tile_punctuation_background_medium.xml",
+    "frameworks/base/packages/SystemUI/res/layout/people_tile_punctuation_background_medium.xml",
+    "frameworks/base/packages/SystemUI/res/layout/people_tile_small.xml",
+    "frameworks/base/packages/SystemUI/res/layout/people_tile_small_horizontal.xml",
+    "frameworks/base/packages/SystemUI/res/layout/screen_share_dialog.xml",
+    "frameworks/base/packages/SystemUI/res/layout/screen_share_dialog_spinner_item_text.xml",
+    "frameworks/base/packages/SystemUI/res/layout/user_switcher_fullscreen.xml",
+    "frameworks/base/packages/SystemUI/res/layout/user_switcher_fullscreen.xml",
+    "frameworks/base/packages/SystemUI/res/layout/wallet_empty_state.xml",
+    "frameworks/base/packages/SystemUI/res/layout/wallet_fullscreen.xml",
+    "frameworks/base/packages/SystemUI/res/layout/wallet_fullscreen.xml",
+    "frameworks/base/packages/SystemUI/res/layout/chipbar.xml",
+    "frameworks/base/packages/SystemUI/res/layout/notification_snooze.xml",
+    "vendor/unbundled_google/packages/SystemUIGoogle/res/drawable/columbus_chip_background_raw.xml",
+    "vendor/unbundled_google/packages/SystemUIGoogle/res/drawable/columbus_chip_background_raw.xml",
+    "vendor/unbundled_google/packages/SystemUIGoogle/res/drawable/columbus_dialog_background.xml",
+    "vendor/unbundled_google/packages/SystemUIGoogle/res/layout/columbus_target_request_dialog.xml",
+    "vendor/unbundled_google/packages/SettingsGoogle/res/color/dream_card_suw_color_state_list.xml",
+    "vendor/unbundled_google/packages/SettingsGoogle/res/drawable/dream_item_suw_rounded_bg.xml"
+]
\ No newline at end of file
diff --git a/packages/SystemUI/scripts/token_alignment/resources/whitelist/xmls2.json b/packages/SystemUI/scripts/token_alignment/resources/whitelist/xmls2.json
new file mode 100644
index 0000000..20a7c76
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/resources/whitelist/xmls2.json
@@ -0,0 +1,13 @@
+[
+    "vendor/google/nexus_overlay/PixelDocumentsUIGoogleOverlay/res/values-v31/themes.xml",
+    "vendor/google/nexus_overlay/PixelDocumentsUIGoogleOverlay/res/values-night-v31/themes.xml",
+    "vendor/unbundled_google/packages/NexusLauncher/res/values/colors.xml",
+    "vendor/unbundled_google/packages/NexusLauncher/res/values/styles.xml",
+    "packages/apps/Settings/res/values-night/colors.xml",
+    "packages/apps/Settings/res/values/colors.xml",
+    "packages/apps/Settings/res/values/styles.xml",
+    "frameworks/base/packages/SystemUI/res-keyguard/values/styles.xml",
+    "frameworks/base/packages/SystemUI/res/values/styles.xml",
+    "vendor/unbundled_google/packages/SettingsGoogle/res/values/styles.xml",
+    "vendor/unbundled_google/packages/SettingsGoogle/res/values/styles.xml"
+]
\ No newline at end of file
diff --git a/packages/SystemUI/scripts/token_alignment/tsconfig.json b/packages/SystemUI/scripts/token_alignment/tsconfig.json
new file mode 100644
index 0000000..20c7321
--- /dev/null
+++ b/packages/SystemUI/scripts/token_alignment/tsconfig.json
@@ -0,0 +1,103 @@
+{
+  "compilerOptions": {
+    /* Visit https://aka.ms/tsconfig to read more about this file */
+
+    /* Projects */
+    // "incremental": true,                              /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
+    // "composite": true,                                /* Enable constraints that allow a TypeScript project to be used with project references. */
+    // "tsBuildInfoFile": "./.tsbuildinfo",              /* Specify the path to .tsbuildinfo incremental compilation file. */
+    // "disableSourceOfProjectReferenceRedirect": true,  /* Disable preferring source files instead of declaration files when referencing composite projects. */
+    // "disableSolutionSearching": true,                 /* Opt a project out of multi-project reference checking when editing. */
+    // "disableReferencedProjectLoad": true,             /* Reduce the number of projects loaded automatically by TypeScript. */
+
+    /* Language and Environment */
+    "target": "ESNext",                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
+    // "lib": [],                                        /* Specify a set of bundled library declaration files that describe the target runtime environment. */
+    // "jsx": "preserve",                                /* Specify what JSX code is generated. */
+    // "experimentalDecorators": true,                   /* Enable experimental support for TC39 stage 2 draft decorators. */
+    // "emitDecoratorMetadata": true,                    /* Emit design-type metadata for decorated declarations in source files. */
+    // "jsxFactory": "",                                 /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
+    // "jsxFragmentFactory": "",                         /* Speciffy the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
+    // "jsxImportSource": "",                            /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
+    // "reactNamespace": "",                             /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
+    // "noLib": true,                                    /* Disable including any library files, including the default lib.d.ts. */
+    // "useDefineForClassFields": true,                  /* Emit ECMAScript-standard-compliant class fields. */
+    // "moduleDetection": "auto",                        /* Control what method is used to detect module-format JS files. */
+
+    /* Modules */
+    "module": "commonjs",                                /* Specify what module code is generated. */
+    // "rootDir": "./",                                  /* Specify the root folder within your source files. */
+    // "moduleResolution": "node",                       /* Specify how TypeScript looks up a file from a given module specifier. */
+    // "baseUrl": "./",                                  /* Specify the base directory to resolve non-relative module names. */
+    // "paths": {},                                      /* Specify a set of entries that re-map imports to additional lookup locations. */
+    // "rootDirs": [],                                   /* Allow multiple folders to be treated as one when resolving modules. */
+    "typeRoots": ["../node_modules/@types"],                                  /* Specify multiple folders that act like './node_modules/@types'. */
+    // "types": [],                                      /* Specify type package names to be included without being referenced in a source file. */
+    // "allowUmdGlobalAccess": true,                     /* Allow accessing UMD globals from modules. */
+    // "moduleSuffixes": [],                             /* List of file name suffixes to search when resolving a module. */
+    "resolveJsonModule": true,                        /* Enable importing .json files. */
+    // "noResolve": true,                                /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
+
+    /* JavaScript Support */
+    // "allowJs": true,                                  /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
+    // "checkJs": true,                                  /* Enable error reporting in type-checked JavaScript files. */
+    // "maxNodeModuleJsDepth": 1,                        /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
+
+    /* Emit */
+    // "declaration": true,                              /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
+    // "declarationMap": true,                           /* Create sourcemaps for d.ts files. */
+    // "emitDeclarationOnly": true,                      /* Only output d.ts files and not JavaScript files. */
+    "sourceMap": true,                                /* Create source map files for emitted JavaScript files. */
+    // "outFile": "./",                                  /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
+    // "outDir": "./",                                   /* Specify an output folder for all emitted files. */
+    // "removeComments": true,                           /* Disable emitting comments. */
+    // "noEmit": true,                                   /* Disable emitting files from a compilation. */
+    // "importHelpers": true,                            /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
+    // "importsNotUsedAsValues": "remove",               /* Specify emit/checking behavior for imports that are only used for types. */
+    // "downlevelIteration": true,                       /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
+    // "sourceRoot": "",                                 /* Specify the root path for debuggers to find the reference source code. */
+    // "mapRoot": "",                                    /* Specify the location where debugger should locate map files instead of generated locations. */
+    // "inlineSourceMap": true,                          /* Include sourcemap files inside the emitted JavaScript. */
+    // "inlineSources": true,                            /* Include source code in the sourcemaps inside the emitted JavaScript. */
+    // "emitBOM": true,                                  /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
+    // "newLine": "crlf",                                /* Set the newline character for emitting files. */
+    // "stripInternal": true,                            /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
+    // "noEmitHelpers": true,                            /* Disable generating custom helper functions like '__extends' in compiled output. */
+    // "noEmitOnError": true,                            /* Disable emitting files if any type checking errors are reported. */
+    // "preserveConstEnums": true,                       /* Disable erasing 'const enum' declarations in generated code. */
+    // "declarationDir": "./",                           /* Specify the output directory for generated declaration files. */
+    // "preserveValueImports": true,                     /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
+
+    /* Interop Constraints */
+    // "isolatedModules": true,                          /* Ensure that each file can be safely transpiled without relying on other imports. */
+    // "allowSyntheticDefaultImports": true,             /* Allow 'import x from y' when a module doesn't have a default export. */
+    "esModuleInterop": true,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
+    // "preserveSymlinks": true,                         /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
+    "forceConsistentCasingInFileNames": true,            /* Ensure that casing is correct in imports. */
+
+    /* Type Checking */
+    "strict": true,                                      /* Enable all strict type-checking options. */
+    // "noImplicitAny": true,                            /* Enable error reporting for expressions and declarations with an implied 'any' type. */
+    // "strictNullChecks": true,                         /* When type checking, take into account 'null' and 'undefined'. */
+    // "strictFunctionTypes": true,                      /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
+    // "strictBindCallApply": true,                      /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
+    // "strictPropertyInitialization": true,             /* Check for class properties that are declared but not set in the constructor. */
+    // "noImplicitThis": true,                           /* Enable error reporting when 'this' is given the type 'any'. */
+    // "useUnknownInCatchVariables": true,               /* Default catch clause variables as 'unknown' instead of 'any'. */
+    // "alwaysStrict": true,                             /* Ensure 'use strict' is always emitted. */
+    // "noUnusedLocals": true,                           /* Enable error reporting when local variables aren't read. */
+    // "noUnusedParameters": true,                       /* Raise an error when a function parameter isn't read. */
+    // "exactOptionalPropertyTypes": true,               /* Interpret optional property types as written, rather than adding 'undefined'. */
+    // "noImplicitReturns": true,                        /* Enable error reporting for codepaths that do not explicitly return in a function. */
+    // "noFallthroughCasesInSwitch": true,               /* Enable error reporting for fallthrough cases in switch statements. */
+    // "noUncheckedIndexedAccess": true,                 /* Add 'undefined' to a type when accessed using an index. */
+    // "noImplicitOverride": true,                       /* Ensure overriding members in derived classes are marked with an override modifier. */
+    // "noPropertyAccessFromIndexSignature": true,       /* Enforces using indexed accessors for keys declared using an indexed type. */
+    // "allowUnusedLabels": true,                        /* Disable error reporting for unused labels. */
+    // "allowUnreachableCode": true,                     /* Disable error reporting for unreachable code. */
+
+    /* Completeness */
+    // "skipDefaultLibCheck": true,                      /* Skip type checking .d.ts files that are included with TypeScript. */
+    "skipLibCheck": true                                 /* Skip type checking all .d.ts files. */
+  }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
index 196f7f0..b6aae69 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
@@ -47,10 +47,6 @@
     val resourceId: Int
 }
 
-interface DeviceConfigFlag<T> : Flag<T> {
-    val default: T
-}
-
 interface SysPropFlag<T> : Flag<T> {
     val default: T
 }
@@ -80,8 +76,8 @@
 
     private constructor(parcel: Parcel) : this(
         id = parcel.readInt(),
-        name = parcel.readString(),
-        namespace = parcel.readString(),
+        name = parcel.readString() ?: "",
+        namespace = parcel.readString() ?: "",
         default = parcel.readBoolean(),
         teamfood = parcel.readBoolean(),
         overridden = parcel.readBoolean()
@@ -137,21 +133,6 @@
 ) : ResourceFlag<Boolean>
 
 /**
- * A Flag that can reads its overrides from DeviceConfig.
- *
- * This is generally useful for flags that come from or are used _outside_ of SystemUI.
- *
- * Prefer [UnreleasedFlag] and [ReleasedFlag].
- */
-data class DeviceConfigBooleanFlag constructor(
-    override val id: Int,
-    override val name: String,
-    override val namespace: String,
-    override val default: Boolean = false,
-    override val teamfood: Boolean = false
-) : DeviceConfigFlag<Boolean>
-
-/**
  * A Flag that can reads its overrides from System Properties.
  *
  * This is generally useful for flags that come from or are used _outside_ of SystemUI.
@@ -164,7 +145,7 @@
     override val namespace: String,
     override val default: Boolean = false,
 ) : SysPropFlag<Boolean> {
-    // TODO(b/223379190): Teamfood not supported for sysprop flags yet.
+    // TODO(b/268520433): Teamfood not supported for sysprop flags yet.
     override val teamfood: Boolean = false
 }
 
@@ -186,8 +167,8 @@
 
     private constructor(parcel: Parcel) : this(
         id = parcel.readInt(),
-        name = parcel.readString(),
-        namespace = parcel.readString(),
+        name = parcel.readString() ?: "",
+        namespace = parcel.readString() ?: "",
         default = parcel.readString() ?: ""
     )
 
@@ -226,8 +207,8 @@
 
     private constructor(parcel: Parcel) : this(
         id = parcel.readInt(),
-        name = parcel.readString(),
-        namespace = parcel.readString(),
+        name = parcel.readString() ?: "",
+        namespace = parcel.readString() ?: "",
         default = parcel.readInt()
     )
 
@@ -266,8 +247,8 @@
 
     private constructor(parcel: Parcel) : this(
         id = parcel.readInt(),
-        name = parcel.readString(),
-        namespace = parcel.readString(),
+        name = parcel.readString() ?: "",
+        namespace = parcel.readString() ?: "",
         default = parcel.readLong()
     )
 
@@ -298,8 +279,8 @@
 
     private constructor(parcel: Parcel) : this(
         id = parcel.readInt(),
-        name = parcel.readString(),
-        namespace = parcel.readString(),
+        name = parcel.readString() ?: "",
+        namespace = parcel.readString() ?: "",
         default = parcel.readFloat()
     )
 
@@ -338,8 +319,8 @@
 
     private constructor(parcel: Parcel) : this(
         id = parcel.readInt(),
-        name = parcel.readString(),
-        namespace = parcel.readString(),
+        name = parcel.readString() ?: "",
+        namespace = parcel.readString() ?: "",
         default = parcel.readDouble()
     )
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagListenable.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagListenable.kt
index 195ba465..72a4fab 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagListenable.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagListenable.kt
@@ -34,7 +34,7 @@
     /** An event representing the change */
     interface FlagEvent {
         /** the id of the flag which changed */
-        val flagId: Int
+        val flagName: String
         /** if all listeners alerted invoke this method, the restart will be skipped */
         fun requestNoRestart()
     }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
index d85292a..da1641c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
@@ -39,7 +39,7 @@
         const val ACTION_GET_FLAGS = "com.android.systemui.action.GET_FLAGS"
         const val FLAGS_PERMISSION = "com.android.systemui.permission.FLAGS"
         const val ACTION_SYSUI_STARTED = "com.android.systemui.STARTED"
-        const val EXTRA_ID = "id"
+        const val EXTRA_NAME = "name"
         const val EXTRA_VALUE = "value"
         const val EXTRA_FLAGS = "flags"
         private const val SETTINGS_PREFIX = "systemui/flags"
@@ -56,7 +56,7 @@
      * that the restart be suppressed
      */
     var onSettingsChangedAction: Consumer<Boolean>? = null
-    var clearCacheAction: Consumer<Int>? = null
+    var clearCacheAction: Consumer<String>? = null
     private val listeners: MutableSet<PerFlagListener> = mutableSetOf()
     private val settingsObserver: ContentObserver = SettingsObserver()
 
@@ -96,35 +96,42 @@
      * Returns the stored value or null if not set.
      * This API is used by TheFlippinApp.
      */
-    fun isEnabled(id: Int): Boolean? = readFlagValue(id, BooleanFlagSerializer)
+    fun isEnabled(name: String): Boolean? = readFlagValue(name, BooleanFlagSerializer)
 
     /**
      * Sets the value of a boolean flag.
      * This API is used by TheFlippinApp.
      */
-    fun setFlagValue(id: Int, enabled: Boolean) {
-        val intent = createIntent(id)
+    fun setFlagValue(name: String, enabled: Boolean) {
+        val intent = createIntent(name)
         intent.putExtra(EXTRA_VALUE, enabled)
 
         context.sendBroadcast(intent)
     }
 
-    fun eraseFlag(id: Int) {
-        val intent = createIntent(id)
+    fun eraseFlag(name: String) {
+        val intent = createIntent(name)
 
         context.sendBroadcast(intent)
     }
 
     /** Returns the stored value or null if not set.  */
+    // TODO(b/265188950): Remove method this once ids are fully deprecated.
     fun <T> readFlagValue(id: Int, serializer: FlagSerializer<T>): T? {
-        val data = settings.getString(idToSettingsKey(id))
+        val data = settings.getStringFromSecure(idToSettingsKey(id))
+        return serializer.fromSettingsData(data)
+    }
+
+    /** Returns the stored value or null if not set.  */
+    fun <T> readFlagValue(name: String, serializer: FlagSerializer<T>): T? {
+        val data = settings.getString(nameToSettingsKey(name))
         return serializer.fromSettingsData(data)
     }
 
     override fun addListener(flag: Flag<*>, listener: FlagListenable.Listener) {
         synchronized(listeners) {
             val registerNeeded = listeners.isEmpty()
-            listeners.add(PerFlagListener(flag.id, listener))
+            listeners.add(PerFlagListener(flag.name, listener))
             if (registerNeeded) {
                 settings.registerContentObserver(SETTINGS_PREFIX, true, settingsObserver)
             }
@@ -143,38 +150,38 @@
         }
     }
 
-    private fun createIntent(id: Int): Intent {
+    private fun createIntent(name: String): Intent {
         val intent = Intent(ACTION_SET_FLAG)
         intent.setPackage(RECEIVING_PACKAGE)
-        intent.putExtra(EXTRA_ID, id)
+        intent.putExtra(EXTRA_NAME, name)
 
         return intent
     }
 
+    // TODO(b/265188950): Remove method this once ids are fully deprecated.
     fun idToSettingsKey(id: Int): String {
         return "$SETTINGS_PREFIX/$id"
     }
 
+    fun nameToSettingsKey(name: String): String {
+        return "$SETTINGS_PREFIX/$name"
+    }
+
     inner class SettingsObserver : ContentObserver(handler) {
         override fun onChange(selfChange: Boolean, uri: Uri?) {
             if (uri == null) {
                 return
             }
             val parts = uri.pathSegments
-            val idStr = parts[parts.size - 1]
-            val id = try {
-                idStr.toInt()
-            } catch (e: NumberFormatException) {
-                return
-            }
-            clearCacheAction?.accept(id)
-            dispatchListenersAndMaybeRestart(id, onSettingsChangedAction)
+            val name = parts[parts.size - 1]
+            clearCacheAction?.accept(name)
+            dispatchListenersAndMaybeRestart(name, onSettingsChangedAction)
         }
     }
 
-    fun dispatchListenersAndMaybeRestart(id: Int, restartAction: Consumer<Boolean>?) {
+    fun dispatchListenersAndMaybeRestart(name: String, restartAction: Consumer<Boolean>?) {
         val filteredListeners: List<FlagListenable.Listener> = synchronized(listeners) {
-            listeners.mapNotNull { if (it.id == id) it.listener else null }
+            listeners.mapNotNull { if (it.name == name) it.listener else null }
         }
         // If there are no listeners, there's nothing to dispatch to, and nothing to suppress it.
         if (filteredListeners.isEmpty()) {
@@ -185,7 +192,7 @@
         val suppressRestartList: List<Boolean> = filteredListeners.map { listener ->
             var didRequestNoRestart = false
             val event = object : FlagListenable.FlagEvent {
-                override val flagId = id
+                override val flagName = name
                 override fun requestNoRestart() {
                     didRequestNoRestart = true
                 }
@@ -198,7 +205,7 @@
         restartAction?.accept(suppressRestart)
     }
 
-    private data class PerFlagListener(val id: Int, val listener: FlagListenable.Listener)
+    private data class PerFlagListener(val name: String, val listener: FlagListenable.Listener)
 }
 
 class NoFlagResultsException : Exception(
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagSettingsHelper.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagSettingsHelper.kt
index 742bb0b..6beb851 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagSettingsHelper.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagSettingsHelper.kt
@@ -22,7 +22,10 @@
 
 class FlagSettingsHelper(private val contentResolver: ContentResolver) {
 
-    fun getString(key: String): String? = Settings.Secure.getString(contentResolver, key)
+    // TODO(b/265188950): Remove method this once ids are fully deprecated.
+    fun getStringFromSecure(key: String): String? = Settings.Secure.getString(contentResolver, key)
+
+    fun getString(key: String): String? = Settings.Global.getString(contentResolver, key)
 
     fun registerContentObserver(
         name: String,
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Monitor.java b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Monitor.java
index 95675ce..209d5e8 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Monitor.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Monitor.java
@@ -38,13 +38,19 @@
 public class Monitor {
     private final String mTag = getClass().getSimpleName();
     private final Executor mExecutor;
+    private final Set<Condition> mPreconditions;
 
     private final HashMap<Condition, ArraySet<Subscription.Token>> mConditions = new HashMap<>();
     private final HashMap<Subscription.Token, SubscriptionState> mSubscriptions = new HashMap<>();
 
     private static class SubscriptionState {
         private final Subscription mSubscription;
+
+        // A subscription must maintain a reference to any active nested subscription so that it may
+        // be later removed when the current subscription becomes invalid.
+        private Subscription.Token mNestedSubscriptionToken;
         private Boolean mAllConditionsMet;
+        private boolean mActive;
 
         SubscriptionState(Subscription subscription) {
             mSubscription = subscription;
@@ -54,7 +60,27 @@
             return mSubscription.mConditions;
         }
 
-        public void update() {
+        /**
+         * Signals that the {@link Subscription} is now being monitored and will receive updates
+         * based on its conditions.
+         */
+        private void setActive(boolean active) {
+            if (mActive == active) {
+                return;
+            }
+
+            mActive = active;
+
+            final Callback callback = mSubscription.getCallback();
+
+            if (callback == null) {
+                return;
+            }
+
+            callback.onActiveChanged(active);
+        }
+
+        public void update(Monitor monitor) {
             final Boolean result = Evaluator.INSTANCE.evaluate(mSubscription.mConditions,
                     Evaluator.OP_AND);
             // Consider unknown (null) as true
@@ -65,7 +91,50 @@
             }
 
             mAllConditionsMet = newAllConditionsMet;
-            mSubscription.mCallback.onConditionsChanged(mAllConditionsMet);
+
+            final Subscription nestedSubscription = mSubscription.getNestedSubscription();
+
+            if (nestedSubscription != null) {
+                if (mAllConditionsMet && mNestedSubscriptionToken == null) {
+                    // When all conditions are met for a subscription with a nested subscription
+                    // that is not currently being monitored, add the nested subscription for
+                    // monitor.
+                    mNestedSubscriptionToken =
+                            monitor.addSubscription(nestedSubscription, null);
+                } else if (!mAllConditionsMet && mNestedSubscriptionToken != null) {
+                    // When conditions are not met and there is an active nested condition, remove
+                    // the nested condition from monitoring.
+                    removeNestedSubscription(monitor);
+                }
+                return;
+            }
+
+            mSubscription.getCallback().onConditionsChanged(mAllConditionsMet);
+        }
+
+        /**
+         * Invoked when the {@link Subscription} has been added to the {@link Monitor}.
+         */
+        public void onAdded() {
+            setActive(true);
+        }
+
+        /**
+         * Invoked when the {@link Subscription} has been removed from the {@link Monitor},
+         * allowing cleanup code to run.
+         */
+        public void onRemoved(Monitor monitor) {
+            setActive(false);
+            removeNestedSubscription(monitor);
+        }
+
+        private void removeNestedSubscription(Monitor monitor) {
+            if (mNestedSubscriptionToken == null) {
+                return;
+            }
+
+            monitor.removeSubscription(mNestedSubscriptionToken);
+            mNestedSubscriptionToken = null;
         }
     }
 
@@ -77,9 +146,20 @@
         }
     };
 
+    /**
+     * Constructor for injected use-cases. By default, no preconditions are present.
+     */
     @Inject
     public Monitor(@Main Executor executor) {
+        this(executor, Collections.emptySet());
+    }
+
+    /**
+     * Main constructor, allowing specifying preconditions.
+     */
+    public Monitor(Executor executor, Set<Condition> preconditions) {
         mExecutor = executor;
+        mPreconditions = preconditions;
     }
 
     private void updateConditionMetState(Condition condition) {
@@ -91,7 +171,7 @@
             return;
         }
 
-        subscriptions.stream().forEach(token -> mSubscriptions.get(token).update());
+        subscriptions.stream().forEach(token -> mSubscriptions.get(token).update(this));
     }
 
     /**
@@ -101,15 +181,25 @@
      * @return A {@link Subscription.Token} that can be used to remove the subscription.
      */
     public Subscription.Token addSubscription(@NonNull Subscription subscription) {
+        return addSubscription(subscription, mPreconditions);
+    }
+
+    private Subscription.Token addSubscription(@NonNull Subscription subscription,
+            Set<Condition> preconditions) {
+        // If preconditions are set on the monitor, set up as a nested condition.
+        final Subscription normalizedCondition = preconditions != null
+                ? new Subscription.Builder(subscription).addConditions(preconditions).build()
+                : subscription;
+
         final Subscription.Token token = new Subscription.Token();
-        final SubscriptionState state = new SubscriptionState(subscription);
+        final SubscriptionState state = new SubscriptionState(normalizedCondition);
 
         mExecutor.execute(() -> {
             if (shouldLog()) Log.d(mTag, "adding subscription");
             mSubscriptions.put(token, state);
 
             // Add and associate conditions.
-            subscription.getConditions().stream().forEach(condition -> {
+            normalizedCondition.getConditions().stream().forEach(condition -> {
                 if (!mConditions.containsKey(condition)) {
                     mConditions.put(condition, new ArraySet<>());
                     condition.addCallback(mConditionCallback);
@@ -118,8 +208,10 @@
                 mConditions.get(condition).add(token);
             });
 
+            state.onAdded();
+
             // Update subscription state.
-            state.update();
+            state.update(this);
 
         });
         return token;
@@ -139,7 +231,9 @@
                 return;
             }
 
-            mSubscriptions.remove(token).getConditions().forEach(condition -> {
+            final SubscriptionState removedSubscription = mSubscriptions.remove(token);
+
+            removedSubscription.getConditions().forEach(condition -> {
                 if (!mConditions.containsKey(condition)) {
                     Log.e(mTag, "condition not present:" + condition);
                     return;
@@ -153,6 +247,8 @@
                     mConditions.remove(condition);
                 }
             });
+
+            removedSubscription.onRemoved(this);
         });
     }
 
@@ -168,12 +264,19 @@
         private final Set<Condition> mConditions;
         private final Callback mCallback;
 
-        /**
-         *
-         */
-        public Subscription(Set<Condition> conditions, Callback callback) {
+        // A nested {@link Subscription} is a special callback where the specified condition's
+        // active state is dependent on the conditions of the parent {@link Subscription} being met.
+        // Once active, the nested subscription's conditions are registered as normal with the
+        // monitor and its callback (which could also be a nested condition) is triggered based on
+        // those conditions. The nested condition will be removed from monitor if the outer
+        // subscription's conditions ever become invalid.
+        private final Subscription mNestedSubscription;
+
+        private Subscription(Set<Condition> conditions, Callback callback,
+                Subscription nestedSubscription) {
             this.mConditions = Collections.unmodifiableSet(conditions);
             this.mCallback = callback;
+            this.mNestedSubscription = nestedSubscription;
         }
 
         public Set<Condition> getConditions() {
@@ -184,6 +287,10 @@
             return mCallback;
         }
 
+        public Subscription getNestedSubscription() {
+            return mNestedSubscription;
+        }
+
         /**
          * A {@link Token} is an identifier that is associated with a {@link Subscription} which is
          * registered with a {@link Monitor}.
@@ -196,14 +303,26 @@
          */
         public static class Builder {
             private final Callback mCallback;
+            private final Subscription mNestedSubscription;
             private final ArraySet<Condition> mConditions;
+            private final ArraySet<Condition> mPreconditions;
 
             /**
              * Default constructor specifying the {@link Callback} for the {@link Subscription}.
              */
             public Builder(Callback callback) {
+                this(null, callback);
+            }
+
+            public Builder(Subscription nestedSubscription) {
+                this(nestedSubscription, null);
+            }
+
+            private Builder(Subscription nestedSubscription, Callback callback) {
+                mNestedSubscription = nestedSubscription;
                 mCallback = callback;
-                mConditions = new ArraySet<>();
+                mConditions = new ArraySet();
+                mPreconditions = new ArraySet();
             }
 
             /**
@@ -217,11 +336,38 @@
             }
 
             /**
+             * Adds a set of {@link Condition} to be a precondition for {@link Subscription}.
+             *
+             * @return The updated {@link Builder}.
+             */
+            public Builder addPreconditions(Set<Condition> condition) {
+                if (condition == null) {
+                    return this;
+                }
+                mPreconditions.addAll(condition);
+                return this;
+            }
+
+            /**
+             * Adds a {@link Condition} to be a precondition for {@link Subscription}.
+             *
+             * @return The updated {@link Builder}.
+             */
+            public Builder addPrecondition(Condition condition) {
+                mPreconditions.add(condition);
+                return this;
+            }
+
+            /**
              * Adds a set of {@link Condition} to be associated with the {@link Subscription}.
              *
              * @return The updated {@link Builder}.
              */
             public Builder addConditions(Set<Condition> condition) {
+                if (condition == null) {
+                    return this;
+                }
+
                 mConditions.addAll(condition);
                 return this;
             }
@@ -232,7 +378,11 @@
              * @return The resulting {@link Subscription}.
              */
             public Subscription build() {
-                return new Subscription(mConditions, mCallback);
+                final Subscription subscription =
+                        new Subscription(mConditions, mCallback, mNestedSubscription);
+                return !mPreconditions.isEmpty()
+                        ? new Subscription(mPreconditions, null, subscription)
+                        : subscription;
             }
         }
     }
@@ -255,5 +405,13 @@
          *                         only partial conditions have been fulfilled.
          */
         void onConditionsChanged(boolean allConditionsMet);
+
+        /**
+         * Called when the active state of the {@link Subscription} changes.
+         * @param active {@code true} when changes to the conditions will affect the
+         *               {@link Subscription}, {@code false} otherwise.
+         */
+        default void onActiveChanged(boolean active) {
+        }
     }
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt
index ef2247f..9a581aa 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt
@@ -114,7 +114,27 @@
 
     /** Dump region sampler */
     fun dump(pw: PrintWriter) {
-        regionSampler?.dump(pw)
+        pw.println("[RegionSampler]")
+        pw.println("regionSamplingEnabled: $regionSamplingEnabled")
+        pw.println("regionDarkness: $regionDarkness")
+        pw.println("lightForegroundColor: ${Integer.toHexString(lightForegroundColor)}")
+        pw.println("darkForegroundColor: ${Integer.toHexString(darkForegroundColor)}")
+        pw.println("passed-in sampledView: $sampledView")
+        pw.println("calculated samplingBounds: $samplingBounds")
+        pw.println(
+            "sampledView width: ${sampledView?.width}, sampledView height: ${sampledView?.height}"
+        )
+        pw.println("screen width: ${displaySize.x}, screen height: ${displaySize.y}")
+        pw.println(
+            "sampledRegionWithOffset: ${convertBounds(calculateSampledRegion(sampledView!!))}"
+        )
+        // TODO(b/265969235): mock initialSampling based on if component is on HS or LS wallpaper
+        // HS Smartspace - wallpaperManager?.getWallpaperColors(WallpaperManager.FLAG_SYSTEM)
+        // LS Smartspace, clock - wallpaperManager?.getWallpaperColors(WallpaperManager.FLAG_LOCK)
+        pw.println(
+            "initialSampling for lockscreen: " +
+                "${wallpaperManager?.getWallpaperColors(WallpaperManager.FLAG_LOCK)}"
+        )
     }
 
     fun calculateSampledRegion(sampledView: View): RectF {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
index 857cc462..5d036fb 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
@@ -35,6 +35,7 @@
 import android.view.animation.AccelerateDecelerateInterpolator;
 import android.widget.FrameLayout;
 
+import androidx.annotation.BoolRes;
 import androidx.core.view.OneShotPreDrawListener;
 
 import com.android.systemui.shared.rotation.FloatingRotationButtonPositionCalculator.Position;
@@ -65,6 +66,8 @@
     private final int mTaskbarBottomMarginResource;
     @DimenRes
     private final int mButtonDiameterResource;
+    @BoolRes
+    private final int mFloatingRotationBtnPositionLeftResource;
 
     private AnimatedVectorDrawable mAnimatedDrawable;
     private boolean mIsShowing;
@@ -84,7 +87,7 @@
             @LayoutRes int layout, @IdRes int keyButtonId, @DimenRes int minMargin,
             @DimenRes int roundedContentPadding, @DimenRes int taskbarLeftMargin,
             @DimenRes int taskbarBottomMargin, @DimenRes int buttonDiameter,
-            @DimenRes int rippleMaxWidth) {
+            @DimenRes int rippleMaxWidth, @BoolRes int floatingRotationBtnPositionLeftResource) {
         mWindowManager = context.getSystemService(WindowManager.class);
         mKeyButtonContainer = (ViewGroup) LayoutInflater.from(context).inflate(layout, null);
         mKeyButtonView = mKeyButtonContainer.findViewById(keyButtonId);
@@ -100,6 +103,7 @@
         mTaskbarLeftMarginResource = taskbarLeftMargin;
         mTaskbarBottomMarginResource = taskbarBottomMargin;
         mButtonDiameterResource = buttonDiameter;
+        mFloatingRotationBtnPositionLeftResource = floatingRotationBtnPositionLeftResource;
 
         updateDimensionResources();
     }
@@ -116,8 +120,11 @@
         int taskbarMarginBottom =
                 res.getDimensionPixelSize(mTaskbarBottomMarginResource);
 
+        boolean floatingRotationButtonPositionLeft =
+                res.getBoolean(mFloatingRotationBtnPositionLeftResource);
+
         mPositionCalculator = new FloatingRotationButtonPositionCalculator(defaultMargin,
-                taskbarMarginLeft, taskbarMarginBottom);
+                taskbarMarginLeft, taskbarMarginBottom, floatingRotationButtonPositionLeft);
 
         final int diameter = res.getDimensionPixelSize(mButtonDiameterResource);
         mContainerSize = diameter + Math.max(defaultMargin, Math.max(taskbarMarginLeft,
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonPositionCalculator.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonPositionCalculator.kt
index ec3c073..40e43a9 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonPositionCalculator.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonPositionCalculator.kt
@@ -10,7 +10,8 @@
 class FloatingRotationButtonPositionCalculator(
     private val defaultMargin: Int,
     private val taskbarMarginLeft: Int,
-    private val taskbarMarginBottom: Int
+    private val taskbarMarginBottom: Int,
+    private val floatingRotationButtonPositionLeft: Boolean
 ) {
 
     fun calculatePosition(
@@ -18,7 +19,6 @@
         taskbarVisible: Boolean,
         taskbarStashed: Boolean
     ): Position {
-
         val isTaskbarSide = currentRotation == Surface.ROTATION_0
             || currentRotation == Surface.ROTATION_90
         val useTaskbarMargin = isTaskbarSide && taskbarVisible && !taskbarStashed
@@ -55,11 +55,21 @@
     )
 
     private fun resolveGravity(rotation: Int): Int =
-        when (rotation) {
-            Surface.ROTATION_0 -> Gravity.BOTTOM or Gravity.LEFT
-            Surface.ROTATION_90 -> Gravity.BOTTOM or Gravity.RIGHT
-            Surface.ROTATION_180 -> Gravity.TOP or Gravity.RIGHT
-            Surface.ROTATION_270 -> Gravity.TOP or Gravity.LEFT
-            else -> throw IllegalArgumentException("Invalid rotation $rotation")
+        if (floatingRotationButtonPositionLeft) {
+            when (rotation) {
+                Surface.ROTATION_0 -> Gravity.BOTTOM or Gravity.LEFT
+                Surface.ROTATION_90 -> Gravity.BOTTOM or Gravity.RIGHT
+                Surface.ROTATION_180 -> Gravity.TOP or Gravity.RIGHT
+                Surface.ROTATION_270 -> Gravity.TOP or Gravity.LEFT
+                else -> throw IllegalArgumentException("Invalid rotation $rotation")
+            }
+        } else {
+            when (rotation) {
+                Surface.ROTATION_0 -> Gravity.BOTTOM or Gravity.RIGHT
+                Surface.ROTATION_90 -> Gravity.TOP or Gravity.RIGHT
+                Surface.ROTATION_180 -> Gravity.TOP or Gravity.LEFT
+                Surface.ROTATION_270 -> Gravity.BOTTOM or Gravity.LEFT
+                else -> throw IllegalArgumentException("Invalid rotation $rotation")
+            }
         }
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt
index 9b73cc3..bd20777 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt
@@ -25,7 +25,7 @@
 import com.android.systemui.shared.shadow.DoubleShadowTextHelper.applyShadows
 
 /** Extension of [TextView] which draws two shadows on the text (ambient and key shadows} */
-class DoubleShadowTextView
+open class DoubleShadowTextView
 @JvmOverloads
 constructor(
     context: Context,
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 766266d..65dedc6 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
@@ -43,6 +43,7 @@
     public static final String KEY_EXTRA_SYSUI_PROXY = "extra_sysui_proxy";
     public static final String KEY_EXTRA_WINDOW_CORNER_RADIUS = "extra_window_corner_radius";
     public static final String KEY_EXTRA_SUPPORTS_WINDOW_CORNERS = "extra_supports_window_corners";
+    public static final String KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER = "extra_unfold_animation";
     // See ISysuiUnlockAnimationController.aidl
     public static final String KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER = "unlock_animation";
 
@@ -112,7 +113,8 @@
     public static final int SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING = 1 << 25;
     // Freeform windows are showing in desktop mode
     public static final int SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE = 1 << 26;
-
+    // Device dreaming state
+    public static final int SYSUI_STATE_DEVICE_DREAMING = 1 << 27;
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({SYSUI_STATE_SCREEN_PINNING,
@@ -141,7 +143,8 @@
             SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED,
             SYSUI_STATE_IMMERSIVE_MODE,
             SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING,
-            SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE
+            SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE,
+            SYSUI_STATE_DEVICE_DREAMING
     })
     public @interface SystemUiStateFlags {}
 
@@ -179,6 +182,7 @@
         str.add((flags & SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING) != 0 ? "vis_win_showing" : "");
         str.add((flags & SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0
                 ? "freeform_active_in_desktop_mode" : "");
+        str.add((flags & SYSUI_STATE_DEVICE_DREAMING) != 0 ? "device_dreaming" : "");
         return str.toString();
     }
 
diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt
index 05372fe..31234cf 100644
--- a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt
+++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt
@@ -35,7 +35,7 @@
         teamfood: Boolean = false
     ): UnreleasedFlag {
         val flag = UnreleasedFlag(id = id, name = name, namespace = namespace, teamfood = teamfood)
-        FlagsFactory.checkForDupesAndAdd(flag)
+        checkForDupesAndAdd(flag)
         return flag
     }
 
@@ -46,7 +46,7 @@
         teamfood: Boolean = false
     ): ReleasedFlag {
         val flag = ReleasedFlag(id = id, name = name, namespace = namespace, teamfood = teamfood)
-        FlagsFactory.checkForDupesAndAdd(flag)
+        checkForDupesAndAdd(flag)
         return flag
     }
 
@@ -65,7 +65,7 @@
                 resourceId = resourceId,
                 teamfood = teamfood
             )
-        FlagsFactory.checkForDupesAndAdd(flag)
+        checkForDupesAndAdd(flag)
         return flag
     }
 
@@ -77,18 +77,13 @@
     ): SysPropBooleanFlag {
         val flag =
             SysPropBooleanFlag(id = id, name = name, namespace = "systemui", default = default)
-        FlagsFactory.checkForDupesAndAdd(flag)
+        checkForDupesAndAdd(flag)
         return flag
     }
 
     private fun checkForDupesAndAdd(flag: Flag<*>) {
         if (flagMap.containsKey(flag.name)) {
-            throw IllegalArgumentException("Name {flag.name} is already registered")
-        }
-        flagMap.forEach {
-            if (it.value.id == flag.id) {
-                throw IllegalArgumentException("Name {flag.id} is already registered")
-            }
+            throw IllegalArgumentException("Name {$flag.name} is already registered")
         }
         flagMap[flag.name] = flag
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
index 38fa354..c4f1ce8 100644
--- a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
@@ -16,12 +16,13 @@
 
 package com.android.keyguard
 
-import android.annotation.IntDef
 import android.content.ContentResolver
 import android.database.ContentObserver
 import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_TIMEOUT
 import android.net.Uri
 import android.os.Handler
+import android.os.PowerManager
+import android.os.PowerManager.WAKE_REASON_UNFOLD_DEVICE
 import android.os.UserHandle
 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL
 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO
@@ -29,6 +30,8 @@
 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT
 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_WAKE
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD
 import android.util.Log
 import com.android.keyguard.KeyguardUpdateMonitor.getCurrentUser
 import com.android.systemui.Dumpable
@@ -52,23 +55,44 @@
 
     companion object {
         const val TAG = "ActiveUnlockConfig"
-
-        const val BIOMETRIC_TYPE_NONE = 0
-        const val BIOMETRIC_TYPE_ANY_FACE = 1
-        const val BIOMETRIC_TYPE_ANY_FINGERPRINT = 2
-        const val BIOMETRIC_TYPE_UNDER_DISPLAY_FINGERPRINT = 3
     }
 
-    @Retention(AnnotationRetention.SOURCE)
-    @IntDef(BIOMETRIC_TYPE_NONE, BIOMETRIC_TYPE_ANY_FACE, BIOMETRIC_TYPE_ANY_FINGERPRINT,
-            BIOMETRIC_TYPE_UNDER_DISPLAY_FINGERPRINT)
-    annotation class BiometricType
-
     /**
      * Indicates the origin for an active unlock request.
      */
-    enum class ACTIVE_UNLOCK_REQUEST_ORIGIN {
-        WAKE, UNLOCK_INTENT, BIOMETRIC_FAIL, ASSISTANT
+    enum class ActiveUnlockRequestOrigin {
+        /**
+         * Trigger ActiveUnlock on wake ups that'd trigger FaceAuth, see [FaceWakeUpTriggersConfig]
+         */
+        WAKE,
+
+        /**
+         * Trigger ActiveUnlock on unlock intents. This includes the bouncer showing or tapping on
+         * a notification. May also include wakeups: [wakeupsConsideredUnlockIntents].
+         */
+        UNLOCK_INTENT,
+
+        /**
+         * Trigger ActiveUnlock on biometric failures. This may include soft errors depending on
+         * the other settings. See: [faceErrorsToTriggerBiometricFailOn],
+         * [faceAcquireInfoToTriggerBiometricFailOn].
+         */
+        BIOMETRIC_FAIL,
+
+        /**
+         * Trigger ActiveUnlock when the assistant is triggered.
+         */
+        ASSISTANT,
+    }
+
+    /**
+     * Biometric type options.
+     */
+    enum class BiometricType(val intValue: Int) {
+        NONE(0),
+        ANY_FACE(1),
+        ANY_FINGERPRINT(2),
+        UNDER_DISPLAY_FINGERPRINT(3),
     }
 
     var keyguardUpdateMonitor: KeyguardUpdateMonitor? = null
@@ -76,9 +100,11 @@
     private var requestActiveUnlockOnUnlockIntent = false
     private var requestActiveUnlockOnBioFail = false
 
-    private var faceErrorsToTriggerBiometricFailOn = mutableSetOf(FACE_ERROR_TIMEOUT)
+    private var faceErrorsToTriggerBiometricFailOn = mutableSetOf<Int>()
     private var faceAcquireInfoToTriggerBiometricFailOn = mutableSetOf<Int>()
-    private var onUnlockIntentWhenBiometricEnrolled = mutableSetOf<Int>(BIOMETRIC_TYPE_NONE)
+    private var onUnlockIntentWhenBiometricEnrolled = mutableSetOf<Int>()
+    private var wakeupsConsideredUnlockIntents = mutableSetOf<Int>()
+    private var wakeupsToForceDismissKeyguard = mutableSetOf<Int>()
 
     private val settingsObserver = object : ContentObserver(handler) {
         private val wakeUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_WAKE)
@@ -89,16 +115,22 @@
                 secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO)
         private val unlockIntentWhenBiometricEnrolledUri =
                 secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED)
+        private val wakeupsConsideredUnlockIntentsUri =
+            secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS)
+        private val wakeupsToForceDismissKeyguardUri =
+            secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD)
 
         fun register() {
             registerUri(
                     listOf(
-                            wakeUri,
-                            unlockIntentUri,
-                            bioFailUri,
-                            faceErrorsUri,
-                            faceAcquireInfoUri,
-                            unlockIntentWhenBiometricEnrolledUri
+                        wakeUri,
+                        unlockIntentUri,
+                        bioFailUri,
+                        faceErrorsUri,
+                        faceAcquireInfoUri,
+                        unlockIntentWhenBiometricEnrolledUri,
+                        wakeupsConsideredUnlockIntentsUri,
+                        wakeupsToForceDismissKeyguardUri,
                     )
             )
 
@@ -153,7 +185,7 @@
                         secureSettings.getStringForUser(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
                                 getCurrentUser()),
                         faceAcquireInfoToTriggerBiometricFailOn,
-                        setOf<Int>())
+                        emptySet())
             }
 
             if (selfChange || uris.contains(unlockIntentWhenBiometricEnrolledUri)) {
@@ -162,7 +194,25 @@
                                 ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
                                 getCurrentUser()),
                         onUnlockIntentWhenBiometricEnrolled,
-                        setOf(BIOMETRIC_TYPE_NONE))
+                        setOf(BiometricType.NONE.intValue))
+            }
+
+            if (selfChange || uris.contains(wakeupsConsideredUnlockIntentsUri)) {
+                processStringArray(
+                    secureSettings.getStringForUser(
+                        ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
+                        getCurrentUser()),
+                    wakeupsConsideredUnlockIntents,
+                    setOf(WAKE_REASON_UNFOLD_DEVICE))
+            }
+
+            if (selfChange || uris.contains(wakeupsToForceDismissKeyguardUri)) {
+                processStringArray(
+                    secureSettings.getStringForUser(
+                        ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
+                        getCurrentUser()),
+                    wakeupsToForceDismissKeyguard,
+                    setOf(WAKE_REASON_UNFOLD_DEVICE))
             }
         }
 
@@ -181,10 +231,12 @@
             out.clear()
             stringSetting?.let {
                 for (code: String in stringSetting.split("|")) {
-                    try {
-                        out.add(code.toInt())
-                    } catch (e: NumberFormatException) {
-                        Log.e(TAG, "Passed an invalid setting=$code")
+                    if (code.isNotEmpty()) {
+                        try {
+                            out.add(code.toInt())
+                        } catch (e: NumberFormatException) {
+                            Log.e(TAG, "Passed an invalid setting=$code")
+                        }
                     }
                 }
             } ?: out.addAll(default)
@@ -221,22 +273,38 @@
     }
 
     /**
+     * Whether the PowerManager wake reason is considered an unlock intent and should use origin
+     * [ActiveUnlockRequestOrigin.UNLOCK_INTENT] instead of [ActiveUnlockRequestOrigin.WAKE].
+     */
+    fun isWakeupConsideredUnlockIntent(pmWakeReason: Int): Boolean {
+        return wakeupsConsideredUnlockIntents.contains(pmWakeReason)
+    }
+
+    /**
+     * Whether the PowerManager wake reason should force dismiss the keyguard if active
+     * unlock is successful.
+     */
+    fun shouldWakeupForceDismissKeyguard(pmWakeReason: Int): Boolean {
+        return wakeupsToForceDismissKeyguard.contains(pmWakeReason)
+    }
+
+    /**
      * Whether to trigger active unlock based on where the request is coming from and
      * the current settings.
      */
-    fun shouldAllowActiveUnlockFromOrigin(requestOrigin: ACTIVE_UNLOCK_REQUEST_ORIGIN): Boolean {
+    fun shouldAllowActiveUnlockFromOrigin(requestOrigin: ActiveUnlockRequestOrigin): Boolean {
         return when (requestOrigin) {
-            ACTIVE_UNLOCK_REQUEST_ORIGIN.WAKE -> requestActiveUnlockOnWakeup
+            ActiveUnlockRequestOrigin.WAKE -> requestActiveUnlockOnWakeup
 
-            ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT ->
+            ActiveUnlockRequestOrigin.UNLOCK_INTENT ->
                 requestActiveUnlockOnUnlockIntent || requestActiveUnlockOnWakeup ||
                         (shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment())
 
-            ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL ->
+            ActiveUnlockRequestOrigin.BIOMETRIC_FAIL ->
                 requestActiveUnlockOnBioFail || requestActiveUnlockOnUnlockIntent ||
                         requestActiveUnlockOnWakeup
 
-            ACTIVE_UNLOCK_REQUEST_ORIGIN.ASSISTANT -> isActiveUnlockEnabled()
+            ActiveUnlockRequestOrigin.ASSISTANT -> isActiveUnlockEnabled()
         }
     }
 
@@ -252,18 +320,18 @@
             val udfpsEnrolled = it.isUdfpsEnrolled
 
             if (!anyFaceEnrolled && !anyFingerprintEnrolled) {
-                return onUnlockIntentWhenBiometricEnrolled.contains(BIOMETRIC_TYPE_NONE)
+                return onUnlockIntentWhenBiometricEnrolled.contains(BiometricType.NONE.intValue)
             }
 
             if (!anyFaceEnrolled && anyFingerprintEnrolled) {
                 return onUnlockIntentWhenBiometricEnrolled.contains(
-                        BIOMETRIC_TYPE_ANY_FINGERPRINT) ||
+                        BiometricType.ANY_FINGERPRINT.intValue) ||
                         (udfpsEnrolled && onUnlockIntentWhenBiometricEnrolled.contains(
-                                BIOMETRIC_TYPE_UNDER_DISPLAY_FINGERPRINT))
+                                BiometricType.UNDER_DISPLAY_FINGERPRINT.intValue))
             }
 
             if (!anyFingerprintEnrolled && anyFaceEnrolled) {
-                return onUnlockIntentWhenBiometricEnrolled.contains(BIOMETRIC_TYPE_ANY_FACE)
+                return onUnlockIntentWhenBiometricEnrolled.contains(BiometricType.ANY_FACE.intValue)
             }
         }
 
@@ -275,11 +343,27 @@
         pw.println("   requestActiveUnlockOnWakeup=$requestActiveUnlockOnWakeup")
         pw.println("   requestActiveUnlockOnUnlockIntent=$requestActiveUnlockOnUnlockIntent")
         pw.println("   requestActiveUnlockOnBioFail=$requestActiveUnlockOnBioFail")
+
+        val onUnlockIntentWhenBiometricEnrolledString =
+            onUnlockIntentWhenBiometricEnrolled.map {
+                for (biometricType in BiometricType.values()) {
+                    if (biometricType.intValue == it) {
+                        return@map biometricType.name
+                    }
+                }
+                return@map "UNKNOWN"
+            }
         pw.println("   requestActiveUnlockOnUnlockIntentWhenBiometricEnrolled=" +
-                "$onUnlockIntentWhenBiometricEnrolled")
+                "$onUnlockIntentWhenBiometricEnrolledString")
         pw.println("   requestActiveUnlockOnFaceError=$faceErrorsToTriggerBiometricFailOn")
         pw.println("   requestActiveUnlockOnFaceAcquireInfo=" +
                 "$faceAcquireInfoToTriggerBiometricFailOn")
+        pw.println("   activeUnlockWakeupsConsideredUnlockIntents=${
+            wakeupsConsideredUnlockIntents.map { PowerManager.wakeReasonToString(it) }
+        }")
+        pw.println("   activeUnlockFromWakeupsToAlwaysDismissKeyguard=${
+            wakeupsToForceDismissKeyguard.map { PowerManager.wakeReasonToString(it) }
+        }")
 
         pw.println("Current state:")
         keyguardUpdateMonitor?.let {
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 1680b47..1254e1e 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -24,6 +24,7 @@
 import android.text.format.DateFormat
 import android.util.TypedValue
 import android.view.View
+import android.view.ViewTreeObserver
 import android.widget.FrameLayout
 import androidx.annotation.VisibleForTesting
 import androidx.lifecycle.Lifecycle
@@ -39,32 +40,37 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.log.dagger.KeyguardSmallClockLog
 import com.android.systemui.log.dagger.KeyguardLargeClockLog
+import com.android.systemui.log.dagger.KeyguardSmallClockLog
 import com.android.systemui.plugins.ClockController
+import com.android.systemui.plugins.ClockFaceController
+import com.android.systemui.plugins.ClockTickRate
 import com.android.systemui.plugins.log.LogBuffer
 import com.android.systemui.plugins.log.LogLevel.DEBUG
 import com.android.systemui.shared.regionsampling.RegionSampler
+import com.android.systemui.plugins.Weather
 import com.android.systemui.statusbar.policy.BatteryController
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback
 import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.util.concurrency.DelayableExecutor
+import java.util.Locale
+import java.util.TimeZone
+import java.util.concurrent.Executor
+import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.DisposableHandle
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.launch
-import java.io.PrintWriter
-import java.util.Locale
-import java.util.TimeZone
-import java.util.concurrent.Executor
-import javax.inject.Inject
 
 /**
  * Controller for a Clock provided by the registry and used on the keyguard. Instantiated by
  * [KeyguardClockSwitchController]. Functionality is forked from [AnimatableClockController].
  */
-open class ClockEventController @Inject constructor(
+open class ClockEventController
+@Inject
+constructor(
     private val keyguardInteractor: KeyguardInteractor,
     private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
     private val broadcastDispatcher: BroadcastDispatcher,
@@ -73,7 +79,7 @@
     private val configurationController: ConfigurationController,
     @Main private val resources: Resources,
     private val context: Context,
-    @Main private val mainExecutor: Executor,
+    @Main private val mainExecutor: DelayableExecutor,
     @Background private val bgExecutor: Executor,
     @KeyguardSmallClockLog private val smallLogBuffer: LogBuffer?,
     @KeyguardLargeClockLog private val largeLogBuffer: LogBuffer?,
@@ -93,8 +99,11 @@
                 if (regionSamplingEnabled) {
                     clock?.smallClock?.view?.addOnLayoutChangeListener(mLayoutChangedListener)
                     clock?.largeClock?.view?.addOnLayoutChangeListener(mLayoutChangedListener)
+                } else {
+                    updateColors()
                 }
                 updateFontSizes()
+                updateTimeListeners()
             }
         }
 
@@ -108,52 +117,59 @@
     private var disposableHandle: DisposableHandle? = null
     private val regionSamplingEnabled = featureFlags.isEnabled(REGION_SAMPLING)
 
-    private val mLayoutChangedListener = object : View.OnLayoutChangeListener {
-        private var currentSmallClockView: View? = null
-        private var currentLargeClockView: View? = null
-        private var currentSmallClockLocation = IntArray(2)
-        private var currentLargeClockLocation = IntArray(2)
+    private val mLayoutChangedListener =
+        object : View.OnLayoutChangeListener {
+            private var currentSmallClockView: View? = null
+            private var currentLargeClockView: View? = null
+            private var currentSmallClockLocation = IntArray(2)
+            private var currentLargeClockLocation = IntArray(2)
 
-        override fun onLayoutChange(
-            view: View?,
-            left: Int,
-            top: Int,
-            right: Int,
-            bottom: Int,
-            oldLeft: Int,
-            oldTop: Int,
-            oldRight: Int,
-            oldBottom: Int
-        ) {
-        val parent = (view?.parent) as FrameLayout
+            override fun onLayoutChange(
+                view: View?,
+                left: Int,
+                top: Int,
+                right: Int,
+                bottom: Int,
+                oldLeft: Int,
+                oldTop: Int,
+                oldRight: Int,
+                oldBottom: Int
+            ) {
+                val parent = (view?.parent) as FrameLayout
 
-        // don't pass in negative bounds when clocks are in transition state
-        if (view.locationOnScreen[0] < 0 || view.locationOnScreen[1] < 0) {
-            return
-        }
+                // don't pass in negative bounds when clocks are in transition state
+                if (view.locationOnScreen[0] < 0 || view.locationOnScreen[1] < 0) {
+                    return
+                }
 
-        // SMALL CLOCK
-        if (parent.id == R.id.lockscreen_clock_view) {
-            // view bounds have changed due to clock size changing (i.e. different character widths)
-            // AND/OR the view has been translated when transitioning between small and large clock
-            if (view != currentSmallClockView ||
-                !view.locationOnScreen.contentEquals(currentSmallClockLocation)) {
-                currentSmallClockView = view
-                currentSmallClockLocation = view.locationOnScreen
-                updateRegionSampler(view)
+                // SMALL CLOCK
+                if (parent.id == R.id.lockscreen_clock_view) {
+                    // view bounds have changed due to clock size changing (i.e. different character
+                    // widths)
+                    // AND/OR the view has been translated when transitioning between small and
+                    // large clock
+                    if (
+                        view != currentSmallClockView ||
+                            !view.locationOnScreen.contentEquals(currentSmallClockLocation)
+                    ) {
+                        currentSmallClockView = view
+                        currentSmallClockLocation = view.locationOnScreen
+                        updateRegionSampler(view)
+                    }
+                }
+                // LARGE CLOCK
+                else if (parent.id == R.id.lockscreen_clock_view_large) {
+                    if (
+                        view != currentLargeClockView ||
+                            !view.locationOnScreen.contentEquals(currentLargeClockLocation)
+                    ) {
+                        currentLargeClockView = view
+                        currentLargeClockLocation = view.locationOnScreen
+                        updateRegionSampler(view)
+                    }
+                }
             }
         }
-        // LARGE CLOCK
-        else if (parent.id == R.id.lockscreen_clock_view_large) {
-            if (view != currentLargeClockView ||
-                !view.locationOnScreen.contentEquals(currentLargeClockLocation)) {
-                currentLargeClockView = view
-                currentLargeClockLocation = view.locationOnScreen
-                updateRegionSampler(view)
-            }
-        }
-        }
-    }
 
     private fun updateColors() {
         val wallpaperManager = WallpaperManager.getInstance(context)
@@ -182,86 +198,106 @@
 
     private fun updateRegionSampler(sampledRegion: View) {
         regionSampler?.stopRegionSampler()
-        regionSampler = createRegionSampler(
-            sampledRegion,
-            mainExecutor,
-            bgExecutor,
-            regionSamplingEnabled,
-            ::updateColors
-        )?.apply { startRegionSampler() }
+        regionSampler =
+            createRegionSampler(
+                    sampledRegion,
+                    mainExecutor,
+                    bgExecutor,
+                    regionSamplingEnabled,
+                    ::updateColors
+                )
+                ?.apply { startRegionSampler() }
 
         updateColors()
     }
 
     protected open fun createRegionSampler(
-            sampledView: View?,
-            mainExecutor: Executor?,
-            bgExecutor: Executor?,
-            regionSamplingEnabled: Boolean,
-            updateColors: () -> Unit
+        sampledView: View?,
+        mainExecutor: Executor?,
+        bgExecutor: Executor?,
+        regionSamplingEnabled: Boolean,
+        updateColors: () -> Unit
     ): RegionSampler? {
         return RegionSampler(
             sampledView,
             mainExecutor,
             bgExecutor,
             regionSamplingEnabled,
-            updateColors)
+            updateColors
+        )
     }
 
     var regionSampler: RegionSampler? = null
+    var smallTimeListener: TimeListener? = null
+    var largeTimeListener: TimeListener? = null
+    val shouldTimeListenerRun: Boolean
+        get() = isKeyguardVisible && dozeAmount < DOZE_TICKRATE_THRESHOLD
 
     private var smallClockIsDark = true
     private var largeClockIsDark = true
 
-    private val configListener = object : ConfigurationController.ConfigurationListener {
-        override fun onThemeChanged() {
-            clock?.events?.onColorPaletteChanged(resources)
-            updateColors()
-        }
-
-        override fun onDensityOrFontScaleChanged() {
-            updateFontSizes()
-        }
-    }
-
-    private val batteryCallback = object : BatteryStateChangeCallback {
-        override fun onBatteryLevelChanged(level: Int, pluggedIn: Boolean, charging: Boolean) {
-            if (isKeyguardVisible && !isCharging && charging) {
-                clock?.animations?.charge()
+    private val configListener =
+        object : ConfigurationController.ConfigurationListener {
+            override fun onThemeChanged() {
+                clock?.events?.onColorPaletteChanged(resources)
+                updateColors()
             }
-            isCharging = charging
-        }
-    }
 
-    private val localeBroadcastReceiver = object : BroadcastReceiver() {
-        override fun onReceive(context: Context, intent: Intent) {
-            clock?.events?.onLocaleChanged(Locale.getDefault())
+            override fun onDensityOrFontScaleChanged() {
+                updateFontSizes()
+            }
         }
-    }
 
-    private val keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() {
-        override fun onKeyguardVisibilityChanged(visible: Boolean) {
-            isKeyguardVisible = visible
-            if (!featureFlags.isEnabled(DOZING_MIGRATION_1)) {
-                if (!isKeyguardVisible) {
-                    clock?.animations?.doze(if (isDozing) 1f else 0f)
+    private val batteryCallback =
+        object : BatteryStateChangeCallback {
+            override fun onBatteryLevelChanged(level: Int, pluggedIn: Boolean, charging: Boolean) {
+                if (isKeyguardVisible && !isCharging && charging) {
+                    clock?.animations?.charge()
+                }
+                isCharging = charging
+            }
+        }
+
+    private val localeBroadcastReceiver =
+        object : BroadcastReceiver() {
+            override fun onReceive(context: Context, intent: Intent) {
+                clock?.events?.onLocaleChanged(Locale.getDefault())
+            }
+        }
+
+    private val keyguardUpdateMonitorCallback =
+        object : KeyguardUpdateMonitorCallback() {
+            override fun onKeyguardVisibilityChanged(visible: Boolean) {
+                isKeyguardVisible = visible
+                if (!featureFlags.isEnabled(DOZING_MIGRATION_1)) {
+                    if (!isKeyguardVisible) {
+                        clock?.animations?.doze(if (isDozing) 1f else 0f)
+                    }
+                }
+
+                smallTimeListener?.update(shouldTimeListenerRun)
+                largeTimeListener?.update(shouldTimeListenerRun)
+            }
+
+            override fun onTimeFormatChanged(timeFormat: String) {
+                clock?.events?.onTimeFormatChanged(DateFormat.is24HourFormat(context))
+            }
+
+            override fun onTimeZoneChanged(timeZone: TimeZone) {
+                clock?.events?.onTimeZoneChanged(timeZone)
+            }
+
+            override fun onUserSwitchComplete(userId: Int) {
+                clock?.events?.onTimeFormatChanged(DateFormat.is24HourFormat(context))
+            }
+
+            override fun onWeatherDataChanged(data: Weather?) {
+                if (data != null) {
+                    clock?.events?.onWeatherDataChanged(data)
                 }
             }
         }
 
-        override fun onTimeFormatChanged(timeFormat: String) {
-            clock?.events?.onTimeFormatChanged(DateFormat.is24HourFormat(context))
-        }
-
-        override fun onTimeZoneChanged(timeZone: TimeZone) {
-            clock?.events?.onTimeZoneChanged(timeZone)
-        }
-
-        override fun onUserSwitchComplete(userId: Int) {
-            clock?.events?.onTimeFormatChanged(DateFormat.is24HourFormat(context))
-        }
-    }
-
     fun registerListeners(parent: View) {
         if (isRegistered) {
             return
@@ -275,17 +311,20 @@
         configurationController.addCallback(configListener)
         batteryController.addCallback(batteryCallback)
         keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
-        disposableHandle = parent.repeatWhenAttached {
-            repeatOnLifecycle(Lifecycle.State.STARTED) {
-                listenForDozing(this)
-                if (featureFlags.isEnabled(DOZING_MIGRATION_1)) {
-                    listenForDozeAmountTransition(this)
-                    listenForAnyStateToAodTransition(this)
-                } else {
-                    listenForDozeAmount(this)
+        disposableHandle =
+            parent.repeatWhenAttached {
+                repeatOnLifecycle(Lifecycle.State.STARTED) {
+                    listenForDozing(this)
+                    if (featureFlags.isEnabled(DOZING_MIGRATION_1)) {
+                        listenForDozeAmountTransition(this)
+                        listenForAnyStateToAodTransition(this)
+                    } else {
+                        listenForDozeAmount(this)
+                    }
                 }
             }
-        }
+        smallTimeListener?.update(shouldTimeListenerRun)
+        largeTimeListener?.update(shouldTimeListenerRun)
     }
 
     fun unregisterListeners() {
@@ -300,76 +339,140 @@
         batteryController.removeCallback(batteryCallback)
         keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback)
         regionSampler?.stopRegionSampler()
+        smallTimeListener?.stop()
+        largeTimeListener?.stop()
+    }
+
+    private fun updateTimeListeners() {
+        smallTimeListener?.stop()
+        largeTimeListener?.stop()
+
+        smallTimeListener = null
+        largeTimeListener = null
+
+        clock?.smallClock?.let {
+            smallTimeListener = TimeListener(it, mainExecutor)
+            smallTimeListener?.update(shouldTimeListenerRun)
+        }
+        clock?.largeClock?.let {
+            largeTimeListener = TimeListener(it, mainExecutor)
+            largeTimeListener?.update(shouldTimeListenerRun)
+        }
     }
 
     private fun updateFontSizes() {
-        clock?.smallClock?.events?.onFontSettingChanged(
-            resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat())
-        clock?.largeClock?.events?.onFontSettingChanged(
-            resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat())
+        clock
+            ?.smallClock
+            ?.events
+            ?.onFontSettingChanged(
+                resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat()
+            )
+        clock
+            ?.largeClock
+            ?.events
+            ?.onFontSettingChanged(
+                resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat()
+            )
     }
 
-    /**
-     * Dump information for debugging
-     */
-    fun dump(pw: PrintWriter) {
-        pw.println(this)
-        clock?.dump(pw)
-        regionSampler?.dump(pw)
+    private fun handleDoze(doze: Float) {
+        dozeAmount = doze
+        clock?.animations?.doze(dozeAmount)
+        smallTimeListener?.update(doze < DOZE_TICKRATE_THRESHOLD)
+        largeTimeListener?.update(doze < DOZE_TICKRATE_THRESHOLD)
     }
 
     @VisibleForTesting
     internal fun listenForDozeAmount(scope: CoroutineScope): Job {
-        return scope.launch {
-            keyguardInteractor.dozeAmount.collect {
-                dozeAmount = it
-                clock?.animations?.doze(dozeAmount)
-            }
-        }
+        return scope.launch { keyguardInteractor.dozeAmount.collect { handleDoze(it) } }
     }
 
     @VisibleForTesting
     internal fun listenForDozeAmountTransition(scope: CoroutineScope): Job {
         return scope.launch {
-            keyguardTransitionInteractor.dozeAmountTransition.collect {
-                dozeAmount = it.value
-                clock?.animations?.doze(dozeAmount)
-            }
+            keyguardTransitionInteractor.dozeAmountTransition.collect { handleDoze(it.value) }
         }
     }
 
     /**
-     * When keyguard is displayed again after being gone, the clock must be reset to full
-     * dozing.
+     * When keyguard is displayed again after being gone, the clock must be reset to full dozing.
      */
     @VisibleForTesting
     internal fun listenForAnyStateToAodTransition(scope: CoroutineScope): Job {
         return scope.launch {
-            keyguardTransitionInteractor.anyStateToAodTransition.filter {
-                it.transitionState == TransitionState.FINISHED
-            }.collect {
-                dozeAmount = 1f
-                clock?.animations?.doze(dozeAmount)
-            }
+            keyguardTransitionInteractor.anyStateToAodTransition
+                .filter { it.transitionState == TransitionState.FINISHED }
+                .collect { handleDoze(1f) }
         }
     }
 
     @VisibleForTesting
     internal fun listenForDozing(scope: CoroutineScope): Job {
         return scope.launch {
-            combine (
-                keyguardInteractor.dozeAmount,
-                keyguardInteractor.isDozing,
-            ) { localDozeAmount, localIsDozing ->
-                localDozeAmount > dozeAmount || localIsDozing
+            combine(
+                    keyguardInteractor.dozeAmount,
+                    keyguardInteractor.isDozing,
+                ) { localDozeAmount, localIsDozing ->
+                    localDozeAmount > dozeAmount || localIsDozing
+                }
+                .collect { localIsDozing -> isDozing = localIsDozing }
+        }
+    }
+
+    class TimeListener(val clockFace: ClockFaceController, val executor: DelayableExecutor) {
+        val predrawListener =
+            ViewTreeObserver.OnPreDrawListener {
+                clockFace.events.onTimeTick()
+                true
             }
-            .collect { localIsDozing ->
-                isDozing = localIsDozing
+
+        val secondsRunnable =
+            object : Runnable {
+                override fun run() {
+                    if (!isRunning) {
+                        return
+                    }
+
+                    executor.executeDelayed(this, 990)
+                    clockFace.events.onTimeTick()
+                }
+            }
+
+        var isRunning: Boolean = false
+            private set
+
+        fun start() {
+            if (isRunning) {
+                return
+            }
+
+            isRunning = true
+            when (clockFace.events.tickRate) {
+                ClockTickRate.PER_MINUTE -> {
+                    /* Handled by KeyguardClockSwitchController */
+                }
+                ClockTickRate.PER_SECOND -> executor.execute(secondsRunnable)
+                ClockTickRate.PER_FRAME -> {
+                    clockFace.view.viewTreeObserver.addOnPreDrawListener(predrawListener)
+                    clockFace.view.invalidate()
+                }
             }
         }
+
+        fun stop() {
+            if (!isRunning) {
+                return
+            }
+
+            isRunning = false
+            clockFace.view.viewTreeObserver.removeOnPreDrawListener(predrawListener)
+        }
+
+        fun update(shouldRun: Boolean) = if (shouldRun) start() else stop()
     }
 
     companion object {
         private val TAG = ClockEventController::class.simpleName!!
+        private val DOZE_TICKRATE_THRESHOLD = 0.99f
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/FaceWakeUpTriggersConfig.kt b/packages/SystemUI/src/com/android/keyguard/FaceWakeUpTriggersConfig.kt
index a0c43fb..788a66d 100644
--- a/packages/SystemUI/src/com/android/keyguard/FaceWakeUpTriggersConfig.kt
+++ b/packages/SystemUI/src/com/android/keyguard/FaceWakeUpTriggersConfig.kt
@@ -29,7 +29,7 @@
 import java.util.stream.Collectors
 import javax.inject.Inject
 
-/** Determines which device wake-ups should trigger face authentication. */
+/** Determines which device wake-ups should trigger passive authentication. */
 @SysUISingleton
 class FaceWakeUpTriggersConfig
 @Inject
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index 7da27b1..baaef19 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -103,6 +103,7 @@
 
     @Override
     public void reset() {
+        super.reset();
         // start fresh
         mDismissing = false;
         mView.resetPasswordText(false /* animate */, false /* announce */);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 4acbb0a..0326b6d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -209,7 +209,6 @@
 
         if (!animate) {
             out.setAlpha(0f);
-            out.setVisibility(INVISIBLE);
             in.setAlpha(1f);
             in.setVisibility(VISIBLE);
             mStatusArea.setTranslationY(statusAreaYTranslation);
@@ -225,10 +224,7 @@
                         direction * -mClockSwitchYAmount));
         mClockOutAnim.addListener(new AnimatorListenerAdapter() {
             public void onAnimationEnd(Animator animation) {
-                if (mClockOutAnim == animation) {
-                    out.setVisibility(INVISIBLE);
-                    mClockOutAnim = null;
-                }
+                mClockOutAnim = null;
             }
         });
 
@@ -242,9 +238,7 @@
         mClockInAnim.setStartDelay(CLOCK_OUT_MILLIS / 2);
         mClockInAnim.addListener(new AnimatorListenerAdapter() {
             public void onAnimationEnd(Animator animation) {
-                if (mClockInAnim == animation) {
-                    mClockInAnim = null;
-                }
+                mClockInAnim = null;
             }
         });
 
@@ -257,9 +251,7 @@
         mStatusAreaAnim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
         mStatusAreaAnim.addListener(new AnimatorListenerAdapter() {
             public void onAnimationEnd(Animator animation) {
-                if (mStatusAreaAnim == animation) {
-                    mStatusAreaAnim = null;
-                }
+                mStatusAreaAnim = null;
             }
         });
         mStatusAreaAnim.start();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 71cd18d..b85b2b8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -45,6 +45,7 @@
 import com.android.systemui.plugins.log.LogLevel;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.shared.clocks.ClockRegistry;
+import com.android.systemui.shared.regionsampling.RegionSampler;
 import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
@@ -88,7 +89,10 @@
     private final ClockRegistry.ClockChangeListener mClockChangedListener;
 
     private ViewGroup mStatusArea;
-    // If set will replace keyguard_slice_view
+
+    // If the SMARTSPACE flag is set, keyguard_slice_view is replaced by the following views.
+    private ViewGroup mDateWeatherView;
+    private View mWeatherView;
     private View mSmartspaceView;
 
     private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
@@ -102,6 +106,12 @@
             updateDoubleLineClock();
         }
     };
+    private final ContentObserver mShowWeatherObserver = new ContentObserver(null) {
+        @Override
+        public void onChange(boolean change) {
+            setWeatherVisibility();
+        }
+    };
 
     private final KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener
             mKeyguardUnlockAnimationListener =
@@ -141,8 +151,13 @@
         mLogBuffer = logBuffer;
         mView.setLogBuffer(mLogBuffer);
 
-        mClockChangedListener = () -> {
-            setClock(mClockRegistry.createCurrentClock());
+        mClockChangedListener = new ClockRegistry.ClockChangeListener() {
+            @Override
+            public void onCurrentClockChanged() {
+                setClock(mClockRegistry.createCurrentClock());
+            }
+            @Override
+            public void onAvailableClocksChanged() { }
         };
     }
 
@@ -192,10 +207,17 @@
 
         if (mSmartspaceController.isEnabled()) {
             View ksv = mView.findViewById(R.id.keyguard_slice_view);
-            int ksvIndex = mStatusArea.indexOfChild(ksv);
+            int viewIndex = mStatusArea.indexOfChild(ksv);
             ksv.setVisibility(View.GONE);
 
-            addSmartspaceView(ksvIndex);
+            // TODO(b/261757708): add content observer for the Settings toggle and add/remove
+            //  weather according to the Settings.
+            if (mSmartspaceController.isDateWeatherDecoupled()) {
+                addDateWeatherView(viewIndex);
+                viewIndex += 1;
+            }
+
+            addSmartspaceView(viewIndex);
         }
 
         mSecureSettings.registerContentObserverForUser(
@@ -205,7 +227,15 @@
                 UserHandle.USER_ALL
         );
 
+        mSecureSettings.registerContentObserverForUser(
+                Settings.Secure.LOCK_SCREEN_WEATHER_ENABLED,
+                false, /* notifyForDescendants */
+                mShowWeatherObserver,
+                UserHandle.USER_ALL
+        );
+
         updateDoubleLineClock();
+        setWeatherVisibility();
 
         mKeyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(
                 mKeyguardUnlockAnimationListener);
@@ -229,6 +259,14 @@
 
     void onLocaleListChanged() {
         if (mSmartspaceController.isEnabled()) {
+            if (mSmartspaceController.isDateWeatherDecoupled()) {
+                mDateWeatherView.removeView(mWeatherView);
+                int index = mStatusArea.indexOfChild(mDateWeatherView);
+                if (index >= 0) {
+                    mStatusArea.removeView(mDateWeatherView);
+                    addDateWeatherView(index);
+                }
+            }
             int index = mStatusArea.indexOfChild(mSmartspaceView);
             if (index >= 0) {
                 mStatusArea.removeView(mSmartspaceView);
@@ -237,6 +275,30 @@
         }
     }
 
+    private void addDateWeatherView(int index) {
+        mDateWeatherView = (ViewGroup) mSmartspaceController.buildAndConnectDateView(mView);
+        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+                MATCH_PARENT, WRAP_CONTENT);
+        mStatusArea.addView(mDateWeatherView, index, lp);
+        int startPadding = getContext().getResources().getDimensionPixelSize(
+                R.dimen.below_clock_padding_start);
+        int endPadding = getContext().getResources().getDimensionPixelSize(
+                R.dimen.below_clock_padding_end);
+        mDateWeatherView.setPaddingRelative(startPadding, 0, endPadding, 0);
+
+        addWeatherView();
+    }
+
+    private void addWeatherView() {
+        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+                WRAP_CONTENT, WRAP_CONTENT);
+        mWeatherView = mSmartspaceController.buildAndConnectWeatherView(mView);
+        // Place weather right after the date, before the extras
+        final int index = mDateWeatherView.getChildCount() == 0 ? 0 : 1;
+        mDateWeatherView.addView(mWeatherView, index, lp);
+        mWeatherView.setPaddingRelative(0, 0, 4, 0);
+    }
+
     private void addSmartspaceView(int index) {
         mSmartspaceView = mSmartspaceController.buildAndConnectView(mView);
         LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
@@ -301,7 +363,8 @@
         }
         ClockController clock = getClock();
         if (clock != null) {
-            clock.getEvents().onTimeTick();
+            clock.getSmallClock().getEvents().onTimeTick();
+            clock.getLargeClock().getEvents().onTimeTick();
         }
     }
 
@@ -346,6 +409,10 @@
             int clockHeight = clock.getLargeClock().getView().getHeight();
             return frameHeight / 2 + clockHeight / 2 + mKeyguardLargeClockTopMargin / -2;
         } else {
+            // This is only called if we've never shown the large clock as the frame is inflated
+            // with 'gone', but then the visibility is never set when it is animated away by
+            // KeyguardClockSwitch, instead it is removed from the view hierarchy.
+            // TODO(b/261755021): Cleanup Large Frame Visibility
             int clockHeight = clock.getSmallClock().getView().getHeight();
             return clockHeight + statusBarHeaderHeight + mKeyguardSmallClockTopMargin;
         }
@@ -363,11 +430,15 @@
         if (mLargeClockFrame.getVisibility() == View.VISIBLE) {
             return clock.getLargeClock().getView().getHeight();
         } else {
+            // Is not called except in certain edge cases, see comment in getClockBottom
+            // TODO(b/261755021): Cleanup Large Frame Visibility
             return clock.getSmallClock().getView().getHeight();
         }
     }
 
     boolean isClockTopAligned() {
+        // Returns false except certain edge cases, see comment in getClockBottom
+        // TODO(b/261755021): Cleanup Large Frame Visibility
         return mLargeClockFrame.getVisibility() != View.VISIBLE;
     }
 
@@ -405,6 +476,14 @@
         }
     }
 
+    private void setWeatherVisibility() {
+        if (mWeatherView != null) {
+            mUiExecutor.execute(
+                    () -> mWeatherView.setVisibility(
+                        mSmartspaceController.isWeatherEnabled() ? View.VISIBLE : View.GONE));
+        }
+    }
+
     /**
      * Sets the clipChildren property on relevant views, to allow the smartspace to draw out of
      * bounds during the unlock transition.
@@ -424,6 +503,10 @@
         if (clock != null) {
             clock.dump(pw);
         }
+        final RegionSampler regionSampler = mClockEventController.getRegionSampler();
+        if (regionSampler != null) {
+            regionSampler.dump(pw);
+        }
     }
 
     /** Gets the animations for the current clock. */
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 02776a2..ec8fa92 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -15,8 +15,6 @@
  */
 package com.android.keyguard;
 
-import static android.view.Display.DEFAULT_DISPLAY;
-
 import android.app.Presentation;
 import android.content.Context;
 import android.graphics.Color;
@@ -37,9 +35,11 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.keyguard.dagger.KeyguardStatusViewComponent;
 import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dagger.qualifiers.UiBackground;
 import com.android.systemui.navigationbar.NavigationBarController;
 import com.android.systemui.navigationbar.NavigationBarView;
+import com.android.systemui.settings.DisplayTracker;
 
 import java.util.concurrent.Executor;
 
@@ -53,6 +53,7 @@
 
     private MediaRouter mMediaRouter = null;
     private final DisplayManager mDisplayService;
+    private final DisplayTracker mDisplayTracker;
     private final Lazy<NavigationBarController> mNavigationBarControllerLazy;
     private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
     private final Context mContext;
@@ -62,46 +63,43 @@
 
     private final SparseArray<Presentation> mPresentations = new SparseArray<>();
 
-    private final DisplayManager.DisplayListener mDisplayListener =
-            new DisplayManager.DisplayListener() {
+    private final DisplayTracker.Callback mDisplayCallback =
+            new DisplayTracker.Callback() {
+                @Override
+                public void onDisplayAdded(int displayId) {
+                    Trace.beginSection(
+                            "KeyguardDisplayManager#onDisplayAdded(displayId=" + displayId + ")");
+                    final Display display = mDisplayService.getDisplay(displayId);
+                    if (mShowing) {
+                        updateNavigationBarVisibility(displayId, false /* navBarVisible */);
+                        showPresentation(display);
+                    }
+                    Trace.endSection();
+                }
 
-        @Override
-        public void onDisplayAdded(int displayId) {
-            Trace.beginSection(
-                    "KeyguardDisplayManager#onDisplayAdded(displayId=" + displayId + ")");
-            final Display display = mDisplayService.getDisplay(displayId);
-            if (mShowing) {
-                updateNavigationBarVisibility(displayId, false /* navBarVisible */);
-                showPresentation(display);
-            }
-            Trace.endSection();
-        }
-
-        @Override
-        public void onDisplayChanged(int displayId) {
-
-        }
-
-        @Override
-        public void onDisplayRemoved(int displayId) {
-            Trace.beginSection(
-                    "KeyguardDisplayManager#onDisplayRemoved(displayId=" + displayId + ")");
-            hidePresentation(displayId);
-            Trace.endSection();
-        }
-    };
+                @Override
+                public void onDisplayRemoved(int displayId) {
+                    Trace.beginSection(
+                            "KeyguardDisplayManager#onDisplayRemoved(displayId=" + displayId + ")");
+                    hidePresentation(displayId);
+                    Trace.endSection();
+                }
+            };
 
     @Inject
     public KeyguardDisplayManager(Context context,
             Lazy<NavigationBarController> navigationBarControllerLazy,
             KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory,
+            DisplayTracker displayTracker,
+            @Main Executor mainExecutor,
             @UiBackground Executor uiBgExecutor) {
         mContext = context;
         mNavigationBarControllerLazy = navigationBarControllerLazy;
         mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
         uiBgExecutor.execute(() -> mMediaRouter = mContext.getSystemService(MediaRouter.class));
         mDisplayService = mContext.getSystemService(DisplayManager.class);
-        mDisplayService.registerDisplayListener(mDisplayListener, null /* handler */);
+        mDisplayTracker = displayTracker;
+        mDisplayTracker.addDisplayChangeCallback(mDisplayCallback, mainExecutor);
     }
 
     private boolean isKeyguardShowable(Display display) {
@@ -109,7 +107,7 @@
             if (DEBUG) Log.i(TAG, "Cannot show Keyguard on null display");
             return false;
         }
-        if (display.getDisplayId() == DEFAULT_DISPLAY) {
+        if (display.getDisplayId() == mDisplayTracker.getDefaultDisplayId()) {
             if (DEBUG) Log.i(TAG, "Do not show KeyguardPresentation on the default display");
             return false;
         }
@@ -224,7 +222,7 @@
     protected boolean updateDisplays(boolean showing) {
         boolean changed = false;
         if (showing) {
-            final Display[] displays = mDisplayService.getDisplays();
+            final Display[] displays = mDisplayTracker.getAllDisplays();
             for (Display display : displays) {
                 int displayId = display.getDisplayId();
                 updateNavigationBarVisibility(displayId, false /* navBarVisible */);
@@ -247,7 +245,7 @@
     //  term solution in R.
     private void updateNavigationBarVisibility(int displayId, boolean navBarVisible) {
         // Leave this task to {@link StatusBarKeyguardViewManager}
-        if (displayId == DEFAULT_DISPLAY) return;
+        if (displayId == mDisplayTracker.getDefaultDisplayId()) return;
 
         NavigationBarView navBarView = mNavigationBarControllerLazy.get()
                 .getNavigationBarView(displayId);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
index 1a06b5f..fe8b8c9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
@@ -27,6 +27,7 @@
     override var userId: Int = 0,
     override var listening: Boolean = false,
     // keep sorted
+    var alternateBouncerShowing: Boolean = false,
     var authInterruptActive: Boolean = false,
     var biometricSettingEnabledForUser: Boolean = false,
     var bouncerFullyShown: Boolean = false,
@@ -44,7 +45,6 @@
     var secureCameraLaunched: Boolean = false,
     var supportsDetect: Boolean = false,
     var switchingUser: Boolean = false,
-    var udfpsBouncerShowing: Boolean = false,
     var udfpsFingerDown: Boolean = false,
     var userNotTrustedOrDetectionIsNeeded: Boolean = false,
 ) : KeyguardListenModel() {
@@ -73,7 +73,7 @@
             secureCameraLaunched.toString(),
             supportsDetect.toString(),
             switchingUser.toString(),
-            udfpsBouncerShowing.toString(),
+            alternateBouncerShowing.toString(),
             udfpsFingerDown.toString(),
             userNotTrustedOrDetectionIsNeeded.toString(),
         )
@@ -95,6 +95,7 @@
                 userId = model.userId
                 listening = model.listening
                 // keep sorted
+                alternateBouncerShowing = model.alternateBouncerShowing
                 biometricSettingEnabledForUser = model.biometricSettingEnabledForUser
                 bouncerFullyShown = model.bouncerFullyShown
                 faceAndFpNotAuthenticated = model.faceAndFpNotAuthenticated
@@ -111,7 +112,6 @@
                 secureCameraLaunched = model.secureCameraLaunched
                 supportsDetect = model.supportsDetect
                 switchingUser = model.switchingUser
-                udfpsBouncerShowing = model.udfpsBouncerShowing
                 switchingUser = model.switchingUser
                 udfpsFingerDown = model.udfpsFingerDown
                 userNotTrustedOrDetectionIsNeeded = model.userNotTrustedOrDetectionIsNeeded
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
index 998dc09..57130ed 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
@@ -27,6 +27,7 @@
     override var userId: Int = 0,
     override var listening: Boolean = false,
     // keepSorted
+    var alternateBouncerShowing: Boolean = false,
     var biometricEnabledForUser: Boolean = false,
     var bouncerIsOrWillShow: Boolean = false,
     var canSkipBouncer: Boolean = false,
@@ -57,6 +58,7 @@
             userId.toString(),
             listening.toString(),
             // keep sorted
+            alternateBouncerShowing.toString(),
             biometricEnabledForUser.toString(),
             bouncerIsOrWillShow.toString(),
             canSkipBouncer.toString(),
@@ -96,6 +98,7 @@
                 userId = model.userId
                 listening = model.listening
                 // keep sorted
+                alternateBouncerShowing = model.alternateBouncerShowing
                 biometricEnabledForUser = model.biometricEnabledForUser
                 bouncerIsOrWillShow = model.bouncerIsOrWillShow
                 canSkipBouncer = model.canSkipBouncer
@@ -141,6 +144,7 @@
                 "userId",
                 "listening",
                 // keep sorted
+                "alternateBouncerShowing",
                 "biometricAllowedForUser",
                 "bouncerIsOrWillShow",
                 "canSkipBouncer",
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
deleted file mode 100644
index 08e9cf6..0000000
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2007 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.keyguard;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.util.AttributeSet;
-import android.widget.FrameLayout;
-
-/**
- * Base class for keyguard view.  {@link #reset} is where you should
- * reset the state of your view.  Use the {@link KeyguardViewCallback} via
- * {@link #getCallback()} to send information back (such as poking the wake lock,
- * or finishing the keyguard).
- *
- * Handles intercepting of media keys that still work when the keyguard is
- * showing.
- */
-public class KeyguardHostView extends FrameLayout {
-
-    protected ViewMediatorCallback mViewMediatorCallback;
-
-
-    public KeyguardHostView(Context context) {
-        this(context, null);
-    }
-
-    public KeyguardHostView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    protected void dispatchDraw(Canvas canvas) {
-        super.dispatchDraw(canvas);
-        if (mViewMediatorCallback != null) {
-            mViewMediatorCallback.keyguardDoneDrawing();
-        }
-    }
-
-    public void setViewMediatorCallback(ViewMediatorCallback viewMediatorCallback) {
-        mViewMediatorCallback = viewMediatorCallback;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
deleted file mode 100644
index ea84438..0000000
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
+++ /dev/null
@@ -1,530 +0,0 @@
-/*
- * Copyright (C) 2020 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.keyguard;
-
-import android.app.ActivityManager;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.media.AudioManager;
-import android.os.SystemClock;
-import android.telephony.TelephonyManager;
-import android.util.Log;
-import android.util.MathUtils;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.View.OnKeyListener;
-import android.view.ViewTreeObserver;
-import android.widget.FrameLayout;
-import android.window.OnBackAnimationCallback;
-
-import androidx.annotation.NonNull;
-
-import com.android.keyguard.KeyguardSecurityContainer.SecurityCallback;
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.keyguard.dagger.KeyguardBouncerScope;
-import com.android.settingslib.Utils;
-import com.android.systemui.R;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.util.ViewController;
-
-import java.io.File;
-
-import javax.inject.Inject;
-
-/** Controller for a {@link KeyguardHostView}. */
-@KeyguardBouncerScope
-public class KeyguardHostViewController extends ViewController<KeyguardHostView> {
-    private static final String TAG = "KeyguardViewBase";
-    public static final boolean DEBUG = KeyguardConstants.DEBUG;
-    // Whether the volume keys should be handled by keyguard. If true, then
-    // they will be handled here for specific media types such as music, otherwise
-    // the audio service will bring up the volume dialog.
-    private static final boolean KEYGUARD_MANAGES_VOLUME = false;
-
-    private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key";
-
-    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    private final KeyguardSecurityContainerController mKeyguardSecurityContainerController;
-    private final TelephonyManager mTelephonyManager;
-    private final ViewMediatorCallback mViewMediatorCallback;
-    private final AudioManager mAudioManager;
-
-    private ActivityStarter.OnDismissAction mDismissAction;
-    private Runnable mCancelAction;
-    private int mTranslationY;
-
-    private final KeyguardUpdateMonitorCallback mUpdateCallback =
-            new KeyguardUpdateMonitorCallback() {
-                @Override
-                public void onTrustGrantedForCurrentUser(
-                        boolean dismissKeyguard,
-                        boolean newlyUnlocked,
-                        TrustGrantFlags flags,
-                        String message
-                ) {
-                    if (dismissKeyguard) {
-                        if (!mView.isVisibleToUser()) {
-                            // The trust agent dismissed the keyguard without the user proving
-                            // that they are present (by swiping up to show the bouncer). That's
-                            // fine if the user proved presence via some other way to the trust
-                            // agent.
-                            Log.i(TAG, "TrustAgent dismissed Keyguard.");
-                        }
-                        mSecurityCallback.dismiss(
-                                false /* authenticated */,
-                                KeyguardUpdateMonitor.getCurrentUser(),
-                                /* bypassSecondaryLockScreen */ false,
-                                SecurityMode.Invalid
-                        );
-                    } else {
-                        if (flags.isInitiatedByUser() || flags.dismissKeyguardRequested()) {
-                            mViewMediatorCallback.playTrustedSound();
-                        }
-                    }
-                }
-            };
-
-    private final SecurityCallback mSecurityCallback = new SecurityCallback() {
-
-        @Override
-        public boolean dismiss(boolean authenticated, int targetUserId,
-                boolean bypassSecondaryLockScreen, SecurityMode expectedSecurityMode) {
-            return mKeyguardSecurityContainerController.showNextSecurityScreenOrFinish(
-                    authenticated, targetUserId, bypassSecondaryLockScreen, expectedSecurityMode);
-        }
-
-        @Override
-        public void userActivity() {
-            mViewMediatorCallback.userActivity();
-        }
-
-        @Override
-        public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput) {
-            mViewMediatorCallback.setNeedsInput(needsInput);
-        }
-
-        /**
-         * Authentication has happened and it's time to dismiss keyguard. This function
-         * should clean up and inform KeyguardViewMediator.
-         *
-         * @param strongAuth whether the user has authenticated with strong authentication like
-         *                   pattern, password or PIN but not by trust agents or fingerprint
-         * @param targetUserId a user that needs to be the foreground user at the dismissal
-         *                    completion.
-         */
-        @Override
-        public void finish(boolean strongAuth, int targetUserId) {
-            // If there's a pending runnable because the user interacted with a widget
-            // and we're leaving keyguard, then run it.
-            boolean deferKeyguardDone = false;
-            if (mDismissAction != null) {
-                deferKeyguardDone = mDismissAction.onDismiss();
-                mDismissAction = null;
-                mCancelAction = null;
-            }
-            if (mViewMediatorCallback != null) {
-                if (deferKeyguardDone) {
-                    mViewMediatorCallback.keyguardDonePending(strongAuth, targetUserId);
-                } else {
-                    mViewMediatorCallback.keyguardDone(strongAuth, targetUserId);
-                }
-            }
-        }
-
-        @Override
-        public void reset() {
-            mViewMediatorCallback.resetKeyguard();
-        }
-
-        @Override
-        public void onCancelClicked() {
-            mViewMediatorCallback.onCancelClicked();
-        }
-    };
-
-    private OnKeyListener mOnKeyListener = (v, keyCode, event) -> interceptMediaKey(event);
-
-    @Inject
-    public KeyguardHostViewController(KeyguardHostView view,
-            KeyguardUpdateMonitor keyguardUpdateMonitor,
-            AudioManager audioManager,
-            TelephonyManager telephonyManager,
-            ViewMediatorCallback viewMediatorCallback,
-            KeyguardSecurityContainerController.Factory
-                    keyguardSecurityContainerControllerFactory) {
-        super(view);
-        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
-        mAudioManager = audioManager;
-        mTelephonyManager = telephonyManager;
-        mViewMediatorCallback = viewMediatorCallback;
-        mKeyguardSecurityContainerController = keyguardSecurityContainerControllerFactory.create(
-                mSecurityCallback);
-    }
-
-    /** Initialize the Controller. */
-    public void onInit() {
-        mKeyguardSecurityContainerController.init();
-        updateResources();
-    }
-
-    @Override
-    protected void onViewAttached() {
-        mView.setViewMediatorCallback(mViewMediatorCallback);
-        // Update ViewMediator with the current input method requirements
-        mViewMediatorCallback.setNeedsInput(mKeyguardSecurityContainerController.needsInput());
-        mKeyguardUpdateMonitor.registerCallback(mUpdateCallback);
-        mView.setOnKeyListener(mOnKeyListener);
-        mKeyguardSecurityContainerController.showPrimarySecurityScreen(false);
-    }
-
-    @Override
-    protected void onViewDetached() {
-        mKeyguardUpdateMonitor.removeCallback(mUpdateCallback);
-        mView.setOnKeyListener(null);
-    }
-
-     /** Called before this view is being removed. */
-    public void cleanUp() {
-        mKeyguardSecurityContainerController.onPause();
-    }
-
-    public void resetSecurityContainer() {
-        mKeyguardSecurityContainerController.reset();
-    }
-
-    /**
-     * Dismisses the keyguard by going to the next screen or making it gone.
-     * @param targetUserId a user that needs to be the foreground user at the dismissal completion.
-     * @return True if the keyguard is done.
-     */
-    public boolean dismiss(int targetUserId) {
-        return mSecurityCallback.dismiss(false, targetUserId, false,
-                getCurrentSecurityMode());
-    }
-
-    /**
-     * Called when the Keyguard is actively shown on the screen.
-     */
-    public void onResume() {
-        if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode()));
-        mKeyguardSecurityContainerController.onResume(KeyguardSecurityView.SCREEN_ON);
-        mView.requestFocus();
-    }
-
-    public CharSequence getAccessibilityTitleForCurrentMode() {
-        return mKeyguardSecurityContainerController.getTitle();
-    }
-
-    /**
-     * Starts the animation when the Keyguard gets shown.
-     */
-    public void appear(int statusBarHeight) {
-        // We might still be collapsed and the view didn't have time to layout yet or still
-        // be small, let's wait on the predraw to do the animation in that case.
-        if (mView.getHeight() != 0 && mView.getHeight() != statusBarHeight) {
-            mKeyguardSecurityContainerController.startAppearAnimation();
-        } else {
-            mView.getViewTreeObserver().addOnPreDrawListener(
-                    new ViewTreeObserver.OnPreDrawListener() {
-                        @Override
-                        public boolean onPreDraw() {
-                            mView.getViewTreeObserver().removeOnPreDrawListener(this);
-                            mKeyguardSecurityContainerController.startAppearAnimation();
-                            return true;
-                        }
-                    });
-            mView.requestLayout();
-        }
-    }
-
-    /**
-     * Show a string explaining why the security view needs to be solved.
-     *
-     * @param reason a flag indicating which string should be shown, see
-     *               {@link KeyguardSecurityView#PROMPT_REASON_NONE},
-     *               {@link KeyguardSecurityView#PROMPT_REASON_RESTART},
-     *               {@link KeyguardSecurityView#PROMPT_REASON_TIMEOUT}, and
-     *               {@link KeyguardSecurityView#PROMPT_REASON_PREPARE_FOR_UPDATE}.
-     */
-    public void showPromptReason(int reason) {
-        mKeyguardSecurityContainerController.showPromptReason(reason);
-    }
-
-    public void showMessage(CharSequence message, ColorStateList colorState) {
-        mKeyguardSecurityContainerController.showMessage(message, colorState);
-    }
-
-    public void showErrorMessage(CharSequence customMessage) {
-        showMessage(customMessage, Utils.getColorError(mView.getContext()));
-    }
-
-    /**
-     * Sets an action to run when keyguard finishes.
-     *
-     * @param action
-     */
-    public void setOnDismissAction(ActivityStarter.OnDismissAction action, Runnable cancelAction) {
-        if (mCancelAction != null) {
-            mCancelAction.run();
-            mCancelAction = null;
-        }
-        mDismissAction = action;
-        mCancelAction = cancelAction;
-    }
-
-    public void cancelDismissAction() {
-        setOnDismissAction(null, null);
-    }
-
-    public void startDisappearAnimation(Runnable finishRunnable) {
-        if (!mKeyguardSecurityContainerController.startDisappearAnimation(finishRunnable)
-                && finishRunnable != null) {
-            finishRunnable.run();
-        }
-    }
-
-    /**
-     * Called when the Keyguard is not actively shown anymore on the screen.
-     */
-    public void onPause() {
-        if (DEBUG) {
-            Log.d(TAG, String.format("screen off, instance %s at %s",
-                    Integer.toHexString(hashCode()), SystemClock.uptimeMillis()));
-        }
-        mKeyguardSecurityContainerController.showPrimarySecurityScreen(true);
-        mKeyguardSecurityContainerController.onPause();
-        mView.clearFocus();
-    }
-
-    /**
-     * Called when the view needs to be shown.
-     */
-    public void showPrimarySecurityScreen() {
-        if (DEBUG) Log.d(TAG, "show()");
-        mKeyguardSecurityContainerController.showPrimarySecurityScreen(false);
-    }
-
-    /**
-     * Fades and translates in/out the security screen.
-     * Fades in as expansion approaches 0.
-     * Animation duration is between 0.33f and 0.67f of panel expansion fraction.
-     * @param fraction amount of the screen that should show.
-     */
-    public void setExpansion(float fraction) {
-        float scaledFraction = BouncerPanelExpansionCalculator.showBouncerProgress(fraction);
-        mView.setAlpha(MathUtils.constrain(1 - scaledFraction, 0f, 1f));
-        mView.setTranslationY(scaledFraction * mTranslationY);
-    }
-
-    /**
-     * When bouncer was visible and is starting to become hidden.
-     */
-    public void onStartingToHide() {
-        mKeyguardSecurityContainerController.onStartingToHide();
-    }
-
-    /** Called when bouncer visibility changes. */
-    public void onBouncerVisibilityChanged(@View.Visibility int visibility) {
-        mKeyguardSecurityContainerController.onBouncerVisibilityChanged(visibility);
-    }
-
-    public boolean hasDismissActions() {
-        return mDismissAction != null || mCancelAction != null;
-    }
-
-    public SecurityMode getCurrentSecurityMode() {
-        return mKeyguardSecurityContainerController.getCurrentSecurityMode();
-    }
-
-    public int getTop() {
-        int top = mView.getTop();
-        // The password view has an extra top padding that should be ignored.
-        if (getCurrentSecurityMode() == SecurityMode.Password) {
-            View messageArea = mView.findViewById(R.id.keyguard_message_area);
-            top += messageArea.getTop();
-        }
-        return top;
-    }
-
-    public boolean handleBackKey() {
-        SecurityMode securityMode = mKeyguardSecurityContainerController.getCurrentSecurityMode();
-        if (securityMode != SecurityMode.None) {
-            mKeyguardSecurityContainerController.dismiss(
-                    false, KeyguardUpdateMonitor.getCurrentUser(), securityMode);
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * In general, we enable unlocking the insecure keyguard with the menu key. However, there are
-     * some cases where we wish to disable it, notably when the menu button placement or technology
-     * is prone to false positives.
-     *
-     * @return true if the menu key should be enabled
-     */
-    public boolean shouldEnableMenuKey() {
-        final Resources res = mView.getResources();
-        final boolean configDisabled = res.getBoolean(R.bool.config_disableMenuKeyInLockScreen);
-        final boolean isTestHarness = ActivityManager.isRunningInTestHarness();
-        final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists();
-        return !configDisabled || isTestHarness || fileOverride;
-    }
-
-    /**
-     * @return true if the current bouncer is password
-     */
-    public boolean dispatchBackKeyEventPreIme() {
-        if (mKeyguardSecurityContainerController.getCurrentSecurityMode()
-                == SecurityMode.Password) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * @return the {@link OnBackAnimationCallback} to animate this view during a back gesture.
-     */
-    @NonNull
-    public OnBackAnimationCallback getBackCallback() {
-        return mKeyguardSecurityContainerController.getBackCallback();
-    }
-
-    /**
-     * Allows the media keys to work when the keyguard is showing.
-     * The media keys should be of no interest to the actual keyguard view(s),
-     * so intercepting them here should not be of any harm.
-     * @param event The key event
-     * @return whether the event was consumed as a media key.
-     */
-    public boolean interceptMediaKey(KeyEvent event) {
-        int keyCode = event.getKeyCode();
-        if (event.getAction() == KeyEvent.ACTION_DOWN) {
-            switch (keyCode) {
-                case KeyEvent.KEYCODE_MEDIA_PLAY:
-                case KeyEvent.KEYCODE_MEDIA_PAUSE:
-                case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
-                    /* Suppress PLAY/PAUSE toggle when phone is ringing or
-                     * in-call to avoid music playback */
-                    if (mTelephonyManager != null &&
-                            mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) {
-                        return true;  // suppress key event
-                    }
-                case KeyEvent.KEYCODE_MUTE:
-                case KeyEvent.KEYCODE_HEADSETHOOK:
-                case KeyEvent.KEYCODE_MEDIA_STOP:
-                case KeyEvent.KEYCODE_MEDIA_NEXT:
-                case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
-                case KeyEvent.KEYCODE_MEDIA_REWIND:
-                case KeyEvent.KEYCODE_MEDIA_RECORD:
-                case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
-                case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
-                    handleMediaKeyEvent(event);
-                    return true;
-                }
-
-                case KeyEvent.KEYCODE_VOLUME_UP:
-                case KeyEvent.KEYCODE_VOLUME_DOWN:
-                case KeyEvent.KEYCODE_VOLUME_MUTE: {
-                    if (KEYGUARD_MANAGES_VOLUME) {
-                        // Volume buttons should only function for music (local or remote).
-                        // TODO: Actually handle MUTE.
-                        mAudioManager.adjustSuggestedStreamVolume(
-                                keyCode == KeyEvent.KEYCODE_VOLUME_UP
-                                        ? AudioManager.ADJUST_RAISE
-                                        : AudioManager.ADJUST_LOWER /* direction */,
-                                AudioManager.STREAM_MUSIC /* stream */, 0 /* flags */);
-                        // Don't execute default volume behavior
-                        return true;
-                    } else {
-                        return false;
-                    }
-                }
-            }
-        } else if (event.getAction() == KeyEvent.ACTION_UP) {
-            switch (keyCode) {
-                case KeyEvent.KEYCODE_MUTE:
-                case KeyEvent.KEYCODE_HEADSETHOOK:
-                case KeyEvent.KEYCODE_MEDIA_PLAY:
-                case KeyEvent.KEYCODE_MEDIA_PAUSE:
-                case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
-                case KeyEvent.KEYCODE_MEDIA_STOP:
-                case KeyEvent.KEYCODE_MEDIA_NEXT:
-                case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
-                case KeyEvent.KEYCODE_MEDIA_REWIND:
-                case KeyEvent.KEYCODE_MEDIA_RECORD:
-                case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
-                case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
-                    handleMediaKeyEvent(event);
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-
-    private void handleMediaKeyEvent(KeyEvent keyEvent) {
-        mAudioManager.dispatchMediaKeyEvent(keyEvent);
-    }
-
-    public void finish(boolean strongAuth, int currentUser) {
-        mSecurityCallback.finish(strongAuth, currentUser);
-    }
-
-    /**
-     * Apply keyguard configuration from the currently active resources. This can be called when the
-     * device configuration changes, to re-apply some resources that are qualified on the device
-     * configuration.
-     */
-    public void updateResources() {
-        int gravity;
-
-        Resources resources = mView.getResources();
-
-        if (resources.getBoolean(R.bool.can_use_one_handed_bouncer)) {
-            gravity = resources.getInteger(
-                    R.integer.keyguard_host_view_one_handed_gravity);
-        } else {
-            gravity = resources.getInteger(R.integer.keyguard_host_view_gravity);
-        }
-
-        mTranslationY = resources
-                .getDimensionPixelSize(R.dimen.keyguard_host_view_translation_y);
-        // Android SysUI uses a FrameLayout as the top-level, but Auto uses RelativeLayout.
-        // We're just changing the gravity here though (which can't be applied to RelativeLayout),
-        // so only attempt the update if mView is inside a FrameLayout.
-        if (mView.getLayoutParams() instanceof FrameLayout.LayoutParams) {
-            FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mView.getLayoutParams();
-            if (lp.gravity != gravity) {
-                lp.gravity = gravity;
-                mView.setLayoutParams(lp);
-            }
-        }
-
-        if (mKeyguardSecurityContainerController != null) {
-            mKeyguardSecurityContainerController.updateResources();
-        }
-    }
-
-    /** Update keyguard position based on a tapped X coordinate. */
-    public void updateKeyguardPosition(float x) {
-        if (mKeyguardSecurityContainerController != null) {
-            mKeyguardSecurityContainerController.updateKeyguardPosition(x);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index d1c9a30..7054393 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -51,26 +51,7 @@
     // The following is used to ignore callbacks from SecurityViews that are no longer current
     // (e.g. face unlock). This avoids unwanted asynchronous events from messing with the
     // state for the current security method.
-    private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {
-        @Override
-        public void userActivity() { }
-        @Override
-        public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { }
-        @Override
-        public boolean isVerifyUnlockOnly() {
-            return false;
-        }
-        @Override
-        public void dismiss(boolean securityVerified, int targetUserId,
-                SecurityMode expectedSecurityMode) { }
-        @Override
-        public void dismiss(boolean authenticated, int targetId,
-                boolean bypassSecondaryLockScreen, SecurityMode expectedSecurityMode) { }
-        @Override
-        public void onUserInput() { }
-        @Override
-        public void reset() {}
-    };
+    private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {};
 
     protected KeyguardInputViewController(T view, SecurityMode securityMode,
             KeyguardSecurityCallback keyguardSecurityCallback,
@@ -121,6 +102,7 @@
 
     @Override
     public void reset() {
+        mMessageAreaController.setMessage("", false);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java
index bc72f79..bf9c3bb 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java
@@ -25,7 +25,9 @@
      * @param targetUserId a user that needs to be the foreground user at the dismissal completion.
      * @param expectedSecurityMode The security mode that is invoking this dismiss.
      */
-    void dismiss(boolean securityVerified, int targetUserId, SecurityMode expectedSecurityMode);
+    default void dismiss(boolean securityVerified, int targetUserId,
+            SecurityMode expectedSecurityMode) {
+    }
 
     /**
      * Dismiss the given security screen.
@@ -35,19 +37,26 @@
      *                                  if any, during this dismissal.
      * @param expectedSecurityMode The security mode that is invoking this dismiss.
      */
-    void dismiss(boolean securityVerified, int targetUserId, boolean bypassSecondaryLockScreen,
-            SecurityMode expectedSecurityMode);
+    default boolean dismiss(boolean securityVerified, int targetUserId,
+            boolean bypassSecondaryLockScreen,
+            SecurityMode expectedSecurityMode) {
+        return false;
+    }
 
     /**
      * Manually report user activity to keep the device awake.
      */
-    void userActivity();
+    default void userActivity() {
+    }
 
     /**
      * Checks if keyguard is in "verify credentials" mode.
+     *
      * @return true if user has been asked to verify security.
      */
-    boolean isVerifyUnlockOnly();
+    default boolean isVerifyUnlockOnly() {
+        return false;
+    }
 
     /**
      * Call to report an unlock attempt.
@@ -56,12 +65,14 @@
      * @param timeoutMs timeout in milliseconds to wait before reattempting an unlock.
      *                  Only nonzero if 'success' is false
      */
-    void reportUnlockAttempt(int userId, boolean success, int timeoutMs);
+    default void reportUnlockAttempt(int userId, boolean success, int timeoutMs) {
+    }
 
     /**
      * Resets the keyguard view.
      */
-    void reset();
+    default void reset() {
+    }
 
     /**
      * Call when cancel button is pressed in bouncer.
@@ -73,5 +84,19 @@
     /**
      * Invoked whenever users are typing their password or drawing a pattern.
      */
-    void onUserInput();
+    default void onUserInput() {
+    }
+
+
+    /**
+     * Dismisses keyguard and go to unlocked state.
+     */
+    default void finish(boolean strongAuth, int targetUserId) {
+    }
+
+    /**
+     * Specifies that security mode has changed.
+     */
+    default void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput) {
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index e4f85db..eec788b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -50,6 +50,7 @@
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BlendMode;
+import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
@@ -92,6 +93,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.settingslib.Utils;
+import com.android.settingslib.drawable.CircleFramedDrawable;
 import com.android.systemui.Gefingerpoken;
 import com.android.systemui.R;
 import com.android.systemui.animation.Interpolators;
@@ -163,6 +165,8 @@
     private boolean mDisappearAnimRunning;
     private SwipeListener mSwipeListener;
     private ViewMode mViewMode = new DefaultViewMode();
+    private boolean mIsInteractable;
+    protected ViewMediatorCallback mViewMediatorCallback;
     /*
      * Using MODE_UNINITIALIZED to mean the view mode is set to DefaultViewMode, but init() has not
      * yet been called on it. This will happen when the ViewController is initialized.
@@ -235,14 +239,6 @@
                     }
                     updateChildren(0 /* translationY */, 1f /* alpha */);
                 }
-
-                private void updateChildren(int translationY, float alpha) {
-                    for (int i = 0; i < KeyguardSecurityContainer.this.getChildCount(); ++i) {
-                        View child = KeyguardSecurityContainer.this.getChildAt(i);
-                        child.setTranslationY(translationY);
-                        child.setAlpha(alpha);
-                    }
-                }
             };
 
     private final OnBackAnimationCallback mBackCallback = new OnBackAnimationCallback() {
@@ -272,31 +268,6 @@
         return mBackCallback;
     }
 
-    // Used to notify the container when something interesting happens.
-    public interface SecurityCallback {
-        /**
-         * Potentially dismiss the current security screen, after validating that all device
-         * security has been unlocked. Otherwise show the next screen.
-         */
-        boolean dismiss(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen,
-                SecurityMode expectedSecurityMode);
-
-        void userActivity();
-
-        void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput);
-
-        /**
-         * @param strongAuth   wheher the user has authenticated with strong authentication like
-         *                     pattern, password or PIN but not by trust agents or fingerprint
-         * @param targetUserId a user that needs to be the foreground user at the finish completion.
-         */
-        void finish(boolean strongAuth, int targetUserId);
-
-        void reset();
-
-        void onCancelClicked();
-    }
-
     public interface SwipeListener {
         void onSwipeUp();
     }
@@ -349,7 +320,7 @@
 
     public KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
-        mSpringAnimation = new SpringAnimation(this, DynamicAnimation.Y);
+        mSpringAnimation = new SpringAnimation(this, DynamicAnimation.TRANSLATION_Y);
         mViewConfiguration = ViewConfiguration.get(context);
         mDoubleTapDetector = new GestureDetector(context, new DoubleTapListener());
     }
@@ -452,6 +423,11 @@
         mViewMode.reset();
     }
 
+    /** Set true if the view can be interacted with */
+    public void setInteractable(boolean isInteractable) {
+        mIsInteractable = isInteractable;
+    }
+
     @Override
     public boolean shouldDelayChildPressedState() {
         return true;
@@ -459,6 +435,10 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent event) {
+        if (!mIsInteractable) {
+            return true;
+        }
+
         boolean result =  mMotionEventListeners.stream().anyMatch(
                 listener -> listener.onInterceptTouchEvent(event))
                 || super.onInterceptTouchEvent(event);
@@ -594,6 +574,7 @@
      * This will run when the bouncer shows in all cases except when the user drags the bouncer up.
      */
     public void startAppearAnimation(SecurityMode securityMode) {
+        updateChildren(0 /* translationY */, 1f /* alpha */);
         mViewMode.startAppearAnimation(securityMode);
     }
 
@@ -645,6 +626,18 @@
         return insets.inset(0, 0, 0, inset);
     }
 
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        super.dispatchDraw(canvas);
+        if (mViewMediatorCallback != null) {
+            mViewMediatorCallback.keyguardDoneDrawing();
+        }
+    }
+
+    public void setViewMediatorCallback(ViewMediatorCallback viewMediatorCallback) {
+        mViewMediatorCallback = viewMediatorCallback;
+    }
+
     private void showDialog(String title, String message) {
         if (mAlertDialog != null) {
             mAlertDialog.dismiss();
@@ -777,6 +770,14 @@
         setScaleY(scale);
     }
 
+    private void updateChildren(int translationY, float alpha) {
+        for (int i = 0; i < getChildCount(); ++i) {
+            View child = getChildAt(i);
+            child.setTranslationY(translationY);
+            child.setAlpha(alpha);
+        }
+    }
+
     /**
      * Enscapsulates the differences between bouncer modes for the container.
      */
@@ -998,8 +999,10 @@
         private Drawable findUserIcon(int userId) {
             Bitmap userIcon = UserManager.get(mView.getContext()).getUserIcon(userId);
             if (userIcon != null) {
-                return new BitmapDrawable(userIcon);
+                return CircleFramedDrawable.getInstance(mView.getContext(),
+                        userIcon);
             }
+
             return UserIcons.getDefaultUserIcon(mResources, userId, false);
         }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 57bfe54..92cbb29 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -29,17 +29,27 @@
 import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_WORK_PROFILE;
 import static com.android.systemui.DejankUtils.whitelistIpcs;
 
+import android.app.ActivityManager;
 import android.app.admin.DevicePolicyManager;
 import android.content.Intent;
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.hardware.biometrics.BiometricOverlayConstants;
 import android.hardware.biometrics.BiometricSourceType;
+import android.media.AudioManager;
 import android.metrics.LogMaker;
+import android.os.SystemClock;
 import android.os.UserHandle;
+import android.telephony.TelephonyManager;
 import android.util.Log;
+import android.util.MathUtils;
 import android.util.Slog;
+import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewTreeObserver;
+import android.widget.FrameLayout;
 import android.window.OnBackAnimationCallback;
 
 import androidx.annotation.NonNull;
@@ -53,7 +63,6 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardSecurityContainer.BouncerUiEvent;
-import com.android.keyguard.KeyguardSecurityContainer.SecurityCallback;
 import com.android.keyguard.KeyguardSecurityContainer.SwipeListener;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.keyguard.dagger.KeyguardBouncerScope;
@@ -67,6 +76,7 @@
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
 import com.android.systemui.log.SessionTracker;
+import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -75,6 +85,7 @@
 import com.android.systemui.util.ViewController;
 import com.android.systemui.util.settings.GlobalSettings;
 
+import java.io.File;
 import java.util.Optional;
 
 import javax.inject.Inject;
@@ -95,7 +106,6 @@
     private final UiEventLogger mUiEventLogger;
     private final KeyguardStateController mKeyguardStateController;
     private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController;
-    private final SecurityCallback mSecurityCallback;
     private final ConfigurationController mConfigurationController;
     private final FalsingCollector mFalsingCollector;
     private final FalsingManager mFalsingManager;
@@ -105,6 +115,20 @@
     private final SessionTracker mSessionTracker;
     private final Optional<SideFpsController> mSideFpsController;
     private final FalsingA11yDelegate mFalsingA11yDelegate;
+    private int mTranslationY;
+    // Whether the volume keys should be handled by keyguard. If true, then
+    // they will be handled here for specific media types such as music, otherwise
+    // the audio service will bring up the volume dialog.
+    private static final boolean KEYGUARD_MANAGES_VOLUME = false;
+
+    private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key";
+
+    private final TelephonyManager mTelephonyManager;
+    private final ViewMediatorCallback mViewMediatorCallback;
+    private final AudioManager mAudioManager;
+    private View.OnKeyListener mOnKeyListener = (v, keyCode, event) -> interceptMediaKey(event);
+    private ActivityStarter.OnDismissAction mDismissAction;
+    private Runnable mCancelAction;
 
     private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED;
 
@@ -149,11 +173,6 @@
     };
 
     private KeyguardSecurityCallback mKeyguardSecurityCallback = new KeyguardSecurityCallback() {
-        public void userActivity() {
-            if (mSecurityCallback != null) {
-                mSecurityCallback.userActivity();
-            }
-        }
 
         @Override
         public void onUserInput() {
@@ -168,16 +187,23 @@
         }
 
         @Override
-        public void dismiss(boolean authenticated, int targetId,
+        public boolean dismiss(boolean authenticated, int targetId,
                 boolean bypassSecondaryLockScreen, SecurityMode expectedSecurityMode) {
-            mSecurityCallback.dismiss(authenticated, targetId, bypassSecondaryLockScreen,
-                    expectedSecurityMode);
+            return showNextSecurityScreenOrFinish(
+                    authenticated, targetId, bypassSecondaryLockScreen, expectedSecurityMode);
         }
 
+        @Override
+        public void userActivity() {
+            mViewMediatorCallback.userActivity();
+        }
+
+        @Override
         public boolean isVerifyUnlockOnly() {
             return false;
         }
 
+        @Override
         public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) {
             int bouncerSide = SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__SIDE__DEFAULT;
             if (mView.isSidedSecurityMode()) {
@@ -214,12 +240,47 @@
                             : BouncerUiEvent.BOUNCER_PASSWORD_FAILURE, getSessionId());
         }
 
+        @Override
         public void reset() {
-            mSecurityCallback.reset();
+            mViewMediatorCallback.resetKeyguard();
         }
 
+        @Override
         public void onCancelClicked() {
-            mSecurityCallback.onCancelClicked();
+            mViewMediatorCallback.onCancelClicked();
+        }
+
+        /**
+         * Authentication has happened and it's time to dismiss keyguard. This function
+         * should clean up and inform KeyguardViewMediator.
+         *
+         * @param strongAuth whether the user has authenticated with strong authentication like
+         *                   pattern, password or PIN but not by trust agents or fingerprint
+         * @param targetUserId a user that needs to be the foreground user at the dismissal
+         *                    completion.
+         */
+        @Override
+        public void finish(boolean strongAuth, int targetUserId) {
+            // If there's a pending runnable because the user interacted with a widget
+            // and we're leaving keyguard, then run it.
+            boolean deferKeyguardDone = false;
+            if (mDismissAction != null) {
+                deferKeyguardDone = mDismissAction.onDismiss();
+                mDismissAction = null;
+                mCancelAction = null;
+            }
+            if (mViewMediatorCallback != null) {
+                if (deferKeyguardDone) {
+                    mViewMediatorCallback.keyguardDonePending(strongAuth, targetUserId);
+                } else {
+                    mViewMediatorCallback.keyguardDone(strongAuth, targetUserId);
+                }
+            }
+        }
+
+        @Override
+        public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput) {
+            mViewMediatorCallback.setNeedsInput(needsInput);
         }
     };
 
@@ -237,7 +298,7 @@
             }
             if (mUpdateMonitor.isFaceEnrolled()) {
                 mUpdateMonitor.requestActiveUnlock(
-                        ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT,
+                        ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT,
                         "swipeUpOnBouncer");
             }
         }
@@ -263,6 +324,34 @@
     private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
             new KeyguardUpdateMonitorCallback() {
                 @Override
+                public void onTrustGrantedForCurrentUser(
+                        boolean dismissKeyguard,
+                        boolean newlyUnlocked,
+                        TrustGrantFlags flags,
+                        String message
+                ) {
+                    if (dismissKeyguard) {
+                        if (!mView.isVisibleToUser()) {
+                            // The trust agent dismissed the keyguard without the user proving
+                            // that they are present (by swiping up to show the bouncer). That's
+                            // fine if the user proved presence via some other way to the trust
+                            // agent.
+                            Log.i(TAG, "TrustAgent dismissed Keyguard.");
+                        }
+                        mKeyguardSecurityCallback.dismiss(
+                                false /* authenticated */,
+                                KeyguardUpdateMonitor.getCurrentUser(),
+                                /* bypassSecondaryLockScreen */ false,
+                                SecurityMode.Invalid
+                        );
+                    } else {
+                        if (flags.isInitiatedByUser() || flags.dismissKeyguardRequested()) {
+                            mViewMediatorCallback.playTrustedSound();
+                        }
+                    }
+                }
+
+                @Override
                 public void onDevicePolicyManagerStateChanged() {
                     showPrimarySecurityScreen(false);
                 }
@@ -281,7 +370,8 @@
                 }
             };
 
-    private KeyguardSecurityContainerController(KeyguardSecurityContainer view,
+    @Inject
+    public KeyguardSecurityContainerController(KeyguardSecurityContainer view,
             AdminSecondaryLockScreenController.Factory adminSecondaryLockScreenControllerFactory,
             LockPatternUtils lockPatternUtils,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -289,7 +379,6 @@
             MetricsLogger metricsLogger,
             UiEventLogger uiEventLogger,
             KeyguardStateController keyguardStateController,
-            SecurityCallback securityCallback,
             KeyguardSecurityViewFlipperController securityViewFlipperController,
             ConfigurationController configurationController,
             FalsingCollector falsingCollector,
@@ -299,7 +388,11 @@
             GlobalSettings globalSettings,
             SessionTracker sessionTracker,
             Optional<SideFpsController> sideFpsController,
-            FalsingA11yDelegate falsingA11yDelegate) {
+            FalsingA11yDelegate falsingA11yDelegate,
+            TelephonyManager telephonyManager,
+            ViewMediatorCallback viewMediatorCallback,
+            AudioManager audioManager
+    ) {
         super(view);
         mLockPatternUtils = lockPatternUtils;
         mUpdateMonitor = keyguardUpdateMonitor;
@@ -307,7 +400,6 @@
         mMetricsLogger = metricsLogger;
         mUiEventLogger = uiEventLogger;
         mKeyguardStateController = keyguardStateController;
-        mSecurityCallback = securityCallback;
         mSecurityViewFlipperController = securityViewFlipperController;
         mAdminSecondaryLockScreenController = adminSecondaryLockScreenControllerFactory.create(
                 mKeyguardSecurityCallback);
@@ -321,11 +413,15 @@
         mSessionTracker = sessionTracker;
         mSideFpsController = sideFpsController;
         mFalsingA11yDelegate = falsingA11yDelegate;
+        mTelephonyManager = telephonyManager;
+        mViewMediatorCallback = viewMediatorCallback;
+        mAudioManager = audioManager;
     }
 
     @Override
     public void onInit() {
         mSecurityViewFlipperController.init();
+        updateResources();
         configureMode();
     }
 
@@ -336,6 +432,11 @@
         mView.addMotionEventListener(mGlobalTouchListener);
         mConfigurationController.addCallback(mConfigurationListener);
         mUserSwitcherController.addUserSwitchCallback(mUserSwitchCallback);
+        mView.setViewMediatorCallback(mViewMediatorCallback);
+        // Update ViewMediator with the current input method requirements
+        mViewMediatorCallback.setNeedsInput(needsInput());
+        mView.setOnKeyListener(mOnKeyListener);
+        showPrimarySecurityScreen(false);
     }
 
     @Override
@@ -348,6 +449,11 @@
 
     /** */
     public void onPause() {
+        if (DEBUG) {
+            Log.d(TAG, String.format("screen off, instance %s at %s",
+                    Integer.toHexString(hashCode()), SystemClock.uptimeMillis()));
+        }
+        showPrimarySecurityScreen(true);
         mAdminSecondaryLockScreenController.hide();
         if (mCurrentSecurityMode != SecurityMode.None) {
             getCurrentSecurityController().onPause();
@@ -356,6 +462,7 @@
         // It might happen that onStartingToHide is not called when the device is locked while on
         // bouncer.
         setBouncerVisible(false);
+        mView.clearFocus();
     }
 
     private void updateSideFpsVisibility() {
@@ -379,7 +486,8 @@
                     + "isUnlockingWithFpAllowed=" + isUnlockingWithFpAllowed);
         }
         if (toShow) {
-            mSideFpsController.get().show(SideFpsUiRequestSource.PRIMARY_BOUNCER);
+            mSideFpsController.get().show(SideFpsUiRequestSource.PRIMARY_BOUNCER,
+                    BiometricOverlayConstants.REASON_AUTH_KEYGUARD);
         } else {
             mSideFpsController.get().hide(SideFpsUiRequestSource.PRIMARY_BOUNCER);
         }
@@ -391,12 +499,22 @@
      * @param turningOff true if the device is being turned off
      */
     public void showPrimarySecurityScreen(boolean turningOff) {
+        if (DEBUG) Log.d(TAG, "show()");
         SecurityMode securityMode = whitelistIpcs(() -> mSecurityModel.getSecurityMode(
                 KeyguardUpdateMonitor.getCurrentUser()));
         if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
         showSecurityScreen(securityMode);
     }
 
+    /**
+     * Show a string explaining why the security view needs to be solved.
+     *
+     * @param reason a flag indicating which string should be shown, see
+     *               {@link KeyguardSecurityView#PROMPT_REASON_NONE},
+     *               {@link KeyguardSecurityView#PROMPT_REASON_RESTART},
+     *               {@link KeyguardSecurityView#PROMPT_REASON_TIMEOUT}, and
+     *               {@link KeyguardSecurityView#PROMPT_REASON_PREPARE_FOR_UPDATE}.
+     */
     @Override
     public void showPromptReason(int reason) {
         if (mCurrentSecurityMode != SecurityMode.None) {
@@ -413,8 +531,32 @@
         }
     }
 
-    public SecurityMode getCurrentSecurityMode() {
-        return mCurrentSecurityMode;
+    /**
+     * Sets an action to run when keyguard finishes.
+     *
+     * @param action callback to be invoked when keyguard disappear animation completes.
+     */
+    public void setOnDismissAction(ActivityStarter.OnDismissAction action, Runnable cancelAction) {
+        if (mCancelAction != null) {
+            mCancelAction.run();
+            mCancelAction = null;
+        }
+        mDismissAction = action;
+        mCancelAction = cancelAction;
+    }
+
+    /**
+     * @return whether dismiss action or cancel action has been set.
+     */
+    public boolean hasDismissActions() {
+        return mDismissAction != null || mCancelAction != null;
+    }
+
+    /**
+     * Remove any dismiss action or cancel action that was set.
+     */
+    public void cancelDismissAction() {
+        setOnDismissAction(null, null);
     }
 
     /**
@@ -426,17 +568,64 @@
         mKeyguardSecurityCallback.dismiss(authenticated, targetUserId, expectedSecurityMode);
     }
 
+    /**
+     * Dismisses the keyguard by going to the next screen or making it gone.
+     * @param targetUserId a user that needs to be the foreground user at the dismissal completion.
+     * @return True if the keyguard is done.
+     */
+    public boolean dismiss(int targetUserId) {
+        return mKeyguardSecurityCallback.dismiss(false, targetUserId, false,
+                getCurrentSecurityMode());
+    }
+
+    public SecurityMode getCurrentSecurityMode() {
+        return mCurrentSecurityMode;
+    }
+
+    /**
+     * @return the top of the corresponding view.
+     */
+    public int getTop() {
+        int top = mView.getTop();
+        // The password view has an extra top padding that should be ignored.
+        if (getCurrentSecurityMode() == SecurityMode.Password) {
+            View messageArea = mView.findViewById(R.id.keyguard_message_area);
+            top += messageArea.getTop();
+        }
+        return top;
+    }
+
+    /** Set true if the view can be interacted with */
+    public void setInteractable(boolean isInteractable) {
+        mView.setInteractable(isInteractable);
+    }
+
+    /**
+     * Dismiss keyguard due to a user unlock event.
+     */
+    public void finish(boolean strongAuth, int currentUser) {
+        mKeyguardSecurityCallback.finish(strongAuth, currentUser);
+    }
+
+    /**
+     * @return the text of the KeyguardMessageArea.
+     */
+    public CharSequence getTitle() {
+        return mView.getTitle();
+    }
+
+    /**
+     *  Resets the state of the views.
+     */
     public void reset() {
         mView.reset();
         mSecurityViewFlipperController.reset();
     }
 
-    public CharSequence getTitle() {
-        return mView.getTitle();
-    }
-
     @Override
     public void onResume(int reason) {
+        if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode()));
+        mView.requestFocus();
         if (mCurrentSecurityMode != SecurityMode.None) {
             int state = SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SHOWN;
             if (mView.isSidedSecurityMode()) {
@@ -454,6 +643,25 @@
                 mKeyguardStateController.isFaceAuthEnabled());
     }
 
+    /**
+     * Show the bouncer and start appear animations.
+     *
+     */
+    public void appear() {
+        // We might still be collapsed and the view didn't have time to layout yet or still
+        // be small, let's wait on the predraw to do the animation in that case.
+        mView.getViewTreeObserver().addOnPreDrawListener(
+                new ViewTreeObserver.OnPreDrawListener() {
+                    @Override
+                    public boolean onPreDraw() {
+                        mView.getViewTreeObserver().removeOnPreDrawListener(this);
+                        startAppearAnimation();
+                        return true;
+                    }
+                });
+        mView.requestLayout();
+    }
+
     public void startAppearAnimation() {
         if (mCurrentSecurityMode != SecurityMode.None) {
             mView.setAlpha(1f);
@@ -463,12 +671,19 @@
     }
 
     public boolean startDisappearAnimation(Runnable onFinishRunnable) {
+        boolean didRunAnimation = false;
+
         if (mCurrentSecurityMode != SecurityMode.None) {
             mView.startDisappearAnimation(mCurrentSecurityMode);
-            return getCurrentSecurityController().startDisappearAnimation(onFinishRunnable);
+            didRunAnimation = getCurrentSecurityController().startDisappearAnimation(
+                    onFinishRunnable);
         }
 
-        return false;
+        if (!didRunAnimation && onFinishRunnable != null) {
+            onFinishRunnable.run();
+        }
+
+        return didRunAnimation;
     }
 
     public void onStartingToHide() {
@@ -583,7 +798,7 @@
             mUiEventLogger.log(uiEvent, getSessionId());
         }
         if (finish) {
-            mSecurityCallback.finish(strongAuth, targetUserId);
+            mKeyguardSecurityCallback.finish(strongAuth, targetUserId);
         }
         return finish;
     }
@@ -596,11 +811,114 @@
      * @return the {@link OnBackAnimationCallback} to animate this view during a back gesture.
      */
     @NonNull
-    OnBackAnimationCallback getBackCallback() {
+    public OnBackAnimationCallback getBackCallback() {
         return mView.getBackCallback();
     }
 
     /**
+     * @return whether we should dispatch the back key event before Ime.
+     */
+    public boolean dispatchBackKeyEventPreIme() {
+        return getCurrentSecurityMode() == SecurityMode.Password;
+    }
+
+    /**
+     * Allows the media keys to work when the keyguard is showing.
+     * The media keys should be of no interest to the actual keyguard view(s),
+     * so intercepting them here should not be of any harm.
+     * @param event The key event
+     * @return whether the event was consumed as a media key.
+     */
+    public boolean interceptMediaKey(KeyEvent event) {
+        int keyCode = event.getKeyCode();
+        if (event.getAction() == KeyEvent.ACTION_DOWN) {
+            switch (keyCode) {
+                case KeyEvent.KEYCODE_MEDIA_PLAY:
+                case KeyEvent.KEYCODE_MEDIA_PAUSE:
+                case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+                    /* Suppress PLAY/PAUSE toggle when phone is ringing or
+                     * in-call to avoid music playback */
+                    if (mTelephonyManager != null
+                            && mTelephonyManager.getCallState()
+                            != TelephonyManager.CALL_STATE_IDLE) {
+                        return true;  // suppress key event
+                    }
+                    return false;
+                case KeyEvent.KEYCODE_MUTE:
+                case KeyEvent.KEYCODE_HEADSETHOOK:
+                case KeyEvent.KEYCODE_MEDIA_STOP:
+                case KeyEvent.KEYCODE_MEDIA_NEXT:
+                case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+                case KeyEvent.KEYCODE_MEDIA_REWIND:
+                case KeyEvent.KEYCODE_MEDIA_RECORD:
+                case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+                case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
+                    handleMediaKeyEvent(event);
+                    return true;
+                }
+
+                case KeyEvent.KEYCODE_VOLUME_UP:
+                case KeyEvent.KEYCODE_VOLUME_DOWN:
+                case KeyEvent.KEYCODE_VOLUME_MUTE: {
+                    if (KEYGUARD_MANAGES_VOLUME) {
+                        // Volume buttons should only function for music (local or remote).
+                        // TODO: Actually handle MUTE.
+                        mAudioManager.adjustSuggestedStreamVolume(
+                                keyCode == KeyEvent.KEYCODE_VOLUME_UP
+                                        ? AudioManager.ADJUST_RAISE
+                                        : AudioManager.ADJUST_LOWER /* direction */,
+                                AudioManager.STREAM_MUSIC /* stream */, 0 /* flags */);
+                        // Don't execute default volume behavior
+                        return true;
+                    } else {
+                        return false;
+                    }
+                }
+            }
+        } else if (event.getAction() == KeyEvent.ACTION_UP) {
+            switch (keyCode) {
+                case KeyEvent.KEYCODE_MUTE:
+                case KeyEvent.KEYCODE_HEADSETHOOK:
+                case KeyEvent.KEYCODE_MEDIA_PLAY:
+                case KeyEvent.KEYCODE_MEDIA_PAUSE:
+                case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+                case KeyEvent.KEYCODE_MEDIA_STOP:
+                case KeyEvent.KEYCODE_MEDIA_NEXT:
+                case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+                case KeyEvent.KEYCODE_MEDIA_REWIND:
+                case KeyEvent.KEYCODE_MEDIA_RECORD:
+                case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+                case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
+                    handleMediaKeyEvent(event);
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+
+    private void handleMediaKeyEvent(KeyEvent keyEvent) {
+        mAudioManager.dispatchMediaKeyEvent(keyEvent);
+    }
+
+    /**
+     * In general, we enable unlocking the insecure keyguard with the menu key. However, there are
+     * some cases where we wish to disable it, notably when the menu button placement or technology
+     * is prone to false positives.
+     *
+     * @return true if the menu key should be enabled
+     */
+    public boolean shouldEnableMenuKey() {
+        final Resources res = mView.getResources();
+        final boolean configDisabled = res.getBoolean(R.bool.config_disableMenuKeyInLockScreen);
+        final boolean isTestHarness = ActivityManager.isRunningInTestHarness();
+        final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists();
+        return !configDisabled || isTestHarness || fileOverride;
+    }
+
+
+    /**
      * Switches to the given security view unless it's already being shown, in which case
      * this is a no-op.
      *
@@ -628,7 +946,7 @@
             configureMode();
         }
 
-        mSecurityCallback.onSecurityModeChanged(
+        mKeyguardSecurityCallback.onSecurityModeChanged(
                 securityMode, newView != null && newView.needsInput());
     }
 
@@ -726,6 +1044,30 @@
      * configuration.
      */
     public void updateResources() {
+        int gravity;
+
+        Resources resources = mView.getResources();
+
+        if (resources.getBoolean(R.bool.can_use_one_handed_bouncer)) {
+            gravity = resources.getInteger(
+                    R.integer.keyguard_host_view_one_handed_gravity);
+        } else {
+            gravity = resources.getInteger(R.integer.keyguard_host_view_gravity);
+        }
+
+        mTranslationY = resources
+                .getDimensionPixelSize(R.dimen.keyguard_host_view_translation_y);
+        // Android SysUI uses a FrameLayout as the top-level, but Auto uses RelativeLayout.
+        // We're just changing the gravity here though (which can't be applied to RelativeLayout),
+        // so only attempt the update if mView is inside a FrameLayout.
+        if (mView.getLayoutParams() instanceof FrameLayout.LayoutParams) {
+            FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mView.getLayoutParams();
+            if (lp.gravity != gravity) {
+                lp.gravity = gravity;
+                mView.setLayoutParams(lp);
+            }
+        }
+
         int newOrientation = getResources().getConfiguration().orientation;
         if (newOrientation != mLastOrientation) {
             mLastOrientation = newOrientation;
@@ -743,93 +1085,34 @@
     }
 
     private void reloadColors() {
-        resetViewFlipper();
+        reinflateViewFlipper();
         mView.reloadColors();
     }
 
     /** Handles density or font scale changes. */
     private void onDensityOrFontScaleChanged() {
-        resetViewFlipper();
+        reinflateViewFlipper();
         mView.onDensityOrFontScaleChanged();
     }
 
-    private void resetViewFlipper() {
+    /**
+     * Reinflate the view flipper child view.
+     */
+    public void reinflateViewFlipper() {
         mSecurityViewFlipperController.clearViews();
         mSecurityViewFlipperController.getSecurityView(mCurrentSecurityMode,
                 mKeyguardSecurityCallback);
     }
 
-    static class Factory {
-
-        private final KeyguardSecurityContainer mView;
-        private final AdminSecondaryLockScreenController.Factory
-                mAdminSecondaryLockScreenControllerFactory;
-        private final LockPatternUtils mLockPatternUtils;
-        private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-        private final KeyguardSecurityModel mKeyguardSecurityModel;
-        private final MetricsLogger mMetricsLogger;
-        private final UiEventLogger mUiEventLogger;
-        private final KeyguardStateController mKeyguardStateController;
-        private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController;
-        private final ConfigurationController mConfigurationController;
-        private final FalsingCollector mFalsingCollector;
-        private final FalsingManager mFalsingManager;
-        private final GlobalSettings mGlobalSettings;
-        private final FeatureFlags mFeatureFlags;
-        private final UserSwitcherController mUserSwitcherController;
-        private final SessionTracker mSessionTracker;
-        private final Optional<SideFpsController> mSidefpsController;
-        private final FalsingA11yDelegate mFalsingA11yDelegate;
-
-        @Inject
-        Factory(KeyguardSecurityContainer view,
-                AdminSecondaryLockScreenController.Factory
-                        adminSecondaryLockScreenControllerFactory,
-                LockPatternUtils lockPatternUtils,
-                KeyguardUpdateMonitor keyguardUpdateMonitor,
-                KeyguardSecurityModel keyguardSecurityModel,
-                MetricsLogger metricsLogger,
-                UiEventLogger uiEventLogger,
-                KeyguardStateController keyguardStateController,
-                KeyguardSecurityViewFlipperController securityViewFlipperController,
-                ConfigurationController configurationController,
-                FalsingCollector falsingCollector,
-                FalsingManager falsingManager,
-                UserSwitcherController userSwitcherController,
-                FeatureFlags featureFlags,
-                GlobalSettings globalSettings,
-                SessionTracker sessionTracker,
-                Optional<SideFpsController> sidefpsController,
-                FalsingA11yDelegate falsingA11yDelegate) {
-            mView = view;
-            mAdminSecondaryLockScreenControllerFactory = adminSecondaryLockScreenControllerFactory;
-            mLockPatternUtils = lockPatternUtils;
-            mKeyguardUpdateMonitor = keyguardUpdateMonitor;
-            mKeyguardSecurityModel = keyguardSecurityModel;
-            mMetricsLogger = metricsLogger;
-            mUiEventLogger = uiEventLogger;
-            mKeyguardStateController = keyguardStateController;
-            mSecurityViewFlipperController = securityViewFlipperController;
-            mConfigurationController = configurationController;
-            mFalsingCollector = falsingCollector;
-            mFalsingManager = falsingManager;
-            mFeatureFlags = featureFlags;
-            mGlobalSettings = globalSettings;
-            mUserSwitcherController = userSwitcherController;
-            mSessionTracker = sessionTracker;
-            mSidefpsController = sidefpsController;
-            mFalsingA11yDelegate = falsingA11yDelegate;
-        }
-
-        public KeyguardSecurityContainerController create(
-                SecurityCallback securityCallback) {
-            return new KeyguardSecurityContainerController(mView,
-                    mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
-                    mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
-                    mKeyguardStateController, securityCallback, mSecurityViewFlipperController,
-                    mConfigurationController, mFalsingCollector, mFalsingManager,
-                    mUserSwitcherController, mFeatureFlags, mGlobalSettings, mSessionTracker,
-                    mSidefpsController, mFalsingA11yDelegate);
-        }
+    /**
+     * Fades and translates in/out the security screen.
+     * Fades in as expansion approaches 0.
+     * Animation duration is between 0.33f and 0.67f of panel expansion fraction.
+     * @param fraction amount of the screen that should show.
+     */
+    public void setExpansion(float fraction) {
+        float scaledFraction = BouncerPanelExpansionCalculator.showBouncerProgress(fraction);
+        mView.setAlpha(MathUtils.constrain(1 - scaledFraction, 0f, 1f));
+        mView.setTranslationY(scaledFraction * mTranslationY);
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
index 0b2b121..e3de8c7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
@@ -17,7 +17,6 @@
 package com.android.keyguard;
 
 import static android.app.slice.Slice.HINT_LIST_ITEM;
-import static android.view.Display.DEFAULT_DISPLAY;
 
 import android.app.PendingIntent;
 import android.net.Uri;
@@ -43,6 +42,7 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.KeyguardSliceProvider;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.util.ViewController;
@@ -64,6 +64,7 @@
     private final ConfigurationController mConfigurationController;
     private final TunerService mTunerService;
     private final DumpManager mDumpManager;
+    private final DisplayTracker mDisplayTracker;
     private int mDisplayId;
     private LiveData<Slice> mLiveData;
     private Uri mKeyguardSliceUri;
@@ -108,12 +109,14 @@
             ActivityStarter activityStarter,
             ConfigurationController configurationController,
             TunerService tunerService,
-            DumpManager dumpManager) {
+            DumpManager dumpManager,
+            DisplayTracker displayTracker) {
         super(keyguardSliceView);
         mActivityStarter = activityStarter;
         mConfigurationController = configurationController;
         mTunerService = tunerService;
         mDumpManager = dumpManager;
+        mDisplayTracker = displayTracker;
     }
 
     @Override
@@ -124,7 +127,7 @@
         }
         mTunerService.addTunable(mTunable, Settings.Secure.KEYGUARD_SLICE_URI);
         // Make sure we always have the most current slice
-        if (mDisplayId == DEFAULT_DISPLAY && mLiveData != null) {
+        if (mDisplayId == mDisplayTracker.getDefaultDisplayId() && mLiveData != null) {
             mLiveData.observeForever(mObserver);
         }
         mConfigurationController.addCallback(mConfigurationListener);
@@ -137,7 +140,7 @@
     @Override
     protected void onViewDetached() {
         // TODO(b/117344873) Remove below work around after this issue be fixed.
-        if (mDisplayId == DEFAULT_DISPLAY) {
+        if (mDisplayId == mDisplayTracker.getDefaultDisplayId()) {
             mLiveData.removeObserver(mObserver);
         }
         mTunerService.removeTunable(mTunable);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index a9695dd..8e11a99 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -26,7 +26,6 @@
 import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_PERMANENT;
 import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_TIMED;
 import static android.hardware.biometrics.BiometricConstants.LockoutMode;
-import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START;
 import static android.hardware.biometrics.BiometricSourceType.FACE;
 import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
 import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
@@ -95,6 +94,7 @@
 import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.BiometricSourceType;
 import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
+import android.hardware.biometrics.SensorProperties;
 import android.hardware.face.FaceManager;
 import android.hardware.face.FaceSensorPropertiesInternal;
 import android.hardware.fingerprint.FingerprintManager;
@@ -109,7 +109,6 @@
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.RemoteException;
-import android.os.SystemClock;
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -149,6 +148,7 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.dump.DumpsysTableLogger;
 import com.android.systemui.log.SessionTracker;
+import com.android.systemui.plugins.Weather;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shared.system.TaskStackChangeListener;
@@ -261,6 +261,7 @@
     @VisibleForTesting
     public static final int BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED = -1;
     public static final int BIOMETRIC_HELP_FACE_NOT_RECOGNIZED = -2;
+    public static final int BIOMETRIC_HELP_FACE_NOT_AVAILABLE = -3;
 
     /**
      * If no cancel signal has been received after this amount of time, set the biometric running
@@ -311,7 +312,7 @@
     private boolean mGoingToSleep;
     private boolean mPrimaryBouncerFullyShown;
     private boolean mPrimaryBouncerIsOrWillBeShowing;
-    private boolean mUdfpsBouncerShowing;
+    private boolean mAlternateBouncerShowing;
     private boolean mAuthInterruptActive;
     private boolean mNeedsSlowUnlockTransition;
     private boolean mAssistantVisible;
@@ -352,7 +353,6 @@
     private final Executor mBackgroundExecutor;
     private final SensorPrivacyManager mSensorPrivacyManager;
     private final ActiveUnlockConfig mActiveUnlockConfig;
-    private final PowerManager mPowerManager;
     private final IDreamManager mDreamManager;
     private final TelephonyManager mTelephonyManager;
     @Nullable
@@ -360,7 +360,6 @@
     @Nullable
     private final FaceManager mFaceManager;
     private final LockPatternUtils mLockPatternUtils;
-    private final boolean mWakeOnFingerprintAcquiredStart;
     @VisibleForTesting
     @DevicePostureController.DevicePostureInt
     protected int mConfigFaceAuthSupportedPosture;
@@ -538,7 +537,8 @@
      * It's assumed that the trust was granted for the current user.
      */
     private boolean shouldDismissKeyguardOnTrustGrantedWithCurrentUser(TrustGrantFlags flags) {
-        final boolean isBouncerShowing = mPrimaryBouncerIsOrWillBeShowing || mUdfpsBouncerShowing;
+        final boolean isBouncerShowing =
+                mPrimaryBouncerIsOrWillBeShowing || mAlternateBouncerShowing;
         return (flags.isInitiatedByUser() || flags.dismissKeyguardRequested())
                 && (mDeviceInteractive || flags.temporaryAndRenewable())
                 && (isBouncerShowing || flags.dismissKeyguardRequested());
@@ -884,11 +884,6 @@
     private void handleFingerprintAcquired(
             @BiometricFingerprintConstants.FingerprintAcquired int acquireInfo) {
         Assert.isMainThread();
-        if (mWakeOnFingerprintAcquiredStart && acquireInfo == FINGERPRINT_ACQUIRED_START) {
-            mPowerManager.wakeUp(
-                    SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_BIOMETRIC,
-                    "com.android.systemui.keyguard:FINGERPRINT_ACQUIRED_START");
-        }
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
@@ -1319,7 +1314,8 @@
     }
 
     public boolean getUserHasTrust(int userId) {
-        return !isTrustDisabled() && mUserHasTrust.get(userId);
+        return !isTrustDisabled() && mUserHasTrust.get(userId)
+                && isUnlockingWithTrustAgentAllowed();
     }
 
     /**
@@ -1327,12 +1323,19 @@
      */
     public boolean getUserUnlockedWithBiometric(int userId) {
         BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(userId);
-        BiometricAuthenticated face = mUserFaceAuthenticated.get(userId);
         boolean fingerprintAllowed = fingerprint != null && fingerprint.mAuthenticated
                 && isUnlockingWithBiometricAllowed(fingerprint.mIsStrongBiometric);
-        boolean faceAllowed = face != null && face.mAuthenticated
+        return fingerprintAllowed || getUserUnlockedWithFace(userId);
+    }
+
+
+    /**
+     * Returns whether the user is unlocked with face.
+     */
+    public boolean getUserUnlockedWithFace(int userId) {
+        BiometricAuthenticated face = mUserFaceAuthenticated.get(userId);
+        return face != null && face.mAuthenticated
                 && isUnlockingWithBiometricAllowed(face.mIsStrongBiometric);
-        return fingerprintAllowed || faceAllowed;
     }
 
     /**
@@ -1407,6 +1410,10 @@
         return mUserTrustIsUsuallyManaged.get(userId);
     }
 
+    private boolean isUnlockingWithTrustAgentAllowed() {
+        return isUnlockingWithBiometricAllowed(true);
+    }
+
     public boolean isUnlockingWithBiometricAllowed(boolean isStrongBiometric) {
         // StrongAuthTracker#isUnlockingWithBiometricAllowed includes
         // STRONG_AUTH_REQUIRED_AFTER_LOCKOUT which is the same as mFingerprintLockedOutPermanent;
@@ -1537,11 +1544,12 @@
     @VisibleForTesting
     void setAssistantVisible(boolean assistantVisible) {
         mAssistantVisible = assistantVisible;
+        mLogger.logAssistantVisible(mAssistantVisible);
         updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
                 FACE_AUTH_UPDATED_ASSISTANT_VISIBILITY_CHANGED);
         if (mAssistantVisible) {
             requestActiveUnlock(
-                    ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.ASSISTANT,
+                    ActiveUnlockConfig.ActiveUnlockRequestOrigin.ASSISTANT,
                     "assistant",
                     false);
         }
@@ -1671,7 +1679,7 @@
                 @Override
                 public void onAuthenticationFailed() {
                     requestActiveUnlockDismissKeyguard(
-                            ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL,
+                            ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL,
                             "fingerprintFailure");
                     handleFingerprintAuthFailed();
                 }
@@ -1736,11 +1744,11 @@
                 public void onAuthenticationFailed() {
                         String reason =
                                 mKeyguardBypassController.canBypass() ? "bypass"
-                                        : mUdfpsBouncerShowing ? "udfpsBouncer"
+                                        : mAlternateBouncerShowing ? "alternateBouncer"
                                                 : mPrimaryBouncerFullyShown ? "bouncer"
                                                         : "udfpsFpDown";
                         requestActiveUnlock(
-                                ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL,
+                                ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL,
                                 "faceFailure-" + reason);
 
                     handleFaceAuthFailed();
@@ -1767,7 +1775,7 @@
 
                     if (mActiveUnlockConfig.shouldRequestActiveUnlockOnFaceError(errMsgId)) {
                         requestActiveUnlock(
-                                ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL,
+                                ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL,
                                 "faceError-" + errMsgId);
                     }
                 }
@@ -1779,7 +1787,7 @@
                     if (mActiveUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
                             acquireInfo)) {
                         requestActiveUnlock(
-                                ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL,
+                                ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL,
                                 "faceAcquireInfo-" + acquireInfo);
                     }
                 }
@@ -1919,8 +1927,23 @@
             FACE_AUTH_UPDATED_STARTED_WAKING_UP.setExtraInfo(pmWakeReason);
             updateFaceListeningState(BIOMETRIC_ACTION_UPDATE,
                     FACE_AUTH_UPDATED_STARTED_WAKING_UP);
-            requestActiveUnlock(ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.WAKE, "wakingUp - "
-                    + PowerManager.wakeReasonToString(pmWakeReason));
+
+            final ActiveUnlockConfig.ActiveUnlockRequestOrigin requestOrigin =
+                    mActiveUnlockConfig.isWakeupConsideredUnlockIntent(pmWakeReason)
+                            ? ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
+                            : ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE;
+            final String reason = "wakingUp - " + PowerManager.wakeReasonToString(pmWakeReason);
+            if (mActiveUnlockConfig.shouldWakeupForceDismissKeyguard(pmWakeReason)) {
+                requestActiveUnlockDismissKeyguard(
+                        requestOrigin,
+                        reason
+                );
+            } else {
+                requestActiveUnlock(
+                        requestOrigin,
+                        reason
+                );
+            }
         } else {
             mLogger.logSkipUpdateFaceListeningOnWakeup(pmWakeReason);
         }
@@ -1944,6 +1967,11 @@
             }
         }
         mGoingToSleep = true;
+        // Resetting assistant visibility state as the device is going to sleep now.
+        // TaskStackChangeListener gets triggered a little late when we transition to AoD,
+        // which results in face auth running once on AoD.
+        mAssistantVisible = false;
+        mLogger.d("Started going to sleep, mGoingToSleep=true, mAssistantVisible=false");
         updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, FACE_AUTH_UPDATED_GOING_TO_SLEEP);
     }
 
@@ -2049,7 +2077,6 @@
             UiEventLogger uiEventLogger,
             // This has to be a provider because SessionTracker depends on KeyguardUpdateMonitor :(
             Provider<SessionTracker> sessionTrackerProvider,
-            PowerManager powerManager,
             TrustManager trustManager,
             SubscriptionManager subscriptionManager,
             UserManager userManager,
@@ -2086,7 +2113,6 @@
         mLogger = logger;
         mUiEventLogger = uiEventLogger;
         mSessionTrackerProvider = sessionTrackerProvider;
-        mPowerManager = powerManager;
         mTrustManager = trustManager;
         mUserManager = userManager;
         mDreamManager = dreamManager;
@@ -2097,8 +2123,6 @@
         mFpm = fingerprintManager;
         mFaceManager = faceManager;
         mActiveUnlockConfig.setKeyguardUpdateMonitor(this);
-        mWakeOnFingerprintAcquiredStart = context.getResources()
-                        .getBoolean(com.android.internal.R.bool.kg_wake_on_acquire_start);
         mFaceAcquiredInfoIgnoreList = Arrays.stream(
                 mContext.getResources().getIntArray(
                         R.array.config_face_acquire_device_entry_ignorelist))
@@ -2483,7 +2507,7 @@
         mAuthInterruptActive = active;
         updateFaceListeningState(BIOMETRIC_ACTION_UPDATE,
                 FACE_AUTH_TRIGGERED_ON_REACH_GESTURE_ON_AOD);
-        requestActiveUnlock(ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.WAKE, "onReach");
+        requestActiveUnlock(ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE, "onReach");
     }
 
     /**
@@ -2553,7 +2577,7 @@
      * Attempts to trigger active unlock from trust agent.
      */
     private void requestActiveUnlock(
-            @NonNull ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN requestOrigin,
+            @NonNull ActiveUnlockConfig.ActiveUnlockRequestOrigin requestOrigin,
             String reason,
             boolean dismissKeyguard
     ) {
@@ -2564,7 +2588,7 @@
 
         final boolean allowRequest =
                 mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(requestOrigin);
-        if (requestOrigin == ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.WAKE
+        if (requestOrigin == ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE
                 && !allowRequest && mActiveUnlockConfig.isActiveUnlockEnabled()) {
             // instead of requesting the active unlock, initiate the unlock
             initiateActiveUnlock(reason);
@@ -2578,12 +2602,13 @@
         }
     }
 
+
     /**
      * Attempts to trigger active unlock from trust agent.
      * Only dismisses the keyguard under certain conditions.
      */
     public void requestActiveUnlock(
-            @NonNull ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN requestOrigin,
+            @NonNull ActiveUnlockConfig.ActiveUnlockRequestOrigin requestOrigin,
             String extraReason
     ) {
         final boolean canFaceBypass = isFaceEnrolled() && mKeyguardBypassController != null
@@ -2591,7 +2616,7 @@
         requestActiveUnlock(
                 requestOrigin,
                 extraReason, canFaceBypass
-                        || mUdfpsBouncerShowing
+                        || mAlternateBouncerShowing
                         || mPrimaryBouncerFullyShown
                         || mAuthController.isUdfpsFingerDown());
     }
@@ -2600,7 +2625,7 @@
      * Attempts to trigger active unlock from trust agent with a request to dismiss the keyguard.
      */
     public void requestActiveUnlockDismissKeyguard(
-            @NonNull ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN requestOrigin,
+            @NonNull ActiveUnlockConfig.ActiveUnlockRequestOrigin requestOrigin,
             String extraReason
     ) {
         requestActiveUnlock(
@@ -2609,23 +2634,24 @@
     }
 
     /**
-     * Whether the UDFPS bouncer is showing.
+     * Whether the alternate bouncer is showing.
      */
-    public void setUdfpsBouncerShowing(boolean showing) {
-        mUdfpsBouncerShowing = showing;
-        if (mUdfpsBouncerShowing) {
+    public void setAlternateBouncerShowing(boolean showing) {
+        mAlternateBouncerShowing = showing;
+        if (mAlternateBouncerShowing) {
             updateFaceListeningState(BIOMETRIC_ACTION_START,
                     FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN);
             requestActiveUnlock(
-                    ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT,
-                    "udfpsBouncer");
+                    ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT,
+                    "alternateBouncer");
         }
+        updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
     }
 
     private boolean shouldTriggerActiveUnlock() {
         // Triggers:
         final boolean triggerActiveUnlockForAssistant = shouldTriggerActiveUnlockForAssistant();
-        final boolean awakeKeyguard = mPrimaryBouncerFullyShown || mUdfpsBouncerShowing
+        final boolean awakeKeyguard = mPrimaryBouncerFullyShown || mAlternateBouncerShowing
                 || (isKeyguardVisible() && !mGoingToSleep
                 && mStatusBarState != StatusBarState.SHADE_LOCKED);
 
@@ -2678,7 +2704,10 @@
 
     private boolean shouldListenForFaceAssistant() {
         BiometricAuthenticated face = mUserFaceAuthenticated.get(getCurrentUser());
-        return mAssistantVisible && mKeyguardOccluded
+        return mAssistantVisible
+                // There can be intermediate states where mKeyguardShowing is false but
+                // mKeyguardOccluded is true, we don't want to run face auth in such a scenario.
+                && (mKeyguardShowing && mKeyguardOccluded)
                 && !(face != null && face.mAuthenticated)
                 && !mUserHasTrust.get(getCurrentUser(), false);
     }
@@ -2701,7 +2730,7 @@
                         || shouldListenForFingerprintAssistant
                         || (mKeyguardOccluded && mIsDreaming)
                         || (mKeyguardOccluded && userDoesNotHaveTrust
-                            && (mOccludingAppRequestingFp || isUdfps));
+                            && (mOccludingAppRequestingFp || isUdfps || mAlternateBouncerShowing));
 
         // Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
         // instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
@@ -2736,12 +2765,14 @@
 
         boolean shouldListen = shouldListenKeyguardState && shouldListenUserState
                 && shouldListenBouncerState && shouldListenUdfpsState
-                && shouldListenSideFpsState;
+                && shouldListenSideFpsState
+                && !isFingerprintLockedOut();
         logListenerModelData(
                 new KeyguardFingerprintListenModel(
                     System.currentTimeMillis(),
                     user,
                     shouldListen,
+                    mAlternateBouncerShowing,
                     biometricEnabledForUser,
                     mPrimaryBouncerIsOrWillBeShowing,
                     userCanSkipBouncer,
@@ -2806,7 +2837,6 @@
         final boolean isPostureAllowedForFaceAuth =
                 mConfigFaceAuthSupportedPosture == 0 /* DEVICE_POSTURE_UNKNOWN */ ? true
                         : (mPostureState == mConfigFaceAuthSupportedPosture);
-
         // Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
         // instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
         final boolean shouldListen =
@@ -2816,11 +2846,11 @@
                         || awakeKeyguard
                         || shouldListenForFaceAssistant
                         || isUdfpsFingerDown
-                        || mUdfpsBouncerShowing)
+                        || mAlternateBouncerShowing)
                 && !mSwitchingUser && !faceDisabledForUser && userNotTrustedOrDetectionIsNeeded
                 && !mKeyguardGoingAway && biometricEnabledForUser
                 && faceAuthAllowedOrDetectionIsNeeded && mIsPrimaryUser
-                && (!mSecureCameraLaunched || mOccludingAppRequestingFace)
+                && (!mSecureCameraLaunched || mAlternateBouncerShowing)
                 && faceAndFpNotAuthenticated
                 && !mGoingToSleep
                 && isPostureAllowedForFaceAuth;
@@ -2831,6 +2861,7 @@
                     System.currentTimeMillis(),
                     user,
                     shouldListen,
+                    mAlternateBouncerShowing,
                     mAuthInterruptActive,
                     biometricEnabledForUser,
                     mPrimaryBouncerFullyShown,
@@ -2848,7 +2879,6 @@
                     mSecureCameraLaunched,
                     supportsDetect,
                     mSwitchingUser,
-                    mUdfpsBouncerShowing,
                     isUdfpsFingerDown,
                     userNotTrustedOrDetectionIsNeeded));
 
@@ -2936,9 +2966,26 @@
             // This would need to be updated for multi-sensor devices
             final boolean supportsFaceDetection = !mFaceSensorProperties.isEmpty()
                     && mFaceSensorProperties.get(0).supportsFaceDetection;
-            if (!isUnlockingWithBiometricAllowed(FACE) && supportsFaceDetection) {
-                mLogger.v("startListeningForFace - detect");
-                mFaceManager.detectFace(mFaceCancelSignal, mFaceDetectionCallback, userId);
+            if (!isUnlockingWithBiometricAllowed(FACE)) {
+                final boolean udfpsFingerprintAuthRunning = isUdfpsSupported()
+                        && isFingerprintDetectionRunning();
+                if (supportsFaceDetection && !udfpsFingerprintAuthRunning) {
+                    // Run face detection. (If a face is detected, show the bouncer.)
+                    mLogger.v("startListeningForFace - detect");
+                    mFaceManager.detectFace(mFaceCancelSignal, mFaceDetectionCallback, userId);
+                } else {
+                    // Don't run face detection. Instead, inform the user
+                    // face auth is unavailable and how to proceed.
+                    // (ie: "Use fingerprint instead" or "Swipe up to open")
+                    mLogger.v("Ignoring \"startListeningForFace - detect\". "
+                            + "Informing user face isn't available.");
+                    mFaceAuthenticationCallback.onAuthenticationHelp(
+                            BIOMETRIC_HELP_FACE_NOT_AVAILABLE,
+                            mContext.getResources().getString(
+                                    R.string.keyguard_face_unlock_unavailable)
+                    );
+                    return;
+                }
             } else {
                 mLogger.v("startListeningForFace - authenticate");
                 final boolean isBypassEnabled = mKeyguardBypassController != null
@@ -2969,6 +3016,23 @@
         return isUnlockWithFacePossible(userId) || isUnlockWithFingerprintPossible(userId);
     }
 
+    /**
+     * If non-strong (i.e. weak or convenience) biometrics hardware is available, not disabled, and
+     * user has enrolled templates. This does NOT check if the device is encrypted or in lockdown.
+     *
+     * @param userId User that's trying to unlock.
+     * @return {@code true} if possible.
+     */
+    public boolean isUnlockingWithNonStrongBiometricsPossible(int userId) {
+        // This assumes that there is at most one face and at most one fingerprint sensor
+        return (mFaceManager != null && !mFaceSensorProperties.isEmpty()
+                && (mFaceSensorProperties.get(0).sensorStrength != SensorProperties.STRENGTH_STRONG)
+                && isUnlockWithFacePossible(userId))
+                || (mFpm != null && !mFingerprintSensorProperties.isEmpty()
+                && (mFingerprintSensorProperties.get(0).sensorStrength
+                != SensorProperties.STRENGTH_STRONG) && isUnlockWithFingerprintPossible(userId));
+    }
+
     @SuppressLint("MissingPermission")
     @VisibleForTesting
     boolean isUnlockWithFingerprintPossible(int userId) {
@@ -3220,6 +3284,24 @@
     }
 
     /**
+     * @param data the weather data (temp, conditions, unit) for weather clock to use
+     */
+    public void sendWeatherData(Weather data) {
+        mHandler.post(()-> {
+            handleWeatherDataUpdate(data); });
+    }
+
+    private void handleWeatherDataUpdate(Weather data) {
+        Assert.isMainThread();
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onWeatherDataChanged(data);
+            }
+        }
+    }
+
+    /**
      * Handle {@link #MSG_BATTERY_UPDATE}
      */
     private void handleBatteryUpdate(BatteryStatus status) {
@@ -3338,7 +3420,8 @@
     /**
      * Handle {@link #MSG_KEYGUARD_RESET}
      */
-    private void handleKeyguardReset() {
+    @VisibleForTesting
+    protected void handleKeyguardReset() {
         mLogger.d("handleKeyguardReset");
         updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
                 FACE_AUTH_UPDATED_KEYGUARD_RESET);
@@ -3400,7 +3483,7 @@
         if (wasPrimaryBouncerFullyShown != mPrimaryBouncerFullyShown) {
             if (mPrimaryBouncerFullyShown) {
                 requestActiveUnlock(
-                        ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT,
+                        ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT,
                         "bouncerFullyShown");
             }
             for (int i = 0; i < mCallbacks.size(); i++) {
@@ -3679,6 +3762,7 @@
                 if (info == null) {
                     return;
                 }
+                mLogger.logTaskStackChangedForAssistant(info.visible);
                 mHandler.sendMessage(mHandler.obtainMessage(MSG_ASSISTANT_STACK_CHANGED,
                         info.visible));
             } catch (RemoteException e) {
@@ -3715,7 +3799,7 @@
     }
 
     // TODO: use these callbacks elsewhere in place of the existing notifyScreen*()
-    // (KeyguardViewMediator, KeyguardHostView)
+    // (KeyguardViewMediator, KeyguardSecurityContainer)
     /**
      * Dispatch wakeup events to:
      *  - update biometric listening states
@@ -3877,7 +3961,6 @@
         pw.println("  getUserHasTrust()=" + getUserHasTrust(getCurrentUser()));
         pw.println("  getUserUnlockedWithBiometric()="
                 + getUserUnlockedWithBiometric(getCurrentUser()));
-        pw.println("  mWakeOnFingerprintAcquiredStart=" + mWakeOnFingerprintAcquiredStart);
         pw.println("  SIM States:");
         for (SimData data : mSimDatas.values()) {
             pw.println("    " + data.toString());
@@ -3923,7 +4006,7 @@
                 pw.println("        mPrimaryBouncerIsOrWillBeShowing="
                         + mPrimaryBouncerIsOrWillBeShowing);
                 pw.println("        mStatusBarState=" + StatusBarState.toString(mStatusBarState));
-                pw.println("        mUdfpsBouncerShowing=" + mUdfpsBouncerShowing);
+                pw.println("        mAlternateBouncerShowing=" + mAlternateBouncerShowing);
             } else if (isSfpsSupported()) {
                 pw.println("        sfpsEnrolled=" + isSfpsEnrolled());
                 pw.println("        shouldListenForSfps=" + shouldListenForFingerprint(false));
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index e6b9ac8..0da799e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -23,6 +23,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.settingslib.fuelgauge.BatteryStatus;
+import com.android.systemui.plugins.Weather;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 
 import java.util.TimeZone;
@@ -58,6 +59,11 @@
     public void onTimeFormatChanged(String timeFormat) { }
 
     /**
+     * Called when receive new weather data.
+     */
+    public void onWeatherDataChanged(Weather data) { }
+
+    /**
      * Called when the carrier PLMN or SPN changes.
      */
     public void onRefreshCarrierInfo() { }
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 1322f16..0887b22 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -429,6 +429,7 @@
         pw.println(" mStatusBarState: " + StatusBarState.toString(mStatusBarState));
         pw.println(" mInterpolatedDarkAmount: " + mInterpolatedDarkAmount);
         pw.println(" mSensorTouchLocation: " + mSensorTouchLocation);
+        pw.println(" mDefaultPaddingPx: " + mDefaultPaddingPx);
 
         if (mView != null) {
             mView.dump(pw, args);
@@ -465,6 +466,17 @@
         }
     }
 
+    /**
+     * @return whether the userUnlockedWithBiometric state changed
+     */
+    private boolean updateUserUnlockedWithBiometric() {
+        final boolean wasUserUnlockedWithBiometric = mUserUnlockedWithBiometric;
+        mUserUnlockedWithBiometric =
+                mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(
+                        KeyguardUpdateMonitor.getCurrentUser());
+        return wasUserUnlockedWithBiometric != mUserUnlockedWithBiometric;
+    }
+
     private StatusBarStateController.StateListener mStatusBarStateListener =
             new StatusBarStateController.StateListener() {
                 @Override
@@ -502,11 +514,7 @@
 
                 @Override
                 public void onBiometricsCleared() {
-                    final boolean wasUserUnlockedWithBiometric = mUserUnlockedWithBiometric;
-                    mUserUnlockedWithBiometric =
-                            mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(
-                                    KeyguardUpdateMonitor.getCurrentUser());
-                    if (wasUserUnlockedWithBiometric != mUserUnlockedWithBiometric) {
+                    if (updateUserUnlockedWithBiometric()) {
                         updateVisibility();
                     }
                 }
@@ -515,10 +523,8 @@
                 public void onBiometricRunningStateChanged(boolean running,
                         BiometricSourceType biometricSourceType) {
                     final boolean wasRunningFps = mRunningFPS;
-                    final boolean wasUserUnlockedWithBiometric = mUserUnlockedWithBiometric;
-                    mUserUnlockedWithBiometric =
-                            mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(
-                                    KeyguardUpdateMonitor.getCurrentUser());
+                    final boolean userUnlockedWithBiometricChanged =
+                            updateUserUnlockedWithBiometric();
 
                     if (biometricSourceType == FINGERPRINT) {
                         mRunningFPS = running;
@@ -536,8 +542,7 @@
                         }
                     }
 
-                    if (wasUserUnlockedWithBiometric != mUserUnlockedWithBiometric
-                            || wasRunningFps != mRunningFPS) {
+                    if (userUnlockedWithBiometricChanged || wasRunningFps != mRunningFPS) {
                         updateVisibility();
                     }
                 }
@@ -548,6 +553,7 @@
         @Override
         public void onUnlockedChanged() {
             mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen();
+            updateUserUnlockedWithBiometric();
             updateKeyguardShowing();
             updateVisibility();
         }
@@ -565,9 +571,7 @@
 
             updateKeyguardShowing();
             if (mIsKeyguardShowing) {
-                mUserUnlockedWithBiometric =
-                    mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(
-                        KeyguardUpdateMonitor.getCurrentUser());
+                updateUserUnlockedWithBiometric();
             }
             updateVisibility();
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
index 41111e3..b30a0e0 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
@@ -51,6 +51,7 @@
     private float mStartRadius;
     private float mEndRadius;
     private int mHeight;
+    private boolean mInitialized;
 
     private static final int EXPAND_ANIMATION_MS = 100;
     private static final int EXPAND_COLOR_ANIMATION_MS = 50;
@@ -95,9 +96,13 @@
         mHeight = height;
         mStartRadius = height / 2f;
         mEndRadius = height / 4f;
-        mBackground.setCornerRadius(mStartRadius);
         mExpandAnimator.setFloatValues(mStartRadius, mEndRadius);
         mContractAnimator.setFloatValues(mEndRadius, mStartRadius);
+        // Set initial corner radius.
+        if (!mInitialized) {
+            mBackground.setCornerRadius(mStartRadius);
+            mInitialized = true;
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
index 676979c..b1a83fb 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
@@ -18,13 +18,12 @@
 
 import android.content.Context;
 import android.content.res.Resources;
-import android.os.Handler;
-import android.os.UserHandle;
 import android.view.LayoutInflater;
 
 import com.android.systemui.R;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Application;
+import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
@@ -34,6 +33,8 @@
 
 import dagger.Module;
 import dagger.Provides;
+import kotlinx.coroutines.CoroutineDispatcher;
+import kotlinx.coroutines.CoroutineScope;
 
 /** Dagger Module for clocks. */
 @Module
@@ -44,17 +45,23 @@
     public static ClockRegistry getClockRegistry(
             @Application Context context,
             PluginManager pluginManager,
-            @Main Handler handler,
+            @Application CoroutineScope scope,
+            @Main CoroutineDispatcher mainDispatcher,
+            @Background CoroutineDispatcher bgDispatcher,
             FeatureFlags featureFlags,
             @Main Resources resources,
             LayoutInflater layoutInflater) {
-        return new ClockRegistry(
+        ClockRegistry registry = new ClockRegistry(
                 context,
                 pluginManager,
-                handler,
+                scope,
+                mainDispatcher,
+                bgDispatcher,
                 featureFlags.isEnabled(Flags.LOCKSCREEN_CUSTOM_CLOCKS),
-                UserHandle.USER_ALL,
+                /* handleAllUsers= */ true,
                 new DefaultClockProvider(context, layoutInflater, resources),
                 context.getString(R.string.lockscreen_clock_id_fallback));
+        registry.registerListeners();
+        return registry;
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java
index 0cbf8bc..154b0ed 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java
@@ -18,15 +18,15 @@
 
 import android.view.ViewGroup;
 
-import com.android.keyguard.KeyguardHostViewController;
+import com.android.keyguard.KeyguardSecurityContainerController;
 import com.android.systemui.dagger.qualifiers.RootView;
-import com.android.systemui.statusbar.phone.KeyguardBouncer;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor;
 
 import dagger.BindsInstance;
 import dagger.Subcomponent;
 
 /**
- * Dagger Subcomponent for the {@link KeyguardBouncer}.
+ * Dagger Subcomponent for the {@link PrimaryBouncerInteractor}.
  */
 @Subcomponent(modules = {KeyguardBouncerModule.class})
 @KeyguardBouncerScope
@@ -37,6 +37,6 @@
         KeyguardBouncerComponent create(@BindsInstance @RootView ViewGroup bouncerContainer);
     }
 
-    /** Returns a {@link KeyguardHostViewController}. */
-    KeyguardHostViewController getKeyguardHostViewController();
+    /** Returns a {@link KeyguardSecurityContainerController}. */
+    KeyguardSecurityContainerController getSecurityContainerController();
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
index ef067b8..38f252a 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
@@ -23,13 +23,12 @@
 import android.view.LayoutInflater;
 import android.view.ViewGroup;
 
-import com.android.keyguard.KeyguardHostView;
 import com.android.keyguard.KeyguardSecurityContainer;
 import com.android.keyguard.KeyguardSecurityViewFlipper;
 import com.android.systemui.R;
 import com.android.systemui.biometrics.SideFpsController;
 import com.android.systemui.dagger.qualifiers.RootView;
-import com.android.systemui.statusbar.phone.KeyguardBouncer;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor;
 
 import java.util.Optional;
 
@@ -39,7 +38,7 @@
 import dagger.Provides;
 
 /**
- * Module to create and access view related to the {@link KeyguardBouncer}.
+ * Module to create and access view related to the {@link PrimaryBouncerInteractor}.
  */
 @Module
 public interface KeyguardBouncerModule {
@@ -47,19 +46,13 @@
     /** */
     @Provides
     @KeyguardBouncerScope
-    static KeyguardHostView providesKeyguardHostView(@RootView ViewGroup rootView,
+    static KeyguardSecurityContainer providesKeyguardSecurityContainer(@RootView ViewGroup rootView,
             LayoutInflater layoutInflater) {
-        KeyguardHostView hostView = (KeyguardHostView) layoutInflater.inflate(
-                R.layout.keyguard_host_view, rootView, false);
-        rootView.addView(hostView);
-        return hostView;
-    }
-
-    /** */
-    @Provides
-    @KeyguardBouncerScope
-    static KeyguardSecurityContainer providesKeyguardSecurityContainer(KeyguardHostView hostView) {
-        return hostView.findViewById(R.id.keyguard_security_container);
+        KeyguardSecurityContainer securityContainer =
+                (KeyguardSecurityContainer) layoutInflater.inflate(
+                        R.layout.keyguard_security_container_view, rootView, false);
+        rootView.addView(securityContainer);
+        return securityContainer;
     }
 
     /** */
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
index 2c7eceb..379c78a 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
@@ -16,9 +16,11 @@
 
 package com.android.keyguard.logging
 
+import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController
 import com.android.systemui.log.dagger.KeyguardLog
 import com.android.systemui.plugins.log.LogBuffer
 import com.android.systemui.plugins.log.LogLevel
+import com.android.systemui.statusbar.KeyguardIndicationController
 import com.google.errorprone.annotations.CompileTimeConstant
 import javax.inject.Inject
 
@@ -76,4 +78,46 @@
             { "$str1 msgId: $str2 msg: $str3" }
         )
     }
+
+    fun logUpdateDeviceEntryIndication(
+        animate: Boolean,
+        visible: Boolean,
+        dozing: Boolean,
+    ) {
+        buffer.log(
+            KeyguardIndicationController.TAG,
+            LogLevel.DEBUG,
+            {
+                bool1 = animate
+                bool2 = visible
+                bool3 = dozing
+            },
+            { "updateDeviceEntryIndication animate:$bool1 visible:$bool2 dozing $bool3" }
+        )
+    }
+
+    fun logKeyguardSwitchIndication(
+        type: Int,
+        message: String?,
+    ) {
+        buffer.log(
+            KeyguardIndicationController.TAG,
+            LogLevel.DEBUG,
+            {
+                int1 = type
+                str1 = message
+            },
+            { "keyguardSwitchIndication ${getKeyguardSwitchIndicationNonSensitiveLog(int1, str1)}" }
+        )
+    }
+
+    fun getKeyguardSwitchIndicationNonSensitiveLog(type: Int, message: String?): String {
+        // only show the battery string. other strings may contain sensitive info
+        return if (type == KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY) {
+            "type=${KeyguardIndicationRotateTextViewController.indicationTypeToString(type)}" +
+                " message=$message"
+        } else {
+            "type=${KeyguardIndicationRotateTextViewController.indicationTypeToString(type)}"
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index 5b42455..c414c08 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -372,7 +372,7 @@
     }
 
     fun logUserRequestedUnlock(
-        requestOrigin: ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN,
+        requestOrigin: ActiveUnlockConfig.ActiveUnlockRequestOrigin,
         reason: String?,
         dismissKeyguard: Boolean
     ) {
@@ -431,4 +431,20 @@
             str1 = PowerManager.wakeReasonToString(pmWakeReason)
         }, { "Skip updating face listening state on wakeup from $str1"})
     }
+
+    fun logTaskStackChangedForAssistant(assistantVisible: Boolean) {
+        logBuffer.log(TAG, VERBOSE, {
+            bool1 = assistantVisible
+        }, {
+            "TaskStackChanged for ACTIVITY_TYPE_ASSISTANT, assistant visible: $bool1"
+        })
+    }
+
+    fun logAssistantVisible(assistantVisible: Boolean) {
+        logBuffer.log(TAG, VERBOSE, {
+            bool1 = assistantVisible
+        }, {
+            "Updating mAssistantVisible to new value: $bool1"
+        })
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/ChooserSelector.kt b/packages/SystemUI/src/com/android/systemui/ChooserSelector.kt
index 9ac45b3..227f0ace 100644
--- a/packages/SystemUI/src/com/android/systemui/ChooserSelector.kt
+++ b/packages/SystemUI/src/com/android/systemui/ChooserSelector.kt
@@ -34,7 +34,7 @@
     override fun start() {
         coroutineScope.launch {
             val listener = FlagListenable.Listener { event ->
-                if (event.flagId == Flags.CHOOSER_UNBUNDLED.id) {
+                if (event.flagName == Flags.CHOOSER_UNBUNDLED.name) {
                     launch { updateUnbundledChooserEnabled() }
                     event.requestNoRestart()
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt b/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt
index 3e0fa45..54939fd 100644
--- a/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt
@@ -35,6 +35,7 @@
 import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.settingslib.Utils
 import com.android.systemui.animation.Interpolators
+import com.android.systemui.log.ScreenDecorationsLogger
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import java.util.concurrent.Executor
 
@@ -47,7 +48,8 @@
     pos: Int,
     val statusBarStateController: StatusBarStateController,
     val keyguardUpdateMonitor: KeyguardUpdateMonitor,
-    val mainExecutor: Executor
+    val mainExecutor: Executor,
+    val logger: ScreenDecorationsLogger,
 ) : ScreenDecorations.DisplayCutoutView(context, pos) {
     private var showScanningAnim = false
     private val rimPaint = Paint()
@@ -55,6 +57,7 @@
     private var rimAnimator: AnimatorSet? = null
     private val rimRect = RectF()
     private var cameraProtectionColor = Color.BLACK
+
     var faceScanningAnimColor = Utils.getColorAttrDefaultColor(context,
             R.attr.wallpaperTextColorAccent)
     private var cameraProtectionAnimator: ValueAnimator? = null
@@ -175,15 +178,22 @@
         }
         if (showScanningAnim) {
             // Make sure that our measured height encompasses the extra space for the animation
-            mTotalBounds.union(mBoundingRect)
+            mTotalBounds.set(mBoundingRect)
             mTotalBounds.union(
                 rimRect.left.toInt(),
                 rimRect.top.toInt(),
                 rimRect.right.toInt(),
                 rimRect.bottom.toInt())
-            setMeasuredDimension(
-                resolveSizeAndState(mTotalBounds.width(), widthMeasureSpec, 0),
-                resolveSizeAndState(mTotalBounds.height(), heightMeasureSpec, 0))
+            val measuredWidth = resolveSizeAndState(mTotalBounds.width(), widthMeasureSpec, 0)
+            val measuredHeight = resolveSizeAndState(mTotalBounds.height(), heightMeasureSpec, 0)
+            logger.boundingRect(rimRect, "onMeasure: Face scanning animation")
+            logger.boundingRect(mBoundingRect, "onMeasure: Display cutout view bounding rect")
+            logger.boundingRect(mTotalBounds, "onMeasure: TotalBounds")
+            logger.onMeasureDimensions(widthMeasureSpec,
+                    heightMeasureSpec,
+                    measuredWidth,
+                    measuredHeight)
+            setMeasuredDimension(measuredWidth, measuredHeight)
         } else {
             setMeasuredDimension(
                 resolveSizeAndState(mBoundingRect.width(), widthMeasureSpec, 0),
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index e6f559b..fb65588 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -36,10 +36,10 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.hardware.display.DisplayManager;
 import android.hardware.graphics.common.AlphaInterpretation;
 import android.hardware.graphics.common.DisplayDecorationSupport;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.provider.Settings.Secure;
@@ -64,6 +64,7 @@
 
 import com.android.internal.util.Preconditions;
 import com.android.settingslib.Utils;
+import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.decor.CutoutDecorProviderFactory;
@@ -75,7 +76,9 @@
 import com.android.systemui.decor.PrivacyDotDecorProviderFactory;
 import com.android.systemui.decor.RoundedCornerDecorProviderFactory;
 import com.android.systemui.decor.RoundedCornerResDelegate;
+import com.android.systemui.log.ScreenDecorationsLogger;
 import com.android.systemui.qs.SettingObserver;
+import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.events.PrivacyDotViewController;
 import com.android.systemui.tuner.TunerService;
@@ -119,8 +122,11 @@
             R.id.display_cutout_right,
             R.id.display_cutout_bottom
     };
+    private final ScreenDecorationsLogger mLogger;
 
-    private DisplayManager mDisplayManager;
+    private final AuthController mAuthController;
+
+    private DisplayTracker mDisplayTracker;
     @VisibleForTesting
     protected boolean mIsRegistered;
     private final Context mContext;
@@ -128,7 +134,7 @@
     private final TunerService mTunerService;
     private final SecureSettings mSecureSettings;
     @VisibleForTesting
-    DisplayManager.DisplayListener mDisplayListener;
+    DisplayTracker.Callback mDisplayListener;
     private CameraAvailabilityListener mCameraListener;
     private final UserTracker mUserTracker;
     private final PrivacyDotViewController mDotViewController;
@@ -152,6 +158,7 @@
     private WindowManager mWindowManager;
     private int mRotation;
     private SettingObserver mColorInversionSetting;
+    @Nullable
     private DelayableExecutor mExecutor;
     private Handler mHandler;
     boolean mPendingConfigChange;
@@ -171,6 +178,7 @@
             DisplayCutoutView overlay = (DisplayCutoutView) getOverlayView(
                     mFaceScanningViewId);
             if (overlay != null) {
+                mLogger.cameraProtectionBoundsForScanningOverlay(bounds);
                 overlay.setProtection(protectionPath, bounds);
                 overlay.enableShowProtection(true);
                 updateOverlayWindowVisibilityIfViewExists(
@@ -183,6 +191,7 @@
         }
 
         if (mScreenDecorHwcLayer != null) {
+            mLogger.hwcLayerCameraProtectionBounds(bounds);
             mScreenDecorHwcLayer.setProtection(protectionPath, bounds);
             mScreenDecorHwcLayer.enableShowProtection(true);
             return;
@@ -196,11 +205,12 @@
             }
             ++setProtectionCnt;
             final DisplayCutoutView dcv = (DisplayCutoutView) view;
+            mLogger.dcvCameraBounds(id, bounds);
             dcv.setProtection(protectionPath, bounds);
             dcv.enableShowProtection(true);
         }
         if (setProtectionCnt == 0) {
-            Log.e(TAG, "CutoutView not initialized showCameraProtection");
+            mLogger.cutoutViewNotInitialized();
         }
     }
 
@@ -302,22 +312,41 @@
             SecureSettings secureSettings,
             TunerService tunerService,
             UserTracker userTracker,
+            DisplayTracker displayTracker,
             PrivacyDotViewController dotViewController,
             ThreadFactory threadFactory,
             PrivacyDotDecorProviderFactory dotFactory,
-            FaceScanningProviderFactory faceScanningFactory) {
+            FaceScanningProviderFactory faceScanningFactory,
+            ScreenDecorationsLogger logger,
+            AuthController authController) {
         mContext = context;
         mMainExecutor = mainExecutor;
         mSecureSettings = secureSettings;
         mTunerService = tunerService;
         mUserTracker = userTracker;
+        mDisplayTracker = displayTracker;
         mDotViewController = dotViewController;
         mThreadFactory = threadFactory;
         mDotFactory = dotFactory;
         mFaceScanningFactory = faceScanningFactory;
         mFaceScanningViewId = com.android.systemui.R.id.face_scanning_anim;
+        mLogger = logger;
+        mAuthController = authController;
     }
 
+
+    private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() {
+        @Override
+        public void onFaceSensorLocationChanged() {
+            mLogger.onSensorLocationChanged();
+            if (mExecutor != null) {
+                mExecutor.execute(
+                        () -> updateOverlayProviderViews(
+                                new Integer[]{mFaceScanningViewId}));
+            }
+        }
+    };
+
     @Override
     public void start() {
         if (DEBUG_DISABLE_SCREEN_DECORATIONS) {
@@ -328,6 +357,7 @@
         mExecutor = mThreadFactory.buildDelayableExecutorOnHandler(mHandler);
         mExecutor.execute(this::startOnScreenDecorationsThread);
         mDotViewController.setUiExecutor(mExecutor);
+        mAuthController.addCallback(mAuthControllerCallback);
     }
 
     private boolean isPrivacyDotEnabled() {
@@ -376,7 +406,6 @@
     private void startOnScreenDecorationsThread() {
         Trace.beginSection("ScreenDecorations#startOnScreenDecorationsThread");
         mWindowManager = mContext.getSystemService(WindowManager.class);
-        mDisplayManager = mContext.getSystemService(DisplayManager.class);
         mContext.getDisplay().getDisplayInfo(mDisplayInfo);
         mRotation = mDisplayInfo.rotation;
         mDisplayMode = mDisplayInfo.getMode();
@@ -393,17 +422,7 @@
         setupDecorations();
         setupCameraListener();
 
-        mDisplayListener = new DisplayManager.DisplayListener() {
-            @Override
-            public void onDisplayAdded(int displayId) {
-                // do nothing
-            }
-
-            @Override
-            public void onDisplayRemoved(int displayId) {
-                // do nothing
-            }
-
+        mDisplayListener = new DisplayTracker.Callback() {
             @Override
             public void onDisplayChanged(int displayId) {
                 mContext.getDisplay().getDisplayInfo(mDisplayInfo);
@@ -474,8 +493,7 @@
                 }
             }
         };
-
-        mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
+        mDisplayTracker.addDisplayChangeCallback(mDisplayListener, new HandlerExecutor(mHandler));
         updateConfiguration();
         Trace.endSection();
     }
@@ -1315,7 +1333,7 @@
 
             if (showProtection) {
                 // Make sure that our measured height encompasses the protection
-                mTotalBounds.union(mBoundingRect);
+                mTotalBounds.set(mBoundingRect);
                 mTotalBounds.union((int) protectionRect.left, (int) protectionRect.top,
                         (int) protectionRect.right, (int) protectionRect.bottom);
                 setMeasuredDimension(
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 191ac76..b62217f 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -40,6 +40,7 @@
 import android.util.TimingsTraceLog;
 import android.view.SurfaceControl;
 import android.view.ThreadedRenderer;
+import android.view.View;
 
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.systemui.dagger.GlobalRootComponent;
@@ -114,6 +115,11 @@
         // the theme set there.
         setTheme(R.style.Theme_SystemUI);
 
+        View.setTraceLayoutSteps(
+                SystemProperties.getBoolean("persist.debug.trace_layouts", false));
+        View.setTracedRequestLayoutClassClass(
+                SystemProperties.get("persist.debug.trace_request_layout_class", null));
+
         if (Process.myUserHandle().equals(UserHandle.SYSTEM)) {
             IntentFilter bootCompletedFilter = new
                     IntentFilter(Intent.ACTION_LOCKED_BOOT_COMPLETED);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt
new file mode 100644
index 0000000..799a4d5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.accessibility
+
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.ColorCorrectionTile
+import com.android.systemui.qs.tiles.ColorInversionTile
+import com.android.systemui.qs.tiles.DreamTile
+import com.android.systemui.qs.tiles.FontScalingTile
+import com.android.systemui.qs.tiles.NightDisplayTile
+import com.android.systemui.qs.tiles.OneHandedModeTile
+import com.android.systemui.qs.tiles.ReduceBrightColorsTile
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
+
+@Module
+interface AccessibilityModule {
+
+    /** Inject ColorInversionTile into tileMap in QSModule */
+    @Binds
+    @IntoMap
+    @StringKey(ColorInversionTile.TILE_SPEC)
+    fun bindColorInversionTile(colorInversionTile: ColorInversionTile): QSTileImpl<*>
+
+    /** Inject NightDisplayTile into tileMap in QSModule */
+    @Binds
+    @IntoMap
+    @StringKey(NightDisplayTile.TILE_SPEC)
+    fun bindNightDisplayTile(nightDisplayTile: NightDisplayTile): QSTileImpl<*>
+
+    /** Inject ReduceBrightColorsTile into tileMap in QSModule */
+    @Binds
+    @IntoMap
+    @StringKey(ReduceBrightColorsTile.TILE_SPEC)
+    fun bindReduceBrightColorsTile(reduceBrightColorsTile: ReduceBrightColorsTile): QSTileImpl<*>
+
+    /** Inject OneHandedModeTile into tileMap in QSModule */
+    @Binds
+    @IntoMap
+    @StringKey(OneHandedModeTile.TILE_SPEC)
+    fun bindOneHandedModeTile(oneHandedModeTile: OneHandedModeTile): QSTileImpl<*>
+
+    /** Inject ColorCorrectionTile into tileMap in QSModule */
+    @Binds
+    @IntoMap
+    @StringKey(ColorCorrectionTile.TILE_SPEC)
+    fun bindColorCorrectionTile(colorCorrectionTile: ColorCorrectionTile): QSTileImpl<*>
+
+    /** Inject DreamTile into tileMap in QSModule */
+    @Binds
+    @IntoMap
+    @StringKey(DreamTile.TILE_SPEC)
+    fun bindDreamTile(dreamTile: DreamTile): QSTileImpl<*>
+
+    /** Inject FontScalingTile into tileMap in QSModule */
+    @Binds
+    @IntoMap
+    @StringKey(FontScalingTile.TILE_SPEC)
+    fun bindFontScalingTile(fontScalingTile: FontScalingTile): QSTileImpl<*>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index 4c92598..ddac25b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -36,7 +36,6 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.Log;
-import android.view.Display;
 import android.view.IWindowManager;
 import android.view.InputDevice;
 import android.view.KeyCharacterMap;
@@ -50,6 +49,7 @@
 import com.android.systemui.CoreStartable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.recents.Recents;
+import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.statusbar.CommandQueue;
@@ -180,6 +180,7 @@
     private final Context mContext;
     private final UserTracker mUserTracker;
     private final Optional<Recents> mRecentsOptional;
+    private final DisplayTracker mDisplayTracker;
     private Locale mLocale;
     private final AccessibilityManager mA11yManager;
     private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
@@ -194,11 +195,13 @@
             NotificationShadeWindowController notificationShadeController,
             ShadeController shadeController,
             Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
-            Optional<Recents> recentsOptional) {
+            Optional<Recents> recentsOptional,
+            DisplayTracker displayTracker) {
         mContext = context;
         mUserTracker = userTracker;
         mShadeController = shadeController;
         mRecentsOptional = recentsOptional;
+        mDisplayTracker = displayTracker;
         mReceiver = new SystemActionsBroadcastReceiver();
         mLocale = mContext.getResources().getConfiguration().getLocales().get(0);
         mA11yManager = (AccessibilityManager) mContext.getSystemService(
@@ -207,7 +210,8 @@
         // Saving in instance variable since to prevent GC since
         // NotificationShadeWindowController.registerCallback() only keeps weak references.
         mNotificationShadeCallback =
-                (keyguardShowing, keyguardOccluded, bouncerShowing, mDozing, panelExpanded) ->
+                (keyguardShowing, keyguardOccluded, bouncerShowing, mDozing, panelExpanded,
+                            isDreaming) ->
                         registerOrUnregisterDismissNotificationShadeAction();
         mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
     }
@@ -522,7 +526,7 @@
 
     private void handleAccessibilityButton() {
         AccessibilityManager.getInstance(mContext).notifyAccessibilityButtonClicked(
-                Display.DEFAULT_DISPLAY);
+                mDisplayTracker.getDefaultDisplayId());
     }
 
     private void handleAccessibilityButtonChooser() {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index ab11fce..b3574bf 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -39,6 +39,7 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.statusbar.CommandQueue;
 
 import java.io.PrintWriter;
@@ -62,6 +63,7 @@
     private final AccessibilityManager mAccessibilityManager;
     private final CommandQueue mCommandQueue;
     private final OverviewProxyService mOverviewProxyService;
+    private final DisplayTracker mDisplayTracker;
 
     private WindowMagnificationConnectionImpl mWindowMagnificationConnectionImpl;
     private SysUiState mSysUiState;
@@ -102,7 +104,8 @@
     @Inject
     public WindowMagnification(Context context, @Main Handler mainHandler,
             CommandQueue commandQueue, ModeSwitchesController modeSwitchesController,
-            SysUiState sysUiState, OverviewProxyService overviewProxyService) {
+            SysUiState sysUiState, OverviewProxyService overviewProxyService,
+            DisplayTracker displayTracker) {
         mContext = context;
         mHandler = mainHandler;
         mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
@@ -110,6 +113,7 @@
         mModeSwitchesController = modeSwitchesController;
         mSysUiState = sysUiState;
         mOverviewProxyService = overviewProxyService;
+        mDisplayTracker = displayTracker;
         mMagnificationControllerSupplier = new ControllerSupplier(context,
                 mHandler, this, context.getSystemService(DisplayManager.class), sysUiState);
     }
@@ -130,14 +134,14 @@
     private void updateSysUiStateFlag() {
         //TODO(b/187510533): support multi-display once SysuiState supports it.
         final WindowMagnificationController controller =
-                mMagnificationControllerSupplier.valueAt(Display.DEFAULT_DISPLAY);
+                mMagnificationControllerSupplier.valueAt(mDisplayTracker.getDefaultDisplayId());
         if (controller != null) {
             controller.updateSysUIStateFlag();
         } else {
             // The instance is initialized when there is an IPC request. Considering
             // self-crash cases, we need to reset the flag in such situation.
             mSysUiState.setFlag(SYSUI_STATE_MAGNIFICATION_OVERLAP, false)
-                    .commitUpdate(Display.DEFAULT_DISPLAY);
+                    .commitUpdate(mDisplayTracker.getDefaultDisplayId());
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialog.kt b/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialog.kt
new file mode 100644
index 0000000..54f933a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialog.kt
@@ -0,0 +1,107 @@
+/*
+ * 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.accessibility.fontscaling
+
+import android.content.Context
+import android.content.pm.ActivityInfo
+import android.content.res.Configuration
+import android.os.Bundle
+import android.provider.Settings
+import android.view.LayoutInflater
+import android.widget.Button
+import android.widget.SeekBar
+import android.widget.SeekBar.OnSeekBarChangeListener
+import android.widget.TextView
+import com.android.systemui.R
+import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.util.settings.SystemSettings
+
+/** The Dialog that contains a seekbar for changing the font size. */
+class FontScalingDialog(context: Context, private val systemSettings: SystemSettings) :
+    SystemUIDialog(context) {
+    private val strEntryValues: Array<String> =
+        context.resources.getStringArray(com.android.settingslib.R.array.entryvalues_font_size)
+    private lateinit var title: TextView
+    private lateinit var doneButton: Button
+    private lateinit var seekBarWithIconButtonsView: SeekBarWithIconButtonsView
+
+    private val configuration: Configuration =
+        Configuration(context.getResources().getConfiguration())
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        setTitle(R.string.font_scaling_dialog_title)
+        setView(LayoutInflater.from(context).inflate(R.layout.font_scaling_dialog, null))
+        setPositiveButton(
+            R.string.quick_settings_done,
+            /* onClick = */ null,
+            /* dismissOnClick = */ true
+        )
+        super.onCreate(savedInstanceState)
+
+        title = requireViewById(com.android.internal.R.id.alertTitle)
+        doneButton = requireViewById(com.android.internal.R.id.button1)
+        seekBarWithIconButtonsView = requireViewById(R.id.font_scaling_slider)
+
+        seekBarWithIconButtonsView.setMax((strEntryValues).size - 1)
+
+        val currentScale = systemSettings.getFloat(Settings.System.FONT_SCALE, 1.0f)
+        seekBarWithIconButtonsView.setProgress(fontSizeValueToIndex(currentScale))
+
+        seekBarWithIconButtonsView.setOnSeekBarChangeListener(
+            object : OnSeekBarChangeListener {
+                override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
+                    systemSettings.putString(Settings.System.FONT_SCALE, strEntryValues[progress])
+                }
+
+                override fun onStartTrackingTouch(seekBar: SeekBar) {
+                    // Do nothing
+                }
+
+                override fun onStopTrackingTouch(seekBar: SeekBar) {
+                    // Do nothing
+                }
+            }
+        )
+        doneButton.setOnClickListener { dismiss() }
+    }
+
+    private fun fontSizeValueToIndex(value: Float): Int {
+        var lastValue = strEntryValues[0].toFloat()
+        for (i in 1 until strEntryValues.size) {
+            val thisValue = strEntryValues[i].toFloat()
+            if (value < lastValue + (thisValue - lastValue) * .5f) {
+                return i - 1
+            }
+            lastValue = thisValue
+        }
+        return strEntryValues.size - 1
+    }
+
+    override fun onConfigurationChanged(configuration: Configuration) {
+        super.onConfigurationChanged(configuration)
+
+        val configDiff = configuration.diff(this.configuration)
+        this.configuration.setTo(configuration)
+
+        if (configDiff and ActivityInfo.CONFIG_FONT_SCALE != 0) {
+            title.post {
+                title.setTextAppearance(R.style.TextAppearance_Dialog_Title)
+                doneButton.setTextAppearance(R.style.Widget_Dialog_Button)
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index da2e28c5..1ea173e 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -1,7 +1,5 @@
 package com.android.systemui.assist;
 
-import static android.view.Display.DEFAULT_DISPLAY;
-
 import static com.android.systemui.DejankUtils.whitelistIpcs;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED;
 
@@ -35,6 +33,7 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -122,6 +121,7 @@
     protected final Lazy<SysUiState> mSysUiState;
     protected final AssistLogger mAssistLogger;
     private final UserTracker mUserTracker;
+    private final DisplayTracker mDisplayTracker;
     private final SecureSettings mSecureSettings;
 
     private final DeviceProvisionedController mDeviceProvisionedController;
@@ -141,6 +141,7 @@
             AssistLogger assistLogger,
             @Main Handler uiHandler,
             UserTracker userTracker,
+            DisplayTracker displayTracker,
             SecureSettings secureSettings) {
         mContext = context;
         mDeviceProvisionedController = controller;
@@ -150,6 +151,7 @@
         mPhoneStateMonitor = phoneStateMonitor;
         mAssistLogger = assistLogger;
         mUserTracker = userTracker;
+        mDisplayTracker = displayTracker;
         mSecureSettings = secureSettings;
 
         registerVoiceInteractionSessionListener();
@@ -214,7 +216,7 @@
                                     .setFlag(
                                             SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED,
                                             hints.getBoolean(CONSTRAINED_KEY, false))
-                                    .commitUpdate(DEFAULT_DISPLAY);
+                                    .commitUpdate(mDisplayTracker.getDefaultDisplayId());
                         }
                     }
                 });
diff --git a/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt b/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt
index 621b99d..6721c5d 100644
--- a/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt
@@ -31,6 +31,7 @@
 import com.android.systemui.controls.controller.ControlsFavoritePersistenceWrapper
 import com.android.systemui.keyguard.domain.backup.KeyguardQuickAffordanceBackupHelper
 import com.android.systemui.people.widget.PeopleBackupHelper
+import com.android.systemui.settings.UserFileManagerImpl
 
 /**
  * Helper for backing up elements in SystemUI
@@ -58,17 +59,8 @@
 
     override fun onCreate(userHandle: UserHandle, operationType: Int) {
         super.onCreate()
-        // The map in mapOf is guaranteed to be order preserving
-        val controlsMap = mapOf(CONTROLS to getPPControlsFile(this))
-        NoOverwriteFileBackupHelper(controlsDataLock, this, controlsMap).also {
-            addHelper(NO_OVERWRITE_FILES_BACKUP_KEY, it)
-        }
 
-        // Conversations widgets backup only works for system user, because widgets' information is
-        // stored in system user's SharedPreferences files and we can't open those from other users.
-        if (!userHandle.isSystem) {
-            return
-        }
+        addControlsHelper(userHandle.identifier)
 
         val keys = PeopleBackupHelper.getFilesToBackup()
         addHelper(
@@ -95,6 +87,18 @@
         sendBroadcastAsUser(intent, UserHandle.SYSTEM, PERMISSION_SELF)
     }
 
+    private fun addControlsHelper(userId: Int) {
+        val file = UserFileManagerImpl.createFile(
+            userId = userId,
+            fileName = CONTROLS,
+        )
+        // The map in mapOf is guaranteed to be order preserving
+        val controlsMap = mapOf(file.getPath() to getPPControlsFile(this, userId))
+        NoOverwriteFileBackupHelper(controlsDataLock, this, controlsMap).also {
+            addHelper(NO_OVERWRITE_FILES_BACKUP_KEY, it)
+        }
+    }
+
     /**
      * Helper class for restoring files ONLY if they are not present.
      *
@@ -136,17 +140,21 @@
     }
 }
 
-private fun getPPControlsFile(context: Context): () -> Unit {
+private fun getPPControlsFile(context: Context, userId: Int): () -> Unit {
     return {
-        val filesDir = context.filesDir
-        val file = Environment.buildPath(filesDir, BackupHelper.CONTROLS)
+        val file = UserFileManagerImpl.createFile(
+            userId = userId,
+            fileName = BackupHelper.CONTROLS,
+        )
         if (file.exists()) {
-            val dest =
-                Environment.buildPath(filesDir, AuxiliaryPersistenceWrapper.AUXILIARY_FILE_NAME)
+            val dest = UserFileManagerImpl.createFile(
+                userId = userId,
+                fileName = AuxiliaryPersistenceWrapper.AUXILIARY_FILE_NAME,
+            )
             file.copyTo(dest)
             val jobScheduler = context.getSystemService(JobScheduler::class.java)
             jobScheduler?.schedule(
-                AuxiliaryPersistenceWrapper.DeletionJobService.getJobForContext(context)
+                AuxiliaryPersistenceWrapper.DeletionJobService.getJobForContext(context, userId)
             )
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt b/packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt
new file mode 100644
index 0000000..4173790
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.battery
+
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.BatterySaverTile
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
+
+@Module
+interface BatterySaverModule {
+
+    /** Inject BatterySaverTile into tileMap in QSModule */
+    @Binds
+    @IntoMap
+    @StringKey(BatterySaverTile.TILE_SPEC)
+    fun bindBatterySaverTile(batterySaverTile: BatterySaverTile): QSTileImpl<*>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
index 436f9df..3ea3cd1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
@@ -18,6 +18,7 @@
 
 import android.annotation.RawRes
 import android.content.Context
+import android.content.Context.FINGERPRINT_SERVICE
 import android.content.res.Configuration
 import android.hardware.fingerprint.FingerprintManager
 import android.view.DisplayInfo
@@ -46,6 +47,8 @@
 
     private var isDeviceFolded: Boolean = false
     private val isSideFps: Boolean
+    private val isReverseDefaultRotation =
+            context.resources.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)
     private val screenSizeFoldProvider: ScreenSizeFoldProvider = ScreenSizeFoldProvider(context)
     var iconLayoutParamSize: Pair<Int, Int> = Pair(1, 1)
         set(value) {
@@ -64,19 +67,14 @@
                 R.dimen.biometric_dialog_fingerprint_icon_width),
                 context.resources.getDimensionPixelSize(
                         R.dimen.biometric_dialog_fingerprint_icon_height))
-        var sideFps = false
-        (context.getSystemService(Context.FINGERPRINT_SERVICE)
-                as FingerprintManager?)?.let { fpm ->
-            for (prop in fpm.sensorPropertiesInternal) {
-                if (prop.isAnySidefpsType) {
-                    sideFps = true
-                }
-            }
-        }
-        isSideFps = sideFps
+        isSideFps =
+            (context.getSystemService(FINGERPRINT_SERVICE) as FingerprintManager?)?.let { fpm ->
+                fpm.sensorPropertiesInternal.any { it.isAnySidefpsType }
+            } ?: false
+        preloadAssets(context)
         val displayInfo = DisplayInfo()
         context.display?.getDisplayInfo(displayInfo)
-        if (isSideFps && displayInfo.rotation == Surface.ROTATION_180) {
+        if (isSideFps && getRotationFromDefault(displayInfo.rotation) == Surface.ROTATION_180) {
             iconView.rotation = 180f
         }
         screenSizeFoldProvider.registerCallback(this, context.mainExecutor)
@@ -86,7 +84,7 @@
     private fun updateIconSideFps(@BiometricState lastState: Int, @BiometricState newState: Int) {
         val displayInfo = DisplayInfo()
         context.display?.getDisplayInfo(displayInfo)
-        val rotation = displayInfo.rotation
+        val rotation = getRotationFromDefault(displayInfo.rotation)
         val iconAnimation = getSideFpsAnimationForTransition(rotation)
         val iconViewOverlayAnimation =
                 getSideFpsOverlayAnimationForTransition(lastState, newState, rotation) ?: return
@@ -104,7 +102,7 @@
 
         iconView.frame = 0
         iconViewOverlay.frame = 0
-        if (shouldAnimateIconViewForTransition(lastState, newState)) {
+        if (shouldAnimateSfpsIconViewForTransition(lastState, newState)) {
             iconView.playAnimation()
         }
 
@@ -169,6 +167,18 @@
         STATE_HELP,
         STATE_ERROR -> true
         STATE_AUTHENTICATING_ANIMATING_IN,
+        STATE_AUTHENTICATING -> oldState == STATE_ERROR || oldState == STATE_HELP
+        STATE_AUTHENTICATED -> true
+        else -> false
+    }
+
+    private fun shouldAnimateSfpsIconViewForTransition(
+            @BiometricState oldState: Int,
+            @BiometricState newState: Int
+    ) = when (newState) {
+        STATE_HELP,
+        STATE_ERROR -> true
+        STATE_AUTHENTICATING_ANIMATING_IN,
         STATE_AUTHENTICATING ->
             oldState == STATE_ERROR || oldState == STATE_HELP || oldState == STATE_IDLE
         STATE_AUTHENTICATED -> true
@@ -217,6 +227,9 @@
         return if (id != null) return id else null
     }
 
+    private fun getRotationFromDefault(rotation: Int): Int =
+            if (isReverseDefaultRotation) (rotation + 1) % 4 else rotation
+
     @RawRes
     private fun getSideFpsAnimationForTransition(rotation: Int): Int = when (rotation) {
         Surface.ROTATION_90 -> if (isDeviceFolded) {
@@ -312,6 +325,40 @@
         else -> null
     }
 
+    private fun preloadAssets(context: Context) {
+        if (isSideFps) {
+            cacheLottieAssetsInContext(
+                context,
+                R.raw.biometricprompt_fingerprint_to_error_landscape,
+                R.raw.biometricprompt_folded_base_bottomright,
+                R.raw.biometricprompt_folded_base_default,
+                R.raw.biometricprompt_folded_base_topleft,
+                R.raw.biometricprompt_landscape_base,
+                R.raw.biometricprompt_portrait_base_bottomright,
+                R.raw.biometricprompt_portrait_base_topleft,
+                R.raw.biometricprompt_symbol_error_to_fingerprint_landscape,
+                R.raw.biometricprompt_symbol_error_to_fingerprint_portrait_bottomright,
+                R.raw.biometricprompt_symbol_error_to_fingerprint_portrait_topleft,
+                R.raw.biometricprompt_symbol_error_to_success_landscape,
+                R.raw.biometricprompt_symbol_error_to_success_portrait_bottomright,
+                R.raw.biometricprompt_symbol_error_to_success_portrait_topleft,
+                R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_bottomright,
+                R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_topleft,
+                R.raw.biometricprompt_symbol_fingerprint_to_success_landscape,
+                R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_bottomright,
+                R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_topleft
+            )
+        } else {
+            cacheLottieAssetsInContext(
+                context,
+                R.raw.fingerprint_dialogue_error_to_fingerprint_lottie,
+                R.raw.fingerprint_dialogue_error_to_success_lottie,
+                R.raw.fingerprint_dialogue_fingerprint_to_error_lottie,
+                R.raw.fingerprint_dialogue_fingerprint_to_success_lottie
+            )
+        }
+    }
+
     override fun onFoldUpdated(isFolded: Boolean) {
         isDeviceFolded = isFolded
     }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 68e1f72..febf75e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -847,7 +847,7 @@
         final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                 ViewGroup.LayoutParams.MATCH_PARENT,
                 ViewGroup.LayoutParams.MATCH_PARENT,
-                WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
+                WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
                 windowFlags,
                 PixelFormat.TRANSLUCENT);
         lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 2dc0cd3..3302073 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -129,7 +129,6 @@
     private float mScaleFactor = 1f;
     // sensor locations without any resolution scaling nor rotation adjustments:
     @Nullable private final Point mFaceSensorLocationDefault;
-    @Nullable private final Point mFingerprintSensorLocationDefault;
     // cached sensor locations:
     @Nullable private Point mFaceSensorLocation;
     @Nullable private Point mFingerprintSensorLocation;
@@ -586,11 +585,23 @@
     @Nullable private Point getFingerprintSensorLocationInNaturalOrientation() {
         if (getUdfpsLocation() != null) {
             return getUdfpsLocation();
+        } else {
+            int xFpLocation = mCachedDisplayInfo.getNaturalWidth() / 2;
+            try {
+                xFpLocation = mContext.getResources().getDimensionPixelSize(
+                        com.android.systemui.R.dimen
+                                .physical_fingerprint_sensor_center_screen_location_x);
+            } catch (Resources.NotFoundException e) {
+            }
+
+            return new Point(
+                    (int) (xFpLocation * mScaleFactor),
+                    (int) (mContext.getResources().getDimensionPixelSize(
+                            com.android.systemui.R.dimen
+                                    .physical_fingerprint_sensor_center_screen_location_y)
+                            * mScaleFactor)
+            );
         }
-        return new Point(
-                (int) (mFingerprintSensorLocationDefault.x * mScaleFactor),
-                (int) (mFingerprintSensorLocationDefault.y * mScaleFactor)
-        );
     }
 
     /**
@@ -774,19 +785,6 @@
         }
 
         mDisplay = mContext.getDisplay();
-        mDisplay.getDisplayInfo(mCachedDisplayInfo);
-        int xFpLocation = mCachedDisplayInfo.getNaturalWidth() / 2;
-        try {
-            xFpLocation = mContext.getResources().getDimensionPixelSize(
-                    com.android.systemui.R.dimen
-                            .physical_fingerprint_sensor_center_screen_location_x);
-        } catch (Resources.NotFoundException e) {
-        }
-        mFingerprintSensorLocationDefault = new Point(
-                xFpLocation,
-                mContext.getResources().getDimensionPixelSize(com.android.systemui.R.dimen
-                        .physical_fingerprint_sensor_center_screen_location_y)
-        );
         updateSensorLocations();
 
         IntentFilter filter = new IntentFilter();
@@ -1246,7 +1244,6 @@
         pw.println("  mScaleFactor=" + mScaleFactor);
         pw.println("  faceAuthSensorLocationDefault=" + mFaceSensorLocationDefault);
         pw.println("  faceAuthSensorLocation=" + getFaceSensorLocation());
-        pw.println("  fingerprintSensorLocationDefault=" + mFingerprintSensorLocationDefault);
         pw.println("  fingerprintSensorLocationInNaturalOrientation="
                 + getFingerprintSensorLocationInNaturalOrientation());
         pw.println("  fingerprintSensorLocation=" + getFingerprintSensorLocation());
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthIconController.kt
similarity index 86%
rename from packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricIconController.kt
rename to packages/SystemUI/src/com/android/systemui/biometrics/AuthIconController.kt
index b3b6fa2..d6ad4da 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthIconController.kt
@@ -24,14 +24,15 @@
 import android.graphics.drawable.Drawable
 import android.util.Log
 import com.airbnb.lottie.LottieAnimationView
+import com.airbnb.lottie.LottieCompositionFactory
 import com.android.systemui.biometrics.AuthBiometricView.BiometricState
 
 private const val TAG = "AuthIconController"
 
 /** Controller for animating the BiometricPrompt icon/affordance. */
 abstract class AuthIconController(
-        protected val context: Context,
-        protected val iconView: LottieAnimationView
+    protected val context: Context,
+    protected val iconView: LottieAnimationView
 ) : Animatable2.AnimationCallback() {
 
     /** If this controller should ignore events and pause. */
@@ -94,4 +95,12 @@
     open fun handleAnimationEnd(drawable: Drawable) {}
 
     open fun onConfigurationChanged(newConfig: Configuration) {}
+
+    // TODO(b/251476085): Migrate this to an extension at the appropriate level?
+    /** Load the given [rawResources] immediately so they are cached for use in the [context]. */
+    protected fun cacheLottieAssetsInContext(context: Context, vararg rawResources: Int) {
+        for (res in rawResources) {
+            LottieCompositionFactory.fromRawRes(context, res)
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
index 4b57d45..58b230f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
@@ -55,11 +55,11 @@
     private val fadeDuration = 83L
     private val retractDuration = 400L
     private var alphaInDuration: Long = 0
-    private var unlockedRippleInProgress: Boolean = false
     private val dwellShader = DwellRippleShader()
     private val dwellPaint = Paint()
     private val rippleShader = RippleShader()
     private val ripplePaint = Paint()
+    private var unlockedRippleAnimator: AnimatorSet? = null
     private var fadeDwellAnimator: Animator? = null
     private var retractDwellAnimator: Animator? = null
     private var dwellPulseOutAnimator: Animator? = null
@@ -86,8 +86,9 @@
 
     init {
         rippleShader.color = 0xffffffff.toInt() // default color
-        rippleShader.progress = 0f
+        rippleShader.rawProgress = 0f
         rippleShader.sparkleStrength = RIPPLE_SPARKLE_STRENGTH
+        setupRippleFadeParams()
         ripplePaint.shader = rippleShader
 
         dwellShader.color = 0xffffffff.toInt() // default color
@@ -205,7 +206,7 @@
      * Plays a ripple animation that grows to the dwellRadius with distortion.
      */
     fun startDwellRipple(isDozing: Boolean) {
-        if (unlockedRippleInProgress || dwellPulseOutAnimator?.isRunning == true) {
+        if (unlockedRippleAnimator?.isRunning == true || dwellPulseOutAnimator?.isRunning == true) {
             return
         }
 
@@ -262,16 +263,14 @@
      * Ripple that bursts outwards from the position of the sensor to the edges of the screen
      */
     fun startUnlockedRipple(onAnimationEnd: Runnable?) {
-        if (unlockedRippleInProgress) {
-            return // Ignore if ripple effect is already playing
-        }
+        unlockedRippleAnimator?.cancel()
 
         val rippleAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
             interpolator = Interpolators.LINEAR_OUT_SLOW_IN
             duration = AuthRippleController.RIPPLE_ANIMATION_DURATION
             addUpdateListener { animator ->
                 val now = animator.currentPlayTime
-                rippleShader.progress = animator.animatedValue as Float
+                rippleShader.rawProgress = animator.animatedValue as Float
                 rippleShader.time = now.toFloat()
 
                 invalidate()
@@ -289,28 +288,26 @@
             }
         }
 
-        val animatorSet = AnimatorSet().apply {
+        unlockedRippleAnimator = AnimatorSet().apply {
             playTogether(
                 rippleAnimator,
                 alphaInAnimator
             )
             addListener(object : AnimatorListenerAdapter() {
                 override fun onAnimationStart(animation: Animator?) {
-                    unlockedRippleInProgress = true
-                    rippleShader.rippleFill = false
                     drawRipple = true
                     visibility = VISIBLE
                 }
 
                 override fun onAnimationEnd(animation: Animator?) {
                     onAnimationEnd?.run()
-                    unlockedRippleInProgress = false
                     drawRipple = false
                     visibility = GONE
+                    unlockedRippleAnimator = null
                 }
             })
         }
-        animatorSet.start()
+        unlockedRippleAnimator?.start()
     }
 
     fun resetRippleAlpha() {
@@ -342,10 +339,22 @@
         )
     }
 
+    private fun setupRippleFadeParams() {
+        with(rippleShader) {
+            baseRingFadeParams.fadeOutStart = RippleShader.DEFAULT_BASE_RING_FADE_OUT_START
+            baseRingFadeParams.fadeOutEnd = RippleShader.DEFAULT_FADE_OUT_END
+
+            centerFillFadeParams.fadeInStart = RippleShader.DEFAULT_FADE_IN_START
+            centerFillFadeParams.fadeInEnd = RippleShader.DEFAULT_CENTER_FILL_FADE_IN_END
+            centerFillFadeParams.fadeOutStart = RippleShader.DEFAULT_CENTER_FILL_FADE_OUT_START
+            centerFillFadeParams.fadeOutEnd = RippleShader.DEFAULT_CENTER_FILL_FADE_OUT_END
+        }
+    }
+
     override fun onDraw(canvas: Canvas?) {
         // To reduce overdraw, we mask the effect to a circle whose radius is big enough to cover
         // the active effect area. Values here should be kept in sync with the
-        // animation implementation in the ripple shader.
+        // animation implementation in the ripple shader. (Twice bigger)
         if (drawDwell) {
             val maskRadius = (1 - (1 - dwellShader.progress) * (1 - dwellShader.progress) *
                     (1 - dwellShader.progress)) * dwellRadius * 2f
@@ -354,10 +363,8 @@
         }
 
         if (drawRipple) {
-            val mask = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
-                    (1 - rippleShader.progress)) * radius * 2f
             canvas?.drawCircle(origin.x.toFloat(), origin.y.toFloat(),
-                    mask, ripplePaint)
+                    rippleShader.currentWidth, ripplePaint)
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
index 6f594d5..6c49078 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
@@ -19,6 +19,8 @@
 import android.animation.AnimatorListenerAdapter
 import android.app.ActivityTaskManager
 import android.content.Context
+import android.content.res.Configuration
+import android.graphics.Color
 import android.graphics.PixelFormat
 import android.graphics.PorterDuff
 import android.graphics.PorterDuffColorFilter
@@ -54,12 +56,18 @@
 import com.android.systemui.Dumpable
 import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
 import com.android.systemui.recents.OverviewProxyService
 import com.android.systemui.util.concurrency.DelayableExecutor
 import java.io.PrintWriter
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
 
 private const val TAG = "SideFpsController"
 
@@ -79,9 +87,12 @@
     displayManager: DisplayManager,
     @Main private val mainExecutor: DelayableExecutor,
     @Main private val handler: Handler,
+    private val alternateBouncerInteractor: AlternateBouncerInteractor,
+    @Application private val scope: CoroutineScope,
+    private val featureFlags: FeatureFlags,
     dumpManager: DumpManager
 ) : Dumpable {
-    val requests: HashSet<SideFpsUiRequestSource> = HashSet()
+    private val requests: HashSet<SideFpsUiRequestSource> = HashSet()
 
     @VisibleForTesting
     val sensorProps: FingerprintSensorPropertiesInternal =
@@ -89,13 +100,17 @@
             ?: throw IllegalStateException("no side fingerprint sensor")
 
     @VisibleForTesting
-    val orientationListener =
-        BiometricDisplayListener(
+    val orientationReasonListener =
+        OrientationReasonListener(
             context,
             displayManager,
             handler,
-            BiometricDisplayListener.SensorType.SideFingerprint(sensorProps)
-        ) { onOrientationChanged() }
+            sensorProps,
+            { reason -> onOrientationChanged(reason) },
+            BiometricOverlayConstants.REASON_UNKNOWN
+        )
+
+    @VisibleForTesting val orientationListener = orientationReasonListener.orientationListener
 
     @VisibleForTesting
     val overviewProxyListener =
@@ -159,7 +174,7 @@
                     @BiometricOverlayConstants.ShowReason reason: Int
                 ) =
                     if (reason.isReasonToAutoShow(activityTaskManager)) {
-                        show(SideFpsUiRequestSource.AUTO_SHOW)
+                        show(SideFpsUiRequestSource.AUTO_SHOW, reason)
                     } else {
                         hide(SideFpsUiRequestSource.AUTO_SHOW)
                     }
@@ -168,15 +183,35 @@
             }
         )
         overviewProxyService.addCallback(overviewProxyListener)
+        listenForAlternateBouncerVisibility()
+
         dumpManager.registerDumpable(this)
     }
 
+    private fun listenForAlternateBouncerVisibility() {
+        alternateBouncerInteractor.setAlternateBouncerUIAvailable(true)
+        if (featureFlags.isEnabled(Flags.MODERN_ALTERNATE_BOUNCER)) {
+            scope.launch {
+                alternateBouncerInteractor.isVisible.collect { isVisible: Boolean ->
+                    if (isVisible) {
+                        show(SideFpsUiRequestSource.ALTERNATE_BOUNCER)
+                    } else {
+                        hide(SideFpsUiRequestSource.ALTERNATE_BOUNCER)
+                    }
+                }
+            }
+        }
+    }
+
     /** Shows the side fps overlay if not already shown. */
-    fun show(request: SideFpsUiRequestSource) {
+    fun show(
+        request: SideFpsUiRequestSource,
+        @BiometricOverlayConstants.ShowReason reason: Int = BiometricOverlayConstants.REASON_UNKNOWN
+    ) {
         requests.add(request)
         mainExecutor.execute {
             if (overlayView == null) {
-                createOverlayForDisplay()
+                createOverlayForDisplay(reason)
             } else {
                 Log.v(TAG, "overlay already shown")
             }
@@ -200,13 +235,13 @@
         }
     }
 
-    private fun onOrientationChanged() {
+    private fun onOrientationChanged(@BiometricOverlayConstants.ShowReason reason: Int) {
         if (overlayView != null) {
-            createOverlayForDisplay()
+            createOverlayForDisplay(reason)
         }
     }
 
-    private fun createOverlayForDisplay() {
+    private fun createOverlayForDisplay(@BiometricOverlayConstants.ShowReason reason: Int) {
         val view = layoutInflater.inflate(R.layout.sidefps_view, null, false)
         overlayView = view
         val display = context.display!!
@@ -237,7 +272,8 @@
                 updateOverlayParams(display, it.bounds)
             }
         }
-        lottie.addOverlayDynamicColor(context)
+        orientationReasonListener.reason = reason
+        lottie.addOverlayDynamicColor(context, reason)
 
         /**
          * Intercepts TYPE_WINDOW_STATE_CHANGED accessibility event, preventing Talkback from
@@ -392,17 +428,36 @@
 private fun WindowInsets.hasBigNavigationBar(): Boolean =
     getInsets(WindowInsets.Type.navigationBars()).bottom >= 70
 
-private fun LottieAnimationView.addOverlayDynamicColor(context: Context) {
+private fun LottieAnimationView.addOverlayDynamicColor(
+    context: Context,
+    @BiometricOverlayConstants.ShowReason reason: Int
+) {
     fun update() {
         val c = context.getColor(R.color.biometric_dialog_accent)
         val chevronFill = context.getColor(R.color.sfps_chevron_fill)
-        for (key in listOf(".blue600", ".blue400")) {
-            addValueCallback(KeyPath(key, "**"), LottieProperty.COLOR_FILTER) {
-                PorterDuffColorFilter(c, PorterDuff.Mode.SRC_ATOP)
+        val isKeyguard = reason == REASON_AUTH_KEYGUARD
+        if (isKeyguard) {
+            for (key in listOf(".blue600", ".blue400")) {
+                addValueCallback(KeyPath(key, "**"), LottieProperty.COLOR_FILTER) {
+                    PorterDuffColorFilter(c, PorterDuff.Mode.SRC_ATOP)
+                }
             }
-        }
-        addValueCallback(KeyPath(".black", "**"), LottieProperty.COLOR_FILTER) {
-            PorterDuffColorFilter(chevronFill, PorterDuff.Mode.SRC_ATOP)
+            addValueCallback(KeyPath(".black", "**"), LottieProperty.COLOR_FILTER) {
+                PorterDuffColorFilter(chevronFill, PorterDuff.Mode.SRC_ATOP)
+            }
+        } else if (!isDarkMode(context)) {
+            addValueCallback(KeyPath(".black", "**"), LottieProperty.COLOR_FILTER) {
+                PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_ATOP)
+            }
+        } else if (isDarkMode(context)) {
+            for (key in listOf(".blue600", ".blue400")) {
+                addValueCallback(KeyPath(key, "**"), LottieProperty.COLOR_FILTER) {
+                    PorterDuffColorFilter(
+                        context.getColor(R.color.settingslib_color_blue400),
+                        PorterDuff.Mode.SRC_ATOP
+                    )
+                }
+            }
         }
     }
 
@@ -413,6 +468,29 @@
     }
 }
 
+private fun isDarkMode(context: Context): Boolean {
+    val darkMode = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
+    return darkMode == Configuration.UI_MODE_NIGHT_YES
+}
+
+@VisibleForTesting
+class OrientationReasonListener(
+    context: Context,
+    displayManager: DisplayManager,
+    handler: Handler,
+    sensorProps: FingerprintSensorPropertiesInternal,
+    onOrientationChanged: (reason: Int) -> Unit,
+    @BiometricOverlayConstants.ShowReason var reason: Int
+) {
+    val orientationListener =
+        BiometricDisplayListener(
+            context,
+            displayManager,
+            handler,
+            BiometricDisplayListener.SensorType.SideFingerprint(sensorProps)
+        ) { onOrientationChanged(reason) }
+}
+
 /**
  * The source of a request to show the side fps visual indicator. This is distinct from
  * [BiometricOverlayConstants] which corrresponds with the reason fingerprint authentication is
@@ -423,4 +501,5 @@
     AUTO_SHOW,
     /** Pin, pattern or password bouncer */
     PRIMARY_BOUNCER,
+    ALTERNATE_BOUNCER
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt
index 63a1b76..addbee9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt
@@ -32,9 +32,7 @@
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
-import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback
 import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
-import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.shade.ShadeExpansionListener
@@ -84,7 +82,6 @@
     ) {
     private val useExpandedOverlay: Boolean =
         featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)
-    private val isModernBouncerEnabled: Boolean = featureFlags.isEnabled(Flags.MODERN_BOUNCER)
     private val isModernAlternateBouncerEnabled: Boolean =
         featureFlags.isEnabled(Flags.MODERN_ALTERNATE_BOUNCER)
     private var showingUdfpsBouncer = false
@@ -110,12 +107,6 @@
                 )
             }
         }
-    /**
-     * Hidden amount of input (pin/pattern/password) bouncer. This is used
-     * [KeyguardBouncerConstants.EXPANSION_VISIBLE] (0f) to
-     * [KeyguardBouncerConstants.EXPANSION_HIDDEN] (1f). Only used for the non-modernBouncer.
-     */
-    private var inputBouncerHiddenAmount = KeyguardBouncerConstants.EXPANSION_HIDDEN
     private var inputBouncerExpansion = 0f // only used for modernBouncer
 
     private val stateListener: StatusBarStateController.StateListener =
@@ -149,21 +140,6 @@
             }
         }
 
-    private val mPrimaryBouncerExpansionCallback: PrimaryBouncerExpansionCallback =
-        object : PrimaryBouncerExpansionCallback {
-            override fun onExpansionChanged(expansion: Float) {
-                inputBouncerHiddenAmount = expansion
-                updateAlpha()
-                updatePauseAuth()
-            }
-
-            override fun onVisibilityChanged(isVisible: Boolean) {
-                updateBouncerHiddenAmount()
-                updateAlpha()
-                updatePauseAuth()
-            }
-        }
-
     private val configurationListener: ConfigurationController.ConfigurationListener =
         object : ConfigurationController.ConfigurationListener {
             override fun onUiModeChanged() {
@@ -269,15 +245,13 @@
     }
 
     init {
-        if (isModernBouncerEnabled || isModernAlternateBouncerEnabled) {
-            view.repeatWhenAttached {
-                // repeatOnLifecycle CREATED (as opposed to STARTED) because the Bouncer expansion
-                // can make the view not visible; and we still want to listen for events
-                // that may make the view visible again.
-                repeatOnLifecycle(Lifecycle.State.CREATED) {
-                    if (isModernBouncerEnabled) listenForBouncerExpansion(this)
-                    if (isModernAlternateBouncerEnabled) listenForAlternateBouncerVisibility(this)
-                }
+        view.repeatWhenAttached {
+            // repeatOnLifecycle CREATED (as opposed to STARTED) because the Bouncer expansion
+            // can make the view not visible; and we still want to listen for events
+            // that may make the view visible again.
+            repeatOnLifecycle(Lifecycle.State.CREATED) {
+                listenForBouncerExpansion(this)
+                if (isModernAlternateBouncerEnabled) listenForAlternateBouncerVisibility(this)
             }
         }
     }
@@ -315,14 +289,6 @@
         statusBarState = statusBarStateController.state
         qsExpansion = keyguardViewManager.qsExpansion
         keyguardViewManager.addCallback(statusBarKeyguardViewManagerCallback)
-        if (!isModernBouncerEnabled) {
-            val bouncer = keyguardViewManager.primaryBouncer
-            bouncer?.expansion?.let {
-                mPrimaryBouncerExpansionCallback.onExpansionChanged(it)
-                bouncer.addBouncerExpansionCallback(mPrimaryBouncerExpansionCallback)
-            }
-            updateBouncerHiddenAmount()
-        }
         configurationController.addCallback(configurationListener)
         shadeExpansionStateManager.addExpansionListener(shadeExpansionListener)
         updateScaleFactor()
@@ -352,16 +318,10 @@
         }
         activityLaunchAnimator.removeListener(activityLaunchAnimatorListener)
         keyguardViewManager.removeCallback(statusBarKeyguardViewManagerCallback)
-        if (!isModernBouncerEnabled) {
-            keyguardViewManager.primaryBouncer?.removeBouncerExpansionCallback(
-                mPrimaryBouncerExpansionCallback
-            )
-        }
     }
 
     override fun dump(pw: PrintWriter, args: Array<String>) {
         super.dump(pw, args)
-        pw.println("isModernBouncerEnabled=$isModernBouncerEnabled")
         pw.println("isModernAlternateBouncerEnabled=$isModernAlternateBouncerEnabled")
         pw.println("showingUdfpsAltBouncer=$showingUdfpsBouncer")
         pw.println(
@@ -381,11 +341,7 @@
         pw.println("udfpsRequestedByApp=$udfpsRequested")
         pw.println("launchTransitionFadingAway=$launchTransitionFadingAway")
         pw.println("lastDozeAmount=$lastDozeAmount")
-        if (isModernBouncerEnabled) {
-            pw.println("inputBouncerExpansion=$inputBouncerExpansion")
-        } else {
-            pw.println("inputBouncerHiddenAmount=$inputBouncerHiddenAmount")
-        }
+        pw.println("inputBouncerExpansion=$inputBouncerExpansion")
         view.dump(pw)
     }
 
@@ -412,7 +368,6 @@
         } else {
             keyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false)
         }
-        updateBouncerHiddenAmount()
         updateAlpha()
         updatePauseAuth()
         return true
@@ -453,19 +408,11 @@
     }
 
     fun isBouncerExpansionGreaterThan(bouncerExpansionThreshold: Float): Boolean {
-        return if (isModernBouncerEnabled) {
-            inputBouncerExpansion >= bouncerExpansionThreshold
-        } else {
-            inputBouncerHiddenAmount < bouncerExpansionThreshold
-        }
+        return inputBouncerExpansion >= bouncerExpansionThreshold
     }
 
     fun isInputBouncerFullyVisible(): Boolean {
-        return if (isModernBouncerEnabled) {
-            inputBouncerExpansion == 1f
-        } else {
-            keyguardViewManager.isBouncerShowing && !alternateBouncerInteractor.isVisibleState()
-        }
+        return inputBouncerExpansion == 1f
     }
 
     override fun listenForTouchesOutsideView(): Boolean {
@@ -517,11 +464,7 @@
     }
 
     private fun getInputBouncerHiddenAmt(): Float {
-        return if (isModernBouncerEnabled) {
-            1f - inputBouncerExpansion
-        } else {
-            inputBouncerHiddenAmount
-        }
+        return 1f - inputBouncerExpansion
     }
 
     /** Update the scale factor based on the device's resolution. */
@@ -529,19 +472,6 @@
         udfpsController.mOverlayParams?.scaleFactor?.let { view.setScaleFactor(it) }
     }
 
-    private fun updateBouncerHiddenAmount() {
-        if (isModernBouncerEnabled) {
-            return
-        }
-        val altBouncerShowing = alternateBouncerInteractor.isVisibleState()
-        if (altBouncerShowing || !keyguardViewManager.primaryBouncerIsOrWillBeShowing()) {
-            inputBouncerHiddenAmount = 1f
-        } else if (keyguardViewManager.isBouncerShowing) {
-            // input bouncer is fully showing
-            inputBouncerHiddenAmount = 0f
-        }
-    }
-
     private val legacyAlternateBouncer: LegacyAlternateBouncer =
         object : LegacyAlternateBouncer {
             override fun showAlternateBouncer(): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt
index 3a01cd5..39ea936 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt
@@ -54,9 +54,12 @@
         return when (event.actionMasked) {
             MotionEvent.ACTION_DOWN,
             MotionEvent.ACTION_POINTER_DOWN,
-            MotionEvent.ACTION_MOVE -> processActionMove(preprocess())
+            MotionEvent.ACTION_MOVE,
+            MotionEvent.ACTION_HOVER_ENTER,
+            MotionEvent.ACTION_HOVER_MOVE -> processActionMove(preprocess())
             MotionEvent.ACTION_UP,
-            MotionEvent.ACTION_POINTER_UP ->
+            MotionEvent.ACTION_POINTER_UP,
+            MotionEvent.ACTION_HOVER_EXIT ->
                 processActionUp(preprocess(), event.getPointerId(event.actionIndex))
             MotionEvent.ACTION_CANCEL -> processActionCancel(NormalizedTouchData())
             else ->
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
index fb0c0a6..5ca36ab 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
@@ -72,7 +72,7 @@
         height = WindowManager.LayoutParams.MATCH_PARENT
         layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
         format = PixelFormat.TRANSLUCENT
-        type = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY
+        type = WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG
         fitInsetsTypes = 0 // Ignore insets from all system bars
         title = "Wired Charging Animation"
         flags = (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
index e9ac840..f6b7133 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
@@ -402,7 +402,7 @@
                 || mAccessibilityManager.isTouchExplorationEnabled()
                 || mDataProvider.isA11yAction()
                 || (mFeatureFlags.isEnabled(Flags.FALSING_OFF_FOR_UNFOLDED)
-                    && !mDataProvider.isFolded());
+                    && mDataProvider.isUnfolded());
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
index 5f347c1..bc0f995 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
@@ -380,8 +380,8 @@
         return mBatteryController.isWirelessCharging() || mDockManager.isDocked();
     }
 
-    public boolean isFolded() {
-        return Boolean.TRUE.equals(mFoldStateListener.getFolded());
+    public boolean isUnfolded() {
+        return Boolean.FALSE.equals(mFoldStateListener.getFolded());
     }
 
     /** Implement to be alerted abotu the beginning and ending of falsing tracking. */
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
index 805a20a..edda8752 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
@@ -18,10 +18,10 @@
 
 import static android.content.ClipDescription.CLASSIFICATION_COMPLETE;
 
-import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLIPBOARD_OVERLAY_ENABLED;
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ENTERED;
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_UPDATED;
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_TOAST_SHOWN;
+import static com.android.systemui.flags.Flags.CLIPBOARD_MINIMIZED_LAYOUT;
 
 import static com.google.android.setupcompat.util.WizardManagerHelper.SETTINGS_SECURE_USER_SETUP_COMPLETE;
 
@@ -29,7 +29,6 @@
 import android.content.ClipboardManager;
 import android.content.Context;
 import android.os.SystemProperties;
-import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.util.Log;
 
@@ -38,8 +37,6 @@
 import com.android.systemui.CoreStartable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
-import com.android.systemui.util.DeviceConfigProxy;
 
 import javax.inject.Inject;
 import javax.inject.Provider;
@@ -59,42 +56,31 @@
             "com.android.systemui.SUPPRESS_CLIPBOARD_OVERLAY";
 
     private final Context mContext;
-    private final DeviceConfigProxy mDeviceConfig;
     private final Provider<ClipboardOverlayController> mOverlayProvider;
-    private final ClipboardOverlayControllerLegacyFactory mOverlayFactory;
     private final ClipboardToast mClipboardToast;
     private final ClipboardManager mClipboardManager;
-    private final UiEventLogger mUiEventLogger;
     private final FeatureFlags mFeatureFlags;
-    private boolean mUsingNewOverlay;
+    private final UiEventLogger mUiEventLogger;
     private ClipboardOverlay mClipboardOverlay;
 
     @Inject
-    public ClipboardListener(Context context, DeviceConfigProxy deviceConfigProxy,
+    public ClipboardListener(Context context,
             Provider<ClipboardOverlayController> clipboardOverlayControllerProvider,
-            ClipboardOverlayControllerLegacyFactory overlayFactory,
             ClipboardToast clipboardToast,
             ClipboardManager clipboardManager,
-            UiEventLogger uiEventLogger,
-            FeatureFlags featureFlags) {
+            FeatureFlags featureFlags,
+            UiEventLogger uiEventLogger) {
         mContext = context;
-        mDeviceConfig = deviceConfigProxy;
         mOverlayProvider = clipboardOverlayControllerProvider;
-        mOverlayFactory = overlayFactory;
         mClipboardToast = clipboardToast;
         mClipboardManager = clipboardManager;
-        mUiEventLogger = uiEventLogger;
         mFeatureFlags = featureFlags;
-
-        mUsingNewOverlay = mFeatureFlags.isEnabled(Flags.CLIPBOARD_OVERLAY_REFACTOR);
+        mUiEventLogger = uiEventLogger;
     }
 
     @Override
     public void start() {
-        if (mDeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED, true)) {
-            mClipboardManager.addPrimaryClipChangedListener(this);
-        }
+        mClipboardManager.addPrimaryClipChangedListener(this);
     }
 
     @Override
@@ -111,8 +97,9 @@
             return;
         }
 
-        if (!isUserSetupComplete()) {
-            // just show a toast, user should not access intents from this state
+        if (!isUserSetupComplete() // user should not access intents from this state
+                || clipData == null // shouldn't happen, but just in case
+                || clipData.getItemCount() == 0) {
             if (shouldShowToast(clipData)) {
                 mUiEventLogger.log(CLIPBOARD_TOAST_SHOWN, 0, clipSource);
                 mClipboardToast.showCopiedToast();
@@ -120,19 +107,17 @@
             return;
         }
 
-        boolean enabled = mFeatureFlags.isEnabled(Flags.CLIPBOARD_OVERLAY_REFACTOR);
-        if (mClipboardOverlay == null || enabled != mUsingNewOverlay) {
-            mUsingNewOverlay = enabled;
-            if (enabled) {
-                mClipboardOverlay = mOverlayProvider.get();
-            } else {
-                mClipboardOverlay = mOverlayFactory.create(mContext);
-            }
+        if (mClipboardOverlay == null) {
+            mClipboardOverlay = mOverlayProvider.get();
             mUiEventLogger.log(CLIPBOARD_OVERLAY_ENTERED, 0, clipSource);
         } else {
             mUiEventLogger.log(CLIPBOARD_OVERLAY_UPDATED, 0, clipSource);
         }
-        mClipboardOverlay.setClipData(clipData, clipSource);
+        if (mFeatureFlags.isEnabled(CLIPBOARD_MINIMIZED_LAYOUT)) {
+            mClipboardOverlay.setClipData(clipData, clipSource);
+        } else {
+            mClipboardOverlay.setClipDataLegacy(clipData, clipSource);
+        }
         mClipboardOverlay.setOnSessionCompleteListener(() -> {
             // Session is complete, free memory until it's needed again.
             mClipboardOverlay = null;
@@ -175,6 +160,8 @@
     }
 
     interface ClipboardOverlay {
+        void setClipDataLegacy(ClipData clipData, String clipSource);
+
         void setClipData(ClipData clipData, String clipSource);
 
         void setOnSessionCompleteListener(Runnable runnable);
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardModel.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardModel.kt
new file mode 100644
index 0000000..789833c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardModel.kt
@@ -0,0 +1,112 @@
+/*
+ * 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.clipboardoverlay
+
+import android.content.ClipData
+import android.content.ClipDescription.EXTRA_IS_SENSITIVE
+import android.content.Context
+import android.graphics.Bitmap
+import android.net.Uri
+import android.text.TextUtils
+import android.util.Log
+import android.util.Size
+import android.view.textclassifier.TextLinks
+import com.android.systemui.R
+import java.io.IOException
+
+data class ClipboardModel(
+    val clipData: ClipData,
+    val source: String,
+    val type: Type,
+    val text: CharSequence?,
+    val textLinks: TextLinks?,
+    val uri: Uri?,
+    val isSensitive: Boolean,
+    val isRemote: Boolean,
+) {
+    private var _bitmap: Bitmap? = null
+
+    fun dataMatches(other: ClipboardModel?): Boolean {
+        if (other == null) {
+            return false
+        }
+        return source == other.source &&
+            type == other.type &&
+            text == other.text &&
+            uri == other.uri &&
+            isSensitive == other.isSensitive
+    }
+
+    fun loadThumbnail(context: Context): Bitmap? {
+        if (_bitmap == null && type == Type.IMAGE && uri != null) {
+            try {
+                val size = context.resources.getDimensionPixelSize(R.dimen.overlay_x_scale)
+                _bitmap = context.contentResolver.loadThumbnail(uri, Size(size, size * 4), null)
+            } catch (e: IOException) {
+                Log.e(TAG, "Thumbnail loading failed!", e)
+            }
+        }
+        return _bitmap
+    }
+
+    internal companion object {
+        private val TAG: String = "ClipboardModel"
+
+        @JvmStatic
+        fun fromClipData(
+            context: Context,
+            utils: ClipboardOverlayUtils,
+            clipData: ClipData,
+            source: String
+        ): ClipboardModel {
+            val sensitive = clipData.description?.extras?.getBoolean(EXTRA_IS_SENSITIVE) ?: false
+            val item = clipData.getItemAt(0)!!
+            val type = getType(context, item)
+            val remote = utils.isRemoteCopy(context, clipData, source)
+            return ClipboardModel(
+                clipData,
+                source,
+                type,
+                item.text,
+                item.textLinks,
+                item.uri,
+                sensitive,
+                remote
+            )
+        }
+
+        private fun getType(context: Context, item: ClipData.Item): Type {
+            return if (!TextUtils.isEmpty(item.text)) {
+                Type.TEXT
+            } else if (item.uri != null) {
+                if (context.contentResolver.getType(item.uri)?.startsWith("image") == true) {
+                    Type.IMAGE
+                } else {
+                    Type.URI
+                }
+            } else {
+                Type.OTHER
+            }
+        }
+    }
+
+    enum class Type {
+        TEXT,
+        IMAGE,
+        URI,
+        OTHER
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index f97d6af..c214f53 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -17,11 +17,8 @@
 package com.android.systemui.clipboardoverlay;
 
 import static android.content.Intent.ACTION_CLOSE_SYSTEM_DIALOGS;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
 
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLIPBOARD_OVERLAY_SHOW_ACTIONS;
-import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLIPBOARD_OVERLAY_SHOW_EDIT_BUTTON;
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ACTION_SHOWN;
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ACTION_TAPPED;
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_DISMISSED_OTHER;
@@ -32,10 +29,9 @@
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SWIPE_DISMISSED;
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_TAP_OUTSIDE;
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_TIMED_OUT;
+import static com.android.systemui.flags.Flags.CLIPBOARD_MINIMIZED_LAYOUT;
 import static com.android.systemui.flags.Flags.CLIPBOARD_REMOTE_BEHAVIOR;
 
-import static java.util.Objects.requireNonNull;
-
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.app.RemoteAction;
@@ -48,7 +44,6 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
-import android.hardware.display.DisplayManager;
 import android.hardware.input.InputManager;
 import android.net.Uri;
 import android.os.Looper;
@@ -56,14 +51,15 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Size;
-import android.view.Display;
 import android.view.InputEvent;
 import android.view.InputEventReceiver;
 import android.view.InputMonitor;
 import android.view.MotionEvent;
+import android.view.WindowInsets;
 
 import androidx.annotation.NonNull;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.R;
 import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -95,7 +91,6 @@
     private final Context mContext;
     private final ClipboardLogger mClipboardLogger;
     private final BroadcastDispatcher mBroadcastDispatcher;
-    private final DisplayManager mDisplayManager;
     private final ClipboardOverlayWindow mWindow;
     private final TimeoutHandler mTimeoutHandler;
     private final ClipboardOverlayUtils mClipboardUtils;
@@ -107,7 +102,6 @@
     private Runnable mOnSessionCompleteListener;
     private Runnable mOnRemoteCopyTapped;
     private Runnable mOnShareTapped;
-    private Runnable mOnEditTapped;
     private Runnable mOnPreviewTapped;
 
     private InputMonitor mInputMonitor;
@@ -121,6 +115,9 @@
 
     private Runnable mOnUiUpdate;
 
+    private boolean mIsMinimized;
+    private ClipboardModel mClipboardModel;
+
     private final ClipboardOverlayView.ClipboardOverlayCallbacks mClipboardCallbacks =
             new ClipboardOverlayView.ClipboardOverlayCallbacks() {
                 @Override
@@ -156,13 +153,6 @@
                 }
 
                 @Override
-                public void onEditButtonTapped() {
-                    if (mOnEditTapped != null) {
-                        mOnEditTapped.run();
-                    }
-                }
-
-                @Override
                 public void onRemoteCopyButtonTapped() {
                     if (mOnRemoteCopyTapped != null) {
                         mOnRemoteCopyTapped.run();
@@ -174,6 +164,13 @@
                     mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISS_TAPPED);
                     animateOut();
                 }
+
+                @Override
+                public void onMinimizedViewTapped() {
+                    if (mFeatureFlags.isEnabled(CLIPBOARD_MINIMIZED_LAYOUT)) {
+                        animateFromMinimized();
+                    }
+                }
             };
 
     @Inject
@@ -187,30 +184,27 @@
             ClipboardOverlayUtils clipboardUtils,
             @Background Executor bgExecutor,
             UiEventLogger uiEventLogger) {
+        mContext = context;
         mBroadcastDispatcher = broadcastDispatcher;
-        mDisplayManager = requireNonNull(context.getSystemService(DisplayManager.class));
-        final Context displayContext = context.createDisplayContext(getDefaultDisplay());
-        mContext = displayContext.createWindowContext(TYPE_SCREENSHOT, null);
 
         mClipboardLogger = new ClipboardLogger(uiEventLogger);
 
         mView = clipboardOverlayView;
         mWindow = clipboardOverlayWindow;
-        mWindow.init(mView::setInsets, () -> {
+        mWindow.init(this::onInsetsChanged, () -> {
             mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
             hideImmediate();
         });
 
+        mFeatureFlags = featureFlags;
         mTimeoutHandler = timeoutHandler;
         mTimeoutHandler.setDefaultTimeoutMillis(CLIPBOARD_DEFAULT_TIMEOUT_MILLIS);
 
-        mFeatureFlags = featureFlags;
         mClipboardUtils = clipboardUtils;
         mBgExecutor = bgExecutor;
 
         mView.setCallbacks(mClipboardCallbacks);
 
-
         mWindow.withWindowAttached(() -> {
             mWindow.setContentView(mView);
             mView.setInsets(mWindow.getWindowInsets(),
@@ -255,8 +249,136 @@
         broadcastSender.sendBroadcast(copyIntent, SELF_PERMISSION);
     }
 
+    @VisibleForTesting
+    void onInsetsChanged(WindowInsets insets, int orientation) {
+        mView.setInsets(insets, orientation);
+        if (mFeatureFlags.isEnabled(CLIPBOARD_MINIMIZED_LAYOUT)) {
+            if (shouldShowMinimized(insets) && !mIsMinimized) {
+                mIsMinimized = true;
+                mView.setMinimized(true);
+            }
+        }
+    }
+
     @Override // ClipboardListener.ClipboardOverlay
-    public void setClipData(ClipData clipData, String clipSource) {
+    public void setClipData(ClipData data, String source) {
+        ClipboardModel model = ClipboardModel.fromClipData(mContext, mClipboardUtils, data, source);
+        if (mExitAnimator != null && mExitAnimator.isRunning()) {
+            mExitAnimator.cancel();
+        }
+        boolean shouldAnimate = !model.dataMatches(mClipboardModel);
+        mClipboardModel = model;
+        mClipboardLogger.setClipSource(mClipboardModel.getSource());
+        if (shouldAnimate) {
+            reset();
+            mClipboardLogger.setClipSource(mClipboardModel.getSource());
+            if (shouldShowMinimized(mWindow.getWindowInsets())) {
+                mIsMinimized = true;
+                mView.setMinimized(true);
+            } else {
+                setExpandedView();
+            }
+            animateIn();
+            mView.announceForAccessibility(getAccessibilityAnnouncement(mClipboardModel.getType()));
+        } else if (!mIsMinimized) {
+            setExpandedView();
+        }
+        if (mFeatureFlags.isEnabled(CLIPBOARD_REMOTE_BEHAVIOR) && mClipboardModel.isRemote()) {
+            mTimeoutHandler.cancelTimeout();
+            mOnUiUpdate = null;
+        } else {
+            mOnUiUpdate = mTimeoutHandler::resetTimeout;
+            mOnUiUpdate.run();
+        }
+    }
+
+    private void setExpandedView() {
+        final ClipboardModel model = mClipboardModel;
+        mView.setMinimized(false);
+        switch (model.getType()) {
+            case TEXT:
+                if ((mFeatureFlags.isEnabled(CLIPBOARD_REMOTE_BEHAVIOR) && model.isRemote())
+                        || DeviceConfig.getBoolean(
+                        DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_SHOW_ACTIONS, false)) {
+                    if (model.getTextLinks() != null) {
+                        classifyText(model);
+                    }
+                }
+                if (model.isSensitive()) {
+                    mView.showTextPreview(mContext.getString(R.string.clipboard_asterisks), true);
+                } else {
+                    mView.showTextPreview(model.getText(), false);
+                }
+                mView.setEditAccessibilityAction(true);
+                mOnPreviewTapped = this::editText;
+                break;
+            case IMAGE:
+                if (model.isSensitive() || model.loadThumbnail(mContext) != null) {
+                    mView.showImagePreview(
+                            model.isSensitive() ? null : model.loadThumbnail(mContext));
+                    mView.setEditAccessibilityAction(true);
+                    mOnPreviewTapped = () -> editImage(model.getUri());
+                } else {
+                    // image loading failed
+                    mView.showDefaultTextPreview();
+                }
+                break;
+            case URI:
+            case OTHER:
+                mView.showDefaultTextPreview();
+                break;
+        }
+        if (mFeatureFlags.isEnabled(CLIPBOARD_REMOTE_BEHAVIOR)) {
+            if (!model.isRemote()) {
+                maybeShowRemoteCopy(model.getClipData());
+            }
+        } else {
+            maybeShowRemoteCopy(model.getClipData());
+        }
+        if (model.getType() != ClipboardModel.Type.OTHER) {
+            mOnShareTapped = () -> shareContent(model.getClipData());
+            mView.showShareChip();
+        }
+    }
+
+    private boolean shouldShowMinimized(WindowInsets insets) {
+        return insets.getInsets(WindowInsets.Type.ime()).bottom > 0;
+    }
+
+    private void animateFromMinimized() {
+        mIsMinimized = false;
+        setExpandedView();
+        animateIn();
+    }
+
+    private String getAccessibilityAnnouncement(ClipboardModel.Type type) {
+        if (type == ClipboardModel.Type.TEXT) {
+            return mContext.getString(R.string.clipboard_text_copied);
+        } else if (type == ClipboardModel.Type.IMAGE) {
+            return mContext.getString(R.string.clipboard_image_copied);
+        } else {
+            return mContext.getString(R.string.clipboard_content_copied);
+        }
+    }
+
+    private void classifyText(ClipboardModel model) {
+        mBgExecutor.execute(() -> {
+            Optional<RemoteAction> remoteAction = mClipboardUtils.getAction(
+                            model.getText(), model.getTextLinks(), model.getSource());
+            if (model.equals(mClipboardModel)) {
+                remoteAction.ifPresent(action -> {
+                    mClipboardLogger.logUnguarded(CLIPBOARD_OVERLAY_ACTION_SHOWN);
+                    mView.setActionChip(action, () -> {
+                        mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_ACTION_TAPPED);
+                        animateOut();
+                    });
+                });
+            }
+        });
+    }
+
+    @Override // ClipboardListener.ClipboardOverlay
+    public void setClipDataLegacy(ClipData clipData, String clipSource) {
         if (mExitAnimator != null && mExitAnimator.isRunning()) {
             mExitAnimator.cancel();
         }
@@ -289,10 +411,10 @@
             accessibilityAnnouncement = mContext.getString(R.string.clipboard_text_copied);
         } else if (clipData.getItemAt(0).getUri() != null) {
             if (tryShowEditableImage(clipData.getItemAt(0).getUri(), isSensitive)) {
-                mOnShareTapped = () -> shareContent(clipData);
-                mView.showShareChip();
                 accessibilityAnnouncement = mContext.getString(R.string.clipboard_image_copied);
             }
+            mOnShareTapped = () -> shareContent(clipData);
+            mView.showShareChip();
         } else {
             mView.showDefaultTextPreview();
         }
@@ -392,11 +514,6 @@
         mView.showTextPreview(text, hidden);
         mView.setEditAccessibilityAction(true);
         mOnPreviewTapped = this::editText;
-        if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
-                CLIPBOARD_OVERLAY_SHOW_EDIT_BUTTON, false)) {
-            mOnEditTapped = this::editText;
-            mView.showEditChip(mContext.getString(R.string.clipboard_edit_text_description));
-        }
     }
 
     private boolean tryShowEditableImage(Uri uri, boolean isSensitive) {
@@ -427,10 +544,6 @@
         } else {
             mView.showDefaultTextPreview();
         }
-        if (isEditableImage && DeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_SHOW_EDIT_BUTTON, false)) {
-            mView.showEditChip(mContext.getString(R.string.clipboard_edit_image_description));
-        }
         return isEditableImage;
     }
 
@@ -506,17 +619,12 @@
     private void reset() {
         mOnRemoteCopyTapped = null;
         mOnShareTapped = null;
-        mOnEditTapped = null;
         mOnPreviewTapped = null;
         mView.reset();
         mTimeoutHandler.cancelTimeout();
         mClipboardLogger.reset();
     }
 
-    private Display getDefaultDisplay() {
-        return mDisplayManager.getDisplay(DEFAULT_DISPLAY);
-    }
-
     static class ClipboardLogger {
         private final UiEventLogger mUiEventLogger;
         private String mClipSource;
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerLegacy.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerLegacy.java
deleted file mode 100644
index 3a040829..0000000
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerLegacy.java
+++ /dev/null
@@ -1,963 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.clipboardoverlay;
-
-import static android.content.Intent.ACTION_CLOSE_SYSTEM_DIALOGS;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
-
-import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLIPBOARD_OVERLAY_SHOW_ACTIONS;
-import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLIPBOARD_OVERLAY_SHOW_EDIT_BUTTON;
-import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ACTION_TAPPED;
-import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_DISMISSED_OTHER;
-import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_DISMISS_TAPPED;
-import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_EDIT_TAPPED;
-import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_REMOTE_COPY_TAPPED;
-import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SHARE_TAPPED;
-import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SWIPE_DISMISSED;
-import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_TAP_OUTSIDE;
-import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_TIMED_OUT;
-
-import static java.util.Objects.requireNonNull;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.annotation.MainThread;
-import android.app.ICompatCameraControlCallback;
-import android.app.RemoteAction;
-import android.content.BroadcastReceiver;
-import android.content.ClipData;
-import android.content.ClipDescription;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Insets;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.graphics.drawable.Icon;
-import android.hardware.display.DisplayManager;
-import android.hardware.input.InputManager;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Looper;
-import android.provider.DeviceConfig;
-import android.text.TextUtils;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.util.MathUtils;
-import android.util.Size;
-import android.util.TypedValue;
-import android.view.Display;
-import android.view.DisplayCutout;
-import android.view.Gravity;
-import android.view.InputEvent;
-import android.view.InputEventReceiver;
-import android.view.InputMonitor;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewRootImpl;
-import android.view.ViewTreeObserver;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityManager;
-import android.view.animation.LinearInterpolator;
-import android.view.animation.PathInterpolator;
-import android.view.textclassifier.TextClassification;
-import android.view.textclassifier.TextClassificationManager;
-import android.view.textclassifier.TextClassifier;
-import android.view.textclassifier.TextLinks;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.core.view.ViewCompat;
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
-
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.policy.PhoneWindow;
-import com.android.systemui.R;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.broadcast.BroadcastSender;
-import com.android.systemui.screenshot.DraggableConstraintLayout;
-import com.android.systemui.screenshot.FloatingWindowUtil;
-import com.android.systemui.screenshot.OverlayActionChip;
-import com.android.systemui.screenshot.TimeoutHandler;
-
-import java.io.IOException;
-import java.util.ArrayList;
-
-/**
- * Controls state and UI for the overlay that appears when something is added to the clipboard
- */
-public class ClipboardOverlayControllerLegacy implements ClipboardListener.ClipboardOverlay {
-    private static final String TAG = "ClipboardOverlayCtrlr";
-    private static final String REMOTE_COPY_ACTION = "android.intent.action.REMOTE_COPY";
-
-    /** Constants for screenshot/copy deconflicting */
-    public static final String SCREENSHOT_ACTION = "com.android.systemui.SCREENSHOT";
-    public static final String SELF_PERMISSION = "com.android.systemui.permission.SELF";
-    public static final String COPY_OVERLAY_ACTION = "com.android.systemui.COPY";
-
-    private static final String EXTRA_EDIT_SOURCE_CLIPBOARD = "edit_source_clipboard";
-
-    private static final int CLIPBOARD_DEFAULT_TIMEOUT_MILLIS = 6000;
-    private static final int SWIPE_PADDING_DP = 12; // extra padding around views to allow swipe
-    private static final int FONT_SEARCH_STEP_PX = 4;
-
-    private final Context mContext;
-    private final ClipboardLogger mClipboardLogger;
-    private final BroadcastDispatcher mBroadcastDispatcher;
-    private final DisplayManager mDisplayManager;
-    private final DisplayMetrics mDisplayMetrics;
-    private final WindowManager mWindowManager;
-    private final WindowManager.LayoutParams mWindowLayoutParams;
-    private final PhoneWindow mWindow;
-    private final TimeoutHandler mTimeoutHandler;
-    private final AccessibilityManager mAccessibilityManager;
-    private final TextClassifier mTextClassifier;
-
-    private final DraggableConstraintLayout mView;
-    private final View mClipboardPreview;
-    private final ImageView mImagePreview;
-    private final TextView mTextPreview;
-    private final TextView mHiddenPreview;
-    private final View mPreviewBorder;
-    private final OverlayActionChip mEditChip;
-    private final OverlayActionChip mShareChip;
-    private final OverlayActionChip mRemoteCopyChip;
-    private final View mActionContainerBackground;
-    private final View mDismissButton;
-    private final LinearLayout mActionContainer;
-    private final ArrayList<OverlayActionChip> mActionChips = new ArrayList<>();
-
-    private Runnable mOnSessionCompleteListener;
-
-    private InputMonitor mInputMonitor;
-    private InputEventReceiver mInputEventReceiver;
-
-    private BroadcastReceiver mCloseDialogsReceiver;
-    private BroadcastReceiver mScreenshotReceiver;
-
-    private boolean mBlockAttach = false;
-    private Animator mExitAnimator;
-    private Animator mEnterAnimator;
-    private final int mOrientation;
-    private boolean mKeyboardVisible;
-
-
-    public ClipboardOverlayControllerLegacy(Context context,
-            BroadcastDispatcher broadcastDispatcher,
-            BroadcastSender broadcastSender,
-            TimeoutHandler timeoutHandler, UiEventLogger uiEventLogger) {
-        mBroadcastDispatcher = broadcastDispatcher;
-        mDisplayManager = requireNonNull(context.getSystemService(DisplayManager.class));
-        final Context displayContext = context.createDisplayContext(getDefaultDisplay());
-        mContext = displayContext.createWindowContext(TYPE_SCREENSHOT, null);
-
-        mClipboardLogger = new ClipboardLogger(uiEventLogger);
-
-        mAccessibilityManager = AccessibilityManager.getInstance(mContext);
-        mTextClassifier = requireNonNull(context.getSystemService(TextClassificationManager.class))
-                .getTextClassifier();
-
-        mWindowManager = mContext.getSystemService(WindowManager.class);
-
-        mDisplayMetrics = new DisplayMetrics();
-        mContext.getDisplay().getRealMetrics(mDisplayMetrics);
-
-        mTimeoutHandler = timeoutHandler;
-        mTimeoutHandler.setDefaultTimeoutMillis(CLIPBOARD_DEFAULT_TIMEOUT_MILLIS);
-
-        // Setup the window that we are going to use
-        mWindowLayoutParams = FloatingWindowUtil.getFloatingWindowParams();
-        mWindowLayoutParams.setTitle("ClipboardOverlay");
-
-        mWindow = FloatingWindowUtil.getFloatingWindow(mContext);
-        mWindow.setWindowManager(mWindowManager, null, null);
-
-        setWindowFocusable(false);
-
-        mView = (DraggableConstraintLayout)
-                LayoutInflater.from(mContext).inflate(R.layout.clipboard_overlay_legacy, null);
-        mActionContainerBackground =
-                requireNonNull(mView.findViewById(R.id.actions_container_background));
-        mActionContainer = requireNonNull(mView.findViewById(R.id.actions));
-        mClipboardPreview = requireNonNull(mView.findViewById(R.id.clipboard_preview));
-        mImagePreview = requireNonNull(mView.findViewById(R.id.image_preview));
-        mTextPreview = requireNonNull(mView.findViewById(R.id.text_preview));
-        mHiddenPreview = requireNonNull(mView.findViewById(R.id.hidden_preview));
-        mPreviewBorder = requireNonNull(mView.findViewById(R.id.preview_border));
-        mEditChip = requireNonNull(mView.findViewById(R.id.edit_chip));
-        mShareChip = requireNonNull(mView.findViewById(R.id.share_chip));
-        mRemoteCopyChip = requireNonNull(mView.findViewById(R.id.remote_copy_chip));
-        mEditChip.setAlpha(1);
-        mShareChip.setAlpha(1);
-        mRemoteCopyChip.setAlpha(1);
-        mDismissButton = requireNonNull(mView.findViewById(R.id.dismiss_button));
-
-        mShareChip.setContentDescription(mContext.getString(com.android.internal.R.string.share));
-        mView.setCallbacks(new DraggableConstraintLayout.SwipeDismissCallbacks() {
-            @Override
-            public void onInteraction() {
-                mTimeoutHandler.resetTimeout();
-            }
-
-            @Override
-            public void onSwipeDismissInitiated(Animator animator) {
-                mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_SWIPE_DISMISSED);
-                mExitAnimator = animator;
-            }
-
-            @Override
-            public void onDismissComplete() {
-                hideImmediate();
-            }
-        });
-
-        mTextPreview.getViewTreeObserver().addOnPreDrawListener(() -> {
-            int availableHeight = mTextPreview.getHeight()
-                    - (mTextPreview.getPaddingTop() + mTextPreview.getPaddingBottom());
-            mTextPreview.setMaxLines(availableHeight / mTextPreview.getLineHeight());
-            return true;
-        });
-
-        mDismissButton.setOnClickListener(view -> {
-            mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISS_TAPPED);
-            animateOut();
-        });
-
-        mEditChip.setIcon(Icon.createWithResource(mContext, R.drawable.ic_screenshot_edit), true);
-        mRemoteCopyChip.setIcon(
-                Icon.createWithResource(mContext, R.drawable.ic_baseline_devices_24), true);
-        mShareChip.setIcon(Icon.createWithResource(mContext, R.drawable.ic_screenshot_share), true);
-        mOrientation = mContext.getResources().getConfiguration().orientation;
-
-        attachWindow();
-        withWindowAttached(() -> {
-            mWindow.setContentView(mView);
-            WindowInsets insets = mWindowManager.getCurrentWindowMetrics().getWindowInsets();
-            mKeyboardVisible = insets.isVisible(WindowInsets.Type.ime());
-            updateInsets(insets);
-            mWindow.peekDecorView().getViewTreeObserver().addOnGlobalLayoutListener(
-                    new ViewTreeObserver.OnGlobalLayoutListener() {
-                        @Override
-                        public void onGlobalLayout() {
-                            WindowInsets insets =
-                                    mWindowManager.getCurrentWindowMetrics().getWindowInsets();
-                            boolean keyboardVisible = insets.isVisible(WindowInsets.Type.ime());
-                            if (keyboardVisible != mKeyboardVisible) {
-                                mKeyboardVisible = keyboardVisible;
-                                updateInsets(insets);
-                            }
-                        }
-                    });
-            mWindow.peekDecorView().getViewRootImpl().setActivityConfigCallback(
-                    new ViewRootImpl.ActivityConfigCallback() {
-                        @Override
-                        public void onConfigurationChanged(Configuration overrideConfig,
-                                int newDisplayId) {
-                            if (mContext.getResources().getConfiguration().orientation
-                                    != mOrientation) {
-                                mClipboardLogger.logSessionComplete(
-                                        CLIPBOARD_OVERLAY_DISMISSED_OTHER);
-                                hideImmediate();
-                            }
-                        }
-
-                        @Override
-                        public void requestCompatCameraControl(
-                                boolean showControl, boolean transformationApplied,
-                                ICompatCameraControlCallback callback) {
-                            Log.w(TAG, "unexpected requestCompatCameraControl call");
-                        }
-                    });
-        });
-
-        mTimeoutHandler.setOnTimeoutRunnable(() -> {
-            mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_TIMED_OUT);
-            animateOut();
-        });
-
-        mCloseDialogsReceiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                if (ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
-                    mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
-                    animateOut();
-                }
-            }
-        };
-
-        mBroadcastDispatcher.registerReceiver(mCloseDialogsReceiver,
-                new IntentFilter(ACTION_CLOSE_SYSTEM_DIALOGS));
-        mScreenshotReceiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                if (SCREENSHOT_ACTION.equals(intent.getAction())) {
-                    mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
-                    animateOut();
-                }
-            }
-        };
-
-        mBroadcastDispatcher.registerReceiver(mScreenshotReceiver,
-                new IntentFilter(SCREENSHOT_ACTION), null, null, Context.RECEIVER_EXPORTED,
-                SELF_PERMISSION);
-        monitorOutsideTouches();
-
-        Intent copyIntent = new Intent(COPY_OVERLAY_ACTION);
-        // Set package name so the system knows it's safe
-        copyIntent.setPackage(mContext.getPackageName());
-        broadcastSender.sendBroadcast(copyIntent, SELF_PERMISSION);
-    }
-
-    @Override // ClipboardListener.ClipboardOverlay
-    public void setClipData(ClipData clipData, String clipSource) {
-        if (mExitAnimator != null && mExitAnimator.isRunning()) {
-            mExitAnimator.cancel();
-        }
-        reset();
-        String accessibilityAnnouncement;
-
-        boolean isSensitive = clipData != null && clipData.getDescription().getExtras() != null
-                && clipData.getDescription().getExtras()
-                .getBoolean(ClipDescription.EXTRA_IS_SENSITIVE);
-        if (clipData == null || clipData.getItemCount() == 0) {
-            showTextPreview(
-                    mContext.getResources().getString(R.string.clipboard_overlay_text_copied),
-                    mTextPreview);
-            accessibilityAnnouncement = mContext.getString(R.string.clipboard_content_copied);
-        } else if (!TextUtils.isEmpty(clipData.getItemAt(0).getText())) {
-            ClipData.Item item = clipData.getItemAt(0);
-            if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
-                    CLIPBOARD_OVERLAY_SHOW_ACTIONS, false)) {
-                if (item.getTextLinks() != null) {
-                    AsyncTask.execute(() -> classifyText(clipData.getItemAt(0), clipSource));
-                }
-            }
-            if (isSensitive) {
-                showEditableText(
-                        mContext.getResources().getString(R.string.clipboard_asterisks), true);
-            } else {
-                showEditableText(item.getText(), false);
-            }
-            showShareChip(clipData);
-            accessibilityAnnouncement = mContext.getString(R.string.clipboard_text_copied);
-        } else if (clipData.getItemAt(0).getUri() != null) {
-            if (tryShowEditableImage(clipData.getItemAt(0).getUri(), isSensitive)) {
-                showShareChip(clipData);
-                accessibilityAnnouncement = mContext.getString(R.string.clipboard_image_copied);
-            } else {
-                accessibilityAnnouncement = mContext.getString(R.string.clipboard_content_copied);
-            }
-        } else {
-            showTextPreview(
-                    mContext.getResources().getString(R.string.clipboard_overlay_text_copied),
-                    mTextPreview);
-            accessibilityAnnouncement = mContext.getString(R.string.clipboard_content_copied);
-        }
-        Intent remoteCopyIntent = IntentCreator.getRemoteCopyIntent(clipData, mContext);
-        // Only show remote copy if it's available.
-        PackageManager packageManager = mContext.getPackageManager();
-        if (packageManager.resolveActivity(
-                remoteCopyIntent, PackageManager.ResolveInfoFlags.of(0)) != null) {
-            mRemoteCopyChip.setContentDescription(
-                    mContext.getString(R.string.clipboard_send_nearby_description));
-            mRemoteCopyChip.setVisibility(View.VISIBLE);
-            mRemoteCopyChip.setOnClickListener((v) -> {
-                mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_REMOTE_COPY_TAPPED);
-                mContext.startActivity(remoteCopyIntent);
-                animateOut();
-            });
-            mActionContainerBackground.setVisibility(View.VISIBLE);
-        } else {
-            mRemoteCopyChip.setVisibility(View.GONE);
-        }
-        withWindowAttached(() -> {
-            if (mEnterAnimator == null || !mEnterAnimator.isRunning()) {
-                mView.post(this::animateIn);
-            }
-            mView.announceForAccessibility(accessibilityAnnouncement);
-        });
-        mTimeoutHandler.resetTimeout();
-    }
-
-    @Override // ClipboardListener.ClipboardOverlay
-    public void setOnSessionCompleteListener(Runnable runnable) {
-        mOnSessionCompleteListener = runnable;
-    }
-
-    private void classifyText(ClipData.Item item, String source) {
-        ArrayList<RemoteAction> actions = new ArrayList<>();
-        for (TextLinks.TextLink link : item.getTextLinks().getLinks()) {
-            TextClassification classification = mTextClassifier.classifyText(
-                    item.getText(), link.getStart(), link.getEnd(), null);
-            actions.addAll(classification.getActions());
-        }
-        mView.post(() -> {
-            resetActionChips();
-            if (actions.size() > 0) {
-                mActionContainerBackground.setVisibility(View.VISIBLE);
-                for (RemoteAction action : actions) {
-                    Intent targetIntent = action.getActionIntent().getIntent();
-                    ComponentName component = targetIntent.getComponent();
-                    if (component != null && !TextUtils.equals(source,
-                            component.getPackageName())) {
-                        OverlayActionChip chip = constructActionChip(action);
-                        mActionContainer.addView(chip);
-                        mActionChips.add(chip);
-                        break; // only show at most one action chip
-                    }
-                }
-            }
-        });
-    }
-
-    private void showShareChip(ClipData clip) {
-        mShareChip.setVisibility(View.VISIBLE);
-        mActionContainerBackground.setVisibility(View.VISIBLE);
-        mShareChip.setOnClickListener((v) -> shareContent(clip));
-    }
-
-    private OverlayActionChip constructActionChip(RemoteAction action) {
-        OverlayActionChip chip = (OverlayActionChip) LayoutInflater.from(mContext).inflate(
-                R.layout.overlay_action_chip, mActionContainer, false);
-        chip.setText(action.getTitle());
-        chip.setContentDescription(action.getTitle());
-        chip.setIcon(action.getIcon(), false);
-        chip.setPendingIntent(action.getActionIntent(), () -> {
-            mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_ACTION_TAPPED);
-            animateOut();
-        });
-        chip.setAlpha(1);
-        return chip;
-    }
-
-    private void monitorOutsideTouches() {
-        InputManager inputManager = mContext.getSystemService(InputManager.class);
-        mInputMonitor = inputManager.monitorGestureInput("clipboard overlay", 0);
-        mInputEventReceiver = new InputEventReceiver(mInputMonitor.getInputChannel(),
-                Looper.getMainLooper()) {
-            @Override
-            public void onInputEvent(InputEvent event) {
-                if (event instanceof MotionEvent) {
-                    MotionEvent motionEvent = (MotionEvent) event;
-                    if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
-                        Region touchRegion = new Region();
-
-                        final Rect tmpRect = new Rect();
-                        mPreviewBorder.getBoundsOnScreen(tmpRect);
-                        tmpRect.inset(
-                                (int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP),
-                                (int) FloatingWindowUtil.dpToPx(mDisplayMetrics,
-                                        -SWIPE_PADDING_DP));
-                        touchRegion.op(tmpRect, Region.Op.UNION);
-                        mActionContainerBackground.getBoundsOnScreen(tmpRect);
-                        tmpRect.inset(
-                                (int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP),
-                                (int) FloatingWindowUtil.dpToPx(mDisplayMetrics,
-                                        -SWIPE_PADDING_DP));
-                        touchRegion.op(tmpRect, Region.Op.UNION);
-                        mDismissButton.getBoundsOnScreen(tmpRect);
-                        touchRegion.op(tmpRect, Region.Op.UNION);
-                        if (!touchRegion.contains(
-                                (int) motionEvent.getRawX(), (int) motionEvent.getRawY())) {
-                            mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_TAP_OUTSIDE);
-                            animateOut();
-                        }
-                    }
-                }
-                finishInputEvent(event, true /* handled */);
-            }
-        };
-    }
-
-    private void editImage(Uri uri) {
-        mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
-        mContext.startActivity(IntentCreator.getImageEditIntent(uri, mContext));
-        animateOut();
-    }
-
-    private void editText() {
-        mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
-        mContext.startActivity(IntentCreator.getTextEditorIntent(mContext));
-        animateOut();
-    }
-
-    private void shareContent(ClipData clip) {
-        mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_SHARE_TAPPED);
-        mContext.startActivity(IntentCreator.getShareIntent(clip, mContext));
-        animateOut();
-    }
-
-    private void showSinglePreview(View v) {
-        mTextPreview.setVisibility(View.GONE);
-        mImagePreview.setVisibility(View.GONE);
-        mHiddenPreview.setVisibility(View.GONE);
-        v.setVisibility(View.VISIBLE);
-    }
-
-    private void showTextPreview(CharSequence text, TextView textView) {
-        showSinglePreview(textView);
-        final CharSequence truncatedText = text.subSequence(0, Math.min(500, text.length()));
-        textView.setText(truncatedText);
-        updateTextSize(truncatedText, textView);
-
-        textView.addOnLayoutChangeListener(
-                (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
-                    if (right - left != oldRight - oldLeft) {
-                        updateTextSize(truncatedText, textView);
-                    }
-                });
-        mEditChip.setVisibility(View.GONE);
-    }
-
-    private void updateTextSize(CharSequence text, TextView textView) {
-        Paint paint = new Paint(textView.getPaint());
-        Resources res = textView.getResources();
-        float minFontSize = res.getDimensionPixelSize(R.dimen.clipboard_overlay_min_font);
-        float maxFontSize = res.getDimensionPixelSize(R.dimen.clipboard_overlay_max_font);
-        if (isOneWord(text) && fitsInView(text, textView, paint, minFontSize)) {
-            // If the text is a single word and would fit within the TextView at the min font size,
-            // find the biggest font size that will fit.
-            float fontSizePx = minFontSize;
-            while (fontSizePx + FONT_SEARCH_STEP_PX < maxFontSize
-                    && fitsInView(text, textView, paint, fontSizePx + FONT_SEARCH_STEP_PX)) {
-                fontSizePx += FONT_SEARCH_STEP_PX;
-            }
-            // Need to turn off autosizing, otherwise setTextSize is a no-op.
-            textView.setAutoSizeTextTypeWithDefaults(TextView.AUTO_SIZE_TEXT_TYPE_NONE);
-            // It's possible to hit the max font size and not fill the width, so centering
-            // horizontally looks better in this case.
-            textView.setGravity(Gravity.CENTER);
-            textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, (int) fontSizePx);
-        } else {
-            // Otherwise just stick with autosize.
-            textView.setAutoSizeTextTypeUniformWithConfiguration((int) minFontSize,
-                    (int) maxFontSize, FONT_SEARCH_STEP_PX, TypedValue.COMPLEX_UNIT_PX);
-            textView.setGravity(Gravity.CENTER_VERTICAL | Gravity.START);
-        }
-    }
-
-    private static boolean fitsInView(CharSequence text, TextView textView, Paint paint,
-            float fontSizePx) {
-        paint.setTextSize(fontSizePx);
-        float size = paint.measureText(text.toString());
-        float availableWidth = textView.getWidth() - textView.getPaddingLeft()
-                - textView.getPaddingRight();
-        return size < availableWidth;
-    }
-
-    private static boolean isOneWord(CharSequence text) {
-        return text.toString().split("\\s+", 2).length == 1;
-    }
-
-    private void showEditableText(CharSequence text, boolean hidden) {
-        TextView textView = hidden ? mHiddenPreview : mTextPreview;
-        showTextPreview(text, textView);
-        View.OnClickListener listener = v -> editText();
-        setAccessibilityActionToEdit(textView);
-        if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
-                CLIPBOARD_OVERLAY_SHOW_EDIT_BUTTON, false)) {
-            mEditChip.setVisibility(View.VISIBLE);
-            mActionContainerBackground.setVisibility(View.VISIBLE);
-            mEditChip.setContentDescription(
-                    mContext.getString(R.string.clipboard_edit_text_description));
-            mEditChip.setOnClickListener(listener);
-        }
-        textView.setOnClickListener(listener);
-    }
-
-    private boolean tryShowEditableImage(Uri uri, boolean isSensitive) {
-        View.OnClickListener listener = v -> editImage(uri);
-        ContentResolver resolver = mContext.getContentResolver();
-        String mimeType = resolver.getType(uri);
-        boolean isEditableImage = mimeType != null && mimeType.startsWith("image");
-        if (isSensitive) {
-            mHiddenPreview.setText(mContext.getString(R.string.clipboard_text_hidden));
-            showSinglePreview(mHiddenPreview);
-            if (isEditableImage) {
-                mHiddenPreview.setOnClickListener(listener);
-                setAccessibilityActionToEdit(mHiddenPreview);
-            }
-        } else if (isEditableImage) { // if the MIMEtype is image, try to load
-            try {
-                int size = mContext.getResources().getDimensionPixelSize(R.dimen.overlay_x_scale);
-                // The width of the view is capped, height maintains aspect ratio, so allow it to be
-                // taller if needed.
-                Bitmap thumbnail = resolver.loadThumbnail(uri, new Size(size, size * 4), null);
-                showSinglePreview(mImagePreview);
-                mImagePreview.setImageBitmap(thumbnail);
-                mImagePreview.setOnClickListener(listener);
-                setAccessibilityActionToEdit(mImagePreview);
-            } catch (IOException e) {
-                Log.e(TAG, "Thumbnail loading failed", e);
-                showTextPreview(
-                        mContext.getResources().getString(R.string.clipboard_overlay_text_copied),
-                        mTextPreview);
-                isEditableImage = false;
-            }
-        } else {
-            showTextPreview(
-                    mContext.getResources().getString(R.string.clipboard_overlay_text_copied),
-                    mTextPreview);
-        }
-        if (isEditableImage && DeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_SHOW_EDIT_BUTTON, false)) {
-            mEditChip.setVisibility(View.VISIBLE);
-            mActionContainerBackground.setVisibility(View.VISIBLE);
-            mEditChip.setOnClickListener(listener);
-            mEditChip.setContentDescription(
-                    mContext.getString(R.string.clipboard_edit_image_description));
-        }
-        return isEditableImage;
-    }
-
-    private void setAccessibilityActionToEdit(View view) {
-        ViewCompat.replaceAccessibilityAction(view,
-                AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CLICK,
-                mContext.getString(R.string.clipboard_edit), null);
-    }
-
-    private void animateIn() {
-        if (mAccessibilityManager.isEnabled()) {
-            mDismissButton.setVisibility(View.VISIBLE);
-        }
-        mEnterAnimator = getEnterAnimation();
-        mEnterAnimator.start();
-    }
-
-    private void animateOut() {
-        if (mExitAnimator != null && mExitAnimator.isRunning()) {
-            return;
-        }
-        Animator anim = getExitAnimation();
-        anim.addListener(new AnimatorListenerAdapter() {
-            private boolean mCancelled;
-
-            @Override
-            public void onAnimationCancel(Animator animation) {
-                super.onAnimationCancel(animation);
-                mCancelled = true;
-            }
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                super.onAnimationEnd(animation);
-                if (!mCancelled) {
-                    hideImmediate();
-                }
-            }
-        });
-        mExitAnimator = anim;
-        anim.start();
-    }
-
-    private Animator getEnterAnimation() {
-        TimeInterpolator linearInterpolator = new LinearInterpolator();
-        TimeInterpolator scaleInterpolator = new PathInterpolator(0, 0, 0, 1f);
-        AnimatorSet enterAnim = new AnimatorSet();
-
-        ValueAnimator rootAnim = ValueAnimator.ofFloat(0, 1);
-        rootAnim.setInterpolator(linearInterpolator);
-        rootAnim.setDuration(66);
-        rootAnim.addUpdateListener(animation -> {
-            mView.setAlpha(animation.getAnimatedFraction());
-        });
-
-        ValueAnimator scaleAnim = ValueAnimator.ofFloat(0, 1);
-        scaleAnim.setInterpolator(scaleInterpolator);
-        scaleAnim.setDuration(333);
-        scaleAnim.addUpdateListener(animation -> {
-            float previewScale = MathUtils.lerp(.9f, 1f, animation.getAnimatedFraction());
-            mClipboardPreview.setScaleX(previewScale);
-            mClipboardPreview.setScaleY(previewScale);
-            mPreviewBorder.setScaleX(previewScale);
-            mPreviewBorder.setScaleY(previewScale);
-
-            float pivotX = mClipboardPreview.getWidth() / 2f + mClipboardPreview.getX();
-            mActionContainerBackground.setPivotX(pivotX - mActionContainerBackground.getX());
-            mActionContainer.setPivotX(pivotX - ((View) mActionContainer.getParent()).getX());
-            float actionsScaleX = MathUtils.lerp(.7f, 1f, animation.getAnimatedFraction());
-            float actionsScaleY = MathUtils.lerp(.9f, 1f, animation.getAnimatedFraction());
-            mActionContainer.setScaleX(actionsScaleX);
-            mActionContainer.setScaleY(actionsScaleY);
-            mActionContainerBackground.setScaleX(actionsScaleX);
-            mActionContainerBackground.setScaleY(actionsScaleY);
-        });
-
-        ValueAnimator alphaAnim = ValueAnimator.ofFloat(0, 1);
-        alphaAnim.setInterpolator(linearInterpolator);
-        alphaAnim.setDuration(283);
-        alphaAnim.addUpdateListener(animation -> {
-            float alpha = animation.getAnimatedFraction();
-            mClipboardPreview.setAlpha(alpha);
-            mPreviewBorder.setAlpha(alpha);
-            mDismissButton.setAlpha(alpha);
-            mActionContainer.setAlpha(alpha);
-        });
-
-        mActionContainer.setAlpha(0);
-        mPreviewBorder.setAlpha(0);
-        mClipboardPreview.setAlpha(0);
-        enterAnim.play(rootAnim).with(scaleAnim);
-        enterAnim.play(alphaAnim).after(50).after(rootAnim);
-
-        enterAnim.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                super.onAnimationEnd(animation);
-                mView.setAlpha(1);
-                mTimeoutHandler.resetTimeout();
-            }
-        });
-        return enterAnim;
-    }
-
-    private Animator getExitAnimation() {
-        TimeInterpolator linearInterpolator = new LinearInterpolator();
-        TimeInterpolator scaleInterpolator = new PathInterpolator(.3f, 0, 1f, 1f);
-        AnimatorSet exitAnim = new AnimatorSet();
-
-        ValueAnimator rootAnim = ValueAnimator.ofFloat(0, 1);
-        rootAnim.setInterpolator(linearInterpolator);
-        rootAnim.setDuration(100);
-        rootAnim.addUpdateListener(anim -> mView.setAlpha(1 - anim.getAnimatedFraction()));
-
-        ValueAnimator scaleAnim = ValueAnimator.ofFloat(0, 1);
-        scaleAnim.setInterpolator(scaleInterpolator);
-        scaleAnim.setDuration(250);
-        scaleAnim.addUpdateListener(animation -> {
-            float previewScale = MathUtils.lerp(1f, .9f, animation.getAnimatedFraction());
-            mClipboardPreview.setScaleX(previewScale);
-            mClipboardPreview.setScaleY(previewScale);
-            mPreviewBorder.setScaleX(previewScale);
-            mPreviewBorder.setScaleY(previewScale);
-
-            float pivotX = mClipboardPreview.getWidth() / 2f + mClipboardPreview.getX();
-            mActionContainerBackground.setPivotX(pivotX - mActionContainerBackground.getX());
-            mActionContainer.setPivotX(pivotX - ((View) mActionContainer.getParent()).getX());
-            float actionScaleX = MathUtils.lerp(1f, .8f, animation.getAnimatedFraction());
-            float actionScaleY = MathUtils.lerp(1f, .9f, animation.getAnimatedFraction());
-            mActionContainer.setScaleX(actionScaleX);
-            mActionContainer.setScaleY(actionScaleY);
-            mActionContainerBackground.setScaleX(actionScaleX);
-            mActionContainerBackground.setScaleY(actionScaleY);
-        });
-
-        ValueAnimator alphaAnim = ValueAnimator.ofFloat(0, 1);
-        alphaAnim.setInterpolator(linearInterpolator);
-        alphaAnim.setDuration(166);
-        alphaAnim.addUpdateListener(animation -> {
-            float alpha = 1 - animation.getAnimatedFraction();
-            mClipboardPreview.setAlpha(alpha);
-            mPreviewBorder.setAlpha(alpha);
-            mDismissButton.setAlpha(alpha);
-            mActionContainer.setAlpha(alpha);
-        });
-
-        exitAnim.play(alphaAnim).with(scaleAnim);
-        exitAnim.play(rootAnim).after(150).after(alphaAnim);
-        return exitAnim;
-    }
-
-    private void hideImmediate() {
-        // Note this may be called multiple times if multiple dismissal events happen at the same
-        // time.
-        mTimeoutHandler.cancelTimeout();
-        final View decorView = mWindow.peekDecorView();
-        if (decorView != null && decorView.isAttachedToWindow()) {
-            mWindowManager.removeViewImmediate(decorView);
-        }
-        if (mCloseDialogsReceiver != null) {
-            mBroadcastDispatcher.unregisterReceiver(mCloseDialogsReceiver);
-            mCloseDialogsReceiver = null;
-        }
-        if (mScreenshotReceiver != null) {
-            mBroadcastDispatcher.unregisterReceiver(mScreenshotReceiver);
-            mScreenshotReceiver = null;
-        }
-        if (mInputEventReceiver != null) {
-            mInputEventReceiver.dispose();
-            mInputEventReceiver = null;
-        }
-        if (mInputMonitor != null) {
-            mInputMonitor.dispose();
-            mInputMonitor = null;
-        }
-        if (mOnSessionCompleteListener != null) {
-            mOnSessionCompleteListener.run();
-        }
-    }
-
-    private void resetActionChips() {
-        for (OverlayActionChip chip : mActionChips) {
-            mActionContainer.removeView(chip);
-        }
-        mActionChips.clear();
-    }
-
-    private void reset() {
-        mView.setTranslationX(0);
-        mView.setAlpha(0);
-        mActionContainerBackground.setVisibility(View.GONE);
-        mShareChip.setVisibility(View.GONE);
-        mEditChip.setVisibility(View.GONE);
-        mRemoteCopyChip.setVisibility(View.GONE);
-        resetActionChips();
-        mTimeoutHandler.cancelTimeout();
-        mClipboardLogger.reset();
-    }
-
-    @MainThread
-    private void attachWindow() {
-        View decorView = mWindow.getDecorView();
-        if (decorView.isAttachedToWindow() || mBlockAttach) {
-            return;
-        }
-        mBlockAttach = true;
-        mWindowManager.addView(decorView, mWindowLayoutParams);
-        decorView.requestApplyInsets();
-        mView.requestApplyInsets();
-        decorView.getViewTreeObserver().addOnWindowAttachListener(
-                new ViewTreeObserver.OnWindowAttachListener() {
-                    @Override
-                    public void onWindowAttached() {
-                        mBlockAttach = false;
-                    }
-
-                    @Override
-                    public void onWindowDetached() {
-                    }
-                }
-        );
-    }
-
-    private void withWindowAttached(Runnable action) {
-        View decorView = mWindow.getDecorView();
-        if (decorView.isAttachedToWindow()) {
-            action.run();
-        } else {
-            decorView.getViewTreeObserver().addOnWindowAttachListener(
-                    new ViewTreeObserver.OnWindowAttachListener() {
-                        @Override
-                        public void onWindowAttached() {
-                            mBlockAttach = false;
-                            decorView.getViewTreeObserver().removeOnWindowAttachListener(this);
-                            action.run();
-                        }
-
-                        @Override
-                        public void onWindowDetached() {
-                        }
-                    });
-        }
-    }
-
-    private void updateInsets(WindowInsets insets) {
-        int orientation = mContext.getResources().getConfiguration().orientation;
-        FrameLayout.LayoutParams p = (FrameLayout.LayoutParams) mView.getLayoutParams();
-        if (p == null) {
-            return;
-        }
-        DisplayCutout cutout = insets.getDisplayCutout();
-        Insets navBarInsets = insets.getInsets(WindowInsets.Type.navigationBars());
-        Insets imeInsets = insets.getInsets(WindowInsets.Type.ime());
-        if (cutout == null) {
-            p.setMargins(0, 0, 0, Math.max(imeInsets.bottom, navBarInsets.bottom));
-        } else {
-            Insets waterfall = cutout.getWaterfallInsets();
-            if (orientation == ORIENTATION_PORTRAIT) {
-                p.setMargins(
-                        waterfall.left,
-                        Math.max(cutout.getSafeInsetTop(), waterfall.top),
-                        waterfall.right,
-                        Math.max(imeInsets.bottom,
-                                Math.max(cutout.getSafeInsetBottom(),
-                                        Math.max(navBarInsets.bottom, waterfall.bottom))));
-            } else {
-                p.setMargins(
-                        waterfall.left,
-                        waterfall.top,
-                        waterfall.right,
-                        Math.max(imeInsets.bottom,
-                                Math.max(navBarInsets.bottom, waterfall.bottom)));
-            }
-        }
-        mView.setLayoutParams(p);
-        mView.requestLayout();
-    }
-
-    private Display getDefaultDisplay() {
-        return mDisplayManager.getDisplay(DEFAULT_DISPLAY);
-    }
-
-    /**
-     * Updates the window focusability.  If the window is already showing, then it updates the
-     * window immediately, otherwise the layout params will be applied when the window is next
-     * shown.
-     */
-    private void setWindowFocusable(boolean focusable) {
-        int flags = mWindowLayoutParams.flags;
-        if (focusable) {
-            mWindowLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-        } else {
-            mWindowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-        }
-        if (mWindowLayoutParams.flags == flags) {
-            return;
-        }
-        final View decorView = mWindow.peekDecorView();
-        if (decorView != null && decorView.isAttachedToWindow()) {
-            mWindowManager.updateViewLayout(decorView, mWindowLayoutParams);
-        }
-    }
-
-    static class ClipboardLogger {
-        private final UiEventLogger mUiEventLogger;
-        private boolean mGuarded = false;
-
-        ClipboardLogger(UiEventLogger uiEventLogger) {
-            mUiEventLogger = uiEventLogger;
-        }
-
-        void logSessionComplete(@NonNull UiEventLogger.UiEventEnum event) {
-            if (!mGuarded) {
-                mGuarded = true;
-                mUiEventLogger.log(event);
-            }
-        }
-
-        void reset() {
-            mGuarded = false;
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerLegacyFactory.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerLegacyFactory.java
deleted file mode 100644
index 0d989a7..0000000
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerLegacyFactory.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2022 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.clipboardoverlay;
-
-import android.content.Context;
-
-import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.broadcast.BroadcastSender;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.screenshot.TimeoutHandler;
-
-import javax.inject.Inject;
-
-/**
- * A factory that churns out ClipboardOverlayControllerLegacys on demand.
- */
-@SysUISingleton
-public class ClipboardOverlayControllerLegacyFactory {
-
-    private final UiEventLogger mUiEventLogger;
-    private final BroadcastDispatcher mBroadcastDispatcher;
-    private final BroadcastSender mBroadcastSender;
-
-    @Inject
-    public ClipboardOverlayControllerLegacyFactory(BroadcastDispatcher broadcastDispatcher,
-            BroadcastSender broadcastSender, UiEventLogger uiEventLogger) {
-        this.mBroadcastDispatcher = broadcastDispatcher;
-        this.mBroadcastSender = broadcastSender;
-        this.mUiEventLogger = uiEventLogger;
-    }
-
-    /**
-     * One new ClipboardOverlayControllerLegacy, coming right up!
-     */
-    public ClipboardOverlayControllerLegacy create(Context context) {
-        return new ClipboardOverlayControllerLegacy(context, mBroadcastDispatcher, mBroadcastSender,
-                new TimeoutHandler(context), mUiEventLogger);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java
index 785e4a0..a85f8b9 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java
@@ -65,6 +65,23 @@
         return false;
     }
 
+    public Optional<RemoteAction> getAction(CharSequence text, TextLinks textLinks, String source) {
+        return getActions(text, textLinks).stream().filter(remoteAction -> {
+            ComponentName component = remoteAction.getActionIntent().getIntent().getComponent();
+            return component != null && !TextUtils.equals(source, component.getPackageName());
+        }).findFirst();
+    }
+
+    private ArrayList<RemoteAction> getActions(CharSequence text, TextLinks textLinks) {
+        ArrayList<RemoteAction> actions = new ArrayList<>();
+        for (TextLinks.TextLink link : textLinks.getLinks()) {
+            TextClassification classification = mTextClassifier.classifyText(
+                    text, link.getStart(), link.getEnd(), null);
+            actions.addAll(classification.getActions());
+        }
+        return actions;
+    }
+
     public Optional<RemoteAction> getAction(ClipData.Item item, String source) {
         return getActions(item).stream().filter(remoteAction -> {
             ComponentName component = remoteAction.getActionIntent().getIntent().getComponent();
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
index 2d33157..f372bb4 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
@@ -18,8 +18,6 @@
 
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 
-import static java.util.Objects.requireNonNull;
-
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
@@ -72,11 +70,11 @@
 
         void onRemoteCopyButtonTapped();
 
-        void onEditButtonTapped();
-
         void onShareButtonTapped();
 
         void onPreviewTapped();
+
+        void onMinimizedViewTapped();
     }
 
     private static final String TAG = "ClipboardView";
@@ -92,8 +90,8 @@
     private ImageView mImagePreview;
     private TextView mTextPreview;
     private TextView mHiddenPreview;
+    private LinearLayout mMinimizedPreview;
     private View mPreviewBorder;
-    private OverlayActionChip mEditChip;
     private OverlayActionChip mShareChip;
     private OverlayActionChip mRemoteCopyChip;
     private View mActionContainerBackground;
@@ -117,26 +115,22 @@
 
     @Override
     protected void onFinishInflate() {
-        mActionContainerBackground =
-                requireNonNull(findViewById(R.id.actions_container_background));
-        mActionContainer = requireNonNull(findViewById(R.id.actions));
-        mClipboardPreview = requireNonNull(findViewById(R.id.clipboard_preview));
-        mImagePreview = requireNonNull(findViewById(R.id.image_preview));
-        mTextPreview = requireNonNull(findViewById(R.id.text_preview));
-        mHiddenPreview = requireNonNull(findViewById(R.id.hidden_preview));
-        mPreviewBorder = requireNonNull(findViewById(R.id.preview_border));
-        mEditChip = requireNonNull(findViewById(R.id.edit_chip));
-        mShareChip = requireNonNull(findViewById(R.id.share_chip));
-        mRemoteCopyChip = requireNonNull(findViewById(R.id.remote_copy_chip));
-        mDismissButton = requireNonNull(findViewById(R.id.dismiss_button));
+        mActionContainerBackground = requireViewById(R.id.actions_container_background);
+        mActionContainer = requireViewById(R.id.actions);
+        mClipboardPreview = requireViewById(R.id.clipboard_preview);
+        mPreviewBorder = requireViewById(R.id.preview_border);
+        mImagePreview = requireViewById(R.id.image_preview);
+        mTextPreview = requireViewById(R.id.text_preview);
+        mHiddenPreview = requireViewById(R.id.hidden_preview);
+        mMinimizedPreview = requireViewById(R.id.minimized_preview);
+        mShareChip = requireViewById(R.id.share_chip);
+        mRemoteCopyChip = requireViewById(R.id.remote_copy_chip);
+        mDismissButton = requireViewById(R.id.dismiss_button);
 
-        mEditChip.setAlpha(1);
         mShareChip.setAlpha(1);
         mRemoteCopyChip.setAlpha(1);
         mShareChip.setContentDescription(mContext.getString(com.android.internal.R.string.share));
 
-        mEditChip.setIcon(
-                Icon.createWithResource(mContext, R.drawable.ic_screenshot_edit), true);
         mRemoteCopyChip.setIcon(
                 Icon.createWithResource(mContext, R.drawable.ic_baseline_devices_24), true);
         mShareChip.setIcon(
@@ -158,11 +152,11 @@
     public void setCallbacks(SwipeDismissCallbacks callbacks) {
         super.setCallbacks(callbacks);
         ClipboardOverlayCallbacks clipboardCallbacks = (ClipboardOverlayCallbacks) callbacks;
-        mEditChip.setOnClickListener(v -> clipboardCallbacks.onEditButtonTapped());
         mShareChip.setOnClickListener(v -> clipboardCallbacks.onShareButtonTapped());
         mDismissButton.setOnClickListener(v -> clipboardCallbacks.onDismissButtonTapped());
         mRemoteCopyChip.setOnClickListener(v -> clipboardCallbacks.onRemoteCopyButtonTapped());
         mClipboardPreview.setOnClickListener(v -> clipboardCallbacks.onPreviewTapped());
+        mMinimizedPreview.setOnClickListener(v -> clipboardCallbacks.onMinimizedViewTapped());
     }
 
     void setEditAccessibilityAction(boolean editable) {
@@ -177,12 +171,28 @@
         }
     }
 
+    void setMinimized(boolean minimized) {
+        if (minimized) {
+            mMinimizedPreview.setVisibility(View.VISIBLE);
+            mClipboardPreview.setVisibility(View.GONE);
+            mPreviewBorder.setVisibility(View.GONE);
+            mActionContainer.setVisibility(View.GONE);
+            mActionContainerBackground.setVisibility(View.GONE);
+        } else {
+            mMinimizedPreview.setVisibility(View.GONE);
+            mClipboardPreview.setVisibility(View.VISIBLE);
+            mPreviewBorder.setVisibility(View.VISIBLE);
+            mActionContainer.setVisibility(View.VISIBLE);
+        }
+    }
+
     void setInsets(WindowInsets insets, int orientation) {
         FrameLayout.LayoutParams p = (FrameLayout.LayoutParams) getLayoutParams();
         if (p == null) {
             return;
         }
         Rect margins = computeMargins(insets, orientation);
+
         p.setMargins(margins.left, margins.top, margins.right, margins.bottom);
         setLayoutParams(p);
         requestLayout();
@@ -204,6 +214,12 @@
                 (int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP));
         touchRegion.op(tmpRect, Region.Op.UNION);
 
+        mMinimizedPreview.getBoundsOnScreen(tmpRect);
+        tmpRect.inset(
+                (int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP),
+                (int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP));
+        touchRegion.op(tmpRect, Region.Op.UNION);
+
         mDismissButton.getBoundsOnScreen(tmpRect);
         touchRegion.op(tmpRect, Region.Op.UNION);
 
@@ -235,7 +251,6 @@
                         updateTextSize(text, textView);
                     }
                 });
-        mEditChip.setVisibility(View.GONE);
     }
 
     void showImagePreview(@Nullable Bitmap thumbnail) {
@@ -248,12 +263,6 @@
         }
     }
 
-    void showEditChip(String contentDescription) {
-        mEditChip.setVisibility(View.VISIBLE);
-        mActionContainerBackground.setVisibility(View.VISIBLE);
-        mEditChip.setContentDescription(contentDescription);
-    }
-
     void showShareChip() {
         mShareChip.setVisibility(View.VISIBLE);
         mActionContainerBackground.setVisibility(View.VISIBLE);
@@ -265,7 +274,6 @@
         mActionContainerBackground.setVisibility(View.GONE);
         mDismissButton.setVisibility(View.GONE);
         mShareChip.setVisibility(View.GONE);
-        mEditChip.setVisibility(View.GONE);
         mRemoteCopyChip.setVisibility(View.GONE);
         setEditAccessibilityAction(false);
         resetActionChips();
@@ -298,6 +306,8 @@
         scaleAnim.setDuration(333);
         scaleAnim.addUpdateListener(animation -> {
             float previewScale = MathUtils.lerp(.9f, 1f, animation.getAnimatedFraction());
+            mMinimizedPreview.setScaleX(previewScale);
+            mMinimizedPreview.setScaleY(previewScale);
             mClipboardPreview.setScaleX(previewScale);
             mClipboardPreview.setScaleY(previewScale);
             mPreviewBorder.setScaleX(previewScale);
@@ -319,12 +329,14 @@
         alphaAnim.setDuration(283);
         alphaAnim.addUpdateListener(animation -> {
             float alpha = animation.getAnimatedFraction();
+            mMinimizedPreview.setAlpha(alpha);
             mClipboardPreview.setAlpha(alpha);
             mPreviewBorder.setAlpha(alpha);
             mDismissButton.setAlpha(alpha);
             mActionContainer.setAlpha(alpha);
         });
 
+        mMinimizedPreview.setAlpha(0);
         mActionContainer.setAlpha(0);
         mPreviewBorder.setAlpha(0);
         mClipboardPreview.setAlpha(0);
@@ -356,6 +368,8 @@
         scaleAnim.setDuration(250);
         scaleAnim.addUpdateListener(animation -> {
             float previewScale = MathUtils.lerp(1f, .9f, animation.getAnimatedFraction());
+            mMinimizedPreview.setScaleX(previewScale);
+            mMinimizedPreview.setScaleY(previewScale);
             mClipboardPreview.setScaleX(previewScale);
             mClipboardPreview.setScaleY(previewScale);
             mPreviewBorder.setScaleX(previewScale);
@@ -377,6 +391,7 @@
         alphaAnim.setDuration(166);
         alphaAnim.addUpdateListener(animation -> {
             float alpha = 1 - animation.getAnimatedFraction();
+            mMinimizedPreview.setAlpha(alpha);
             mClipboardPreview.setAlpha(alpha);
             mPreviewBorder.setAlpha(alpha);
             mDismissButton.setAlpha(alpha);
@@ -399,6 +414,7 @@
         mTextPreview.setVisibility(View.GONE);
         mImagePreview.setVisibility(View.GONE);
         mHiddenPreview.setVisibility(View.GONE);
+        mMinimizedPreview.setVisibility(View.GONE);
         v.setVisibility(View.VISIBLE);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java
index 2244813..09b2e44 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.clipboardoverlay.dagger;
 
-import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
 
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
@@ -28,6 +27,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.clipboardoverlay.ClipboardOverlayView;
+import com.android.systemui.settings.DisplayTracker;
 
 import java.lang.annotation.Documented;
 import java.lang.annotation.Retention;
@@ -46,8 +46,9 @@
      */
     @Provides
     @OverlayWindowContext
-    static Context provideWindowContext(DisplayManager displayManager, Context context) {
-        Display display = displayManager.getDisplay(DEFAULT_DISPLAY);
+    static Context provideWindowContext(DisplayManager displayManager,
+            DisplayTracker displayTracker, Context context) {
+        Display display = displayManager.getDisplay(displayTracker.getDefaultDisplayId());
         return context.createWindowContext(display, TYPE_SCREENSHOT, null);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/common/coroutine/CoroutineResult.kt b/packages/SystemUI/src/com/android/systemui/common/coroutine/CoroutineResult.kt
new file mode 100644
index 0000000..b973667
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/coroutine/CoroutineResult.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.coroutine
+
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.TimeoutCancellationException
+import kotlinx.coroutines.currentCoroutineContext
+import kotlinx.coroutines.ensureActive
+import kotlinx.coroutines.withTimeout
+
+/**
+ * Calls the specified function [block] and returns its encapsulated result if invocation was
+ * successful, catching any [Throwable] exception that was thrown from the block function execution
+ * and encapsulating it as a failure.
+ *
+ * Unlike [runCatching], [suspendRunCatching] does not break structured concurrency by rethrowing
+ * any [CancellationException].
+ *
+ * **Heads-up:** [TimeoutCancellationException] extends [CancellationException] but catching it does
+ * not breaks structured concurrency and therefore, will not be rethrown. Therefore, you can use
+ * [suspendRunCatching] with [withTimeout], and handle any timeout gracefully.
+ *
+ * @see <a href="https://github.com/Kotlin/kotlinx.coroutines/issues/1814">link</a>
+ */
+suspend inline fun <T> suspendRunCatching(crossinline block: suspend () -> T): Result<T> =
+    try {
+        Result.success(block())
+    } catch (e: Throwable) {
+        // Ensures the try-catch block will not break structured concurrency.
+        currentCoroutineContext().ensureActive()
+        Result.failure(e)
+    }
+
+/**
+ * Calls the specified function [block] and returns its encapsulated result if invocation was
+ * successful, catching any [Throwable] exception that was thrown from the block function execution
+ * and encapsulating it as a failure.
+ *
+ * Unlike [runCatching], [suspendRunCatching] does not break structured concurrency by rethrowing
+ * any [CancellationException].
+ *
+ * **Heads-up:** [TimeoutCancellationException] extends [CancellationException] but catching it does
+ * not breaks structured concurrency and therefore, will not be rethrown. Therefore, you can use
+ * [suspendRunCatching] with [withTimeout], and handle any timeout gracefully.
+ *
+ * @see <a href="https://github.com/Kotlin/kotlinx.coroutines/issues/1814">link</a>
+ */
+suspend inline fun <T, R> T.suspendRunCatching(crossinline block: suspend T.() -> R): Result<R> =
+    // Overload with a `this` receiver, matches with `kotlin.runCatching` functions.
+    // Qualified name needs to be used to avoid a recursive call.
+    com.android.systemui.common.coroutine.suspendRunCatching { block(this) }
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableImageView.kt b/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableConstraintLayout.kt
similarity index 76%
copy from packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableImageView.kt
copy to packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableConstraintLayout.kt
index 7bbfec7..9763665 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableImageView.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableConstraintLayout.kt
@@ -12,37 +12,37 @@
  * 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.ui.view
 
 import android.content.Context
 import android.util.AttributeSet
-import android.widget.ImageView
+import androidx.constraintlayout.widget.ConstraintLayout
 import com.android.systemui.animation.LaunchableView
 import com.android.systemui.animation.LaunchableViewDelegate
 
-class LaunchableImageView : ImageView, LaunchableView {
+/** A [ConstraintLayout] that also implements [LaunchableView]. */
+open class LaunchableConstraintLayout : ConstraintLayout, LaunchableView {
     private val delegate =
         LaunchableViewDelegate(
             this,
             superSetVisibility = { super.setVisibility(it) },
         )
 
-    constructor(context: Context?) : super(context)
-    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
+    constructor(context: Context) : super(context)
+    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
     constructor(
-        context: Context?,
+        context: Context,
         attrs: AttributeSet?,
-        defStyleAttr: Int,
+        defStyleAttr: Int
     ) : super(context, attrs, defStyleAttr)
 
     constructor(
-        context: Context?,
+        context: Context,
         attrs: AttributeSet?,
         defStyleAttr: Int,
-        defStyleRes: Int,
+        defStyleRes: Int
     ) : super(context, attrs, defStyleAttr, defStyleRes)
 
     override fun setShouldBlockVisibilityChanges(block: Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingView.kt b/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingView.kt
new file mode 100644
index 0000000..2dd98dc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingView.kt
@@ -0,0 +1,123 @@
+/*
+ * 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.ui.view
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.util.AttributeSet
+import android.view.MotionEvent
+import android.view.View
+import kotlin.math.pow
+import kotlin.math.sqrt
+import kotlinx.coroutines.DisposableHandle
+
+/**
+ * View designed to handle long-presses.
+ *
+ * The view will not handle any long pressed by default. To set it up, set up a listener and, when
+ * ready to start consuming long-presses, set [setLongPressHandlingEnabled] to `true`.
+ */
+class LongPressHandlingView(
+    context: Context,
+    attrs: AttributeSet?,
+) :
+    View(
+        context,
+        attrs,
+    ) {
+    interface Listener {
+        /** Notifies that a long-press has been detected by the given view. */
+        fun onLongPressDetected(
+            view: View,
+            x: Int,
+            y: Int,
+        )
+
+        /** Notifies that the gesture was too short for a long press, it is actually a click. */
+        fun onSingleTapDetected(view: View) = Unit
+    }
+
+    var listener: Listener? = null
+
+    private val interactionHandler: LongPressHandlingViewInteractionHandler by lazy {
+        LongPressHandlingViewInteractionHandler(
+            postDelayed = { block, timeoutMs ->
+                val dispatchToken = Any()
+
+                handler.postDelayed(
+                    block,
+                    dispatchToken,
+                    timeoutMs,
+                )
+
+                DisposableHandle { handler.removeCallbacksAndMessages(dispatchToken) }
+            },
+            isAttachedToWindow = ::isAttachedToWindow,
+            onLongPressDetected = { x, y ->
+                listener?.onLongPressDetected(
+                    view = this,
+                    x = x,
+                    y = y,
+                )
+            },
+            onSingleTapDetected = { listener?.onSingleTapDetected(this@LongPressHandlingView) },
+        )
+    }
+
+    fun setLongPressHandlingEnabled(isEnabled: Boolean) {
+        interactionHandler.isLongPressHandlingEnabled = isEnabled
+    }
+
+    @SuppressLint("ClickableViewAccessibility")
+    override fun onTouchEvent(event: MotionEvent?): Boolean {
+        return interactionHandler.onTouchEvent(event?.toModel())
+    }
+}
+
+private fun MotionEvent.toModel(): LongPressHandlingViewInteractionHandler.MotionEventModel {
+    return when (actionMasked) {
+        MotionEvent.ACTION_DOWN ->
+            LongPressHandlingViewInteractionHandler.MotionEventModel.Down(
+                x = x.toInt(),
+                y = y.toInt(),
+            )
+        MotionEvent.ACTION_MOVE ->
+            LongPressHandlingViewInteractionHandler.MotionEventModel.Move(
+                distanceMoved = distanceMoved(),
+            )
+        MotionEvent.ACTION_UP ->
+            LongPressHandlingViewInteractionHandler.MotionEventModel.Up(
+                distanceMoved = distanceMoved(),
+                gestureDuration = gestureDuration(),
+            )
+        MotionEvent.ACTION_CANCEL -> LongPressHandlingViewInteractionHandler.MotionEventModel.Cancel
+        else -> LongPressHandlingViewInteractionHandler.MotionEventModel.Other
+    }
+}
+
+private fun MotionEvent.distanceMoved(): Float {
+    return if (historySize > 0) {
+        sqrt((x - getHistoricalX(0)).pow(2) + (y - getHistoricalY(0)).pow(2))
+    } else {
+        0f
+    }
+}
+
+private fun MotionEvent.gestureDuration(): Long {
+    return eventTime - downTime
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandler.kt
new file mode 100644
index 0000000..c2d4d12
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandler.kt
@@ -0,0 +1,132 @@
+/*
+ * 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.ui.view
+
+import android.view.ViewConfiguration
+import kotlinx.coroutines.DisposableHandle
+
+/** Encapsulates logic to handle complex touch interactions with a [LongPressHandlingView]. */
+class LongPressHandlingViewInteractionHandler(
+    /**
+     * Callback to run the given [Runnable] with the given delay, returning a [DisposableHandle]
+     * allowing the delayed runnable to be canceled before it is run.
+     */
+    private val postDelayed: (block: Runnable, delayMs: Long) -> DisposableHandle,
+    /** Callback to be queried to check if the view is attached to its window. */
+    private val isAttachedToWindow: () -> Boolean,
+    /** Callback reporting the a long-press gesture was detected at the given coordinates. */
+    private val onLongPressDetected: (x: Int, y: Int) -> Unit,
+    /** Callback reporting the a single tap gesture was detected at the given coordinates. */
+    private val onSingleTapDetected: () -> Unit,
+) {
+    sealed class MotionEventModel {
+        object Other : MotionEventModel()
+
+        data class Down(
+            val x: Int,
+            val y: Int,
+        ) : MotionEventModel()
+
+        data class Move(
+            val distanceMoved: Float,
+        ) : MotionEventModel()
+
+        data class Up(
+            val distanceMoved: Float,
+            val gestureDuration: Long,
+        ) : MotionEventModel()
+
+        object Cancel : MotionEventModel()
+    }
+
+    var isLongPressHandlingEnabled: Boolean = false
+    var scheduledLongPressHandle: DisposableHandle? = null
+
+    fun onTouchEvent(event: MotionEventModel?): Boolean {
+        if (!isLongPressHandlingEnabled) {
+            return false
+        }
+
+        return when (event) {
+            is MotionEventModel.Down -> {
+                scheduleLongPress(event.x, event.y)
+                true
+            }
+            is MotionEventModel.Move -> {
+                if (event.distanceMoved > ViewConfiguration.getTouchSlop()) {
+                    cancelScheduledLongPress()
+                }
+                false
+            }
+            is MotionEventModel.Up -> {
+                cancelScheduledLongPress()
+                if (
+                    event.distanceMoved <= ViewConfiguration.getTouchSlop() &&
+                        event.gestureDuration < ViewConfiguration.getLongPressTimeout()
+                ) {
+                    dispatchSingleTap()
+                }
+                false
+            }
+            is MotionEventModel.Cancel -> {
+                cancelScheduledLongPress()
+                false
+            }
+            else -> false
+        }
+    }
+
+    private fun scheduleLongPress(
+        x: Int,
+        y: Int,
+    ) {
+        scheduledLongPressHandle =
+            postDelayed(
+                {
+                    dispatchLongPress(
+                        x = x,
+                        y = y,
+                    )
+                },
+                ViewConfiguration.getLongPressTimeout().toLong(),
+            )
+    }
+
+    private fun dispatchLongPress(
+        x: Int,
+        y: Int,
+    ) {
+        if (!isAttachedToWindow()) {
+            return
+        }
+
+        onLongPressDetected(x, y)
+    }
+
+    private fun cancelScheduledLongPress() {
+        scheduledLongPressHandle?.dispose()
+    }
+
+    private fun dispatchSingleTap() {
+        if (!isAttachedToWindow()) {
+            return
+        }
+
+        onSingleTapDetected()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsView.java b/packages/SystemUI/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsView.java
new file mode 100644
index 0000000..8262539
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsView.java
@@ -0,0 +1,202 @@
+/*
+ * 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.ui.view;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+
+import com.android.systemui.R;
+
+/**
+ * The layout contains a seekbar whose progress could be modified
+ * through the icons on two ends of the seekbar.
+ */
+public class SeekBarWithIconButtonsView extends LinearLayout {
+
+    private static final int DEFAULT_SEEKBAR_MAX = 6;
+    private static final int DEFAULT_SEEKBAR_PROGRESS = 0;
+
+    private ViewGroup mIconStartFrame;
+    private ViewGroup mIconEndFrame;
+    private ImageView mIconStart;
+    private ImageView mIconEnd;
+    private SeekBar mSeekbar;
+
+    private SeekBarChangeListener mSeekBarListener = new SeekBarChangeListener();
+
+    public SeekBarWithIconButtonsView(Context context) {
+        this(context, null);
+    }
+
+    public SeekBarWithIconButtonsView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public SeekBarWithIconButtonsView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public SeekBarWithIconButtonsView(Context context,
+            AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+        LayoutInflater.from(context).inflate(
+                R.layout.seekbar_with_icon_buttons, this, /* attachToRoot= */ true);
+
+        mIconStartFrame = findViewById(R.id.icon_start_frame);
+        mIconEndFrame = findViewById(R.id.icon_end_frame);
+        mIconStart = findViewById(R.id.icon_start);
+        mIconEnd = findViewById(R.id.icon_end);
+        mSeekbar = findViewById(R.id.seekbar);
+
+        if (attrs != null) {
+            TypedArray typedArray = context.obtainStyledAttributes(
+                    attrs,
+                    R.styleable.SeekBarWithIconButtonsView_Layout,
+                    defStyleAttr, defStyleRes
+            );
+            int max = typedArray.getInt(
+                    R.styleable.SeekBarWithIconButtonsView_Layout_max, DEFAULT_SEEKBAR_MAX);
+            int progress = typedArray.getInt(
+                    R.styleable.SeekBarWithIconButtonsView_Layout_progress,
+                    DEFAULT_SEEKBAR_PROGRESS);
+            mSeekbar.setMax(max);
+            setProgress(progress);
+
+            int iconStartFrameContentDescriptionId = typedArray.getResourceId(
+                    R.styleable.SeekBarWithIconButtonsView_Layout_iconStartContentDescription,
+                    /* defValue= */ 0);
+            int iconEndFrameContentDescriptionId = typedArray.getResourceId(
+                    R.styleable.SeekBarWithIconButtonsView_Layout_iconEndContentDescription,
+                    /* defValue= */ 0);
+            if (iconStartFrameContentDescriptionId != 0) {
+                final String contentDescription =
+                        context.getString(iconStartFrameContentDescriptionId);
+                mIconStartFrame.setContentDescription(contentDescription);
+            }
+            if (iconEndFrameContentDescriptionId != 0) {
+                final String contentDescription =
+                        context.getString(iconEndFrameContentDescriptionId);
+                mIconEndFrame.setContentDescription(contentDescription);
+            }
+
+            typedArray.recycle();
+        } else {
+            mSeekbar.setMax(DEFAULT_SEEKBAR_MAX);
+            setProgress(DEFAULT_SEEKBAR_PROGRESS);
+        }
+
+        mSeekbar.setOnSeekBarChangeListener(mSeekBarListener);
+
+        mIconStart.setOnClickListener((view) -> {
+            final int progress = mSeekbar.getProgress();
+            if (progress > 0) {
+                mSeekbar.setProgress(progress - 1);
+                setIconViewAndFrameEnabled(mIconStart, mSeekbar.getProgress() > 0);
+            }
+        });
+
+        mIconEnd.setOnClickListener((view) -> {
+            final int progress = mSeekbar.getProgress();
+            if (progress < mSeekbar.getMax()) {
+                mSeekbar.setProgress(progress + 1);
+                setIconViewAndFrameEnabled(mIconEnd, mSeekbar.getProgress() < mSeekbar.getMax());
+            }
+        });
+    }
+
+    private static void setIconViewAndFrameEnabled(View iconView, boolean enabled) {
+        iconView.setEnabled(enabled);
+        final ViewGroup iconFrame = (ViewGroup) iconView.getParent();
+        iconFrame.setEnabled(enabled);
+    }
+
+    /**
+     * Sets a onSeekbarChangeListener to the seekbar in the layout.
+     * We update the Start Icon and End Icon if needed when the seekbar progress is changed.
+     */
+    public void setOnSeekBarChangeListener(
+            @Nullable SeekBar.OnSeekBarChangeListener onSeekBarChangeListener) {
+        mSeekBarListener.setOnSeekBarChangeListener(onSeekBarChangeListener);
+    }
+
+    /**
+     * Start and End icons might need to be updated when there is a change in seekbar progress.
+     * Icon Start will need to be enabled when the seekbar progress is larger than 0.
+     * Icon End will need to be enabled when the seekbar progress is less than Max.
+     */
+    private void updateIconViewIfNeeded(int progress) {
+        setIconViewAndFrameEnabled(mIconStart, progress > 0);
+        setIconViewAndFrameEnabled(mIconEnd, progress < mSeekbar.getMax());
+    }
+
+    /**
+     * Sets max to the seekbar in the layout.
+     */
+    public void setMax(int max) {
+        mSeekbar.setMax(max);
+    }
+
+    /**
+     * Sets progress to the seekbar in the layout.
+     * If the progress is smaller than or equals to 0, the IconStart will be disabled. If the
+     * progress is larger than or equals to Max, the IconEnd will be disabled. The seekbar progress
+     * will be constrained in {@link SeekBar}.
+     */
+    public void setProgress(int progress) {
+        mSeekbar.setProgress(progress);
+        updateIconViewIfNeeded(progress);
+    }
+
+    private class SeekBarChangeListener implements SeekBar.OnSeekBarChangeListener {
+        private SeekBar.OnSeekBarChangeListener mOnSeekBarChangeListener = null;
+
+        @Override
+        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+            if (mOnSeekBarChangeListener != null) {
+                mOnSeekBarChangeListener.onProgressChanged(seekBar, progress, fromUser);
+            }
+            updateIconViewIfNeeded(progress);
+        }
+
+        @Override
+        public void onStartTrackingTouch(SeekBar seekBar) {
+            if (mOnSeekBarChangeListener != null) {
+                mOnSeekBarChangeListener.onStartTrackingTouch(seekBar);
+            }
+        }
+
+        @Override
+        public void onStopTrackingTouch(SeekBar seekBar) {
+            if (mOnSeekBarChangeListener != null) {
+                mOnSeekBarChangeListener.onStopTrackingTouch(seekBar);
+            }
+        }
+
+        void setOnSeekBarChangeListener(SeekBar.OnSeekBarChangeListener listener) {
+            mOnSeekBarChangeListener = listener;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt
index dbe301d..860149d 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt
@@ -28,12 +28,13 @@
 import android.content.pm.ServiceInfo
 import android.os.UserHandle
 import android.service.controls.ControlsProviderService
+import androidx.annotation.VisibleForTesting
 import androidx.annotation.WorkerThread
 import com.android.settingslib.applications.DefaultAppInfo
 import com.android.systemui.R
 import java.util.Objects
 
-class ControlsServiceInfo(
+open class ControlsServiceInfo(
     private val context: Context,
     val serviceInfo: ServiceInfo
 ) : DefaultAppInfo(
@@ -64,7 +65,7 @@
      * [R.array.config_controlsPreferredPackages] can declare activities for use as a panel.
      */
     var panelActivity: ComponentName? = null
-        private set
+        protected set
 
     private var resolved: Boolean = false
 
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapper.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapper.kt
index 0a6335e..b3c18fb 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapper.kt
@@ -21,8 +21,10 @@
 import android.app.job.JobService
 import android.content.ComponentName
 import android.content.Context
+import android.os.PersistableBundle
 import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.backup.BackupHelper
+import com.android.systemui.settings.UserFileManagerImpl
 import java.io.File
 import java.util.concurrent.Executor
 import java.util.concurrent.TimeUnit
@@ -33,14 +35,14 @@
  * This file is a copy of the `controls_favorites.xml` file restored from a back up. It is used to
  * keep track of controls that were restored but its corresponding app has not been installed yet.
  */
-class AuxiliaryPersistenceWrapper @VisibleForTesting internal constructor(
-    wrapper: ControlsFavoritePersistenceWrapper
-) {
+class AuxiliaryPersistenceWrapper
+@VisibleForTesting
+internal constructor(wrapper: ControlsFavoritePersistenceWrapper) {
 
     constructor(
         file: File,
         executor: Executor
-    ): this(ControlsFavoritePersistenceWrapper(file, executor))
+    ) : this(ControlsFavoritePersistenceWrapper(file, executor))
 
     companion object {
         const val AUXILIARY_FILE_NAME = "aux_controls_favorites.xml"
@@ -48,9 +50,7 @@
 
     private var persistenceWrapper: ControlsFavoritePersistenceWrapper = wrapper
 
-    /**
-     * Access the current list of favorites as tracked by the auxiliary file
-     */
+    /** Access the current list of favorites as tracked by the auxiliary file */
     var favorites: List<StructureInfo> = emptyList()
         private set
 
@@ -73,18 +73,19 @@
      * exist, it will be initialized to an empty list.
      */
     fun initialize() {
-        favorites = if (persistenceWrapper.fileExists) {
-            persistenceWrapper.readFavorites()
-        } else {
-            emptyList()
-        }
+        favorites =
+            if (persistenceWrapper.fileExists) {
+                persistenceWrapper.readFavorites()
+            } else {
+                emptyList()
+            }
     }
 
     /**
      * Gets the list of favorite controls as persisted in the auxiliary file for a given component.
      *
-     * When the favorites for that application are returned, they will be removed from the
-     * auxiliary file immediately, so they won't be retrieved again.
+     * When the favorites for that application are returned, they will be removed from the auxiliary
+     * file immediately, so they won't be retrieved again.
      * @param componentName the name of the service that provided the controls
      * @return a list of structures with favorites
      */
@@ -103,20 +104,20 @@
         }
     }
 
-    /**
-     * [JobService] to delete the auxiliary file after a week.
-     */
+    /** [JobService] to delete the auxiliary file after a week. */
     class DeletionJobService : JobService() {
         companion object {
-            @VisibleForTesting
-            internal val DELETE_FILE_JOB_ID = 1000
+            @VisibleForTesting internal val DELETE_FILE_JOB_ID = 1000
+            @VisibleForTesting internal val USER = "USER"
             private val WEEK_IN_MILLIS = TimeUnit.DAYS.toMillis(7)
-            fun getJobForContext(context: Context): JobInfo {
+            fun getJobForContext(context: Context, targetUserId: Int): JobInfo {
                 val jobId = DELETE_FILE_JOB_ID + context.userId
                 val componentName = ComponentName(context, DeletionJobService::class.java)
+                val bundle = PersistableBundle().also { it.putInt(USER, targetUserId) }
                 return JobInfo.Builder(jobId, componentName)
                     .setMinimumLatency(WEEK_IN_MILLIS)
                     .setPersisted(true)
+                    .setExtras(bundle)
                     .build()
             }
         }
@@ -127,8 +128,14 @@
         }
 
         override fun onStartJob(params: JobParameters): Boolean {
+            val userId = params.getExtras()?.getInt(USER, 0) ?: 0
             synchronized(BackupHelper.controlsDataLock) {
-                baseContext.deleteFile(AUXILIARY_FILE_NAME)
+                val file =
+                    UserFileManagerImpl.createFile(
+                        userId = userId,
+                        fileName = AUXILIARY_FILE_NAME,
+                    )
+                baseContext.deleteFile(file.getPath())
             }
             return false
         }
@@ -137,4 +144,4 @@
             return true // reschedule and try again if the job was stopped without completing
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
index f29f6d0..822190f 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
@@ -188,6 +188,8 @@
     /** See [ControlsUiController.getPreferredSelectedItem]. */
     fun getPreferredSelection(): SelectedItem
 
+    fun setPreferredSelection(selectedItem: SelectedItem)
+
     /**
      * Bind to a service that provides a Device Controls panel (embedded activity). This will allow
      * the app to remain "warm", and reduce latency.
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index 111fcbb..278ee70 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -36,6 +36,7 @@
 import com.android.systemui.controls.ControlStatus
 import com.android.systemui.controls.ControlsServiceInfo
 import com.android.systemui.controls.management.ControlsListingController
+import com.android.systemui.controls.panels.AuthorizedPanelsRepository
 import com.android.systemui.controls.ui.ControlsUiController
 import com.android.systemui.controls.ui.SelectedItem
 import com.android.systemui.dagger.SysUISingleton
@@ -61,6 +62,7 @@
     private val listingController: ControlsListingController,
     private val userFileManager: UserFileManager,
     private val userTracker: UserTracker,
+    private val authorizedPanelsRepository: AuthorizedPanelsRepository,
     optionalWrapper: Optional<ControlsFavoritePersistenceWrapper>,
     dumpManager: DumpManager,
 ) : Dumpable, ControlsController {
@@ -119,16 +121,13 @@
         userChanging = false
     }
 
-    private val userTrackerCallback = object : UserTracker.Callback {
-        override fun onUserChanged(newUser: Int, userContext: Context) {
-            userChanging = true
-            val newUserHandle = UserHandle.of(newUser)
-            if (currentUser == newUserHandle) {
-                userChanging = false
-                return
-            }
-            setValuesForUser(newUserHandle)
+    override fun changeUser(newUser: UserHandle) {
+        userChanging = true
+        if (currentUser == newUser) {
+            userChanging = false
+            return
         }
+        setValuesForUser(newUser)
     }
 
     @VisibleForTesting
@@ -229,7 +228,6 @@
         dumpManager.registerDumpable(javaClass.name, this)
         resetFavorites()
         userChanging = false
-        userTracker.addCallback(userTrackerCallback, executor)
         context.registerReceiver(
             restoreFinishedReceiver,
             IntentFilter(BackupHelper.ACTION_RESTORE_FINISHED),
@@ -241,7 +239,6 @@
     }
 
     fun destroy() {
-        userTracker.removeCallback(userTrackerCallback)
         context.unregisterReceiver(restoreFinishedReceiver)
         listingController.removeCallback(listingCallback)
     }
@@ -249,6 +246,11 @@
     private fun resetFavorites() {
         Favorites.clear()
         Favorites.load(persistenceWrapper.readFavorites())
+        // After loading favorites, add the package names of any apps with favorites to the list
+        // of authorized panels. That way, if the user has previously favorited controls for an app,
+        // that panel will be authorized.
+        authorizedPanelsRepository.addAuthorizedPanels(
+                Favorites.getAllStructures().map { it.componentName.packageName }.toSet())
     }
 
     private fun confirmAvailability(): Boolean {
@@ -489,6 +491,7 @@
         if (!confirmAvailability()) return
         executor.execute {
             if (Favorites.addFavorite(componentName, structureName, controlInfo)) {
+                authorizedPanelsRepository.addAuthorizedPanels(setOf(componentName.packageName))
                 persistenceWrapper.storeFavorites(Favorites.getAllStructures())
             }
         }
@@ -555,6 +558,10 @@
         return uiController.getPreferredSelectedItem(getFavorites())
     }
 
+    override fun setPreferredSelection(selectedItem: SelectedItem) {
+        uiController.updatePreferences(selectedItem)
+    }
+
     override fun dump(pw: PrintWriter, args: Array<out String>) {
         pw.println("ControlsController state:")
         pw.println("  Changing users: $userChanging")
diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt
index 27466d4..7509a8a 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt
@@ -19,11 +19,11 @@
 import android.content.Context
 import com.android.internal.widget.LockPatternUtils
 import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
-import com.android.systemui.controls.settings.ControlsSettingsRepository
 import com.android.systemui.controls.controller.ControlsController
 import com.android.systemui.controls.controller.ControlsTileResourceConfiguration
 import com.android.systemui.controls.controller.ControlsTileResourceConfigurationImpl
 import com.android.systemui.controls.management.ControlsListingController
+import com.android.systemui.controls.settings.ControlsSettingsRepository
 import com.android.systemui.controls.ui.ControlsUiController
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.settings.UserTracker
diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
index 6d6410d..d949d11 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
@@ -34,6 +34,8 @@
 import com.android.systemui.controls.management.ControlsListingControllerImpl
 import com.android.systemui.controls.management.ControlsProviderSelectorActivity
 import com.android.systemui.controls.management.ControlsRequestDialog
+import com.android.systemui.controls.panels.AuthorizedPanelsRepository
+import com.android.systemui.controls.panels.AuthorizedPanelsRepositoryImpl
 import com.android.systemui.controls.settings.ControlsSettingsDialogManager
 import com.android.systemui.controls.settings.ControlsSettingsDialogManagerImpl
 import com.android.systemui.controls.ui.ControlActionCoordinator
@@ -42,12 +44,15 @@
 import com.android.systemui.controls.ui.ControlsUiController
 import com.android.systemui.controls.ui.ControlsUiControllerImpl
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.DeviceControlsTile
 import dagger.Binds
 import dagger.BindsOptionalOf
 import dagger.Module
 import dagger.Provides
 import dagger.multibindings.ClassKey
 import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
 
 /**
  * Module for injecting classes in `com.android.systemui.controls`-
@@ -104,6 +109,11 @@
         coordinator: ControlActionCoordinatorImpl
     ): ControlActionCoordinator
 
+    @Binds
+    abstract fun provideAuthorizedPanelsRepository(
+        repository: AuthorizedPanelsRepositoryImpl
+    ): AuthorizedPanelsRepository
+
     @BindsOptionalOf
     abstract fun optionalPersistenceWrapper(): ControlsFavoritePersistenceWrapper
 
@@ -142,4 +152,9 @@
     @IntoMap
     @ClassKey(ControlsActivity::class)
     abstract fun provideControlsActivity(activity: ControlsActivity): Activity
+
+    @Binds
+    @IntoMap
+    @StringKey(DeviceControlsTile.TILE_SPEC)
+    abstract fun bindDeviceControlsTile(controlsTile: DeviceControlsTile): QSTileImpl<*>
 }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/StartControlsStartableModule.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/StartControlsStartableModule.kt
new file mode 100644
index 0000000..3f20c26
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/StartControlsStartableModule.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.controls.dagger
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.controls.start.ControlsStartable
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+
+@Module
+abstract class StartControlsStartableModule {
+    @Binds
+    @IntoMap
+    @ClassKey(ControlsStartable::class)
+    abstract fun bindFeature(impl: ControlsStartable): CoreStartable
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
index 753d5ad..3fe0f03 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
@@ -45,14 +45,15 @@
  * @param onAppSelected a callback to indicate that an app has been selected in the list.
  */
 class AppAdapter(
-    backgroundExecutor: Executor,
-    uiExecutor: Executor,
-    lifecycle: Lifecycle,
-    controlsListingController: ControlsListingController,
-    private val layoutInflater: LayoutInflater,
-    private val onAppSelected: (ComponentName?) -> Unit = {},
-    private val favoritesRenderer: FavoritesRenderer,
-    private val resources: Resources
+        backgroundExecutor: Executor,
+        uiExecutor: Executor,
+        lifecycle: Lifecycle,
+        controlsListingController: ControlsListingController,
+        private val layoutInflater: LayoutInflater,
+        private val onAppSelected: (ControlsServiceInfo) -> Unit = {},
+        private val favoritesRenderer: FavoritesRenderer,
+        private val resources: Resources,
+        private val authorizedPanels: Set<String> = emptySet(),
 ) : RecyclerView.Adapter<AppAdapter.Holder>() {
 
     private var listOfServices = emptyList<ControlsServiceInfo>()
@@ -64,8 +65,10 @@
                 val localeComparator = compareBy<ControlsServiceInfo, CharSequence>(collator) {
                     it.loadLabel() ?: ""
                 }
-                listOfServices = serviceInfos.filter { it.panelActivity == null }
-                        .sortedWith(localeComparator)
+                // No panel or the panel is not authorized
+                listOfServices = serviceInfos.filter {
+                    it.panelActivity == null || it.panelActivity?.packageName !in authorizedPanels
+                }.sortedWith(localeComparator)
                 uiExecutor.execute(::notifyDataSetChanged)
             }
         }
@@ -86,8 +89,8 @@
 
     override fun onBindViewHolder(holder: Holder, index: Int) {
         holder.bindData(listOfServices[index])
-        holder.itemView.setOnClickListener {
-            onAppSelected(ComponentName.unflattenFromString(listOfServices[index].key))
+        holder.view.setOnClickListener {
+            onAppSelected(listOfServices[index])
         }
     }
 
@@ -95,6 +98,8 @@
      * Holder for binding views in the [RecyclerView]-
      */
     class Holder(view: View, val favRenderer: FavoritesRenderer) : RecyclerView.ViewHolder(view) {
+        val view: View = itemView
+
         private val icon: ImageView = itemView.requireViewById(com.android.internal.R.id.icon)
         private val title: TextView = itemView.requireViewById(com.android.internal.R.id.title)
         private val favorites: TextView = itemView.requireViewById(R.id.favorites)
@@ -106,7 +111,11 @@
         fun bindData(data: ControlsServiceInfo) {
             icon.setImageDrawable(data.loadIcon())
             title.text = data.loadLabel()
-            val text = favRenderer.renderFavoritesForComponent(data.componentName)
+            val text = if (data.panelActivity == null) {
+                favRenderer.renderFavoritesForComponent(data.componentName)
+            } else {
+                null
+            }
             favorites.text = text
             favorites.visibility = if (text == null) View.GONE else View.VISIBLE
         }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
index 90bc5d0..3808e73 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.controls.management
 
 import android.app.ActivityOptions
+import android.app.Dialog
 import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
@@ -31,12 +32,15 @@
 import android.window.OnBackInvokedCallback
 import android.window.OnBackInvokedDispatcher
 import androidx.activity.ComponentActivity
+import androidx.annotation.VisibleForTesting
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
 import com.android.systemui.R
+import com.android.systemui.controls.ControlsServiceInfo
 import com.android.systemui.controls.controller.ControlsController
+import com.android.systemui.controls.panels.AuthorizedPanelsRepository
 import com.android.systemui.controls.ui.ControlsActivity
-import com.android.systemui.controls.ui.ControlsUiController
+import com.android.systemui.controls.ui.SelectedItem
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.settings.UserTracker
@@ -52,7 +56,8 @@
     private val listingController: ControlsListingController,
     private val controlsController: ControlsController,
     private val userTracker: UserTracker,
-    private val uiController: ControlsUiController
+    private val authorizedPanelsRepository: AuthorizedPanelsRepository,
+    private val panelConfirmationDialogFactory: PanelConfirmationDialogFactory
 ) : ComponentActivity() {
 
     companion object {
@@ -72,6 +77,7 @@
             }
         }
     }
+    private var dialog: Dialog? = null
 
     private val mOnBackInvokedCallback = OnBackInvokedCallback {
         if (DEBUG) {
@@ -138,9 +144,11 @@
                 lifecycle,
                 listingController,
                 LayoutInflater.from(this),
-                ::launchFavoritingActivity,
+                ::onAppSelected,
                 FavoritesRenderer(resources, controlsController::countFavoritesForComponent),
-                resources).apply {
+                resources,
+                authorizedPanelsRepository.getAuthorizedPanels()
+        ).apply {
             registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
                 var hasAnimated = false
                 override fun onChanged() {
@@ -167,13 +175,35 @@
             Log.d(TAG, "Unregistered onBackInvokedCallback")
         }
         onBackInvokedDispatcher.unregisterOnBackInvokedCallback(mOnBackInvokedCallback)
+        dialog?.cancel()
+    }
+
+    fun onAppSelected(serviceInfo: ControlsServiceInfo) {
+        dialog?.cancel()
+        if (serviceInfo.panelActivity == null) {
+            launchFavoritingActivity(serviceInfo.componentName)
+        } else {
+            val appName = serviceInfo.loadLabel() ?: ""
+            dialog = panelConfirmationDialogFactory.createConfirmationDialog(this, appName) { ok ->
+                if (ok) {
+                    authorizedPanelsRepository.addAuthorizedPanels(
+                            setOf(serviceInfo.componentName.packageName)
+                    )
+                    val selected = SelectedItem.PanelItem(appName, componentName)
+                    controlsController.setPreferredSelection(selected)
+                    animateExitAndFinish()
+                    openControlsOrigin()
+                }
+                dialog = null
+            }.also { it.show() }
+        }
     }
 
     /**
      * Launch the [ControlsFavoritingActivity] for the specified component.
      * @param component a component name for a [ControlsProviderService]
      */
-    fun launchFavoritingActivity(component: ComponentName?) {
+    private fun launchFavoritingActivity(component: ComponentName?) {
         executor.execute {
             component?.let {
                 val intent = Intent(applicationContext, ControlsFavoritingActivity::class.java)
@@ -194,7 +224,15 @@
         super.onDestroy()
     }
 
-    private fun animateExitAndFinish() {
+    private fun openControlsOrigin() {
+        startActivity(
+                Intent(applicationContext, ControlsActivity::class.java),
+                ActivityOptions.makeSceneTransitionAnimation(this).toBundle()
+        )
+    }
+
+    @VisibleForTesting
+    internal open fun animateExitAndFinish() {
         val rootView = requireViewById<ViewGroup>(R.id.controls_management_root)
         ControlsAnimations.exitAnimation(
                 rootView,
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/PanelConfirmationDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/controls/management/PanelConfirmationDialogFactory.kt
new file mode 100644
index 0000000..6f87aa9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/PanelConfirmationDialogFactory.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.controls.management
+
+import android.app.Dialog
+import android.content.Context
+import android.content.DialogInterface
+import com.android.systemui.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import java.util.function.Consumer
+import javax.inject.Inject
+
+/**
+ * Factory to create dialogs for consenting to show app panels for specific apps.
+ *
+ * [internalDialogFactory] is for facilitating testing.
+ */
+class PanelConfirmationDialogFactory(
+    private val internalDialogFactory: (Context) -> SystemUIDialog
+) {
+    @Inject constructor() : this({ SystemUIDialog(it) })
+
+    /**
+     * Creates a dialog to show to the user. [response] will be true if an only if the user responds
+     * affirmatively.
+     */
+    fun createConfirmationDialog(
+        context: Context,
+        appName: CharSequence,
+        response: Consumer<Boolean>
+    ): Dialog {
+        val listener =
+            DialogInterface.OnClickListener { _, which ->
+                response.accept(which == DialogInterface.BUTTON_POSITIVE)
+            }
+        return internalDialogFactory(context).apply {
+            setTitle(this.context.getString(R.string.controls_panel_authorization_title, appName))
+            setMessage(this.context.getString(R.string.controls_panel_authorization, appName))
+            setCanceledOnTouchOutside(true)
+            setOnCancelListener { response.accept(false) }
+            setPositiveButton(R.string.controls_dialog_ok, listener)
+            setNeutralButton(R.string.cancel, listener)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepository.kt b/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepository.kt
new file mode 100644
index 0000000..3e672f3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepository.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.controls.panels
+
+/**
+ * Repository for keeping track of which packages the panel has authorized to show control panels
+ * (embedded activity).
+ */
+interface AuthorizedPanelsRepository {
+
+    /** A set of package names that the user has previously authorized to show panels. */
+    fun getAuthorizedPanels(): Set<String>
+
+    /** Adds [packageNames] to the set of packages that the user has authorized to show panels. */
+    fun addAuthorizedPanels(packageNames: Set<String>)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImpl.kt
new file mode 100644
index 0000000..f7e43a7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImpl.kt
@@ -0,0 +1,82 @@
+/*
+ * 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.controls.panels
+
+import android.content.Context
+import android.content.SharedPreferences
+import com.android.systemui.R
+import com.android.systemui.settings.UserFileManager
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl
+import javax.inject.Inject
+
+class AuthorizedPanelsRepositoryImpl
+@Inject
+constructor(
+    private val context: Context,
+    private val userFileManager: UserFileManager,
+    private val userTracker: UserTracker
+) : AuthorizedPanelsRepository {
+
+    override fun getAuthorizedPanels(): Set<String> {
+        return getAuthorizedPanelsInternal(instantiateSharedPrefs())
+    }
+
+    override fun addAuthorizedPanels(packageNames: Set<String>) {
+        addAuthorizedPanelsInternal(instantiateSharedPrefs(), packageNames)
+    }
+
+    private fun getAuthorizedPanelsInternal(sharedPreferences: SharedPreferences): Set<String> {
+        return sharedPreferences.getStringSet(KEY, emptySet())!!
+    }
+
+    private fun addAuthorizedPanelsInternal(
+        sharedPreferences: SharedPreferences,
+        packageNames: Set<String>
+    ) {
+        val currentSet = getAuthorizedPanelsInternal(sharedPreferences)
+        sharedPreferences.edit().putStringSet(KEY, currentSet + packageNames).apply()
+    }
+
+    private fun instantiateSharedPrefs(): SharedPreferences {
+        val sharedPref =
+            userFileManager.getSharedPreferences(
+                DeviceControlsControllerImpl.PREFS_CONTROLS_FILE,
+                Context.MODE_PRIVATE,
+                userTracker.userId,
+            )
+
+        // If we've never run this (i.e., the key doesn't exist), add the default packages
+        if (sharedPref.getStringSet(KEY, null) == null) {
+            sharedPref
+                .edit()
+                .putStringSet(
+                    KEY,
+                    context.resources
+                        .getStringArray(R.array.config_controlsPreferredPackages)
+                        .toSet()
+                )
+                .apply()
+        }
+        return sharedPref
+    }
+
+    companion object {
+        private const val KEY = "authorized_panels"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt b/packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt
new file mode 100644
index 0000000..9d99253
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt
@@ -0,0 +1,119 @@
+/*
+ * 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.controls.start
+
+import android.content.Context
+import android.content.res.Resources
+import android.os.UserHandle
+import com.android.systemui.CoreStartable
+import com.android.systemui.R
+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.ui.SelectedItem
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.settings.UserTracker
+import java.util.concurrent.Executor
+import javax.inject.Inject
+
+/**
+ * Started with SystemUI to perform early operations for device controls subsystem (only if enabled)
+ *
+ * In particular, it will perform the following:
+ * * If there is no preferred selection for provider and at least one of the preferred packages 
+ * provides a panel, it will select the first one that does.
+ * * If the preferred selection provides a panel, it will bind to that service (to reduce latency on
+ * displaying the panel).
+ *
+ * It will also perform those operations on user change.
+ */
+@SysUISingleton
+class ControlsStartable
+@Inject
+constructor(
+    @Main private val resources: Resources,
+    @Background private val executor: Executor,
+    private val controlsComponent: ControlsComponent,
+    private val userTracker: UserTracker
+) : CoreStartable {
+
+    // These two controllers can only be accessed after `start` method once we've checked if the
+    // feature is enabled
+    private val controlsController: ControlsController
+        get() = controlsComponent.getControlsController().get()
+
+    private val controlsListingController: ControlsListingController
+        get() = controlsComponent.getControlsListingController().get()
+
+    private val userTrackerCallback =
+        object : UserTracker.Callback {
+            override fun onUserChanged(newUser: Int, userContext: Context) {
+                controlsController.changeUser(UserHandle.of(newUser))
+                startForUser()
+            }
+        }
+
+    override fun start() {
+        if (!controlsComponent.isEnabled()) {
+            // Controls is disabled, we don't need this anymore
+            return
+        }
+        startForUser()
+        userTracker.addCallback(userTrackerCallback, executor)
+    }
+
+    private fun startForUser() {
+        selectDefaultPanelIfNecessary()
+        bindToPanel()
+    }
+
+    private fun selectDefaultPanelIfNecessary() {
+        val currentSelection = controlsController.getPreferredSelection()
+        if (currentSelection == SelectedItem.EMPTY_SELECTION) {
+            val availableServices = controlsListingController.getCurrentServices()
+            val panels = availableServices.filter { it.panelActivity != null }
+            resources
+                .getStringArray(R.array.config_controlsPreferredPackages)
+                // Looking for the first element in the string array such that there is one package
+                // that has a panel. It will return null if there are no packages in the array,
+                // or if no packages in the array have a panel associated with it.
+                .firstNotNullOfOrNull { name ->
+                    panels.firstOrNull { it.componentName.packageName == name }
+                }
+                ?.let { info ->
+                    controlsController.setPreferredSelection(
+                        SelectedItem.PanelItem(info.loadLabel(), info.componentName)
+                    )
+                }
+        }
+    }
+
+    private fun bindToPanel() {
+        val currentSelection = controlsController.getPreferredSelection()
+        val panels =
+            controlsListingController.getCurrentServices().filter { it.panelActivity != null }
+        if (
+            currentSelection is SelectedItem.PanelItem &&
+                panels.firstOrNull { it.componentName == currentSelection.componentName } != null
+        ) {
+            controlsController.bindComponentForPanel(currentSelection.componentName)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
index d8d8c0e..3a3f9b4 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
@@ -134,7 +134,8 @@
         super.onStop()
         mExitToDream = false
 
-        uiController.hide()
+        // parent is set in onStart, so the field is initialized when we get here
+        uiController.hide(parent)
         controlsSettingsDialogManager.closeDialog()
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
index f5c5905..58673bb 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
@@ -31,7 +31,13 @@
     }
 
     fun show(parent: ViewGroup, onDismiss: Runnable, activityContext: Context)
-    fun hide()
+
+    /**
+     * Hide the controls content if it's attached to this parent.
+     */
+    fun hide(parent: ViewGroup)
+
+    val isShowing: Boolean
 
     /**
      * Returns the preferred activity to start, depending on if the user has favorited any
@@ -58,6 +64,8 @@
      * This element will be the one that appears when the user first opens the controls activity.
      */
     fun getPreferredSelectedItem(structures: List<StructureInfo>): SelectedItem
+
+    fun updatePreferences(selectedItem: SelectedItem)
 }
 
 sealed class SelectedItem {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 6289788..9405c60 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -25,8 +25,10 @@
 import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
+import android.content.pm.PackageManager
 import android.graphics.drawable.Drawable
 import android.graphics.drawable.LayerDrawable
+import android.os.Trace
 import android.service.controls.Control
 import android.service.controls.ControlsProviderService
 import android.util.Log
@@ -38,6 +40,7 @@
 import android.view.animation.DecelerateInterpolator
 import android.widget.AdapterView
 import android.widget.ArrayAdapter
+import android.widget.BaseAdapter
 import android.widget.FrameLayout
 import android.widget.ImageView
 import android.widget.LinearLayout
@@ -60,10 +63,13 @@
 import com.android.systemui.controls.management.ControlsFavoritingActivity
 import com.android.systemui.controls.management.ControlsListingController
 import com.android.systemui.controls.management.ControlsProviderSelectorActivity
+import com.android.systemui.controls.panels.AuthorizedPanelsRepository
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
 import com.android.systemui.globalactions.GlobalActionsPopupMenu
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.settings.UserFileManager
@@ -87,6 +93,7 @@
 class ControlsUiControllerImpl @Inject constructor (
         val controlsController: Lazy<ControlsController>,
         val context: Context,
+        private val packageManager: PackageManager,
         @Main val uiExecutor: DelayableExecutor,
         @Background val bgExecutor: DelayableExecutor,
         val controlsListingController: Lazy<ControlsListingController>,
@@ -99,6 +106,8 @@
         private val userTracker: UserTracker,
         private val taskViewFactory: Optional<TaskViewFactory>,
         private val controlsSettingsRepository: ControlsSettingsRepository,
+        private val authorizedPanelsRepository: AuthorizedPanelsRepository,
+        private val featureFlags: FeatureFlags,
         dumpManager: DumpManager
 ) : ControlsUiController, Dumpable {
 
@@ -108,6 +117,11 @@
         private const val PREF_IS_PANEL = "controls_is_panel"
 
         private const val FADE_IN_MILLIS = 200L
+
+        private const val OPEN_APP_ID = 0L
+        private const val ADD_CONTROLS_ID = 1L
+        private const val ADD_APP_ID = 2L
+        private const val EDIT_CONTROLS_ID = 3L
     }
 
     private var selectedItem: SelectedItem = SelectedItem.EMPTY_SELECTION
@@ -135,6 +149,9 @@
         it.getTitle()
     }
 
+    private var openAppIntent: Intent? = null
+    private var overflowMenuAdapter: BaseAdapter? = null
+
     private val onSeedingComplete = Consumer<Boolean> {
         accepted ->
             if (accepted) {
@@ -151,6 +168,9 @@
     private lateinit var activityContext: Context
     private lateinit var listingCallback: ControlsListingController.ControlsListingCallback
 
+    override val isShowing: Boolean
+        get() = !hidden
+
     init {
         dumpManager.registerDumpable(javaClass.name, this)
     }
@@ -160,6 +180,7 @@
     ): ControlsListingController.ControlsListingCallback {
         return object : ControlsListingController.ControlsListingCallback {
             override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {
+                val authorizedPanels = authorizedPanelsRepository.getAuthorizedPanels()
                 val lastItems = serviceInfos.map {
                     val uid = it.serviceInfo.applicationInfo.uid
 
@@ -169,7 +190,11 @@
                             it.loadIcon(),
                             it.componentName,
                             uid,
-                            it.panelActivity
+                            if (it.componentName.packageName in authorizedPanels) {
+                                it.panelActivity
+                            } else {
+                                null
+                            }
                     )
                 }
                 uiExecutor.execute {
@@ -203,9 +228,12 @@
         activityContext: Context
     ) {
         Log.d(ControlsUiController.TAG, "show()")
+        Trace.instant(Trace.TRACE_TAG_APP, "ControlsUiControllerImpl#show")
         this.parent = parent
         this.onDismiss = onDismiss
         this.activityContext = activityContext
+        this.openAppIntent = null
+        this.overflowMenuAdapter = null
         hidden = false
         retainCache = false
 
@@ -296,6 +324,12 @@
         startTargetedActivity(si, ControlsEditingActivity::class.java)
     }
 
+    private fun startDefaultActivity() {
+        openAppIntent?.let {
+            startActivity(it, animateExtra = false)
+        }
+    }
+
     private fun startTargetedActivity(si: StructureInfo, klazz: Class<*>) {
         val i = Intent(activityContext, klazz)
         putIntentExtras(i, si)
@@ -319,9 +353,11 @@
         startActivity(i)
     }
 
-    private fun startActivity(intent: Intent) {
+    private fun startActivity(intent: Intent, animateExtra: Boolean = true) {
         // Force animations when transitioning from a dialog to an activity
-        intent.putExtra(ControlsUiController.EXTRA_ANIMATE, true)
+        if (animateExtra) {
+            intent.putExtra(ControlsUiController.EXTRA_ANIMATE, true)
+        }
 
         if (keyguardStateController.isShowing()) {
             activityStarter.postStartActivityDismissingKeyguard(intent, 0 /* delay */)
@@ -373,8 +409,31 @@
             Log.w(ControlsUiController.TAG, "Not TaskViewFactory to display panel $selectionItem")
         }
 
+        bgExecutor.execute {
+            val intent = Intent(Intent.ACTION_MAIN)
+                    .addCategory(Intent.CATEGORY_LAUNCHER)
+                    .setPackage(selectionItem.componentName.packageName)
+                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or
+                            Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
+            val intents = packageManager
+                    .queryIntentActivities(intent, PackageManager.ResolveInfoFlags.of(0L))
+            intents.firstOrNull { it.activityInfo.exported }?.let { resolved ->
+                intent.setPackage(null)
+                intent.setComponent(resolved.activityInfo.componentName)
+                openAppIntent = intent
+                parent.post {
+                    // This will call show on the PopupWindow in the same thread, so make sure this
+                    // happens in the view thread.
+                    overflowMenuAdapter?.notifyDataSetChanged()
+                }
+            }
+        }
         createDropDown(panelsAndStructures, selectionItem)
-        createMenu()
+
+        val currentApps = panelsAndStructures.map { it.componentName }.toSet()
+        val allApps = controlsListingController.get()
+                .getCurrentServices().map { it.componentName }.toSet()
+        createMenu(extraApps = (allApps - currentApps).isNotEmpty())
     }
 
     private fun createPanelView(componentName: ComponentName) {
@@ -413,22 +472,41 @@
         }
     }
 
-    private fun createMenu() {
+    private fun createMenu(extraApps: Boolean) {
         val isPanel = selectedItem is SelectedItem.PanelItem
         val selectedStructure = (selectedItem as? SelectedItem.StructureItem)?.structure
                 ?: EMPTY_STRUCTURE
+        val newFlows = featureFlags.isEnabled(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS)
 
-        val items = if (isPanel) {
-            arrayOf(
-                    context.resources.getString(R.string.controls_menu_add),
-            )
-        } else {
-            arrayOf(
-                    context.resources.getString(R.string.controls_menu_add),
-                    context.resources.getString(R.string.controls_menu_edit)
-            )
+        val items = buildList {
+            add(OverflowMenuAdapter.MenuItem(
+                    context.getText(R.string.controls_open_app),
+                    OPEN_APP_ID
+            ))
+            if (newFlows || isPanel) {
+                if (extraApps) {
+                    add(OverflowMenuAdapter.MenuItem(
+                            context.getText(R.string.controls_menu_add_another_app),
+                            ADD_APP_ID
+                    ))
+                }
+            } else {
+                add(OverflowMenuAdapter.MenuItem(
+                        context.getText(R.string.controls_menu_add),
+                        ADD_CONTROLS_ID
+                ))
+            }
+            if (!isPanel) {
+                add(OverflowMenuAdapter.MenuItem(
+                        context.getText(R.string.controls_menu_edit),
+                        EDIT_CONTROLS_ID
+                ))
+            }
         }
-        var adapter = ArrayAdapter<String>(context, R.layout.controls_more_item, items)
+
+        val adapter = OverflowMenuAdapter(context, R.layout.controls_more_item, items) { position ->
+                getItemId(position) != OPEN_APP_ID || openAppIntent != null
+        }
 
         val anchor = parent.requireViewById<ImageView>(R.id.controls_more)
         anchor.setOnClickListener(object : View.OnClickListener {
@@ -446,25 +524,21 @@
                             pos: Int,
                             id: Long
                         ) {
-                            when (pos) {
-                                // 0: Add Control
-                                0 -> {
-                                    if (isPanel) {
-                                        startProviderSelectorActivity()
-                                    } else {
-                                        startFavoritingActivity(selectedStructure)
-                                    }
-                                }
-                                // 1: Edit controls
-                                1 -> startEditingActivity(selectedStructure)
+                            when (id) {
+                                OPEN_APP_ID -> startDefaultActivity()
+                                ADD_APP_ID -> startProviderSelectorActivity()
+                                ADD_CONTROLS_ID -> startFavoritingActivity(selectedStructure)
+                                EDIT_CONTROLS_ID -> startEditingActivity(selectedStructure)
                             }
                             dismiss()
                         }
                     })
                     show()
+                    listView?.post { listView?.requestAccessibilityFocus() }
                 }
             }
         })
+        overflowMenuAdapter = adapter
     }
 
     private fun createDropDown(items: List<SelectionItem>, selected: SelectionItem) {
@@ -526,6 +600,7 @@
                         }
                     })
                     show()
+                    listView?.post { listView?.requestAccessibilityFocus() }
                 }
             }
         })
@@ -610,12 +685,12 @@
         }
     }
 
-    private fun updatePreferences(si: SelectedItem) {
+    override fun updatePreferences(selectedItem: SelectedItem) {
         sharedPreferences.edit()
-                .putString(PREF_COMPONENT, si.componentName.flattenToString())
-                .putString(PREF_STRUCTURE_OR_APP_NAME, si.name.toString())
-                .putBoolean(PREF_IS_PANEL, si is SelectedItem.PanelItem)
-                .commit()
+                .putString(PREF_COMPONENT, selectedItem.componentName.flattenToString())
+                .putString(PREF_STRUCTURE_OR_APP_NAME, selectedItem.name.toString())
+                .putBoolean(PREF_IS_PANEL, selectedItem is SelectedItem.PanelItem)
+                .apply()
     }
 
     private fun maybeUpdateSelectedItem(item: SelectionItem): Boolean {
@@ -655,21 +730,27 @@
         controlActionCoordinator.closeDialogs()
     }
 
-    override fun hide() {
-        hidden = true
+    override fun hide(parent: ViewGroup) {
+        // We need to check for the parent because it's possible that  we have started showing in a
+        // different activity. In that case, make sure to only clear things associated with the
+        // passed parent
+        if (parent == this.parent) {
+            Log.d(ControlsUiController.TAG, "hide()")
+            hidden = true
 
-        closeDialogs(true)
-        controlsController.get().unsubscribe()
-        taskViewController?.dismiss()
-        taskViewController = null
+            closeDialogs(true)
+            controlsController.get().unsubscribe()
+            taskViewController?.dismiss()
+            taskViewController = null
 
+            controlsById.clear()
+            controlViewsById.clear()
+
+            controlsListingController.get().removeCallback(listingCallback)
+
+            if (!retainCache) RenderInfo.clearCache()
+        }
         parent.removeAllViews()
-        controlsById.clear()
-        controlViewsById.clear()
-
-        controlsListingController.get().removeCallback(listingCallback)
-
-        if (!retainCache) RenderInfo.clearCache()
     }
 
     override fun onRefreshState(componentName: ComponentName, controls: List<Control>) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/OverflowMenuAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/OverflowMenuAdapter.kt
new file mode 100644
index 0000000..6b84e36
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/OverflowMenuAdapter.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.controls.ui
+
+import android.content.Context
+import android.widget.ArrayAdapter
+import androidx.annotation.LayoutRes
+
+open class OverflowMenuAdapter(
+    context: Context,
+    @LayoutRes layoutId: Int,
+    itemsWithIds: List<MenuItem>,
+    private val isEnabledInternal: OverflowMenuAdapter.(Int) -> Boolean
+) : ArrayAdapter<CharSequence>(context, layoutId, itemsWithIds.map(MenuItem::text)) {
+
+    private val ids = itemsWithIds.map(MenuItem::id)
+
+    override fun getItemId(position: Int): Long {
+        return ids[position]
+    }
+
+    override fun isEnabled(position: Int): Boolean {
+        return isEnabledInternal(position)
+    }
+
+    data class MenuItem(val text: CharSequence, val id: Long)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt
index f5764c2..3b6ab20 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt
@@ -27,6 +27,7 @@
 import android.graphics.Color
 import android.graphics.drawable.ShapeDrawable
 import android.graphics.drawable.shapes.RoundRectShape
+import android.os.Trace
 import com.android.systemui.R
 import com.android.systemui.util.boundsOnScreen
 import com.android.wm.shell.TaskView
@@ -84,6 +85,7 @@
                         options,
                         taskView.boundsOnScreen
                     )
+                    Trace.instant(Trace.TRACE_TAG_APP, "PanelTaskViewController - startActivity")
                 }
             }
 
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 4bb5d04..d1c34a8 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -31,6 +31,7 @@
 import android.app.UiModeManager;
 import android.app.WallpaperManager;
 import android.app.admin.DevicePolicyManager;
+import android.app.ambientcontext.AmbientContextManager;
 import android.app.job.JobScheduler;
 import android.app.role.RoleManager;
 import android.app.smartspace.SmartspaceManager;
@@ -79,6 +80,7 @@
 import android.safetycenter.SafetyCenterManager;
 import android.service.dreams.DreamService;
 import android.service.dreams.IDreamManager;
+import android.service.vr.IVrManager;
 import android.telecom.TelecomManager;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
@@ -146,6 +148,13 @@
         return Optional.ofNullable(context.getSystemService(SystemUpdateManager.class));
     }
 
+    @Provides
+    @Nullable
+    @Singleton
+    static AmbientContextManager provideAmbientContextManager(Context context) {
+        return context.getSystemService(AmbientContextManager.class);
+    }
+
     /** */
     @Provides
     public AmbientDisplayConfiguration provideAmbientDisplayConfiguration(Context context) {
@@ -261,6 +270,13 @@
     @Provides
     @Singleton
     @Nullable
+    static IVrManager provideIVrManager() {
+        return IVrManager.Stub.asInterface(ServiceManager.getService(Context.VR_SERVICE));
+    }
+
+    @Provides
+    @Singleton
+    @Nullable
     static FaceManager provideFaceManager(Context context) {
         if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)) {
             return context.getSystemService(FaceManager.class);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index fd690df..03a1dc0 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -27,6 +27,7 @@
 
 import com.android.internal.logging.UiEventLogger;
 import com.android.keyguard.KeyguardViewController;
+import com.android.systemui.battery.BatterySaverModule;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.dock.DockManagerImpl;
@@ -40,6 +41,7 @@
 import com.android.systemui.qs.tileimpl.QSFactoryImpl;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsImplementation;
+import com.android.systemui.rotationlock.RotationLockModule;
 import com.android.systemui.screenshot.ReferenceScreenshotModule;
 import com.android.systemui.shade.NotificationShadeWindowControllerImpl;
 import com.android.systemui.shade.ShadeController;
@@ -92,11 +94,13 @@
  */
 @Module(includes = {
         AospPolicyModule.class,
+        BatterySaverModule.class,
         GestureModule.class,
         MediaModule.class,
         PowerModule.class,
         QSModule.class,
         ReferenceScreenshotModule.class,
+        RotationLockModule.class,
         StartCentralSurfacesModule.class,
         VolumeModule.class
 })
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 68f4dbe..625a028 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -35,6 +35,8 @@
 import com.android.systemui.unfold.FoldStateLoggingProvider;
 import com.android.systemui.unfold.SysUIUnfoldComponent;
 import com.android.systemui.unfold.UnfoldLatencyTracker;
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
+import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder;
 import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
 import com.android.wm.shell.TaskViewFactory;
 import com.android.wm.shell.back.BackAnimation;
@@ -137,6 +139,10 @@
         getUnfoldLatencyTracker().init();
         getFoldStateLoggingProvider().ifPresent(FoldStateLoggingProvider::init);
         getFoldStateLogger().ifPresent(FoldStateLogger::init);
+        getUnfoldTransitionProgressProvider().ifPresent((progressProvider) ->
+                getUnfoldTransitionProgressForwarder().ifPresent((forwarder) ->
+                        progressProvider.addCallback(forwarder)
+                ));
     }
 
     /**
@@ -164,6 +170,18 @@
     UnfoldLatencyTracker getUnfoldLatencyTracker();
 
     /**
+     * Creates a UnfoldTransitionProgressProvider.
+     */
+    @SysUISingleton
+    Optional<UnfoldTransitionProgressProvider> getUnfoldTransitionProgressProvider();
+
+    /**
+     * Creates a UnfoldTransitionProgressForwarder.
+     */
+    @SysUISingleton
+    Optional<UnfoldTransitionProgressForwarder> getUnfoldTransitionProgressForwarder();
+
+    /**
      * Creates a FoldStateLoggingProvider.
      */
     @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemPropertiesFlagsModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemPropertiesFlagsModule.kt
new file mode 100644
index 0000000..c6f833b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemPropertiesFlagsModule.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.dagger
+
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags
+import dagger.Module
+import dagger.Provides
+
+/** A module which provides access to the default [SystemUiSystemPropertiesFlags.FlagResolver] */
+@Module
+object SystemPropertiesFlagsModule {
+    /** provide the default FlagResolver. */
+    @Provides
+    fun provideFlagResolver(): SystemUiSystemPropertiesFlags.FlagResolver =
+        SystemUiSystemPropertiesFlags.getResolver()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index 8e9992f..947888b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -26,10 +26,14 @@
 import com.android.systemui.accessibility.WindowMagnification
 import com.android.systemui.biometrics.AuthController
 import com.android.systemui.clipboardoverlay.ClipboardListener
+import com.android.systemui.controls.dagger.StartControlsStartableModule
 import com.android.systemui.dagger.qualifiers.PerUser
+import com.android.systemui.dreams.DreamMonitor
 import com.android.systemui.globalactions.GlobalActionsComponent
+import com.android.systemui.keyboard.PhysicalKeyboardCoreStartable
 import com.android.systemui.keyboard.KeyboardUI
 import com.android.systemui.keyguard.KeyguardViewMediator
+import com.android.systemui.keyguard.data.quickaffordance.MuteQuickAffordanceCoreStartable
 import com.android.systemui.log.SessionTracker
 import com.android.systemui.media.dialog.MediaOutputSwitcherDialogUI
 import com.android.systemui.media.RingtonePlayer
@@ -52,7 +56,6 @@
 import com.android.systemui.toast.ToastUI
 import com.android.systemui.usb.StorageNotification
 import com.android.systemui.util.NotificationChannels
-import com.android.systemui.util.leak.GarbageMonitor
 import com.android.systemui.volume.VolumeUI
 import com.android.systemui.wmshell.WMShell
 import dagger.Binds
@@ -63,7 +66,10 @@
 /**
  * Collection of {@link CoreStartable}s that should be run on AOSP.
  */
-@Module(includes = [MultiUserUtilsModule::class])
+@Module(includes = [
+    MultiUserUtilsModule::class,
+    StartControlsStartableModule::class
+])
 abstract class SystemUICoreStartableModule {
     /** Inject into AuthController.  */
     @Binds
@@ -101,12 +107,6 @@
     @ClassKey(FsiChromeViewBinder::class)
     abstract fun bindFsiChromeWindowBinder(sysui: FsiChromeViewBinder): CoreStartable
 
-    /** Inject into GarbageMonitor.Service.  */
-    @Binds
-    @IntoMap
-    @ClassKey(GarbageMonitor::class)
-    abstract fun bindGarbageMonitorService(sysui: GarbageMonitor.Service): CoreStartable
-
     /** Inject into GlobalActionsComponent.  */
     @Binds
     @IntoMap
@@ -286,4 +286,23 @@
     @IntoMap
     @ClassKey(StylusUsiPowerStartable::class)
     abstract fun bindStylusUsiPowerStartable(sysui: StylusUsiPowerStartable): CoreStartable
+
+    @Binds
+    @IntoMap
+    @ClassKey(PhysicalKeyboardCoreStartable::class)
+    abstract fun bindKeyboardCoreStartable(listener: PhysicalKeyboardCoreStartable): CoreStartable
+
+    /** Inject into MuteQuickAffordanceCoreStartable*/
+    @Binds
+    @IntoMap
+    @ClassKey(MuteQuickAffordanceCoreStartable::class)
+    abstract fun bindMuteQuickAffordanceCoreStartable(
+            sysui: MuteQuickAffordanceCoreStartable
+    ): CoreStartable
+
+    /**Inject into DreamMonitor */
+    @Binds
+    @IntoMap
+    @ClassKey(DreamMonitor::class)
+    abstract fun bindDreamMonitor(sysui: DreamMonitor): CoreStartable
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 2d0dfa1..5b4ce06 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -28,6 +28,7 @@
 import com.android.keyguard.dagger.KeyguardBouncerComponent;
 import com.android.systemui.BootCompleteCache;
 import com.android.systemui.BootCompleteCacheImpl;
+import com.android.systemui.accessibility.AccessibilityModule;
 import com.android.systemui.appops.dagger.AppOpsModule;
 import com.android.systemui.assist.AssistModule;
 import com.android.systemui.biometrics.AlternateUdfpsTouchProvider;
@@ -46,6 +47,7 @@
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.FlagsModule;
 import com.android.systemui.fragments.FragmentService;
+import com.android.systemui.keyboard.KeyboardModule;
 import com.android.systemui.keyguard.data.BouncerViewModule;
 import com.android.systemui.log.dagger.LogModule;
 import com.android.systemui.mediaprojection.appselector.MediaProjectionModule;
@@ -57,18 +59,22 @@
 import com.android.systemui.plugins.BcSmartspaceConfigPlugin;
 import com.android.systemui.plugins.BcSmartspaceDataPlugin;
 import com.android.systemui.privacy.PrivacyModule;
+import com.android.systemui.qrcodescanner.dagger.QRCodeScannerModule;
 import com.android.systemui.qs.FgsManagerController;
 import com.android.systemui.qs.FgsManagerControllerImpl;
 import com.android.systemui.qs.footer.dagger.FooterActionsModule;
 import com.android.systemui.recents.Recents;
+import com.android.systemui.screenrecord.ScreenRecordModule;
 import com.android.systemui.screenshot.dagger.ScreenshotModule;
 import com.android.systemui.security.data.repository.SecurityRepositoryModule;
+import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.settings.dagger.MultiUserUtilsModule;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.smartspace.dagger.SmartspaceModule;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.connectivity.ConnectivityModule;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
@@ -84,6 +90,7 @@
 import com.android.systemui.statusbar.pipeline.dagger.StatusBarPipelineModule;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.PolicyModule;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.statusbar.policy.dagger.SmartRepliesInflationModule;
 import com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule;
@@ -96,6 +103,7 @@
 import com.android.systemui.util.concurrency.SysUIConcurrencyModule;
 import com.android.systemui.util.dagger.UtilModule;
 import com.android.systemui.util.kotlin.CoroutinesModule;
+import com.android.systemui.util.leak.GarbageMonitorModule;
 import com.android.systemui.util.sensors.SensorModule;
 import com.android.systemui.util.settings.SettingsUtilModule;
 import com.android.systemui.util.time.SystemClock;
@@ -107,6 +115,8 @@
 import java.util.Optional;
 import java.util.concurrent.Executor;
 
+import javax.inject.Named;
+
 import dagger.Binds;
 import dagger.BindsOptionalOf;
 import dagger.Module;
@@ -123,6 +133,7 @@
  * may not appreciate that.
  */
 @Module(includes = {
+            AccessibilityModule.class,
             AppOpsModule.class,
             AssistModule.class,
             BiometricsModule.class,
@@ -130,24 +141,31 @@
             ClipboardOverlayModule.class,
             ClockInfoModule.class,
             ClockRegistryModule.class,
+            ConnectivityModule.class,
             CoroutinesModule.class,
             DreamModule.class,
             ControlsModule.class,
             DemoModeModule.class,
             FalsingModule.class,
             FlagsModule.class,
+            SystemPropertiesFlagsModule.class,
             FooterActionsModule.class,
+            GarbageMonitorModule.class,
+            KeyboardModule.class,
             LogModule.class,
             MediaProjectionModule.class,
             MotionToolModule.class,
             PeopleHubModule.class,
             PeopleModule.class,
             PluginModule.class,
+            PolicyModule.class,
             PrivacyModule.class,
+            QRCodeScannerModule.class,
             ScreenshotModule.class,
             SensorModule.class,
             MultiUserUtilsModule.class,
             SecurityRepositoryModule.class,
+            ScreenRecordModule.class,
             SettingsUtilModule.class,
             SmartRepliesInflationModule.class,
             SmartspaceModule.class,
@@ -196,8 +214,8 @@
 
     @SysUISingleton
     @Provides
-    static SysUiState provideSysUiState(DumpManager dumpManager) {
-        final SysUiState state = new SysUiState();
+    static SysUiState provideSysUiState(DisplayTracker displayTracker, DumpManager dumpManager) {
+        final SysUiState state = new SysUiState(displayTracker);
         dumpManager.registerDumpable(state);
         return state;
     }
@@ -215,6 +233,14 @@
     abstract BcSmartspaceConfigPlugin optionalBcSmartspaceConfigPlugin();
 
     @BindsOptionalOf
+    @Named(SmartspaceModule.DATE_SMARTSPACE_DATA_PLUGIN)
+    abstract BcSmartspaceDataPlugin optionalDateSmartspaceConfigPlugin();
+
+    @BindsOptionalOf
+    @Named(SmartspaceModule.WEATHER_SMARTSPACE_DATA_PLUGIN)
+    abstract BcSmartspaceDataPlugin optionalWeatherSmartspaceConfigPlugin();
+
+    @BindsOptionalOf
     abstract Recents optionalRecents();
 
     @BindsOptionalOf
diff --git a/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt b/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt
index 976afd4..88c0c50 100644
--- a/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt
@@ -34,6 +34,7 @@
 import com.android.systemui.biometrics.AuthController
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.log.ScreenDecorationsLogger
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import java.util.concurrent.Executor
 import javax.inject.Inject
@@ -45,6 +46,7 @@
     private val statusBarStateController: StatusBarStateController,
     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
     @Main private val mainExecutor: Executor,
+    private val logger: ScreenDecorationsLogger,
 ) : DecorProviderFactory() {
     private val display = context.display
     private val displayInfo = DisplayInfo()
@@ -82,7 +84,8 @@
                                         authController,
                                         statusBarStateController,
                                         keyguardUpdateMonitor,
-                                        mainExecutor
+                                        mainExecutor,
+                                        logger,
                                 )
                         )
                     }
@@ -104,7 +107,8 @@
     private val authController: AuthController,
     private val statusBarStateController: StatusBarStateController,
     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
-    private val mainExecutor: Executor
+    private val mainExecutor: Executor,
+    private val logger: ScreenDecorationsLogger,
 ) : BoundDecorProvider() {
     override val viewId: Int = com.android.systemui.R.id.face_scanning_anim
 
@@ -136,7 +140,8 @@
                 alignedBound,
                 statusBarStateController,
                 keyguardUpdateMonitor,
-                mainExecutor
+                mainExecutor,
+                logger,
         )
         view.id = viewId
         view.setColor(tintColor)
@@ -155,8 +160,9 @@
         layoutParams.let { lp ->
             lp.width = ViewGroup.LayoutParams.MATCH_PARENT
             lp.height = ViewGroup.LayoutParams.MATCH_PARENT
+            logger.faceSensorLocation(authController.faceSensorLocation)
             authController.faceSensorLocation?.y?.let { faceAuthSensorHeight ->
-                val faceScanningHeight = (faceAuthSensorHeight * 2).toInt()
+                val faceScanningHeight = (faceAuthSensorHeight * 2)
                 when (rotation) {
                     Surface.ROTATION_0, Surface.ROTATION_180 ->
                         lp.height = faceScanningHeight
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamMonitor.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamMonitor.java
new file mode 100644
index 0000000..055cd52
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamMonitor.java
@@ -0,0 +1,65 @@
+/*
+ * 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.dreams;
+
+import static com.android.systemui.dreams.dagger.DreamModule.DREAM_PRETEXT_MONITOR;
+
+import android.util.Log;
+
+import com.android.systemui.CoreStartable;
+import com.android.systemui.dreams.callbacks.DreamStatusBarStateCallback;
+import com.android.systemui.dreams.conditions.DreamCondition;
+import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.util.condition.ConditionalCoreStartable;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * A {@link CoreStartable} to retain a monitor for tracking dreaming.
+ */
+public class DreamMonitor extends ConditionalCoreStartable {
+    private static final String TAG = "DreamMonitor";
+
+    // We retain a reference to the monitor so it is not garbage-collected.
+    private final Monitor mConditionMonitor;
+    private final DreamCondition mDreamCondition;
+    private final DreamStatusBarStateCallback mCallback;
+
+
+    @Inject
+    public DreamMonitor(Monitor monitor, DreamCondition dreamCondition,
+            @Named(DREAM_PRETEXT_MONITOR) Monitor pretextMonitor,
+            DreamStatusBarStateCallback callback) {
+        super(pretextMonitor);
+        mConditionMonitor = monitor;
+        mDreamCondition = dreamCondition;
+        mCallback = callback;
+
+    }
+
+    @Override
+    protected void onStart() {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "started");
+        }
+
+        mConditionMonitor.addSubscription(new Monitor.Subscription.Builder(mCallback)
+                .addCondition(mDreamCondition)
+                .build());
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
index c882f8a..c3bd5d9 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
@@ -182,6 +182,18 @@
             }
     }
 
+    /**
+     * Ends the dream content and dream overlay animations, if they're currently running.
+     * @see [AnimatorSet.end]
+     */
+    fun endAnimations() {
+        mAnimator =
+            mAnimator?.let {
+                it.end()
+                null
+            }
+    }
+
     private fun blurAnimator(
         view: View,
         fromBlurRadius: Float,
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index 33c8379..50cfb6a 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -37,11 +37,11 @@
 import com.android.systemui.dreams.complication.ComplicationHostViewController;
 import com.android.systemui.dreams.dagger.DreamOverlayComponent;
 import com.android.systemui.dreams.dagger.DreamOverlayModule;
+import com.android.systemui.dreams.touch.scrim.BouncerlessScrimController;
 import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor;
 import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
+import com.android.systemui.shade.ShadeExpansionChangeEvent;
 import com.android.systemui.statusbar.BlurUtils;
-import com.android.systemui.statusbar.phone.KeyguardBouncer;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.util.ViewController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
@@ -56,7 +56,6 @@
 @DreamOverlayComponent.DreamOverlayScope
 public class DreamOverlayContainerViewController extends ViewController<DreamOverlayContainerView> {
     private final DreamOverlayStatusBarViewController mStatusBarViewController;
-    private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     private final BlurUtils mBlurUtils;
     private final DreamOverlayAnimationsController mDreamOverlayAnimationsController;
     private final DreamOverlayStateController mStateController;
@@ -85,6 +84,22 @@
     private long mJitterStartTimeMillis;
 
     private boolean mBouncerAnimating;
+    private boolean mWakingUpFromSwipe;
+
+    private final BouncerlessScrimController mBouncerlessScrimController;
+
+    private final BouncerlessScrimController.Callback mBouncerlessExpansionCallback =
+            new BouncerlessScrimController.Callback() {
+        @Override
+        public void onExpansion(ShadeExpansionChangeEvent event) {
+            updateTransitionState(event.getFraction());
+        }
+
+        @Override
+        public void onWakeup() {
+            mWakingUpFromSwipe = true;
+        }
+    };
 
     private final PrimaryBouncerExpansionCallback
             mBouncerExpansionCallback =
@@ -127,13 +142,29 @@
                 }
             };
 
+    /**
+     * If true, overlay entry animations should be skipped once.
+     *
+     * This is turned on when exiting low light and should be turned off once the entry animations
+     * are skipped once.
+     */
+    private boolean mSkipEntryAnimations;
+
+    private final DreamOverlayStateController.Callback
+            mDreamOverlayStateCallback =
+            new DreamOverlayStateController.Callback() {
+                @Override
+                public void onExitLowLight() {
+                    mSkipEntryAnimations = true;
+                }
+            };
+
     @Inject
     public DreamOverlayContainerViewController(
             DreamOverlayContainerView containerView,
             ComplicationHostViewController complicationHostViewController,
             @Named(DreamOverlayModule.DREAM_OVERLAY_CONTENT_VIEW) ViewGroup contentView,
             DreamOverlayStatusBarViewController statusBarViewController,
-            StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             BlurUtils blurUtils,
             @Main Handler handler,
             @Main Resources resources,
@@ -143,15 +174,18 @@
             @Named(DreamOverlayModule.MILLIS_UNTIL_FULL_JITTER) long millisUntilFullJitter,
             PrimaryBouncerCallbackInteractor primaryBouncerCallbackInteractor,
             DreamOverlayAnimationsController animationsController,
-            DreamOverlayStateController stateController) {
+            DreamOverlayStateController stateController,
+            BouncerlessScrimController bouncerlessScrimController) {
         super(containerView);
         mDreamOverlayContentView = contentView;
         mStatusBarViewController = statusBarViewController;
-        mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
         mBlurUtils = blurUtils;
         mDreamOverlayAnimationsController = animationsController;
         mStateController = stateController;
 
+        mBouncerlessScrimController = bouncerlessScrimController;
+        mBouncerlessScrimController.addCallback(mBouncerlessExpansionCallback);
+
         mComplicationHostViewController = complicationHostViewController;
         mDreamOverlayMaxTranslationY = resources.getDimensionPixelSize(
                 R.dimen.dream_overlay_y_offset);
@@ -170,6 +204,7 @@
 
     @Override
     protected void onInit() {
+        mStateController.addCallback(mDreamOverlayStateCallback);
         mStatusBarViewController.init();
         mComplicationHostViewController.init();
         mDreamOverlayAnimationsController.init(mView);
@@ -177,27 +212,27 @@
 
     @Override
     protected void onViewAttached() {
+        mWakingUpFromSwipe = false;
         mJitterStartTimeMillis = System.currentTimeMillis();
         mHandler.postDelayed(this::updateBurnInOffsets, mBurnInProtectionUpdateInterval);
-        final KeyguardBouncer bouncer = mStatusBarKeyguardViewManager.getPrimaryBouncer();
-        if (bouncer != null) {
-            bouncer.addBouncerExpansionCallback(mBouncerExpansionCallback);
-        }
         mPrimaryBouncerCallbackInteractor.addBouncerExpansionCallback(mBouncerExpansionCallback);
 
         // Start dream entry animations. Skip animations for low light clock.
         if (!mStateController.isLowLightActive()) {
             mDreamOverlayAnimationsController.startEntryAnimations();
+
+            if (mSkipEntryAnimations) {
+                // If we're transitioning from the low light dream back to the user dream, skip the
+                // overlay animations and show immediately.
+                mDreamOverlayAnimationsController.endAnimations();
+                mSkipEntryAnimations = false;
+            }
         }
     }
 
     @Override
     protected void onViewDetached() {
         mHandler.removeCallbacks(this::updateBurnInOffsets);
-        final KeyguardBouncer bouncer = mStatusBarKeyguardViewManager.getPrimaryBouncer();
-        if (bouncer != null) {
-            bouncer.removeBouncerExpansionCallback(mBouncerExpansionCallback);
-        }
         mPrimaryBouncerCallbackInteractor.removeBouncerExpansionCallback(mBouncerExpansionCallback);
 
         mDreamOverlayAnimationsController.cancelAnimations();
@@ -266,6 +301,13 @@
      */
     public void wakeUp(@NonNull Runnable onAnimationEnd,
             @NonNull DelayableExecutor callbackExecutor) {
+        // When swiping causes wakeup, do not run any animations as the dream should exit as soon
+        // as possible.
+        if (mWakingUpFromSwipe) {
+            onAnimationEnd.run();
+            return;
+        }
+
         mDreamOverlayAnimationsController.wakeUp(onAnimationEnd, callbackExecutor);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java
index 87c5f51..a2dcdf5 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java
@@ -17,6 +17,7 @@
 package com.android.systemui.dreams;
 
 import static com.android.systemui.dreams.dagger.DreamModule.DREAM_OVERLAY_SERVICE_COMPONENT;
+import static com.android.systemui.dreams.dagger.DreamModule.DREAM_PRETEXT_MONITOR;
 
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -33,8 +34,9 @@
 import android.service.dreams.IDreamManager;
 import android.util.Log;
 
-import com.android.systemui.CoreStartable;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.util.condition.ConditionalCoreStartable;
 
 import javax.inject.Inject;
 import javax.inject.Named;
@@ -43,7 +45,7 @@
  * {@link DreamOverlayRegistrant} is responsible for telling system server that SystemUI should be
  * the designated dream overlay component.
  */
-public class DreamOverlayRegistrant implements CoreStartable {
+public class DreamOverlayRegistrant extends ConditionalCoreStartable {
     private static final String TAG = "DreamOverlayRegistrant";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     private final IDreamManager mDreamManager;
@@ -102,7 +104,9 @@
 
     @Inject
     public DreamOverlayRegistrant(Context context, @Main Resources resources,
-            @Named(DREAM_OVERLAY_SERVICE_COMPONENT) ComponentName dreamOverlayServiceComponent) {
+            @Named(DREAM_OVERLAY_SERVICE_COMPONENT) ComponentName dreamOverlayServiceComponent,
+            @Named(DREAM_PRETEXT_MONITOR) Monitor monitor) {
+        super(monitor);
         mContext = context;
         mResources = resources;
         mDreamManager = IDreamManager.Stub.asInterface(
@@ -111,7 +115,7 @@
     }
 
     @Override
-    public void start() {
+    protected void onStart() {
         final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_CHANGED);
         filter.addDataScheme("package");
         filter.addDataSchemeSpecificPart(mOverlayServiceComponent.getPackageName(),
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index dd01be0..5aebc32 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -243,6 +243,8 @@
      */
     private void addOverlayWindowLocked(WindowManager.LayoutParams layoutParams) {
         mWindow = new PhoneWindow(mContext);
+        // Default to SystemUI name for TalkBack.
+        mWindow.setTitle("");
         mWindow.setAttributes(layoutParams);
         mWindow.setWindowManager(null, layoutParams.token, "DreamOverlay", true);
 
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
index ccfdd096..2c7ecb1 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
@@ -83,6 +83,12 @@
          */
         default void onAvailableComplicationTypesChanged() {
         }
+
+        /**
+         * Called when the low light dream is exiting and transitioning back to the user dream.
+         */
+        default void onExitLowLight() {
+        }
     }
 
     private final Executor mExecutor;
@@ -278,6 +284,10 @@
      * @param active {@code true} if low light mode is active, {@code false} otherwise.
      */
     public void setLowLightActive(boolean active) {
+        if (isLowLightActive() && !active) {
+            // Notify that we're exiting low light only on the transition from active to not active.
+            mCallbacks.forEach(Callback::onExitLowLight);
+        }
         modifyState(active ? OP_SET_STATE : OP_CLEAR_STATE, STATE_LOW_LIGHT_ACTIVE);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
index 90c440c..7394e236 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -257,7 +257,8 @@
                         mConnectivityManager.getActiveNetwork());
         final boolean available = capabilities != null
                 && capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI);
-        showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, !available);
+        showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, !available,
+                R.string.wifi_unavailable_dream_overlay_content_description);
     }
 
     private void updateAlarmStatusIcon() {
@@ -294,13 +295,16 @@
         @DreamOverlayStatusBarView.StatusIconType int iconType = Resources.ID_NULL;
         showIcon(
                 DreamOverlayStatusBarView.STATUS_ICON_CAMERA_DISABLED,
-                !micBlocked && cameraBlocked);
+                !micBlocked && cameraBlocked,
+                R.string.camera_blocked_dream_overlay_content_description);
         showIcon(
                 DreamOverlayStatusBarView.STATUS_ICON_MIC_DISABLED,
-                micBlocked && !cameraBlocked);
+                micBlocked && !cameraBlocked,
+                R.string.microphone_blocked_dream_overlay_content_description);
         showIcon(
                 DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED,
-                micBlocked && cameraBlocked);
+                micBlocked && cameraBlocked,
+                R.string.camera_and_microphone_blocked_dream_overlay_content_description);
     }
 
     private String buildNotificationsContentDescription(int notificationCount) {
@@ -313,11 +317,13 @@
     private void updatePriorityModeStatusIcon() {
         showIcon(
                 DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON,
-                mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF);
+                mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF,
+                R.string.priority_mode_dream_overlay_content_description);
     }
 
-    private void showIcon(@DreamOverlayStatusBarView.StatusIconType int iconType, boolean show) {
-        showIcon(iconType, show, null);
+    private void showIcon(@DreamOverlayStatusBarView.StatusIconType int iconType, boolean show,
+            int contentDescriptionResId) {
+        showIcon(iconType, show, mResources.getString(contentDescriptionResId));
     }
 
     private void showIcon(
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/callbacks/DreamStatusBarStateCallback.java b/packages/SystemUI/src/com/android/systemui/dreams/callbacks/DreamStatusBarStateCallback.java
new file mode 100644
index 0000000..c8c9470
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/callbacks/DreamStatusBarStateCallback.java
@@ -0,0 +1,46 @@
+/*
+ * 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.dreams.callbacks;
+
+import android.util.Log;
+
+import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
+
+import javax.inject.Inject;
+
+/**
+ * A callback that informs {@link SysuiStatusBarStateController} when the dream state has changed.
+ */
+public class DreamStatusBarStateCallback implements Monitor.Callback {
+    private static final String TAG = "DreamStatusBarCallback";
+
+    private final SysuiStatusBarStateController mStateController;
+
+    @Inject
+    public DreamStatusBarStateCallback(SysuiStatusBarStateController statusBarStateController) {
+        mStateController = statusBarStateController;
+    }
+
+    @Override
+    public void onConditionsChanged(boolean allConditionsMet) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "onConditionChanged:" + allConditionsMet);
+        }
+
+        mStateController.setIsDreaming(allConditionsMet);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java
index ee2f1af..244212b 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java
@@ -16,27 +16,31 @@
 
 package com.android.systemui.dreams.complication;
 
+import static com.android.systemui.dreams.dagger.DreamModule.DREAM_PRETEXT_MONITOR;
+
 import android.database.ContentObserver;
 import android.os.UserHandle;
 import android.provider.Settings;
 
 import com.android.settingslib.dream.DreamBackend;
-import com.android.systemui.CoreStartable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.util.condition.ConditionalCoreStartable;
 import com.android.systemui.util.settings.SecureSettings;
 
 import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
+import javax.inject.Named;
 
 /**
  * {@link ComplicationTypesUpdater} observes the state of available complication types set by the
  * user, and pushes updates to {@link DreamOverlayStateController}.
  */
 @SysUISingleton
-public class ComplicationTypesUpdater implements CoreStartable {
+public class ComplicationTypesUpdater extends ConditionalCoreStartable {
     private final DreamBackend mDreamBackend;
     private final Executor mExecutor;
     private final SecureSettings mSecureSettings;
@@ -48,7 +52,9 @@
             DreamBackend dreamBackend,
             @Main Executor executor,
             SecureSettings secureSettings,
-            DreamOverlayStateController dreamOverlayStateController) {
+            DreamOverlayStateController dreamOverlayStateController,
+            @Named(DREAM_PRETEXT_MONITOR) Monitor monitor) {
+        super(monitor);
         mDreamBackend = dreamBackend;
         mExecutor = executor;
         mSecureSettings = secureSettings;
@@ -56,7 +62,7 @@
     }
 
     @Override
-    public void start() {
+    public void onStart() {
         final ContentObserver settingsObserver = new ContentObserver(null /*handler*/) {
             @Override
             public void onChange(boolean selfChange) {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java
index 77e1fc9..bb1e6e2 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java
@@ -18,11 +18,14 @@
 
 import static com.android.systemui.dreams.complication.dagger.DreamClockTimeComplicationModule.DREAM_CLOCK_TIME_COMPLICATION_VIEW;
 import static com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule.DREAM_CLOCK_TIME_COMPLICATION_LAYOUT_PARAMS;
+import static com.android.systemui.dreams.dagger.DreamModule.DREAM_PRETEXT_MONITOR;
 
 import android.view.View;
 
 import com.android.systemui.CoreStartable;
 import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.util.condition.ConditionalCoreStartable;
 
 import javax.inject.Inject;
 import javax.inject.Named;
@@ -60,7 +63,7 @@
      * {@link CoreStartable} responsible for registering {@link DreamClockTimeComplication} with
      * SystemUI.
      */
-    public static class Registrant implements CoreStartable {
+    public static class Registrant extends ConditionalCoreStartable {
         private final DreamOverlayStateController mDreamOverlayStateController;
         private final DreamClockTimeComplication mComplication;
 
@@ -70,13 +73,15 @@
         @Inject
         public Registrant(
                 DreamOverlayStateController dreamOverlayStateController,
-                DreamClockTimeComplication dreamClockTimeComplication) {
+                DreamClockTimeComplication dreamClockTimeComplication,
+                @Named(DREAM_PRETEXT_MONITOR) Monitor monitor) {
+            super(monitor);
             mDreamOverlayStateController = dreamOverlayStateController;
             mComplication = dreamClockTimeComplication;
         }
 
         @Override
-        public void start() {
+        public void onStart() {
             mDreamOverlayStateController.addComplication(mComplication);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java
index 1065b94..7f395d8 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java
@@ -21,6 +21,7 @@
 import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.UNAVAILABLE;
 import static com.android.systemui.dreams.complication.dagger.DreamHomeControlsComplicationComponent.DreamHomeControlsModule.DREAM_HOME_CONTROLS_CHIP_VIEW;
 import static com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule.DREAM_HOME_CONTROLS_CHIP_LAYOUT_PARAMS;
+import static com.android.systemui.dreams.dagger.DreamModule.DREAM_PRETEXT_MONITOR;
 
 import android.content.Context;
 import android.content.Intent;
@@ -42,7 +43,9 @@
 import com.android.systemui.dreams.DreamOverlayStateController;
 import com.android.systemui.dreams.complication.dagger.DreamHomeControlsComplicationComponent;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.shared.condition.Monitor;
 import com.android.systemui.util.ViewController;
+import com.android.systemui.util.condition.ConditionalCoreStartable;
 
 import java.util.List;
 
@@ -75,7 +78,7 @@
     /**
      * {@link CoreStartable} for registering the complication with SystemUI on startup.
      */
-    public static class Registrant implements CoreStartable {
+    public static class Registrant extends ConditionalCoreStartable {
         private final DreamHomeControlsComplication mComplication;
         private final DreamOverlayStateController mDreamOverlayStateController;
         private final ControlsComponent mControlsComponent;
@@ -105,14 +108,16 @@
         @Inject
         public Registrant(DreamHomeControlsComplication complication,
                 DreamOverlayStateController dreamOverlayStateController,
-                ControlsComponent controlsComponent) {
+                ControlsComponent controlsComponent,
+                @Named(DREAM_PRETEXT_MONITOR) Monitor monitor) {
+            super(monitor);
             mComplication = complication;
             mControlsComponent = controlsComponent;
             mDreamOverlayStateController = dreamOverlayStateController;
         }
 
         @Override
-        public void start() {
+        public void onStart() {
             mControlsComponent.getControlsListingController().ifPresent(
                     c -> c.addCallback(mControlsCallback));
             mDreamOverlayStateController.addCallback(mOverlayStateCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java
index c3aaf0c..e39073b 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java
@@ -17,6 +17,7 @@
 package com.android.systemui.dreams.complication;
 
 import static com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule.DREAM_SMARTSPACE_LAYOUT_PARAMS;
+import static com.android.systemui.dreams.dagger.DreamModule.DREAM_PRETEXT_MONITOR;
 
 import android.content.Context;
 import android.os.Parcelable;
@@ -28,6 +29,8 @@
 import com.android.systemui.dreams.DreamOverlayStateController;
 import com.android.systemui.dreams.smartspace.DreamSmartspaceController;
 import com.android.systemui.plugins.BcSmartspaceDataPlugin;
+import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.util.condition.ConditionalCoreStartable;
 
 import java.util.List;
 
@@ -61,7 +64,7 @@
      * {@link CoreStartable} responsbile for registering {@link SmartSpaceComplication} with
      * SystemUI.
      */
-    public static class Registrant implements CoreStartable {
+    public static class Registrant extends ConditionalCoreStartable {
         private final DreamSmartspaceController mSmartSpaceController;
         private final DreamOverlayStateController mDreamOverlayStateController;
         private final SmartSpaceComplication mComplication;
@@ -81,14 +84,16 @@
         public Registrant(
                 DreamOverlayStateController dreamOverlayStateController,
                 SmartSpaceComplication smartSpaceComplication,
-                DreamSmartspaceController smartSpaceController) {
+                DreamSmartspaceController smartSpaceController,
+                @Named(DREAM_PRETEXT_MONITOR) Monitor monitor) {
+            super(monitor);
             mDreamOverlayStateController = dreamOverlayStateController;
             mComplication = smartSpaceComplication;
             mSmartSpaceController = smartSpaceController;
         }
 
         @Override
-        public void start() {
+        public void onStart() {
             mDreamOverlayStateController.addCallback(new DreamOverlayStateController.Callback() {
                 @Override
                 public void onStateChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java b/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java
new file mode 100644
index 0000000..2befce7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java
@@ -0,0 +1,73 @@
+/*
+ * 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.dreams.conditions;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.text.TextUtils;
+
+import com.android.systemui.shared.condition.Condition;
+
+import javax.inject.Inject;
+
+/**
+ * {@link DreamCondition} provides a signal when a dream begins and ends.
+ */
+public class DreamCondition extends Condition {
+    private final Context mContext;
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            processIntent(intent);
+        }
+    };
+
+    @Inject
+    public DreamCondition(Context context) {
+        mContext = context;
+    }
+
+    private void processIntent(Intent intent) {
+        // In the case of a non-existent sticky broadcast, ignore when there is no intent.
+        if (intent == null) {
+            return;
+        }
+        if (TextUtils.equals(intent.getAction(), Intent.ACTION_DREAMING_STARTED)) {
+            updateCondition(true);
+        } else if (TextUtils.equals(intent.getAction(), Intent.ACTION_DREAMING_STOPPED)) {
+            updateCondition(false);
+        } else {
+            throw new IllegalStateException("unexpected intent:" + intent);
+        }
+    }
+
+    @Override
+    protected void start() {
+        final IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_DREAMING_STARTED);
+        filter.addAction(Intent.ACTION_DREAMING_STOPPED);
+        final Intent stickyIntent = mContext.registerReceiver(mReceiver, filter);
+        processIntent(stickyIntent);
+    }
+
+    @Override
+    protected void stop() {
+        mContext.unregisterReceiver(mReceiver);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index e7b29bb..88c02b8 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -28,13 +28,21 @@
 import com.android.systemui.dreams.DreamOverlayNotificationCountProvider;
 import com.android.systemui.dreams.DreamOverlayService;
 import com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule;
+import com.android.systemui.dreams.touch.scrim.dagger.ScrimModule;
+import com.android.systemui.process.condition.UserProcessCondition;
+import com.android.systemui.shared.condition.Condition;
+import com.android.systemui.shared.condition.Monitor;
 
 import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.Executor;
 
 import javax.inject.Named;
 
+import dagger.Binds;
 import dagger.Module;
 import dagger.Provides;
+import dagger.multibindings.IntoSet;
 
 /**
  * Dagger Module providing Dream-related functionality.
@@ -42,6 +50,7 @@
 @Module(includes = {
             RegisteredComplicationsModule.class,
             LowLightDreamModule.class,
+            ScrimModule.class
         },
         subcomponents = {
             DreamOverlayComponent.class,
@@ -52,6 +61,8 @@
     String DREAM_OVERLAY_ENABLED = "dream_overlay_enabled";
 
     String DREAM_SUPPORTED = "dream_supported";
+    String DREAM_PRETEXT_CONDITIONS = "dream_pretext_conditions";
+    String DREAM_PRETEXT_MONITOR = "dream_prtext_monitor";
 
     /**
      * Provides the dream component
@@ -110,4 +121,19 @@
     static boolean providesDreamSupported(@Main Resources resources) {
         return resources.getBoolean(com.android.internal.R.bool.config_dreamsSupported);
     }
+
+    /** */
+    @Binds
+    @IntoSet
+    @Named(DREAM_PRETEXT_CONDITIONS)
+    Condition bindsUserProcessCondition(UserProcessCondition condition);
+
+    /** */
+    @Provides
+    @Named(DREAM_PRETEXT_MONITOR)
+    static Monitor providesDockerPretextMonitor(
+            @Main Executor executor,
+            @Named(DREAM_PRETEXT_CONDITIONS) Set<Condition> pretextConditions) {
+        return new Monitor(executor, pretextConditions);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
index 44207f4..73c2289 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
@@ -36,11 +36,12 @@
 
 import com.android.internal.logging.UiEvent;
 import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.dreams.touch.scrim.ScrimController;
+import com.android.systemui.dreams.touch.scrim.ScrimManager;
 import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants;
 import com.android.systemui.shade.ShadeExpansionChangeEvent;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.wm.shell.animation.FlingAnimationUtils;
 
 import java.util.Optional;
@@ -78,7 +79,8 @@
     private final NotificationShadeWindowController mNotificationShadeWindowController;
     private final float mBouncerZoneScreenPercentage;
 
-    private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+    private final ScrimManager mScrimManager;
+    private ScrimController mCurrentScrimController;
     private float mCurrentExpansion;
     private final Optional<CentralSurfaces> mCentralSurfaces;
 
@@ -90,6 +92,7 @@
     private final DisplayMetrics mDisplayMetrics;
 
     private Boolean mCapture;
+    private Boolean mExpanded;
 
     private boolean mBouncerInitiallyShowing;
 
@@ -101,6 +104,17 @@
 
     private final UiEventLogger mUiEventLogger;
 
+    private final ScrimManager.Callback mScrimManagerCallback = new ScrimManager.Callback() {
+        @Override
+        public void onScrimControllerChanged(ScrimController controller) {
+            if (mCurrentScrimController != null) {
+                mCurrentScrimController.reset();
+            }
+
+            mCurrentScrimController = controller;
+        }
+    };
+
     private final GestureDetector.OnGestureListener mOnGestureListener =
             new GestureDetector.SimpleOnGestureListener() {
                 @Override
@@ -115,8 +129,10 @@
                                 .orElse(false);
 
                         if (mCapture) {
+                            // reset expanding
+                            mExpanded = false;
                             // Since the user is dragging the bouncer up, set scrimmed to false.
-                            mStatusBarKeyguardViewManager.showPrimaryBouncer(false);
+                            mCurrentScrimController.show();
                         }
                     }
 
@@ -157,10 +173,10 @@
         ShadeExpansionChangeEvent event =
                 new ShadeExpansionChangeEvent(
                         /* fraction= */ mCurrentExpansion,
-                        /* expanded= */ false,
+                        /* expanded= */ mExpanded,
                         /* tracking= */ true,
                         /* dragDownPxAmount= */ dragDownAmount);
-        mStatusBarKeyguardViewManager.onPanelExpansionChanged(event);
+        mCurrentScrimController.expand(event);
     }
 
 
@@ -187,7 +203,7 @@
     @Inject
     public BouncerSwipeTouchHandler(
             DisplayMetrics displayMetrics,
-            StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+            ScrimManager scrimManager,
             Optional<CentralSurfaces> centralSurfaces,
             NotificationShadeWindowController notificationShadeWindowController,
             ValueAnimatorCreator valueAnimatorCreator,
@@ -200,7 +216,7 @@
             UiEventLogger uiEventLogger) {
         mDisplayMetrics = displayMetrics;
         mCentralSurfaces = centralSurfaces;
-        mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+        mScrimManager = scrimManager;
         mNotificationShadeWindowController = notificationShadeWindowController;
         mBouncerZoneScreenPercentage = swipeRegionPercentage;
         mFlingAnimationUtils = flingAnimationUtils;
@@ -234,9 +250,12 @@
         mTouchSession = session;
         mVelocityTracker.clear();
         mNotificationShadeWindowController.setForcePluginOpen(true, this);
+        mScrimManager.addCallback(mScrimManagerCallback);
+        mCurrentScrimController = mScrimManager.getCurrentController();
 
         session.registerCallback(() -> {
             mVelocityTracker.recycle();
+            mScrimManager.removeCallback(mScrimManagerCallback);
             mCapture = null;
             mNotificationShadeWindowController.setForcePluginOpen(false, this);
         });
@@ -273,9 +292,10 @@
                 final float velocityVector =
                         (float) Math.hypot(horizontalVelocity, verticalVelocity);
 
-                final float expansion = flingRevealsOverlay(verticalVelocity, velocityVector)
-                        ? KeyguardBouncerConstants.EXPANSION_HIDDEN
-                        : KeyguardBouncerConstants.EXPANSION_VISIBLE;
+                mExpanded = !flingRevealsOverlay(verticalVelocity, velocityVector);
+                final float expansion = mExpanded
+                        ? KeyguardBouncerConstants.EXPANSION_VISIBLE
+                        : KeyguardBouncerConstants.EXPANSION_HIDDEN;
 
                 // Log the swiping up to show Bouncer event.
                 if (!mBouncerInitiallyShowing
@@ -286,7 +306,7 @@
                 flingToExpansion(verticalVelocity, expansion);
 
                 if (expansion == KeyguardBouncerConstants.EXPANSION_HIDDEN) {
-                    mStatusBarKeyguardViewManager.reset(false);
+                    mCurrentScrimController.reset();
                 }
                 break;
             default:
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
index 695b59a..b8b459e 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
@@ -226,6 +226,15 @@
             return;
         }
 
+        // When we stop monitoring touches, we must ensure that all active touch sessions and
+        // descendants informed of the removal so any cleanup for active tracking can proceed.
+        mExecutor.execute(() -> mActiveTouchSessions.forEach(touchSession -> {
+            while (touchSession != null) {
+                touchSession.onRemoved();
+                touchSession = touchSession.getPredecessor();
+            }
+        }));
+
         mCurrentInputSession.dispose();
         mCurrentInputSession = null;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/InputSession.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/InputSession.java
index 4382757..e1d0339 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/InputSession.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/InputSession.java
@@ -21,10 +21,10 @@
 
 import android.os.Looper;
 import android.view.Choreographer;
-import android.view.Display;
 import android.view.GestureDetector;
 import android.view.MotionEvent;
 
+import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.shared.system.InputChannelCompat;
 import com.android.systemui.shared.system.InputMonitorCompat;
 
@@ -55,8 +55,9 @@
     public InputSession(@Named(INPUT_SESSION_NAME) String sessionName,
             InputChannelCompat.InputEventListener inputEventListener,
             GestureDetector.OnGestureListener gestureListener,
+            DisplayTracker displayTracker,
             @Named(PILFER_ON_GESTURE_CONSUME) boolean pilferOnGestureConsume) {
-        mInputMonitor = new InputMonitorCompat(sessionName, Display.DEFAULT_DISPLAY);
+        mInputMonitor = new InputMonitorCompat(sessionName, displayTracker.getDefaultDisplayId());
         mGestureDetector = new GestureDetector(gestureListener);
 
         mInputEventReceiver = mInputMonitor.getInputReceiver(Looper.getMainLooper(),
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/BouncerScrimController.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/BouncerScrimController.java
new file mode 100644
index 0000000..f5bbba7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/BouncerScrimController.java
@@ -0,0 +1,49 @@
+/*
+ * 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.dreams.touch.scrim;
+
+import com.android.systemui.shade.ShadeExpansionChangeEvent;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+
+import javax.inject.Inject;
+
+/**
+ * Implementation for handling swipe movements on the overlay when the keyguard is present.
+ */
+public class BouncerScrimController implements ScrimController {
+    private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+
+    @Inject
+    BouncerScrimController(StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
+        mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+    }
+
+    @Override
+    public void show() {
+        mStatusBarKeyguardViewManager.showBouncer(false);
+    }
+
+    @Override
+    public void expand(ShadeExpansionChangeEvent event) {
+        mStatusBarKeyguardViewManager.onPanelExpansionChanged(event);
+    }
+
+    @Override
+    public void reset() {
+        mStatusBarKeyguardViewManager.reset(false);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimController.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimController.java
new file mode 100644
index 0000000..01e4d04
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimController.java
@@ -0,0 +1,91 @@
+/*
+ * 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.dreams.touch.scrim;
+
+import android.os.PowerManager;
+import android.os.SystemClock;
+
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.shade.ShadeExpansionChangeEvent;
+import com.android.systemui.unfold.util.CallbackController;
+
+import java.util.HashSet;
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+
+/**
+ * {@link BouncerlessScrimController} handles scrim progression when no keyguard is set. When
+ * fully expanded, the controller dismisses the dream.
+ */
+@SysUISingleton
+public class BouncerlessScrimController implements ScrimController,
+        CallbackController<BouncerlessScrimController.Callback> {
+    private static final String TAG = "BLScrimController";
+
+    /**
+     * {@link Callback} allows {@link BouncerlessScrimController} clients to be informed of
+     * expansion progression and wakeup
+     */
+    public interface Callback {
+        /**
+         * Invoked when there is a change to the scrim expansion.
+         */
+        void onExpansion(ShadeExpansionChangeEvent event);
+
+        /**
+         * Invoked after {@link BouncerlessScrimController} has started waking up the device.
+         */
+        void onWakeup();
+    }
+
+    private final Executor mExecutor;
+    private final PowerManager mPowerManager;
+
+    @Override
+    public void addCallback(Callback listener) {
+        mExecutor.execute(() -> mCallbacks.add(listener));
+    }
+
+    @Override
+    public void removeCallback(Callback listener) {
+        mExecutor.execute(() -> mCallbacks.remove(listener));
+    }
+
+    private final HashSet<Callback> mCallbacks;
+
+
+    @Inject
+    public BouncerlessScrimController(@Main Executor executor,
+            PowerManager powerManager) {
+        mExecutor = executor;
+        mPowerManager = powerManager;
+        mCallbacks = new HashSet<>();
+    }
+
+    @Override
+    public void expand(ShadeExpansionChangeEvent event) {
+        if (event.getExpanded())  {
+            mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE,
+                    "com.android.systemui:SwipeUp");
+            mExecutor.execute(() -> mCallbacks.forEach(callback -> callback.onWakeup()));
+        } else {
+            mExecutor.execute(() -> mCallbacks.forEach(callback -> callback.onExpansion(event)));
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/ScrimController.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/ScrimController.java
new file mode 100644
index 0000000..61629ef
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/ScrimController.java
@@ -0,0 +1,44 @@
+/*
+ * 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.dreams.touch.scrim;
+
+import com.android.systemui.shade.ShadeExpansionChangeEvent;
+
+/**
+ * {@link ScrimController} provides an interface for the different consumers of scrolling/expansion
+ * events over the dream.
+ */
+public interface ScrimController {
+    /**
+     * Called at the start of expansion before any expansion amount updates.
+     */
+    default void show() {
+    }
+
+    /**
+     * Called for every expansion update.
+     * @param event {@link ShadeExpansionChangeEvent} detailing the change.
+     */
+    default void expand(ShadeExpansionChangeEvent event) {
+    }
+
+    /**
+     * Called at the end of the movement.
+     */
+    default void reset() {
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/ScrimManager.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/ScrimManager.java
new file mode 100644
index 0000000..0d0dff6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/ScrimManager.java
@@ -0,0 +1,112 @@
+/*
+ * 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.dreams.touch.scrim;
+
+import static com.android.systemui.dreams.touch.scrim.dagger.ScrimModule.BOUNCERLESS_SCRIM_CONTROLLER;
+import static com.android.systemui.dreams.touch.scrim.dagger.ScrimModule.BOUNCER_SCRIM_CONTROLLER;
+
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+import java.util.HashSet;
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * {@link ScrimManager} helps manage multiple {@link ScrimController} instances, specifying the
+ * appropriate one to use at the current moment and managing the handoff between controllers.
+ */
+public class ScrimManager {
+    private final ScrimController mBouncerScrimController;
+    private final ScrimController mBouncerlessScrimController;
+    private final KeyguardStateController mKeyguardStateController;
+    private final Executor mExecutor;
+
+    private ScrimController mCurrentController;
+    private final HashSet<Callback> mCallbacks;
+
+    /**
+     * Interface implemented for receiving updates to the active {@link ScrimController}.
+     */
+    public interface Callback {
+        /**
+         * Invoked when the controller changes.
+         * @param controller The currently active {@link ScrimController}.
+         */
+        void onScrimControllerChanged(ScrimController controller);
+    }
+
+    private final KeyguardStateController.Callback mKeyguardStateCallback =
+            new KeyguardStateController.Callback() {
+                @Override
+                public void onKeyguardShowingChanged() {
+                    mExecutor.execute(() -> updateController());
+                }
+            };
+
+    @Inject
+    ScrimManager(@Main Executor executor,
+            @Named(BOUNCER_SCRIM_CONTROLLER) ScrimController bouncerScrimController,
+            @Named(BOUNCERLESS_SCRIM_CONTROLLER)ScrimController bouncerlessScrimController,
+            KeyguardStateController keyguardStateController) {
+        mExecutor = executor;
+        mCallbacks = new HashSet<>();
+        mBouncerlessScrimController = bouncerlessScrimController;
+        mBouncerScrimController = bouncerScrimController;
+        mKeyguardStateController = keyguardStateController;
+
+        mKeyguardStateController.addCallback(mKeyguardStateCallback);
+        updateController();
+    }
+
+    private void updateController() {
+        final ScrimController existingController = mCurrentController;
+        mCurrentController =  mKeyguardStateController.canDismissLockScreen()
+                ? mBouncerlessScrimController
+                : mBouncerScrimController;
+
+        if (existingController == mCurrentController) {
+            return;
+        }
+
+        mCallbacks.forEach(callback -> callback.onScrimControllerChanged(mCurrentController));
+    }
+
+    /**
+     * Adds a {@link Callback} to receive future changes to the active {@link ScrimController}.
+     */
+    public void addCallback(Callback callback) {
+        mExecutor.execute(() -> mCallbacks.add(callback));
+    }
+
+    /**
+     * Removes the {@link Callback} from receiving further updates.
+     */
+    public void removeCallback(Callback callback) {
+        mExecutor.execute(() -> mCallbacks.remove(callback));
+    }
+
+    /**
+     * Returns the currently get {@link ScrimController}.
+     * @return
+     */
+    public ScrimController getCurrentController() {
+        return mCurrentController;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/dagger/ScrimModule.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/dagger/ScrimModule.java
new file mode 100644
index 0000000..40bc0ea
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/scrim/dagger/ScrimModule.java
@@ -0,0 +1,51 @@
+/*
+ * 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.dreams.touch.scrim.dagger;
+
+import com.android.systemui.dreams.touch.scrim.BouncerScrimController;
+import com.android.systemui.dreams.touch.scrim.BouncerlessScrimController;
+import com.android.systemui.dreams.touch.scrim.ScrimController;
+
+import javax.inject.Named;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Module for scrim related dependencies.
+ */
+@Module
+public interface ScrimModule {
+    String BOUNCERLESS_SCRIM_CONTROLLER = "bouncerless_scrim_controller";
+    String BOUNCER_SCRIM_CONTROLLER = "bouncer_scrim_controller";
+
+    /** */
+    @Provides
+    @Named(BOUNCERLESS_SCRIM_CONTROLLER)
+    static ScrimController providesBouncerlessScrimController(
+            BouncerlessScrimController controller) {
+        return controller;
+    }
+
+    /** */
+    @Provides
+    @Named(BOUNCER_SCRIM_CONTROLLER)
+    static ScrimController providesBouncerScrimController(
+            BouncerScrimController controller) {
+        return controller;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
index d1a14a1..58fe094 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
@@ -19,7 +19,7 @@
 import static com.android.systemui.flags.FlagManager.ACTION_GET_FLAGS;
 import static com.android.systemui.flags.FlagManager.ACTION_SET_FLAG;
 import static com.android.systemui.flags.FlagManager.EXTRA_FLAGS;
-import static com.android.systemui.flags.FlagManager.EXTRA_ID;
+import static com.android.systemui.flags.FlagManager.EXTRA_NAME;
 import static com.android.systemui.flags.FlagManager.EXTRA_VALUE;
 import static com.android.systemui.flags.FlagsCommonModule.ALL_FLAGS;
 
@@ -39,7 +39,7 @@
 
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.util.settings.SecureSettings;
+import com.android.systemui.util.settings.GlobalSettings;
 
 import org.jetbrains.annotations.NotNull;
 
@@ -72,21 +72,22 @@
 
     private final FlagManager mFlagManager;
     private final Context mContext;
-    private final SecureSettings mSecureSettings;
+    private final GlobalSettings mGlobalSettings;
     private final Resources mResources;
     private final SystemPropertiesHelper mSystemProperties;
     private final ServerFlagReader mServerFlagReader;
-    private final Map<Integer, Flag<?>> mAllFlags;
-    private final Map<Integer, Boolean> mBooleanFlagCache = new TreeMap<>();
-    private final Map<Integer, String> mStringFlagCache = new TreeMap<>();
-    private final Map<Integer, Integer> mIntFlagCache = new TreeMap<>();
+    private final Map<String, Flag<?>> mAllFlags;
+    private final Map<String, Boolean> mBooleanFlagCache = new TreeMap<>();
+    private final Map<String, String> mStringFlagCache = new TreeMap<>();
+    private final Map<String, Integer> mIntFlagCache = new TreeMap<>();
     private final Restarter mRestarter;
 
     private final ServerFlagReader.ChangeListener mOnPropertiesChanged =
             new ServerFlagReader.ChangeListener() {
                 @Override
-                public void onChange() {
-                    mRestarter.restartSystemUI();
+                public void onChange(Flag<?> flag) {
+                    mRestarter.restartSystemUI(
+                            "Server flag change: " + flag.getNamespace() + "." + flag.getName());
                 }
             };
 
@@ -94,15 +95,15 @@
     public FeatureFlagsDebug(
             FlagManager flagManager,
             Context context,
-            SecureSettings secureSettings,
+            GlobalSettings globalSettings,
             SystemPropertiesHelper systemProperties,
             @Main Resources resources,
             ServerFlagReader serverFlagReader,
-            @Named(ALL_FLAGS) Map<Integer, Flag<?>> allFlags,
+            @Named(ALL_FLAGS) Map<String, Flag<?>> allFlags,
             Restarter restarter) {
         mFlagManager = flagManager;
         mContext = context;
-        mSecureSettings = secureSettings;
+        mGlobalSettings = globalSettings;
         mResources = resources;
         mSystemProperties = systemProperties;
         mServerFlagReader = serverFlagReader;
@@ -115,7 +116,8 @@
         IntentFilter filter = new IntentFilter();
         filter.addAction(ACTION_SET_FLAG);
         filter.addAction(ACTION_GET_FLAGS);
-        mFlagManager.setOnSettingsChangedAction(this::restartSystemUI);
+        mFlagManager.setOnSettingsChangedAction(
+                suppressRestart -> restartSystemUI(suppressRestart, "Settings changed"));
         mFlagManager.setClearCacheAction(this::removeFromCache);
         mContext.registerReceiver(mReceiver, filter, null, null,
                 Context.RECEIVER_EXPORTED_UNAUDITED);
@@ -133,96 +135,107 @@
     }
 
     private boolean isEnabledInternal(@NotNull BooleanFlag flag) {
-        int id = flag.getId();
-        if (!mBooleanFlagCache.containsKey(id)) {
-            mBooleanFlagCache.put(id,
+        String name = flag.getName();
+        if (!mBooleanFlagCache.containsKey(name)) {
+            mBooleanFlagCache.put(name,
                     readBooleanFlagInternal(flag, flag.getDefault()));
         }
 
-        return mBooleanFlagCache.get(id);
+        return mBooleanFlagCache.get(name);
     }
 
     @Override
     public boolean isEnabled(@NonNull ResourceBooleanFlag flag) {
-        int id = flag.getId();
-        if (!mBooleanFlagCache.containsKey(id)) {
-            mBooleanFlagCache.put(id,
+        String name = flag.getName();
+        if (!mBooleanFlagCache.containsKey(name)) {
+            mBooleanFlagCache.put(name,
                     readBooleanFlagInternal(flag, mResources.getBoolean(flag.getResourceId())));
         }
 
-        return mBooleanFlagCache.get(id);
+        return mBooleanFlagCache.get(name);
     }
 
     @Override
     public boolean isEnabled(@NonNull SysPropBooleanFlag flag) {
-        int id = flag.getId();
-        if (!mBooleanFlagCache.containsKey(id)) {
+        String name = flag.getName();
+        if (!mBooleanFlagCache.containsKey(name)) {
             // Use #readFlagValue to get the default. That will allow it to fall through to
             // teamfood if need be.
             mBooleanFlagCache.put(
-                    id,
+                    name,
                     mSystemProperties.getBoolean(
                             flag.getName(),
                             readBooleanFlagInternal(flag, flag.getDefault())));
         }
 
-        return mBooleanFlagCache.get(id);
+        return mBooleanFlagCache.get(name);
     }
 
     @NonNull
     @Override
     public String getString(@NonNull StringFlag flag) {
-        int id = flag.getId();
-        if (!mStringFlagCache.containsKey(id)) {
-            mStringFlagCache.put(id,
-                    readFlagValueInternal(id, flag.getDefault(), StringFlagSerializer.INSTANCE));
+        String name = flag.getName();
+        if (!mStringFlagCache.containsKey(name)) {
+            mStringFlagCache.put(name,
+                    readFlagValueInternal(
+                            flag.getId(), name, flag.getDefault(), StringFlagSerializer.INSTANCE));
         }
 
-        return mStringFlagCache.get(id);
+        return mStringFlagCache.get(name);
     }
 
     @NonNull
     @Override
     public String getString(@NonNull ResourceStringFlag flag) {
-        int id = flag.getId();
-        if (!mStringFlagCache.containsKey(id)) {
-            mStringFlagCache.put(id,
-                    readFlagValueInternal(id, mResources.getString(flag.getResourceId()),
+        String name = flag.getName();
+        if (!mStringFlagCache.containsKey(name)) {
+            mStringFlagCache.put(name,
+                    readFlagValueInternal(
+                            flag.getId(), name, mResources.getString(flag.getResourceId()),
                             StringFlagSerializer.INSTANCE));
         }
 
-        return mStringFlagCache.get(id);
+        return mStringFlagCache.get(name);
     }
 
 
     @NonNull
     @Override
     public int getInt(@NonNull IntFlag flag) {
-        int id = flag.getId();
-        if (!mIntFlagCache.containsKey(id)) {
-            mIntFlagCache.put(id,
-                    readFlagValueInternal(id, flag.getDefault(), IntFlagSerializer.INSTANCE));
+        String name = flag.getName();
+        if (!mIntFlagCache.containsKey(name)) {
+            mIntFlagCache.put(name,
+                    readFlagValueInternal(
+                            flag.getId(), name, flag.getDefault(), IntFlagSerializer.INSTANCE));
         }
 
-        return mIntFlagCache.get(id);
+        return mIntFlagCache.get(name);
     }
 
     @NonNull
     @Override
     public int getInt(@NonNull ResourceIntFlag flag) {
-        int id = flag.getId();
-        if (!mIntFlagCache.containsKey(id)) {
-            mIntFlagCache.put(id,
-                    readFlagValueInternal(id, mResources.getInteger(flag.getResourceId()),
+        String name = flag.getName();
+        if (!mIntFlagCache.containsKey(name)) {
+            mIntFlagCache.put(name,
+                    readFlagValueInternal(
+                            flag.getId(), name, mResources.getInteger(flag.getResourceId()),
                             IntFlagSerializer.INSTANCE));
         }
 
-        return mIntFlagCache.get(id);
+        return mIntFlagCache.get(name);
     }
 
     /** Specific override for Boolean flags that checks against the teamfood list.*/
     private boolean readBooleanFlagInternal(Flag<Boolean> flag, boolean defaultValue) {
-        Boolean result = readBooleanFlagOverride(flag.getId());
+        Boolean result = readBooleanFlagOverride(flag.getName());
+        if (result == null) {
+            result = readBooleanFlagOverride(flag.getId());
+            if (result != null) {
+                // Move overrides from id to name
+                setFlagValueInternal(flag.getName(), result, BooleanFlagSerializer.INSTANCE);
+            }
+        }
         boolean hasServerOverride = mServerFlagReader.hasOverride(
                 flag.getNamespace(), flag.getName());
 
@@ -231,7 +244,7 @@
         if (!hasServerOverride
                 && !defaultValue
                 && result == null
-                && flag.getId() != Flags.TEAMFOOD.getId()
+                && !flag.getName().equals(Flags.TEAMFOOD.getName())
                 && flag.getTeamfood()) {
             return isEnabled(Flags.TEAMFOOD);
         }
@@ -244,16 +257,31 @@
         return readFlagValueInternal(id, BooleanFlagSerializer.INSTANCE);
     }
 
+    private Boolean readBooleanFlagOverride(String name) {
+        return readFlagValueInternal(name, BooleanFlagSerializer.INSTANCE);
+    }
+
+    // TODO(b/265188950): Remove id from this method once ids are fully deprecated.
     @NonNull
     private <T> T readFlagValueInternal(
-            int id, @NonNull T defaultValue, FlagSerializer<T> serializer) {
+            int id, String name, @NonNull T defaultValue, FlagSerializer<T> serializer) {
         requireNonNull(defaultValue, "defaultValue");
-        T result = readFlagValueInternal(id, serializer);
-        return result == null ? defaultValue : result;
+        T resultForName = readFlagValueInternal(name, serializer);
+        if (resultForName == null) {
+            T resultForId = readFlagValueInternal(id, serializer);
+            if (resultForId == null) {
+                return defaultValue;
+            } else {
+                setFlagValue(name, resultForId, serializer);
+                return resultForId;
+            }
+        }
+        return resultForName;
     }
 
 
     /** Returns the stored value or null if not set. */
+    // TODO(b/265188950): Remove method this once ids are fully deprecated.
     @Nullable
     private <T> T readFlagValueInternal(int id, FlagSerializer<T> serializer) {
         try {
@@ -264,51 +292,87 @@
         return null;
     }
 
-    private <T> void setFlagValue(int id, @NonNull T value, FlagSerializer<T> serializer) {
+    /** Returns the stored value or null if not set. */
+    @Nullable
+    private <T> T readFlagValueInternal(String name, FlagSerializer<T> serializer) {
+        try {
+            return mFlagManager.readFlagValue(name, serializer);
+        } catch (Exception e) {
+            eraseInternal(name);
+        }
+        return null;
+    }
+
+    private <T> void setFlagValue(String name, @NonNull T value, FlagSerializer<T> serializer) {
         requireNonNull(value, "Cannot set a null value");
-        T currentValue = readFlagValueInternal(id, serializer);
+        T currentValue = readFlagValueInternal(name, serializer);
         if (Objects.equals(currentValue, value)) {
-            Log.i(TAG, "Flag id " + id + " is already " + value);
+            Log.i(TAG, "Flag \"" + name + "\" is already " + value);
             return;
         }
+        setFlagValueInternal(name, value, serializer);
+        Log.i(TAG, "Set flag \"" + name + "\" to " + value);
+        removeFromCache(name);
+        mFlagManager.dispatchListenersAndMaybeRestart(
+                name,
+                suppressRestart -> restartSystemUI(
+                        suppressRestart, "Flag \"" + name + "\" changed to " + value));
+    }
+
+    private <T> void setFlagValueInternal(
+            String name, @NonNull T value, FlagSerializer<T> serializer) {
         final String data = serializer.toSettingsData(value);
         if (data == null) {
-            Log.w(TAG, "Failed to set id " + id + " to " + value);
+            Log.w(TAG, "Failed to set flag " + name + " to " + value);
             return;
         }
-        mSecureSettings.putStringForUser(mFlagManager.idToSettingsKey(id), data,
+        mGlobalSettings.putStringForUser(mFlagManager.nameToSettingsKey(name), data,
                 UserHandle.USER_CURRENT);
-        Log.i(TAG, "Set id " + id + " to " + value);
-        removeFromCache(id);
-        mFlagManager.dispatchListenersAndMaybeRestart(id, this::restartSystemUI);
     }
 
     <T> void eraseFlag(Flag<T> flag) {
         if (flag instanceof SysPropFlag) {
-            mSystemProperties.erase(((SysPropFlag<T>) flag).getName());
-            dispatchListenersAndMaybeRestart(flag.getId(), this::restartAndroid);
+            mSystemProperties.erase(flag.getName());
+            dispatchListenersAndMaybeRestart(
+                    flag.getName(),
+                    suppressRestart -> restartSystemUI(
+                            suppressRestart,
+                            "SysProp Flag \"" + flag.getNamespace() + "."
+                                    + flag.getName() + "\" reset to default."));
         } else {
-            eraseFlag(flag.getId());
+            eraseFlag(flag.getName());
         }
     }
 
     /** Erase a flag's overridden value if there is one. */
-    private void eraseFlag(int id) {
-        eraseInternal(id);
-        removeFromCache(id);
-        dispatchListenersAndMaybeRestart(id, this::restartSystemUI);
+    private void eraseFlag(String name) {
+        eraseInternal(name);
+        removeFromCache(name);
+        dispatchListenersAndMaybeRestart(
+                name,
+                suppressRestart -> restartSystemUI(
+                        suppressRestart, "Flag \"" + name + "\" reset to default"));
     }
 
-    private void dispatchListenersAndMaybeRestart(int id, Consumer<Boolean> restartAction) {
-        mFlagManager.dispatchListenersAndMaybeRestart(id, restartAction);
+    private void dispatchListenersAndMaybeRestart(String name, Consumer<Boolean> restartAction) {
+        mFlagManager.dispatchListenersAndMaybeRestart(name, restartAction);
     }
 
-    /** Works just like {@link #eraseFlag(int)} except that it doesn't restart SystemUI. */
+    /** Works just like {@link #eraseFlag(String)} except that it doesn't restart SystemUI. */
+    // TODO(b/265188950): Remove method this once ids are fully deprecated.
     private void eraseInternal(int id) {
-        // We can't actually "erase" things from sysprops, but we can set them to empty!
-        mSecureSettings.putStringForUser(mFlagManager.idToSettingsKey(id), "",
+        // We can't actually "erase" things from settings, but we can set them to empty!
+        mGlobalSettings.putStringForUser(mFlagManager.idToSettingsKey(id), "",
                 UserHandle.USER_CURRENT);
-        Log.i(TAG, "Erase id " + id);
+        Log.i(TAG, "Erase name " + id);
+    }
+
+    /** Works just like {@link #eraseFlag(String)} except that it doesn't restart SystemUI. */
+    private void eraseInternal(String name) {
+        // We can't actually "erase" things from settings, but we can set them to empty!
+        mGlobalSettings.putStringForUser(mFlagManager.nameToSettingsKey(name), "",
+                UserHandle.USER_CURRENT);
+        Log.i(TAG, "Erase name " + name);
     }
 
     @Override
@@ -321,32 +385,35 @@
         mFlagManager.removeListener(listener);
     }
 
-    private void restartSystemUI(boolean requestSuppress) {
+    private void restartSystemUI(boolean requestSuppress, String reason) {
         if (requestSuppress) {
             Log.i(TAG, "SystemUI Restart Suppressed");
             return;
         }
-        mRestarter.restartSystemUI();
+        mRestarter.restartSystemUI(reason);
     }
 
-    private void restartAndroid(boolean requestSuppress) {
+    private void restartAndroid(boolean requestSuppress, String reason) {
         if (requestSuppress) {
             Log.i(TAG, "Android Restart Suppressed");
             return;
         }
-        mRestarter.restartAndroid();
+        mRestarter.restartAndroid(reason);
     }
 
     void setBooleanFlagInternal(Flag<?> flag, boolean value) {
         if (flag instanceof BooleanFlag) {
-            setFlagValue(flag.getId(), value, BooleanFlagSerializer.INSTANCE);
+            setFlagValue(flag.getName(), value, BooleanFlagSerializer.INSTANCE);
         } else if (flag instanceof ResourceBooleanFlag) {
-            setFlagValue(flag.getId(), value, BooleanFlagSerializer.INSTANCE);
+            setFlagValue(flag.getName(), value, BooleanFlagSerializer.INSTANCE);
         } else if (flag instanceof SysPropBooleanFlag) {
             // Store SysProp flags in SystemProperties where they can read by outside parties.
             mSystemProperties.setBoolean(((SysPropBooleanFlag) flag).getName(), value);
-            dispatchListenersAndMaybeRestart(flag.getId(),
-                    FeatureFlagsDebug.this::restartAndroid);
+            dispatchListenersAndMaybeRestart(
+                    flag.getName(),
+                    suppressRestart -> restartSystemUI(
+                            suppressRestart,
+                            "Flag \"" + flag.getName() + "\" changed to " + value));
         } else {
             throw new IllegalArgumentException("Unknown flag type");
         }
@@ -354,9 +421,9 @@
 
     void setStringFlagInternal(Flag<?> flag, String value) {
         if (flag instanceof StringFlag) {
-            setFlagValue(flag.getId(), value, StringFlagSerializer.INSTANCE);
+            setFlagValue(flag.getName(), value, StringFlagSerializer.INSTANCE);
         } else if (flag instanceof ResourceStringFlag) {
-            setFlagValue(flag.getId(), value, StringFlagSerializer.INSTANCE);
+            setFlagValue(flag.getName(), value, StringFlagSerializer.INSTANCE);
         } else {
             throw new IllegalArgumentException("Unknown flag type");
         }
@@ -364,9 +431,9 @@
 
     void setIntFlagInternal(Flag<?> flag, int value) {
         if (flag instanceof IntFlag) {
-            setFlagValue(flag.getId(), value, IntFlagSerializer.INSTANCE);
+            setFlagValue(flag.getName(), value, IntFlagSerializer.INSTANCE);
         } else if (flag instanceof ResourceIntFlag) {
-            setFlagValue(flag.getId(), value, IntFlagSerializer.INSTANCE);
+            setFlagValue(flag.getName(), value, IntFlagSerializer.INSTANCE);
         } else {
             throw new IllegalArgumentException("Unknown flag type");
         }
@@ -405,17 +472,17 @@
                 Log.w(TAG, "No extras");
                 return;
             }
-            int id = extras.getInt(EXTRA_ID);
-            if (id <= 0) {
-                Log.w(TAG, "ID not set or less than  or equal to 0: " + id);
+            String name = extras.getString(EXTRA_NAME);
+            if (name == null || name.isEmpty()) {
+                Log.w(TAG, "NAME not set or is empty: " + name);
                 return;
             }
 
-            if (!mAllFlags.containsKey(id)) {
-                Log.w(TAG, "Tried to set unknown id: " + id);
+            if (!mAllFlags.containsKey(name)) {
+                Log.w(TAG, "Tried to set unknown name: " + name);
                 return;
             }
-            Flag<?> flag = mAllFlags.get(id);
+            Flag<?> flag = mAllFlags.get(name);
 
             if (!extras.containsKey(EXTRA_VALUE)) {
                 eraseFlag(flag);
@@ -452,13 +519,16 @@
 
             if (f instanceof ReleasedFlag) {
                 enabled = isEnabled((ReleasedFlag) f);
-                overridden = readBooleanFlagOverride(f.getId()) != null;
+                overridden = readBooleanFlagOverride(f.getName()) != null
+                            || readBooleanFlagOverride(f.getId()) != null;
             } else if (f instanceof UnreleasedFlag) {
                 enabled = isEnabled((UnreleasedFlag) f);
-                overridden = readBooleanFlagOverride(f.getId()) != null;
+                overridden = readBooleanFlagOverride(f.getName()) != null
+                            || readBooleanFlagOverride(f.getId()) != null;
             } else if (f instanceof ResourceBooleanFlag) {
                 enabled = isEnabled((ResourceBooleanFlag) f);
-                overridden = readBooleanFlagOverride(f.getId()) != null;
+                overridden = readBooleanFlagOverride(f.getName()) != null
+                            || readBooleanFlagOverride(f.getId()) != null;
             } else if (f instanceof SysPropBooleanFlag) {
                 // TODO(b/223379190): Teamfood not supported for sysprop flags yet.
                 enabled = isEnabled((SysPropBooleanFlag) f);
@@ -480,9 +550,9 @@
         }
     };
 
-    private void removeFromCache(int id) {
-        mBooleanFlagCache.remove(id);
-        mStringFlagCache.remove(id);
+    private void removeFromCache(String name) {
+        mBooleanFlagCache.remove(name);
+        mStringFlagCache.remove(name);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugRestarter.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugRestarter.kt
index 069e612..a6956a4 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugRestarter.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugRestarter.kt
@@ -29,6 +29,7 @@
 ) : Restarter {
 
     private var androidRestartRequested = false
+    private var pendingReason = ""
 
     val observer =
         object : WakefulnessLifecycle.Observer {
@@ -38,18 +39,20 @@
             }
         }
 
-    override fun restartSystemUI() {
+    override fun restartSystemUI(reason: String) {
         Log.d(FeatureFlagsDebug.TAG, "SystemUI Restart requested. Restarting on next screen off.")
-        scheduleRestart()
+        Log.i(FeatureFlagsDebug.TAG, reason)
+        scheduleRestart(reason)
     }
 
-    override fun restartAndroid() {
+    override fun restartAndroid(reason: String) {
         Log.d(FeatureFlagsDebug.TAG, "Android Restart requested. Restarting on next screen off.")
         androidRestartRequested = true
-        scheduleRestart()
+        scheduleRestart(reason)
     }
 
-    fun scheduleRestart() {
+    fun scheduleRestart(reason: String) {
+        pendingReason = reason
         if (wakefulnessLifecycle.wakefulness == WakefulnessLifecycle.WAKEFULNESS_ASLEEP) {
             restartNow()
         } else {
@@ -59,9 +62,9 @@
 
     private fun restartNow() {
         if (androidRestartRequested) {
-            systemExitRestarter.restartAndroid()
+            systemExitRestarter.restartAndroid(pendingReason)
         } else {
-            systemExitRestarter.restartSystemUI()
+            systemExitRestarter.restartSystemUI(pendingReason)
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt
index b94d781..06ca0ad 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt
@@ -21,6 +21,8 @@
 import com.android.systemui.broadcast.BroadcastSender
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.util.InitializationChecker
+import com.android.systemui.util.concurrency.DelayableExecutor
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.ClassKey
@@ -34,7 +36,10 @@
     private val commandRegistry: CommandRegistry,
     private val flagCommand: FlagCommand,
     private val featureFlags: FeatureFlagsDebug,
-    private val broadcastSender: BroadcastSender
+    private val broadcastSender: BroadcastSender,
+    private val initializationChecker: InitializationChecker,
+    private val restartDozeListener: RestartDozeListener,
+    private val delayableExecutor: DelayableExecutor
 ) : CoreStartable {
 
     init {
@@ -46,8 +51,14 @@
     override fun start() {
         featureFlags.init()
         commandRegistry.registerCommand(FlagCommand.FLAG_COMMAND) { flagCommand }
-        val intent = Intent(FlagManager.ACTION_SYSUI_STARTED)
-        broadcastSender.sendBroadcast(intent)
+        if (initializationChecker.initializeComponents()) {
+            // protected broadcast should only be sent for the main process
+            val intent = Intent(FlagManager.ACTION_SYSUI_STARTED)
+            broadcastSender.sendBroadcast(intent)
+
+            restartDozeListener.init()
+            delayableExecutor.executeDelayed({ restartDozeListener.maybeRestartSleep() }, 1000)
+        }
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
index 8bddacc..9859ff6 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
@@ -21,18 +21,16 @@
 import static java.util.Objects.requireNonNull;
 
 import android.content.res.Resources;
-import android.util.SparseArray;
-import android.util.SparseBooleanArray;
 
 import androidx.annotation.NonNull;
 
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.util.DeviceConfigProxy;
 
 import org.jetbrains.annotations.NotNull;
 
 import java.io.PrintWriter;
+import java.util.HashMap;
 import java.util.Map;
 
 import javax.inject.Inject;
@@ -50,18 +48,18 @@
 
     private final Resources mResources;
     private final SystemPropertiesHelper mSystemProperties;
-    private final DeviceConfigProxy mDeviceConfigProxy;
     private final ServerFlagReader mServerFlagReader;
     private final Restarter mRestarter;
-    private final Map<Integer, Flag<?>> mAllFlags;
-    SparseBooleanArray mBooleanCache = new SparseBooleanArray();
-    SparseArray<String> mStringCache = new SparseArray<>();
+    private final Map<String, Flag<?>> mAllFlags;
+    private final Map<String, Boolean> mBooleanCache = new HashMap<>();
+    private final Map<String, String> mStringCache = new HashMap<>();
 
     private final ServerFlagReader.ChangeListener mOnPropertiesChanged =
             new ServerFlagReader.ChangeListener() {
                 @Override
-                public void onChange() {
-                    mRestarter.restartSystemUI();
+                public void onChange(Flag<?> flag) {
+                    mRestarter.restartSystemUI(
+                            "Server flag change: " + flag.getNamespace() + "." + flag.getName());
                 }
             };
 
@@ -69,13 +67,11 @@
     public FeatureFlagsRelease(
             @Main Resources resources,
             SystemPropertiesHelper systemProperties,
-            DeviceConfigProxy deviceConfigProxy,
             ServerFlagReader serverFlagReader,
-            @Named(ALL_FLAGS) Map<Integer, Flag<?>> allFlags,
+            @Named(ALL_FLAGS) Map<String, Flag<?>> allFlags,
             Restarter restarter) {
         mResources = resources;
         mSystemProperties = systemProperties;
-        mDeviceConfigProxy = deviceConfigProxy;
         mServerFlagReader = serverFlagReader;
         mAllFlags = allFlags;
         mRestarter = restarter;
@@ -106,50 +102,48 @@
 
     @Override
     public boolean isEnabled(ResourceBooleanFlag flag) {
-        int cacheIndex = mBooleanCache.indexOfKey(flag.getId());
-        if (cacheIndex < 0) {
-            return isEnabled(flag.getId(), mResources.getBoolean(flag.getResourceId()));
+        if (!mBooleanCache.containsKey(flag.getName())) {
+            return isEnabled(flag.getName(), mResources.getBoolean(flag.getResourceId()));
         }
 
-        return mBooleanCache.valueAt(cacheIndex);
+        return mBooleanCache.get(flag.getName());
     }
 
     @Override
     public boolean isEnabled(SysPropBooleanFlag flag) {
-        int cacheIndex = mBooleanCache.indexOfKey(flag.getId());
-        if (cacheIndex < 0) {
+        if (!mBooleanCache.containsKey(flag.getName())) {
             return isEnabled(
-                    flag.getId(), mSystemProperties.getBoolean(flag.getName(), flag.getDefault()));
+                    flag.getName(),
+                    mSystemProperties.getBoolean(flag.getName(), flag.getDefault()));
         }
 
-        return mBooleanCache.valueAt(cacheIndex);
+        return mBooleanCache.get(flag.getName());
     }
 
-    private boolean isEnabled(int key, boolean defaultValue) {
-        mBooleanCache.append(key, defaultValue);
+    private boolean isEnabled(String name, boolean defaultValue) {
+        mBooleanCache.put(name, defaultValue);
         return defaultValue;
     }
 
     @NonNull
     @Override
     public String getString(@NonNull StringFlag flag) {
-        return getString(flag.getId(), flag.getDefault());
+        return getString(flag.getName(), flag.getDefault());
     }
 
     @NonNull
     @Override
     public String getString(@NonNull ResourceStringFlag flag) {
-        int cacheIndex = mStringCache.indexOfKey(flag.getId());
-        if (cacheIndex < 0) {
-            return getString(flag.getId(),
+        if (!mStringCache.containsKey(flag.getName())) {
+            return getString(flag.getName(),
                     requireNonNull(mResources.getString(flag.getResourceId())));
         }
 
-        return mStringCache.valueAt(cacheIndex);
+        return mStringCache.get(flag.getName());
     }
 
-    private String getString(int key, String defaultValue) {
-        mStringCache.append(key, defaultValue);
+    private String getString(String name, String defaultValue) {
+        mStringCache.put(name, defaultValue);
         return defaultValue;
     }
 
@@ -169,11 +163,17 @@
     public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
         pw.println("can override: false");
         Map<String, Flag<?>> knownFlags = FlagsFactory.INSTANCE.getKnownFlags();
+        pw.println("Booleans: ");
         for (Map.Entry<String, Flag<?>> nameToFlag : knownFlags.entrySet()) {
             Flag<?> flag = nameToFlag.getValue();
-            int id = flag.getId();
+            if (!(flag instanceof BooleanFlag)
+                    || !(flag instanceof ResourceBooleanFlag)
+                    || !(flag instanceof SysPropBooleanFlag)) {
+                continue;
+            }
+
             boolean def = false;
-            if (mBooleanCache.indexOfKey(flag.getId()) < 0) {
+            if (!mBooleanCache.containsKey(flag.getName())) {
                 if (flag instanceof SysPropBooleanFlag) {
                     SysPropBooleanFlag f = (SysPropBooleanFlag) flag;
                     def = mSystemProperties.getBoolean(f.getName(), f.getDefault());
@@ -185,15 +185,32 @@
                     def = f.getDefault();
                 }
             }
-            pw.println("  sysui_flag_" + id + ": " + (mBooleanCache.get(id, def)));
+            pw.println(
+                    "  " + flag.getName() + ": "
+                            + (mBooleanCache.getOrDefault(flag.getName(), def)));
         }
-        int numStrings = mStringCache.size();
-        pw.println("Strings: " + numStrings);
-        for (int i = 0; i < numStrings; i++) {
-            final int id = mStringCache.keyAt(i);
-            final String value = mStringCache.valueAt(i);
-            final int length = value.length();
-            pw.println("  sysui_flag_" + id + ": [length=" + length + "] \"" + value + "\"");
+
+        pw.println("Strings: ");
+        for (Map.Entry<String, Flag<?>> nameToFlag : knownFlags.entrySet()) {
+            Flag<?> flag = nameToFlag.getValue();
+            if (!(flag instanceof StringFlag)
+                    || !(flag instanceof ResourceStringFlag)) {
+                continue;
+            }
+
+            String def = "";
+            if (!mBooleanCache.containsKey(flag.getName())) {
+                if (flag instanceof ResourceStringFlag) {
+                    ResourceStringFlag f = (ResourceStringFlag) flag;
+                    def = mResources.getString(f.getResourceId());
+                } else if (flag instanceof StringFlag) {
+                    StringFlag f = (StringFlag) flag;
+                    def = f.getDefault();
+                }
+            }
+            String value = mStringCache.getOrDefault(flag.getName(), def);
+            pw.println(
+                    "  " + flag.getName() + ": [length=" + value.length() + "] \"" + value + "\"");
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseRestarter.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseRestarter.kt
index 7ff3876..c08266c 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseRestarter.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseRestarter.kt
@@ -36,41 +36,43 @@
 ) : Restarter {
     var listenersAdded = false
     var pendingRestart: Runnable? = null
+    private var pendingReason = ""
     var androidRestartRequested = false
 
     val observer =
         object : WakefulnessLifecycle.Observer {
             override fun onFinishedGoingToSleep() {
-                scheduleRestart()
+                scheduleRestart(pendingReason)
             }
         }
 
     val batteryCallback =
         object : BatteryController.BatteryStateChangeCallback {
             override fun onBatteryLevelChanged(level: Int, pluggedIn: Boolean, charging: Boolean) {
-                scheduleRestart()
+                scheduleRestart(pendingReason)
             }
         }
 
-    override fun restartSystemUI() {
+    override fun restartSystemUI(reason: String) {
         Log.d(
             FeatureFlagsDebug.TAG,
             "SystemUI Restart requested. Restarting when plugged in and idle."
         )
-        scheduleRestart()
+        scheduleRestart(reason)
     }
 
-    override fun restartAndroid() {
+    override fun restartAndroid(reason: String) {
         Log.d(
             FeatureFlagsDebug.TAG,
             "Android Restart requested. Restarting when plugged in and idle."
         )
         androidRestartRequested = true
-        scheduleRestart()
+        scheduleRestart(reason)
     }
 
-    private fun scheduleRestart() {
+    private fun scheduleRestart(reason: String) {
         // Don't bother adding listeners twice.
+        pendingReason = reason
         if (!listenersAdded) {
             listenersAdded = true
             wakefulnessLifecycle.addObserver(observer)
@@ -91,9 +93,9 @@
     private fun restartNow() {
         Log.d(FeatureFlagsRelease.TAG, "Restarting due to systemui flag change")
         if (androidRestartRequested) {
-            systemExitRestarter.restartAndroid()
+            systemExitRestarter.restartAndroid(pendingReason)
         } else {
-            systemExitRestarter.restartSystemUI()
+            systemExitRestarter.restartSystemUI(pendingReason)
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseStartable.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseStartable.kt
index d088d74..133e67f 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseStartable.kt
@@ -18,6 +18,8 @@
 
 import com.android.systemui.CoreStartable
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.InitializationChecker
+import com.android.systemui.util.concurrency.DelayableExecutor
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.ClassKey
@@ -26,7 +28,13 @@
 
 class FeatureFlagsReleaseStartable
 @Inject
-constructor(dumpManager: DumpManager, featureFlags: FeatureFlags) : CoreStartable {
+constructor(
+    dumpManager: DumpManager,
+    featureFlags: FeatureFlags,
+    private val initializationChecker: InitializationChecker,
+    private val restartDozeListener: RestartDozeListener,
+    private val delayableExecutor: DelayableExecutor
+) : CoreStartable {
 
     init {
         dumpManager.registerCriticalDumpable(FeatureFlagsRelease.TAG) { pw, args ->
@@ -35,7 +43,10 @@
     }
 
     override fun start() {
-        // no-op
+        if (initializationChecker.initializeComponents()) {
+            restartDozeListener.init()
+            delayableExecutor.executeDelayed({ restartDozeListener.maybeRestartSleep() }, 1000)
+        }
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java b/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java
index b7fc0e4..daf9429 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java
@@ -39,12 +39,12 @@
     private final List<String> mOffCommands = List.of("false", "off", "0", "disable");
     private final List<String> mSetCommands = List.of("set", "put");
     private final FeatureFlagsDebug mFeatureFlags;
-    private final Map<Integer, Flag<?>> mAllFlags;
+    private final Map<String, Flag<?>> mAllFlags;
 
     @Inject
     FlagCommand(
             FeatureFlagsDebug featureFlags,
-            @Named(ALL_FLAGS) Map<Integer, Flag<?>> allFlags
+            @Named(ALL_FLAGS) Map<String, Flag<?>> allFlags
     ) {
         mFeatureFlags = featureFlags;
         mAllFlags = allFlags;
@@ -53,30 +53,22 @@
     @Override
     public void execute(@NonNull PrintWriter pw, @NonNull List<String> args) {
         if (args.size() == 0) {
-            pw.println("Error: no flag id supplied");
+            pw.println("Error: no flag name supplied");
             help(pw);
             pw.println();
             printKnownFlags(pw);
             return;
         }
 
-        int id = 0;
-        try {
-            id = Integer.parseInt(args.get(0));
-            if (!mAllFlags.containsKey(id)) {
-                pw.println("Unknown flag id: " + id);
-                pw.println();
-                printKnownFlags(pw);
-                return;
-            }
-        } catch (NumberFormatException e) {
-            id = flagNameToId(args.get(0));
-            if (id == 0) {
-                pw.println("Invalid flag. Must an integer id or flag name: " + args.get(0));
-                return;
-            }
+        String name = args.get(0);
+        if (!mAllFlags.containsKey(name)) {
+            pw.println("Unknown flag name: " + name);
+            pw.println();
+            printKnownFlags(pw);
+            return;
         }
-        Flag<?> flag = mAllFlags.get(id);
+
+        Flag<?> flag = mAllFlags.get(name);
 
         String cmd = "";
         if (args.size() > 1) {
@@ -117,7 +109,7 @@
                 return;
             }
 
-            pw.println("Flag " + id + " is " + newValue);
+            pw.println("Flag " + name + " is " + newValue);
             pw.flush();  // Next command will restart sysui, so flush before we do so.
             if (shouldSet) {
                 mFeatureFlags.setBooleanFlagInternal(flag, newValue);
@@ -136,11 +128,11 @@
                     return;
                 }
                 String value = args.get(2);
-                pw.println("Setting Flag " + id + " to " + value);
+                pw.println("Setting Flag " + name + " to " + value);
                 pw.flush();  // Next command will restart sysui, so flush before we do so.
                 mFeatureFlags.setStringFlagInternal(flag, args.get(2));
             } else {
-                pw.println("Flag " + id + " is " + getStringFlag(flag));
+                pw.println("Flag " + name + " is " + getStringFlag(flag));
             }
             return;
         } else if (isIntFlag(flag)) {
@@ -155,11 +147,11 @@
                     return;
                 }
                 int value = Integer.parseInt(args.get(2));
-                pw.println("Setting Flag " + id + " to " + value);
+                pw.println("Setting Flag " + name + " to " + value);
                 pw.flush();  // Next command will restart sysui, so flush before we do so.
                 mFeatureFlags.setIntFlagInternal(flag, value);
             } else {
-                pw.println("Flag " + id + " is " + getIntFlag(flag));
+                pw.println("Flag " + name + " is " + getIntFlag(flag));
             }
             return;
         }
@@ -182,8 +174,7 @@
     private boolean isBooleanFlag(Flag<?> flag) {
         return (flag instanceof BooleanFlag)
                 || (flag instanceof ResourceBooleanFlag)
-                || (flag instanceof SysPropFlag)
-                || (flag instanceof DeviceConfigBooleanFlag);
+                || (flag instanceof SysPropFlag);
     }
 
     private boolean isBooleanFlagEnabled(Flag<?> flag) {
@@ -252,15 +243,14 @@
         for (int i = 0; i < longestFieldName - "Flag Name".length() + 1; i++) {
             pw.print(" ");
         }
-        pw.println("ID   Value");
+        pw.println(" Value");
         for (int i = 0; i < longestFieldName; i++) {
             pw.print("=");
         }
-        pw.println(" ==== ========");
+        pw.println(" ========");
         for (String fieldName : fields.keySet()) {
             Flag<?> flag = fields.get(fieldName);
-            int id = flag.getId();
-            if (id == 0 || !mAllFlags.containsKey(id)) {
+            if (!mAllFlags.containsKey(flag.getName())) {
                 continue;
             }
             pw.print(fieldName);
@@ -268,9 +258,9 @@
             for (int i = 0; i < longestFieldName - fieldWidth + 1; i++) {
                 pw.print(" ");
             }
-            pw.printf("%-4d ", id);
+            pw.print(" ");
             if (isBooleanFlag(flag)) {
-                pw.println(isBooleanFlagEnabled(mAllFlags.get(id)));
+                pw.println(isBooleanFlagEnabled(flag));
             } else if (isStringFlag(flag)) {
                 pw.println(getStringFlag(flag));
             } else if (isIntFlag(flag)) {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 0749bcf..6bc1edd 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -59,7 +59,7 @@
         )
 
     // TODO(b/254512517): Tracking Bug
-    val FSI_REQUIRES_KEYGUARD = unreleasedFlag(110, "fsi_requires_keyguard", teamfood = true)
+    val FSI_REQUIRES_KEYGUARD = releasedFlag(110, "fsi_requires_keyguard")
 
     // TODO(b/259130119): Tracking Bug
     val FSI_ON_DND_UPDATE = unreleasedFlag(259130119, "fsi_on_dnd_update", teamfood = true)
@@ -68,7 +68,7 @@
     @JvmField val DISABLE_FSI = unreleasedFlag(265804648, "disable_fsi")
 
     // TODO(b/254512538): Tracking Bug
-    val INSTANT_VOICE_REPLY = unreleasedFlag(111, "instant_voice_reply", teamfood = true)
+    val INSTANT_VOICE_REPLY = releasedFlag(111, "instant_voice_reply")
 
     // TODO(b/254512425): Tracking Bug
     val NOTIFICATION_MEMORY_MONITOR_ENABLED =
@@ -85,7 +85,7 @@
     // TODO(b/259217907)
     @JvmField
     val NOTIFICATION_GROUP_DISMISSAL_ANIMATION =
-        unreleasedFlag(259217907, "notification_group_dismissal_animation", teamfood = true)
+        releasedFlag(259217907, "notification_group_dismissal_animation")
 
     // TODO(b/257506350): Tracking Bug
     @JvmField val FSI_CHROME = unreleasedFlag(117, "fsi_chrome")
@@ -100,7 +100,7 @@
     // TODO(b/260335638): Tracking Bug
     @JvmField
     val NOTIFICATION_INLINE_REPLY_ANIMATION =
-        unreleasedFlag(174148361, "notification_inline_reply_animation", teamfood = true)
+        releasedFlag(174148361, "notification_inline_reply_animation")
 
     val FILTER_UNSEEN_NOTIFS_ON_KEYGUARD =
         releasedFlag(254647461, "filter_unseen_notifs_on_keyguard", teamfood = true)
@@ -128,13 +128,6 @@
     val LOCKSCREEN_CUSTOM_CLOCKS = unreleasedFlag(207, "lockscreen_custom_clocks", teamfood = true)
 
     /**
-     * Flag to enable the usage of the new bouncer data source. This is a refactor of and eventual
-     * replacement of KeyguardBouncer.java.
-     */
-    // TODO(b/254512385): Tracking Bug
-    @JvmField val MODERN_BOUNCER = releasedFlag(208, "modern_bouncer")
-
-    /**
      * Whether the clock on a wide lock screen should use the new "stepping" animation for moving
      * the digits when the clock moves.
      */
@@ -194,12 +187,11 @@
 
     // TODO(b/262780002): Tracking Bug
     @JvmField
-    val REVAMPED_WALLPAPER_UI = unreleasedFlag(222, "revamped_wallpaper_ui", teamfood = false)
+    val REVAMPED_WALLPAPER_UI = unreleasedFlag(222, "revamped_wallpaper_ui", teamfood = true)
 
     /** A different path for unocclusion transitions back to keyguard */
     // TODO(b/262859270): Tracking Bug
-    @JvmField
-    val UNOCCLUSION_TRANSITION = unreleasedFlag(223, "unocclusion_transition", teamfood = true)
+    @JvmField val UNOCCLUSION_TRANSITION = releasedFlag(223, "unocclusion_transition")
 
     // flag for controlling auto pin confirmation and material u shapes in bouncer
     @JvmField
@@ -216,9 +208,17 @@
         unreleasedFlag(226, "enable_wallet_contextual_loyalty_cards", teamfood = false)
 
     // TODO(b/242908637): Tracking Bug
+    @JvmField val WALLPAPER_FULLSCREEN_PREVIEW = releasedFlag(227, "wallpaper_fullscreen_preview")
+
+    /** Whether the long-press gesture to open wallpaper picker is enabled. */
+    // TODO(b/266242192): Tracking Bug
     @JvmField
-    val WALLPAPER_FULLSCREEN_PREVIEW =
-        unreleasedFlag(227, "wallpaper_fullscreen_preview", teamfood = true)
+    val LOCK_SCREEN_LONG_PRESS_ENABLED =
+        unreleasedFlag(
+            228,
+            "lock_screen_long_press_enabled",
+            teamfood = true,
+        )
 
     // 300 - power menu
     // TODO(b/254512600): Tracking Bug
@@ -229,7 +229,6 @@
     // TODO(b/254513100): Tracking Bug
     val SMARTSPACE_SHARED_ELEMENT_TRANSITION_ENABLED =
         releasedFlag(401, "smartspace_shared_element_transition_enabled")
-    val SMARTSPACE = resourceBooleanFlag(402, R.bool.flag_smartspace, "smartspace")
 
     // TODO(b/258517050): Clean up after the feature is launched.
     @JvmField
@@ -264,13 +263,19 @@
     val QS_SECONDARY_DATA_SUB_INFO =
         unreleasedFlag(508, "qs_secondary_data_sub_info", teamfood = true)
 
+    /** Enables Font Scaling Quick Settings tile */
+    // TODO(b/269341316): Tracking Bug
+    @JvmField
+    val ENABLE_FONT_SCALING_TILE = unreleasedFlag(509, "enable_font_scaling_tile", teamfood = false)
+
     // 600- status bar
 
     // TODO(b/256614753): Tracking Bug
-    val NEW_STATUS_BAR_MOBILE_ICONS = unreleasedFlag(606, "new_status_bar_mobile_icons")
+    val NEW_STATUS_BAR_MOBILE_ICONS =
+        unreleasedFlag(606, "new_status_bar_mobile_icons", teamfood = true)
 
     // TODO(b/256614210): Tracking Bug
-    val NEW_STATUS_BAR_WIFI_ICON = unreleasedFlag(607, "new_status_bar_wifi_icon")
+    val NEW_STATUS_BAR_WIFI_ICON = unreleasedFlag(607, "new_status_bar_wifi_icon", teamfood = true)
 
     // TODO(b/256614751): Tracking Bug
     val NEW_STATUS_BAR_MOBILE_ICONS_BACKEND =
@@ -311,9 +316,7 @@
     val SCREEN_CONTENTS_TRANSLATION = unreleasedFlag(803, "screen_contents_translation")
 
     // 804 - monochromatic themes
-    @JvmField
-    val MONOCHROMATIC_THEMES =
-        sysPropBooleanFlag(804, "persist.sysui.monochromatic", default = false)
+    @JvmField val MONOCHROMATIC_THEME = unreleasedFlag(804, "monochromatic", teamfood = true)
 
     // 900 - media
     // TODO(b/254512697): Tracking Bug
@@ -337,26 +340,33 @@
     // TODO(b/254513168): Tracking Bug
     @JvmField val UMO_SURFACE_RIPPLE = unreleasedFlag(907, "umo_surface_ripple")
 
-    @JvmField
-    val MEDIA_FALSING_PENALTY = unreleasedFlag(908, "media_falsing_media", teamfood = true)
+    @JvmField val MEDIA_FALSING_PENALTY = releasedFlag(908, "media_falsing_media")
 
     // TODO(b/261734857): Tracking Bug
     @JvmField val UMO_TURBULENCE_NOISE = unreleasedFlag(909, "umo_turbulence_noise")
 
     // TODO(b/263272731): Tracking Bug
-    val MEDIA_TTT_RECEIVER_SUCCESS_RIPPLE =
-        unreleasedFlag(910, "media_ttt_receiver_success_ripple", teamfood = true)
+    val MEDIA_TTT_RECEIVER_SUCCESS_RIPPLE = releasedFlag(910, "media_ttt_receiver_success_ripple")
 
     // TODO(b/263512203): Tracking Bug
     val MEDIA_EXPLICIT_INDICATOR = unreleasedFlag(911, "media_explicit_indicator", teamfood = true)
 
     // TODO(b/265813373): Tracking Bug
-    val MEDIA_TAP_TO_TRANSFER_DISMISS_GESTURE =
-        unreleasedFlag(912, "media_ttt_dismiss_gesture", teamfood = true)
+    val MEDIA_TAP_TO_TRANSFER_DISMISS_GESTURE = releasedFlag(912, "media_ttt_dismiss_gesture")
 
     // TODO(b/266157412): Tracking Bug
     val MEDIA_RETAIN_SESSIONS = unreleasedFlag(913, "media_retain_sessions")
 
+    // TODO(b/266739309): Tracking Bug
+    @JvmField
+    val MEDIA_RECOMMENDATION_CARD_UPDATE = unreleasedFlag(914, "media_recommendation_card_update")
+
+    // TODO(b/267007629): Tracking Bug
+    val MEDIA_RESUME_PROGRESS = unreleasedFlag(915, "media_resume_progress")
+
+    // TODO(b/267166152) : Tracking Bug
+    val MEDIA_RETAIN_RECOMMENDATIONS = unreleasedFlag(916, "media_retain_recommendations")
+
     // 1000 - dock
     val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag(1000, "simulate_dock_through_charging")
 
@@ -438,6 +448,18 @@
             teamfood = false
         )
 
+    // TODO(b/198643358): Tracking bug
+    @Keep
+    @JvmField
+    val ENABLE_PIP_SIZE_LARGE_SCREEN =
+        sysPropBooleanFlag(1114, "persist.wm.debug.enable_pip_size_large_screen", default = false)
+
+    // TODO(b/265998256): Tracking bug
+    @Keep
+    @JvmField
+    val ENABLE_PIP_APP_ICON_OVERLAY =
+        sysPropBooleanFlag(1115, "persist.wm.debug.enable_pip_app_icon_overlay", default = false)
+
     // 1200 - predictive back
     @Keep
     @JvmField
@@ -447,7 +469,7 @@
     @Keep
     @JvmField
     val WM_ENABLE_PREDICTIVE_BACK_ANIM =
-        sysPropBooleanFlag(1201, "persist.wm.debug.predictive_back_anim", default = false)
+        sysPropBooleanFlag(1201, "persist.wm.debug.predictive_back_anim", default = true)
 
     @Keep
     @JvmField
@@ -455,8 +477,7 @@
         sysPropBooleanFlag(1202, "persist.wm.debug.predictive_back_always_enforce", default = false)
 
     // TODO(b/254512728): Tracking Bug
-    @JvmField
-    val NEW_BACK_AFFORDANCE = unreleasedFlag(1203, "new_back_affordance", teamfood = false)
+    @JvmField val NEW_BACK_AFFORDANCE = unreleasedFlag(1203, "new_back_affordance", teamfood = true)
 
     // TODO(b/255854141): Tracking Bug
     @JvmField
@@ -475,12 +496,12 @@
     // TODO(b/238475428): Tracking Bug
     @JvmField
     val WM_SHADE_ALLOW_BACK_GESTURE =
-        unreleasedFlag(1207, "persist.wm.debug.shade_allow_back_gesture", teamfood = false)
+        sysPropBooleanFlag(1207, "persist.wm.debug.shade_allow_back_gesture", default = false)
 
     // TODO(b/238475428): Tracking Bug
     @JvmField
     val WM_SHADE_ANIMATE_BACK_GESTURE =
-        unreleasedFlag(1208, "persist.wm.debug.shade_animate_back_gesture", teamfood = true)
+        unreleasedFlag(1208, "persist.wm.debug.shade_animate_back_gesture", teamfood = false)
 
     // TODO(b/265639042): Tracking Bug
     @JvmField
@@ -490,8 +511,19 @@
     // 1300 - screenshots
     // TODO(b/254513155): Tracking Bug
     @JvmField
-    val SCREENSHOT_WORK_PROFILE_POLICY =
-        unreleasedFlag(1301, "screenshot_work_profile_policy", teamfood = true)
+    val SCREENSHOT_WORK_PROFILE_POLICY = releasedFlag(1301, "screenshot_work_profile_policy")
+
+    // TODO(b/264916608): Tracking Bug
+    @JvmField val SCREENSHOT_METADATA = unreleasedFlag(1302, "screenshot_metadata", teamfood = true)
+
+    // TODO(b/266955521): Tracking bug
+    @JvmField
+    val SCREENSHOT_DETECTION = unreleasedFlag(1303, "screenshot_detection", teamfood = true)
+
+    // TODO(b/268484562): Tracking bug
+    @JvmField
+    val SCREENSHOT_METADATA_REFACTOR =
+        unreleasedFlag(1305, "screenshot_metadata_refactor", teamfood = true)
 
     // 1400 - columbus
     // TODO(b/254512756): Tracking Bug
@@ -501,9 +533,25 @@
     val QUICK_TAP_FLOW_FRAMEWORK =
         unreleasedFlag(1401, "quick_tap_flow_framework", teamfood = false)
 
-    // 1500 - chooser
+    // 1500 - chooser aka sharesheet
     // TODO(b/254512507): Tracking Bug
-    val CHOOSER_UNBUNDLED = unreleasedFlag(1500, "chooser_unbundled", teamfood = true)
+    val CHOOSER_UNBUNDLED = releasedFlag(1500, "chooser_unbundled")
+
+    // TODO(b/266983432) Tracking Bug
+    val SHARESHEET_CUSTOM_ACTIONS =
+        unreleasedFlag(1501, "sharesheet_custom_actions", teamfood = true)
+
+    // TODO(b/266982749) Tracking Bug
+    val SHARESHEET_RESELECTION_ACTION =
+        unreleasedFlag(1502, "sharesheet_reselection_action", teamfood = true)
+
+    // TODO(b/266983474) Tracking Bug
+    val SHARESHEET_IMAGE_AND_TEXT_PREVIEW =
+        unreleasedFlag(1503, "sharesheet_image_text_preview", teamfood = true)
+
+    // TODO(b/267355521) Tracking Bug
+    val SHARESHEET_SCROLLABLE_IMAGE_PREVIEW =
+        unreleasedFlag(1504, "sharesheet_scrollable_image_preview")
 
     // 1600 - accessibility
     @JvmField
@@ -511,23 +559,37 @@
         unreleasedFlag(1600, "a11y_floating_menu_fling_spring_animations")
 
     // 1700 - clipboard
-    @JvmField val CLIPBOARD_OVERLAY_REFACTOR = releasedFlag(1700, "clipboard_overlay_refactor")
     @JvmField val CLIPBOARD_REMOTE_BEHAVIOR = releasedFlag(1701, "clipboard_remote_behavior")
+    // TODO(b/267162944): Tracking bug
+    @JvmField
+    val CLIPBOARD_MINIMIZED_LAYOUT = unreleasedFlag(1702, "clipboard_data_model", teamfood = true)
 
     // 1800 - shade container
     @JvmField
     val LEAVE_SHADE_OPEN_FOR_BUGREPORT = releasedFlag(1800, "leave_shade_open_for_bugreport")
+    // TODO(b/265944639): Tracking Bug
+    @JvmField val DUAL_SHADE = releasedFlag(1801, "dual_shade")
 
     // 1900
     @JvmField val NOTE_TASKS = unreleasedFlag(1900, "keycode_flag")
 
     // 2000 - device controls
-    @Keep @JvmField val USE_APP_PANELS = unreleasedFlag(2000, "use_app_panels", teamfood = true)
+    @Keep @JvmField val USE_APP_PANELS = releasedFlag(2000, "use_app_panels", teamfood = true)
 
     @JvmField
     val APP_PANELS_ALL_APPS_ALLOWED =
         unreleasedFlag(2001, "app_panels_all_apps_allowed", teamfood = true)
 
+    @JvmField
+    val CONTROLS_MANAGEMENT_NEW_FLOWS =
+        unreleasedFlag(2002, "controls_management_new_flows", teamfood = true)
+
+    // Enables removing app from Home control panel as a part of a new flow
+    // TODO(b/269132640): Tracking Bug
+    @JvmField
+    val APP_PANELS_REMOVE_APPS_ALLOWED =
+        unreleasedFlag(2003, "app_panels_remove_apps_allowed", teamfood = false)
+
     // 2100 - Falsing Manager
     @JvmField val FALSING_FOR_LONG_TAPS = releasedFlag(2100, "falsing_for_long_taps")
 
@@ -538,7 +600,8 @@
     @JvmField val UDFPS_ELLIPSE_DETECTION = unreleasedFlag(2202, "udfps_ellipse_detection")
 
     // 2300 - stylus
-    @JvmField val TRACK_STYLUS_EVER_USED = unreleasedFlag(2300, "track_stylus_ever_used")
+    @JvmField
+    val TRACK_STYLUS_EVER_USED = unreleasedFlag(2300, "track_stylus_ever_used", teamfood = true)
     @JvmField val ENABLE_STYLUS_CHARGING_UI = unreleasedFlag(2301, "enable_stylus_charging_ui")
     @JvmField
     val ENABLE_USI_BATTERY_NOTIFICATIONS = unreleasedFlag(2302, "enable_usi_battery_notifications")
@@ -563,7 +626,7 @@
     // TODO(b/20911786): Tracking Bug
     @JvmField
     val OUTPUT_SWITCHER_SHOW_API_ENABLED =
-        unreleasedFlag(2503, "output_switcher_show_api_enabled", teamfood = true)
+        releasedFlag(2503, "output_switcher_show_api_enabled", teamfood = true)
 
     // 2700 - unfold transitions
     // TODO(b/265764985): Tracking Bug
@@ -575,7 +638,11 @@
     // TODO(b259590361): Tracking bug
     val EXPERIMENTAL_FLAG = unreleasedFlag(2, "exp_flag_release")
 
-    // 2600 - keyboard shortcut
+    // 2600 - keyboard
     // TODO(b/259352579): Tracking Bug
     @JvmField val SHORTCUT_LIST_SEARCH_LAYOUT = unreleasedFlag(2600, "shortcut_list_search_layout")
+
+    // TODO(b/259428678): Tracking Bug
+    @JvmField
+    val KEYBOARD_BACKLIGHT_INDICATOR = unreleasedFlag(2601, "keyboard_backlight_indicator")
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagsCommonModule.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagsCommonModule.kt
index 8442230..0054d26 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagsCommonModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagsCommonModule.kt
@@ -28,8 +28,8 @@
         @JvmStatic
         @Provides
         @Named(ALL_FLAGS)
-        fun providesAllFlags(): Map<Int, Flag<*>> {
-            return FlagsFactory.knownFlags.map { it.value.id to it.value }.toMap()
+        fun providesAllFlags(): Map<String, Flag<*>> {
+            return FlagsFactory.knownFlags
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/RestartDozeListener.kt b/packages/SystemUI/src/com/android/systemui/flags/RestartDozeListener.kt
new file mode 100644
index 0000000..bd74f4e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/flags/RestartDozeListener.kt
@@ -0,0 +1,72 @@
+/*
+ * 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.flags
+
+import android.os.PowerManager
+import android.util.Log
+import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.time.SystemClock
+import javax.inject.Inject
+
+@SysUISingleton
+class RestartDozeListener
+@Inject
+constructor(
+    private val settings: SecureSettings,
+    private val statusBarStateController: StatusBarStateController,
+    private val powerManager: PowerManager,
+    private val systemClock: SystemClock,
+) {
+
+    companion object {
+        @VisibleForTesting val RESTART_NAP_KEY = "restart_nap_after_start"
+    }
+
+    private var inited = false
+
+    val listener =
+        object : StatusBarStateController.StateListener {
+            override fun onDreamingChanged(isDreaming: Boolean) {
+                settings.putBool(RESTART_NAP_KEY, isDreaming)
+            }
+        }
+
+    fun init() {
+        if (inited) {
+            return
+        }
+        inited = true
+
+        statusBarStateController.addCallback(listener)
+    }
+
+    fun destroy() {
+        statusBarStateController.removeCallback(listener)
+    }
+
+    fun maybeRestartSleep() {
+        if (settings.getBool(RESTART_NAP_KEY, false)) {
+            Log.d("RestartDozeListener", "Restarting sleep state")
+            powerManager.wakeUp(systemClock.uptimeMillis())
+            powerManager.goToSleep(systemClock.uptimeMillis())
+            settings.putBool(RESTART_NAP_KEY, false)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Restarter.kt b/packages/SystemUI/src/com/android/systemui/flags/Restarter.kt
index ce8b821..9c67795 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Restarter.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Restarter.kt
@@ -16,7 +16,7 @@
 package com.android.systemui.flags
 
 interface Restarter {
-    fun restartSystemUI()
+    fun restartSystemUI(reason: String)
 
-    fun restartAndroid()
+    fun restartAndroid(reason: String)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt b/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt
index ae05c46..9b748d0 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt
@@ -17,8 +17,10 @@
 package com.android.systemui.flags
 
 import android.provider.DeviceConfig
+import android.util.Log
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.TestHarness
 import com.android.systemui.util.DeviceConfigProxy
 import dagger.Module
 import dagger.Provides
@@ -35,21 +37,28 @@
     fun listenForChanges(values: Collection<Flag<*>>, listener: ChangeListener)
 
     interface ChangeListener {
-        fun onChange()
+        fun onChange(flag: Flag<*>)
     }
 }
 
 class ServerFlagReaderImpl @Inject constructor(
     private val namespace: String,
     private val deviceConfig: DeviceConfigProxy,
-    @Background private val executor: Executor
+    @Background private val executor: Executor,
+    @TestHarness private val isTestHarness: Boolean
 ) : ServerFlagReader {
 
+    private val TAG = "ServerFlagReader"
+
     private val listeners =
         mutableListOf<Pair<ServerFlagReader.ChangeListener, Collection<Flag<*>>>>()
 
     private val onPropertiesChangedListener = object : DeviceConfig.OnPropertiesChangedListener {
         override fun onPropertiesChanged(properties: DeviceConfig.Properties) {
+            if (isTestHarness) {
+                Log.w(TAG, "Ignore server flag changes in Test Harness mode.")
+                return
+            }
             if (properties.namespace != namespace) {
                 return
             }
@@ -57,8 +66,8 @@
             for ((listener, flags) in listeners) {
                 propLoop@ for (propName in properties.keyset) {
                     for (flag in flags) {
-                        if (propName == getServerOverrideName(flag.id)) {
-                            listener.onChange()
+                        if (propName == flag.name) {
+                            listener.onChange(flag)
                             break@propLoop
                         }
                     }
@@ -94,10 +103,6 @@
         }
         listeners.add(Pair(listener, flags))
     }
-
-    private fun getServerOverrideName(flagId: Int): String {
-        return "flag_override_$flagId"
-    }
 }
 
 @Module
@@ -110,10 +115,11 @@
         @SysUISingleton
         fun bindsReader(
             deviceConfig: DeviceConfigProxy,
-            @Background executor: Executor
+            @Background executor: Executor,
+            @TestHarness isTestHarness: Boolean
         ): ServerFlagReader {
             return ServerFlagReaderImpl(
-                SYSUI_NAMESPACE, deviceConfig, executor
+                SYSUI_NAMESPACE, deviceConfig, executor, isTestHarness
             )
         }
     }
@@ -138,7 +144,7 @@
         for ((listener, flags) in listeners) {
             flagLoop@ for (flag in flags) {
                 if (name == flag.name) {
-                    listener.onChange()
+                    listener.onChange(flag)
                     break@flagLoop
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/SystemExitRestarter.kt b/packages/SystemUI/src/com/android/systemui/flags/SystemExitRestarter.kt
index 89daa64..46e28a7 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/SystemExitRestarter.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/SystemExitRestarter.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.flags
 
+import android.util.Log
 import com.android.internal.statusbar.IStatusBarService
 import javax.inject.Inject
 
@@ -24,11 +25,13 @@
 constructor(
     private val barService: IStatusBarService,
 ) : Restarter {
-    override fun restartAndroid() {
+    override fun restartAndroid(reason: String) {
+        Log.d(FeatureFlagsDebug.TAG, "Restarting Android: " + reason)
         barService.restart()
     }
 
-    override fun restartSystemUI() {
+    override fun restartSystemUI(reason: String) {
+        Log.d(FeatureFlagsDebug.TAG, "Restarting SystemUI: " + reason)
         System.exit(0)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/ExtensionFragmentListener.java b/packages/SystemUI/src/com/android/systemui/fragments/ExtensionFragmentListener.java
index 18fb423..98896b1 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/ExtensionFragmentListener.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/ExtensionFragmentListener.java
@@ -37,9 +37,14 @@
     private final int mId;
     private String mOldClass;
 
-    private ExtensionFragmentListener(View view, String tag, int id, Extension<T> extension) {
+    private ExtensionFragmentListener(
+            FragmentService fragmentService,
+            View view,
+            String tag,
+            int id,
+            Extension<T> extension) {
         mTag = tag;
-        mFragmentHostManager = FragmentHostManager.get(view);
+        mFragmentHostManager = fragmentService.getFragmentHostManager(view);
         mExtension = extension;
         mId = id;
         mFragmentHostManager.getFragmentManager().beginTransaction()
@@ -61,8 +66,13 @@
         mExtension.clearItem(true);
     }
 
-    public static <T> void attachExtensonToFragment(View view, String tag, int id,
+    public static <T> void attachExtensonToFragment(
+            FragmentService fragmentService,
+            View view,
+            String tag,
+            int id,
             Extension<T> extension) {
-        extension.addCallback(new ExtensionFragmentListener(view, tag, id, extension));
+        extension.addCallback(
+                new ExtensionFragmentListener(fragmentService, view, tag, id, extension));
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
index 9c7411b..6a27ee7 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
@@ -36,7 +36,6 @@
 import androidx.annotation.NonNull;
 
 import com.android.settingslib.applications.InterestingConfigChanges;
-import com.android.systemui.Dependency;
 import com.android.systemui.plugins.Plugin;
 import com.android.systemui.util.leak.LeakDetector;
 
@@ -46,12 +45,17 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+
 public class FragmentHostManager {
 
     private final Handler mHandler = new Handler(Looper.getMainLooper());
     private final Context mContext;
     private final HashMap<String, ArrayList<FragmentListener>> mListeners = new HashMap<>();
     private final View mRootView;
+    private final LeakDetector mLeakDetector;
     private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
             ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE
                     | ActivityInfo.CONFIG_ASSETS_PATHS);
@@ -61,14 +65,24 @@
     private FragmentController mFragments;
     private FragmentLifecycleCallbacks mLifecycleCallbacks;
 
-    FragmentHostManager(FragmentService manager, View rootView) {
+    @AssistedInject
+    FragmentHostManager(
+            @Assisted View rootView,
+            FragmentService manager,
+            LeakDetector leakDetector) {
         mContext = rootView.getContext();
         mManager = manager;
         mRootView = rootView;
+        mLeakDetector = leakDetector;
         mConfigChanges.applyNewConfig(mContext.getResources());
         createFragmentHost(null);
     }
 
+    @AssistedFactory
+    public interface Factory {
+        FragmentHostManager create(View rootView);
+    }
+
     private void createFragmentHost(Parcelable savedState) {
         mFragments = FragmentController.createController(new HostCallbacks());
         mFragments.attachHost(null);
@@ -86,7 +100,7 @@
 
             @Override
             public void onFragmentDestroyed(FragmentManager fm, Fragment f) {
-                Dependency.get(LeakDetector.class).trackGarbage(f);
+                mLeakDetector.trackGarbage(f);
             }
         };
         mFragments.getFragmentManager().registerFragmentLifecycleCallbacks(mLifecycleCallbacks,
@@ -211,19 +225,6 @@
         }
     }
 
-    public static FragmentHostManager get(View view) {
-        try {
-            return Dependency.get(FragmentService.class).getFragmentHostManager(view);
-        } catch (ClassCastException e) {
-            // TODO: Some auto handling here?
-            throw e;
-        }
-    }
-
-    public static void removeAndDestroy(View view) {
-        Dependency.get(FragmentService.class).removeAndDestroy(view);
-    }
-
     public void reloadFragments() {
         Trace.beginSection("FrargmentHostManager#reloadFragments");
         // Save the old state.
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
index fe945fb..d302b13a 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
@@ -53,6 +53,7 @@
      */
     private final ArrayMap<String, FragmentInstantiationInfo> mInjectionMap = new ArrayMap<>();
     private final Handler mHandler = new Handler();
+    private final FragmentHostManager.Factory mFragmentHostManagerFactory;
 
     private ConfigurationController.ConfigurationListener mConfigurationListener =
             new ConfigurationController.ConfigurationListener() {
@@ -67,8 +68,10 @@
     @Inject
     public FragmentService(
             FragmentCreator.Factory fragmentCreatorFactory,
+            FragmentHostManager.Factory fragmentHostManagerFactory,
             ConfigurationController configurationController,
             DumpManager dumpManager) {
+        mFragmentHostManagerFactory = fragmentHostManagerFactory;
         addFragmentInstantiationProvider(fragmentCreatorFactory.build());
         configurationController.addCallback(mConfigurationListener);
 
@@ -152,7 +155,7 @@
 
         public FragmentHostState(View view) {
             mView = view;
-            mFragmentHostManager = new FragmentHostManager(FragmentService.this, mView);
+            mFragmentHostManager = mFragmentHostManagerFactory.create(mView);
         }
 
         public void sendConfigurationChange(Configuration newConfig) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardModule.kt b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardModule.kt
new file mode 100644
index 0000000..e9b8908
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardModule.kt
@@ -0,0 +1,22 @@
+/*
+ *  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.keyboard
+
+import dagger.Module
+
+@Module abstract class KeyboardModule
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/PhysicalKeyboardCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyboard/PhysicalKeyboardCoreStartable.kt
new file mode 100644
index 0000000..b0f9c4e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/PhysicalKeyboardCoreStartable.kt
@@ -0,0 +1,38 @@
+/*
+ *  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.keyboard
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import javax.inject.Inject
+
+/** A [CoreStartable] that launches components interested in physical keyboard interaction. */
+@SysUISingleton
+class PhysicalKeyboardCoreStartable
+@Inject
+constructor(
+    private val featureFlags: FeatureFlags,
+) : CoreStartable {
+    override fun start() {
+        if (featureFlags.isEnabled(Flags.KEYBOARD_BACKLIGHT_INDICATOR)) {
+            // TODO(b/268645743) start listening for keyboard backlight brightness
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
index 482138e..27a5974 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
@@ -31,10 +31,12 @@
 import android.util.Log
 import com.android.systemui.SystemUIAppComponentFactoryBase
 import com.android.systemui.SystemUIAppComponentFactoryBase.ContextAvailableCallback
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
 import com.android.systemui.keyguard.ui.preview.KeyguardRemotePreviewManager
 import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.runBlocking
 
 class CustomizationProvider :
@@ -42,6 +44,7 @@
 
     @Inject lateinit var interactor: KeyguardQuickAffordanceInteractor
     @Inject lateinit var previewManager: KeyguardRemotePreviewManager
+    @Inject @Main lateinit var mainDispatcher: CoroutineDispatcher
 
     private lateinit var contextAvailableCallback: ContextAvailableCallback
 
@@ -128,7 +131,7 @@
             throw UnsupportedOperationException()
         }
 
-        return insertSelection(values)
+        return runBlocking(mainDispatcher) { insertSelection(values) }
     }
 
     override fun query(
@@ -138,12 +141,14 @@
         selectionArgs: Array<out String>?,
         sortOrder: String?,
     ): Cursor? {
-        return when (uriMatcher.match(uri)) {
-            MATCH_CODE_ALL_AFFORDANCES -> runBlocking { queryAffordances() }
-            MATCH_CODE_ALL_SLOTS -> querySlots()
-            MATCH_CODE_ALL_SELECTIONS -> runBlocking { querySelections() }
-            MATCH_CODE_ALL_FLAGS -> queryFlags()
-            else -> null
+        return runBlocking(mainDispatcher) {
+            when (uriMatcher.match(uri)) {
+                MATCH_CODE_ALL_AFFORDANCES -> queryAffordances()
+                MATCH_CODE_ALL_SLOTS -> querySlots()
+                MATCH_CODE_ALL_SELECTIONS -> querySelections()
+                MATCH_CODE_ALL_FLAGS -> queryFlags()
+                else -> null
+            }
         }
     }
 
@@ -166,7 +171,7 @@
             throw UnsupportedOperationException()
         }
 
-        return deleteSelection(uri, selectionArgs)
+        return runBlocking(mainDispatcher) { deleteSelection(uri, selectionArgs) }
     }
 
     override fun call(method: String, arg: String?, extras: Bundle?): Bundle? {
@@ -184,7 +189,7 @@
         }
     }
 
-    private fun insertSelection(values: ContentValues?): Uri? {
+    private suspend fun insertSelection(values: ContentValues?): Uri? {
         if (values == null) {
             throw IllegalArgumentException("Cannot insert selection, no values passed in!")
         }
@@ -306,7 +311,7 @@
             }
     }
 
-    private fun querySlots(): Cursor {
+    private suspend fun querySlots(): Cursor {
         return MatrixCursor(
                 arrayOf(
                     Contract.LockScreenQuickAffordances.SlotTable.Columns.ID,
@@ -325,7 +330,7 @@
             }
     }
 
-    private fun queryFlags(): Cursor {
+    private suspend fun queryFlags(): Cursor {
         return MatrixCursor(
                 arrayOf(
                     Contract.FlagsTable.Columns.NAME,
@@ -348,7 +353,7 @@
             }
     }
 
-    private fun deleteSelection(
+    private suspend fun deleteSelection(
         uri: Uri,
         selectionArgs: Array<out String>?,
     ): Int {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
index 9235e10..0745456 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
@@ -24,6 +24,7 @@
 
 import androidx.annotation.IntDef;
 
+import com.android.keyguard.logging.KeyguardLogger;
 import com.android.systemui.Dumpable;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -64,6 +65,7 @@
             2000L + KeyguardIndicationTextView.Y_IN_DURATION;
 
     private final StatusBarStateController mStatusBarStateController;
+    private final KeyguardLogger mLogger;
     private final float mMaxAlpha;
     private final ColorStateList mInitialTextColorState;
 
@@ -85,7 +87,8 @@
     public KeyguardIndicationRotateTextViewController(
             KeyguardIndicationTextView view,
             @Main DelayableExecutor executor,
-            StatusBarStateController statusBarStateController
+            StatusBarStateController statusBarStateController,
+            KeyguardLogger logger
     ) {
         super(view);
         mMaxAlpha = view.getAlpha();
@@ -93,6 +96,7 @@
         mInitialTextColorState = mView != null
                 ? mView.getTextColors() : ColorStateList.valueOf(Color.WHITE);
         mStatusBarStateController = statusBarStateController;
+        mLogger = logger;
         init();
     }
 
@@ -259,6 +263,8 @@
         mLastIndicationSwitch = SystemClock.uptimeMillis();
         if (!TextUtils.equals(previousMessage, mCurrMessage)
                 || previousIndicationType != mCurrIndicationType) {
+            mLogger.logKeyguardSwitchIndication(type,
+                    mCurrMessage != null ? mCurrMessage.toString() : null);
             mView.switchIndication(mIndicationMessages.get(type));
         }
 
@@ -352,9 +358,10 @@
     @Override
     public void dump(PrintWriter pw, String[] args) {
         pw.println("KeyguardIndicationRotatingTextViewController:");
-        pw.println("    currentMessage=" + mView.getText());
+        pw.println("    currentTextViewMessage=" + mView.getText());
+        pw.println("    currentStoredMessage=" + mView.getMessage());
         pw.println("    dozing:" + mIsDozing);
-        pw.println("    queue:" + mIndicationQueue.toString());
+        pw.println("    queue:" + mIndicationQueue);
         pw.println("    showNextIndicationRunnable:" + mShowNextIndicationRunnable);
 
         if (hasIndications()) {
@@ -398,4 +405,40 @@
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface IndicationType{}
+
+    /**
+     * Get human-readable string representation of the indication type.
+     */
+    public static String indicationTypeToString(@IndicationType int type) {
+        switch (type) {
+            case INDICATION_TYPE_NONE:
+                return "none";
+            case INDICATION_TYPE_DISCLOSURE:
+                return "disclosure";
+            case INDICATION_TYPE_OWNER_INFO:
+                return "owner_info";
+            case INDICATION_TYPE_LOGOUT:
+                return "logout";
+            case INDICATION_TYPE_BATTERY:
+                return "battery";
+            case INDICATION_TYPE_ALIGNMENT:
+                return "alignment";
+            case INDICATION_TYPE_TRANSIENT:
+                return "transient";
+            case INDICATION_TYPE_TRUST:
+                return "trust";
+            case INDICATION_TYPE_PERSISTENT_UNLOCK_MESSAGE:
+                return "persistent_unlock_message";
+            case INDICATION_TYPE_USER_LOCKED:
+                return "user_locked";
+            case INDICATION_TYPE_REVERSE_CHARGING:
+                return "reverse_charging";
+            case INDICATION_TYPE_BIOMETRIC_MESSAGE:
+                return "biometric_message";
+            case INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP:
+                return "biometric_message_followup";
+            default:
+                return "unknown[" + type + "]";
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index f4a1227..47872d2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -17,7 +17,6 @@
 package com.android.systemui.keyguard;
 
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.RemoteAnimationTarget.MODE_CLOSING;
 import static android.view.RemoteAnimationTarget.MODE_OPENING;
 import static android.view.WindowManager.TRANSIT_CLOSE;
@@ -80,6 +79,7 @@
 import com.android.internal.policy.IKeyguardStateCallback;
 import com.android.keyguard.mediator.ScreenOnCoordinator;
 import com.android.systemui.SystemUIApplication;
+import com.android.systemui.settings.DisplayTracker;
 import com.android.wm.shell.transition.ShellTransitions;
 import com.android.wm.shell.transition.Transitions;
 
@@ -123,6 +123,7 @@
     private final KeyguardLifecyclesDispatcher mKeyguardLifecyclesDispatcher;
     private final ScreenOnCoordinator mScreenOnCoordinator;
     private final ShellTransitions mShellTransitions;
+    private final DisplayTracker mDisplayTracker;
 
     private static int newModeToLegacyMode(int newMode) {
         switch (newMode) {
@@ -286,12 +287,14 @@
     public KeyguardService(KeyguardViewMediator keyguardViewMediator,
                            KeyguardLifecyclesDispatcher keyguardLifecyclesDispatcher,
                            ScreenOnCoordinator screenOnCoordinator,
-                           ShellTransitions shellTransitions) {
+                           ShellTransitions shellTransitions,
+                           DisplayTracker displayTracker) {
         super();
         mKeyguardViewMediator = keyguardViewMediator;
         mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
         mScreenOnCoordinator = screenOnCoordinator;
         mShellTransitions = shellTransitions;
+        mDisplayTracker = displayTracker;
     }
 
     @Override
@@ -328,7 +331,7 @@
                         unoccludeAnimationAdapter);
             }
             ActivityTaskManager.getInstance().registerRemoteAnimationsForDisplay(
-                    DEFAULT_DISPLAY, definition);
+                    mDisplayTracker.getDefaultDisplayId(), definition);
             return;
         }
         if (sEnableRemoteKeyguardGoingAwayAnimation) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 228320b..f964cb3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -933,7 +933,7 @@
         }
 
         // The smartspace is not visible if the bouncer is showing, so don't shared element it.
-        if (keyguardStateController.isBouncerShowing) {
+        if (keyguardStateController.isPrimaryBouncerShowing) {
             return false
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 95bba13..6db1f89 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1148,12 +1148,12 @@
     private final KeyguardStateController.Callback mKeyguardStateControllerCallback =
             new KeyguardStateController.Callback() {
         @Override
-        public void onBouncerShowingChanged() {
+        public void onPrimaryBouncerShowingChanged() {
             synchronized (KeyguardViewMediator.this) {
-                if (mKeyguardStateController.isBouncerShowing()) {
+                if (mKeyguardStateController.isPrimaryBouncerShowing()) {
                     mPendingPinLock = false;
                 }
-                adjustStatusBarLocked(mKeyguardStateController.isBouncerShowing(), false);
+                adjustStatusBarLocked(mKeyguardStateController.isPrimaryBouncerShowing(), false);
             }
         }
     };
@@ -2445,15 +2445,28 @@
         }
         mKeyguardDisplayManager.show();
 
-        // schedule 4hr idle timeout after which non-strong biometrics (i.e. weak or convenience
-        // biometric) can't be used to unlock device until unlocking with strong biometric or
-        // primary auth (i.e. PIN/pattern/password)
-        mLockPatternUtils.scheduleNonStrongBiometricIdleTimeout(
-                KeyguardUpdateMonitor.getCurrentUser());
+        scheduleNonStrongBiometricIdleTimeout();
 
         Trace.endSection();
     }
 
+    /**
+     * Schedule 4-hour idle timeout for non-strong biometrics when the device is locked
+     */
+    private void scheduleNonStrongBiometricIdleTimeout() {
+        final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
+        // If unlocking with non-strong (i.e. weak or convenience) biometrics is possible, schedule
+        // 4hr idle timeout after which non-strong biometrics can't be used to unlock device until
+        // unlocking with strong biometric or primary auth (i.e. PIN/pattern/password)
+        if (mUpdateMonitor.isUnlockingWithNonStrongBiometricsPossible(currentUser)) {
+            if (DEBUG) {
+                Log.d(TAG, "scheduleNonStrongBiometricIdleTimeout: schedule an alarm for "
+                        + "currentUser=" + currentUser);
+            }
+            mLockPatternUtils.scheduleNonStrongBiometricIdleTimeout(currentUser);
+        }
+    }
+
     private final Runnable mKeyguardGoingAwayRunnable = new Runnable() {
         @Override
         public void run() {
@@ -2947,6 +2960,8 @@
             if (DEBUG) Log.d(TAG, "handleReset");
             mKeyguardViewControllerLazy.get().reset(true /* hideBouncerWhenShowing */);
         }
+
+        scheduleNonStrongBiometricIdleTimeout();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
index 76c2430..80675d3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
@@ -29,6 +29,7 @@
     const val DO_NOT_DISTURB = "do_not_disturb"
     const val FLASHLIGHT = "flashlight"
     const val HOME_CONTROLS = "home"
+    const val MUTE = "mute"
     const val QR_CODE_SCANNER = "qr_code_scanner"
     const val QUICK_ACCESS_WALLET = "wallet"
     const val VIDEO_CAMERA = "video_camera"
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
index f6e6d6b..c9f645d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
@@ -18,7 +18,9 @@
 package com.android.systemui.keyguard.data.quickaffordance
 
 import android.app.StatusBarManager
+import android.app.admin.DevicePolicyManager
 import android.content.Context
+import android.content.pm.PackageManager
 import com.android.systemui.R
 import com.android.systemui.animation.Expandable
 import com.android.systemui.camera.CameraGestureHelper
@@ -26,18 +28,25 @@
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.settings.UserTracker
 import dagger.Lazy
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.withContext
 
 @SysUISingleton
 class CameraQuickAffordanceConfig
 @Inject
 constructor(
     @Application private val context: Context,
+    private val packageManager: PackageManager,
     private val cameraGestureHelper: Lazy<CameraGestureHelper>,
+    private val userTracker: UserTracker,
+    private val devicePolicyManager: DevicePolicyManager,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
 ) : KeyguardQuickAffordanceConfig {
 
     override val key: String
@@ -78,7 +87,12 @@
         return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
     }
 
-    private fun isLaunchable(): Boolean {
-        return cameraGestureHelper.get().canCameraGestureBeLaunched(StatusBarState.KEYGUARD)
+    private suspend fun isLaunchable(): Boolean {
+        return packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY) &&
+            withContext(backgroundDispatcher) {
+                !devicePolicyManager.getCameraDisabled(null, userTracker.userId) &&
+                    devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId) and
+                        DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA == 0
+            }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
index a1cce5c..4556195 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
@@ -33,12 +33,13 @@
         @Provides
         @ElementsIntoSet
         fun quickAffordanceConfigs(
+            camera: CameraQuickAffordanceConfig,
             doNotDisturb: DoNotDisturbQuickAffordanceConfig,
             flashlight: FlashlightQuickAffordanceConfig,
             home: HomeControlsKeyguardQuickAffordanceConfig,
+            mute: MuteQuickAffordanceConfig,
             quickAccessWallet: QuickAccessWalletKeyguardQuickAffordanceConfig,
             qrCodeScanner: QrCodeScannerKeyguardQuickAffordanceConfig,
-            camera: CameraQuickAffordanceConfig,
             videoCamera: VideoCameraQuickAffordanceConfig,
         ): Set<KeyguardQuickAffordanceConfig> {
             return setOf(
@@ -46,6 +47,7 @@
                 doNotDisturb,
                 flashlight,
                 home,
+                mute,
                 quickAccessWallet,
                 qrCodeScanner,
                 videoCamera,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt
new file mode 100644
index 0000000..da91572
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.quickaffordance
+
+import android.content.Context
+import android.media.AudioManager
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.Observer
+import com.android.systemui.R
+import com.android.systemui.animation.Expandable
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
+import com.android.systemui.settings.UserFileManager
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.RingerModeTracker
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import javax.inject.Inject
+
+@SysUISingleton
+class MuteQuickAffordanceConfig @Inject constructor(
+        context: Context,
+        private val userTracker: UserTracker,
+        private val userFileManager: UserFileManager,
+        private val ringerModeTracker: RingerModeTracker,
+        private val audioManager: AudioManager,
+        @Application private val coroutineScope: CoroutineScope,
+        @Main private val mainDispatcher: CoroutineDispatcher,
+        @Background private val backgroundDispatcher: CoroutineDispatcher,
+) : KeyguardQuickAffordanceConfig {
+
+    private var previousNonSilentMode: Int = DEFAULT_LAST_NON_SILENT_VALUE
+
+    override val key: String = BuiltInKeyguardQuickAffordanceKeys.MUTE
+
+    override val pickerName: String = context.getString(R.string.volume_ringer_status_silent)
+
+    override val pickerIconResourceId: Int = R.drawable.ic_notifications_silence
+
+    override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> =
+        ringerModeTracker.ringerModeInternal.asFlow()
+            .onStart { getLastNonSilentRingerMode() }
+            .distinctUntilChanged()
+            .onEach { mode ->
+                // only remember last non-SILENT ringer mode
+                if (mode != null && mode != AudioManager.RINGER_MODE_SILENT) {
+                    previousNonSilentMode = mode
+                }
+            }
+            .map { mode ->
+                val (activationState, contentDescriptionRes) = when {
+                    audioManager.isVolumeFixed ->
+                        ActivationState.NotSupported to
+                            R.string.volume_ringer_hint_mute
+                    mode == AudioManager.RINGER_MODE_SILENT ->
+                        ActivationState.Active to
+                            R.string.volume_ringer_hint_mute
+                    else ->
+                        ActivationState.Inactive to
+                            R.string.volume_ringer_hint_unmute
+                }
+
+                KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+                    Icon.Resource(
+                        R.drawable.ic_notifications_silence,
+                        ContentDescription.Resource(contentDescriptionRes),
+                    ),
+                    activationState,
+                )
+            }
+            .flowOn(backgroundDispatcher)
+
+    override fun onTriggered(
+        expandable: Expandable?
+    ): KeyguardQuickAffordanceConfig.OnTriggeredResult {
+        coroutineScope.launch(backgroundDispatcher) {
+            val newRingerMode: Int
+            val currentRingerMode = audioManager.ringerModeInternal
+            if (currentRingerMode == AudioManager.RINGER_MODE_SILENT) {
+                newRingerMode = previousNonSilentMode
+            } else {
+                previousNonSilentMode = currentRingerMode
+                newRingerMode = AudioManager.RINGER_MODE_SILENT
+            }
+
+            if (currentRingerMode != newRingerMode) {
+                audioManager.ringerModeInternal = newRingerMode
+            }
+        }
+        return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+    }
+
+    override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState =
+        withContext(backgroundDispatcher) {
+            if (audioManager.isVolumeFixed) {
+                KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice
+            } else {
+                KeyguardQuickAffordanceConfig.PickerScreenState.Default()
+            }
+        }
+
+    /**
+     * Gets the last non-silent ringer mode from shared-preferences if it exists. This is
+     *  cached by [MuteQuickAffordanceCoreStartable] while this affordance is selected
+     */
+    private suspend fun getLastNonSilentRingerMode(): Int =
+        withContext(backgroundDispatcher) {
+            userFileManager.getSharedPreferences(
+                    MUTE_QUICK_AFFORDANCE_PREFS_FILE_NAME,
+                    Context.MODE_PRIVATE,
+                    userTracker.userId
+            ).getInt(
+                    LAST_NON_SILENT_RINGER_MODE_KEY,
+                    ringerModeTracker.ringerModeInternal.value ?: DEFAULT_LAST_NON_SILENT_VALUE
+            )
+        }
+
+    private fun <T> LiveData<T>.asFlow(): Flow<T?> =
+        conflatedCallbackFlow {
+            val observer = Observer { value: T -> trySend(value) }
+            observeForever(observer)
+            send(value)
+            awaitClose { removeObserver(observer) }
+        }.flowOn(mainDispatcher)
+
+    companion object {
+        const val LAST_NON_SILENT_RINGER_MODE_KEY = "key_last_non_silent_ringer_mode"
+        const val MUTE_QUICK_AFFORDANCE_PREFS_FILE_NAME = "quick_affordance_mute_ringer_mode_cache"
+        private const val DEFAULT_LAST_NON_SILENT_VALUE = AudioManager.RINGER_MODE_NORMAL
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartable.kt
new file mode 100644
index 0000000..cd0805e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartable.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.quickaffordance
+
+import android.content.Context
+import android.media.AudioManager
+import androidx.lifecycle.Observer
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
+import com.android.systemui.settings.UserFileManager
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.RingerModeTracker
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+/**
+ * Store previous non-silent Ringer Mode into shared prefs to be used for Mute Lockscreen Shortcut
+ */
+@SysUISingleton
+class MuteQuickAffordanceCoreStartable @Inject constructor(
+    private val featureFlags: FeatureFlags,
+    private val userTracker: UserTracker,
+    private val ringerModeTracker: RingerModeTracker,
+    private val userFileManager: UserFileManager,
+    private val keyguardQuickAffordanceRepository: KeyguardQuickAffordanceRepository,
+    @Application private val coroutineScope: CoroutineScope,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
+) : CoreStartable {
+
+    private val observer = Observer(this::updateLastNonSilentRingerMode)
+
+    override fun start() {
+        if (!featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES)) return
+
+        // only listen to ringerModeInternal changes when Mute is one of the selected affordances
+        keyguardQuickAffordanceRepository
+            .selections
+            .map { selections ->
+                // determines if Mute is selected in any lockscreen shortcut position
+                val muteSelected: Boolean = selections.values.any { configList ->
+                    configList.any { config ->
+                        config.key == BuiltInKeyguardQuickAffordanceKeys.MUTE
+                    }
+                }
+                if (muteSelected) {
+                    ringerModeTracker.ringerModeInternal.observeForever(observer)
+                } else {
+                    ringerModeTracker.ringerModeInternal.removeObserver(observer)
+                }
+            }
+            .launchIn(coroutineScope)
+    }
+
+    private fun updateLastNonSilentRingerMode(lastRingerMode: Int) {
+        coroutineScope.launch(backgroundDispatcher) {
+            if (AudioManager.RINGER_MODE_SILENT != lastRingerMode) {
+                userFileManager.getSharedPreferences(
+                        MuteQuickAffordanceConfig.MUTE_QUICK_AFFORDANCE_PREFS_FILE_NAME,
+                        Context.MODE_PRIVATE,
+                        userTracker.userId
+                )
+                .edit()
+                .putInt(MuteQuickAffordanceConfig.LAST_NON_SILENT_RINGER_MODE_KEY, lastRingerMode)
+                .apply()
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt
index d9ec3b1..6f821a2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt
@@ -18,6 +18,7 @@
 package com.android.systemui.keyguard.data.quickaffordance
 
 import android.app.StatusBarManager
+import android.app.admin.DevicePolicyManager
 import android.content.Context
 import android.content.Intent
 import com.android.systemui.ActivityIntentHelper
@@ -29,10 +30,13 @@
 import com.android.systemui.common.shared.model.Icon
 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 javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.withContext
 
 @SysUISingleton
 class VideoCameraQuickAffordanceConfig
@@ -42,6 +46,8 @@
     private val cameraIntents: CameraIntentsWrapper,
     private val activityIntentHelper: ActivityIntentHelper,
     private val userTracker: UserTracker,
+    private val devicePolicyManager: DevicePolicyManager,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
 ) : KeyguardQuickAffordanceConfig {
 
     private val intent: Intent by lazy {
@@ -63,8 +69,8 @@
         get() = R.drawable.ic_videocam
 
     override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState>
-        get() =
-            flowOf(
+        get() = flow {
+            emit(
                 if (isLaunchable()) {
                     KeyguardQuickAffordanceConfig.LockScreenState.Visible(
                         icon =
@@ -77,6 +83,7 @@
                     KeyguardQuickAffordanceConfig.LockScreenState.Hidden
                 }
             )
+        }
 
     override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState {
         return if (isLaunchable()) {
@@ -95,11 +102,14 @@
         )
     }
 
-    private fun isLaunchable(): Boolean {
+    private suspend fun isLaunchable(): Boolean {
         return activityIntentHelper.getTargetActivityInfo(
             intent,
             userTracker.userId,
             true,
-        ) != null
+        ) != null &&
+            withContext(backgroundDispatcher) {
+                !devicePolicyManager.getCameraDisabled(null, userTracker.userId)
+            }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricRepository.kt
deleted file mode 100644
index 25d8f40..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricRepository.kt
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.keyguard.data.repository
-
-import android.app.admin.DevicePolicyManager
-import android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED
-import android.content.Context
-import android.content.IntentFilter
-import android.os.Looper
-import android.os.UserHandle
-import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.biometrics.AuthController
-import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.user.data.repository.UserRepository
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.flow.transformLatest
-
-/**
- * Acts as source of truth for biometric features.
- *
- * Abstracts-away data sources and their schemas so the rest of the app doesn't need to worry about
- * upstream changes.
- */
-interface BiometricRepository {
-    /** Whether any fingerprints are enrolled for the current user. */
-    val isFingerprintEnrolled: StateFlow<Boolean>
-
-    /**
-     * Whether the current user is allowed to use a strong biometric for device entry based on
-     * Android Security policies. If false, the user may be able to use primary authentication for
-     * device entry.
-     */
-    val isStrongBiometricAllowed: StateFlow<Boolean>
-
-    /** Whether fingerprint feature is enabled for the current user by the DevicePolicy */
-    val isFingerprintEnabledByDevicePolicy: StateFlow<Boolean>
-}
-
-@SysUISingleton
-class BiometricRepositoryImpl
-@Inject
-constructor(
-    context: Context,
-    lockPatternUtils: LockPatternUtils,
-    broadcastDispatcher: BroadcastDispatcher,
-    authController: AuthController,
-    userRepository: UserRepository,
-    devicePolicyManager: DevicePolicyManager,
-    @Application scope: CoroutineScope,
-    @Background backgroundDispatcher: CoroutineDispatcher,
-    @Main looper: Looper,
-) : BiometricRepository {
-
-    /** UserId of the current selected user. */
-    private val selectedUserId: Flow<Int> =
-        userRepository.selectedUserInfo.map { it.id }.distinctUntilChanged()
-
-    override val isFingerprintEnrolled: StateFlow<Boolean> =
-        selectedUserId
-            .flatMapLatest { userId ->
-                conflatedCallbackFlow {
-                    val callback =
-                        object : AuthController.Callback {
-                            override fun onEnrollmentsChanged(
-                                sensorBiometricType: BiometricType,
-                                userId: Int,
-                                hasEnrollments: Boolean
-                            ) {
-                                if (sensorBiometricType.isFingerprint) {
-                                    trySendWithFailureLogging(
-                                        hasEnrollments,
-                                        TAG,
-                                        "update fpEnrollment"
-                                    )
-                                }
-                            }
-                        }
-                    authController.addCallback(callback)
-                    awaitClose { authController.removeCallback(callback) }
-                }
-            }
-            .stateIn(
-                scope,
-                started = SharingStarted.Eagerly,
-                initialValue =
-                    authController.isFingerprintEnrolled(userRepository.getSelectedUserInfo().id)
-            )
-
-    override val isStrongBiometricAllowed: StateFlow<Boolean> =
-        selectedUserId
-            .flatMapLatest { currUserId ->
-                conflatedCallbackFlow {
-                    val callback =
-                        object : LockPatternUtils.StrongAuthTracker(context, looper) {
-                            override fun onStrongAuthRequiredChanged(userId: Int) {
-                                if (currUserId != userId) {
-                                    return
-                                }
-
-                                trySendWithFailureLogging(
-                                    isBiometricAllowedForUser(true, currUserId),
-                                    TAG
-                                )
-                            }
-
-                            override fun onIsNonStrongBiometricAllowedChanged(userId: Int) {
-                                // no-op
-                            }
-                        }
-                    lockPatternUtils.registerStrongAuthTracker(callback)
-                    awaitClose { lockPatternUtils.unregisterStrongAuthTracker(callback) }
-                }
-            }
-            .stateIn(
-                scope,
-                started = SharingStarted.Eagerly,
-                initialValue =
-                    lockPatternUtils.isBiometricAllowedForUser(
-                        userRepository.getSelectedUserInfo().id
-                    )
-            )
-
-    override val isFingerprintEnabledByDevicePolicy: StateFlow<Boolean> =
-        selectedUserId
-            .flatMapLatest { userId ->
-                broadcastDispatcher
-                    .broadcastFlow(
-                        filter = IntentFilter(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
-                        user = UserHandle.ALL
-                    )
-                    .transformLatest {
-                        emit(
-                            (devicePolicyManager.getKeyguardDisabledFeatures(null, userId) and
-                                DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT) == 0
-                        )
-                    }
-                    .flowOn(backgroundDispatcher)
-                    .distinctUntilChanged()
-            }
-            .stateIn(
-                scope,
-                started = SharingStarted.Eagerly,
-                initialValue =
-                    devicePolicyManager.getKeyguardDisabledFeatures(
-                        null,
-                        userRepository.getSelectedUserInfo().id
-                    ) and DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT == 0
-            )
-
-    companion object {
-        private const val TAG = "BiometricsRepositoryImpl"
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
new file mode 100644
index 0000000..baadc66
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import android.app.admin.DevicePolicyManager
+import android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED
+import android.content.Context
+import android.content.IntentFilter
+import android.hardware.biometrics.BiometricManager
+import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback
+import android.os.Looper
+import android.os.UserHandle
+import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.Dumpable
+import com.android.systemui.biometrics.AuthController
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.user.data.repository.UserRepository
+import java.io.PrintWriter
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.transformLatest
+
+/**
+ * Acts as source of truth for biometric authentication related settings like enrollments, device
+ * policy, etc.
+ *
+ * Abstracts-away data sources and their schemas so the rest of the app doesn't need to worry about
+ * upstream changes.
+ */
+interface BiometricSettingsRepository {
+    /** Whether any fingerprints are enrolled for the current user. */
+    val isFingerprintEnrolled: StateFlow<Boolean>
+
+    /** Whether face authentication is enrolled for the current user. */
+    val isFaceEnrolled: Flow<Boolean>
+
+    /**
+     * Whether face authentication is enabled/disabled based on system settings like device policy,
+     * biometrics setting.
+     */
+    val isFaceAuthenticationEnabled: Flow<Boolean>
+
+    /**
+     * Whether the current user is allowed to use a strong biometric for device entry based on
+     * Android Security policies. If false, the user may be able to use primary authentication for
+     * device entry.
+     */
+    val isStrongBiometricAllowed: StateFlow<Boolean>
+
+    /** Whether fingerprint feature is enabled for the current user by the DevicePolicy */
+    val isFingerprintEnabledByDevicePolicy: StateFlow<Boolean>
+}
+
+@SysUISingleton
+class BiometricSettingsRepositoryImpl
+@Inject
+constructor(
+    context: Context,
+    lockPatternUtils: LockPatternUtils,
+    broadcastDispatcher: BroadcastDispatcher,
+    authController: AuthController,
+    userRepository: UserRepository,
+    devicePolicyManager: DevicePolicyManager,
+    @Application scope: CoroutineScope,
+    @Background backgroundDispatcher: CoroutineDispatcher,
+    biometricManager: BiometricManager?,
+    @Main looper: Looper,
+    dumpManager: DumpManager,
+) : BiometricSettingsRepository, Dumpable {
+
+    init {
+        dumpManager.registerDumpable(this)
+    }
+
+    override fun dump(pw: PrintWriter, args: Array<String?>) {
+        pw.println("isFingerprintEnrolled=${isFingerprintEnrolled.value}")
+        pw.println("isStrongBiometricAllowed=${isStrongBiometricAllowed.value}")
+        pw.println("isFingerprintEnabledByDevicePolicy=${isFingerprintEnabledByDevicePolicy.value}")
+    }
+
+    /** UserId of the current selected user. */
+    private val selectedUserId: Flow<Int> =
+        userRepository.selectedUserInfo.map { it.id }.distinctUntilChanged()
+
+    private val devicePolicyChangedForAllUsers =
+        broadcastDispatcher.broadcastFlow(
+            filter = IntentFilter(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
+            user = UserHandle.ALL
+        )
+
+    override val isFingerprintEnrolled: StateFlow<Boolean> =
+        selectedUserId
+            .flatMapLatest { currentUserId ->
+                conflatedCallbackFlow {
+                    val callback =
+                        object : AuthController.Callback {
+                            override fun onEnrollmentsChanged(
+                                sensorBiometricType: BiometricType,
+                                userId: Int,
+                                hasEnrollments: Boolean
+                            ) {
+                                if (sensorBiometricType.isFingerprint && userId == currentUserId) {
+                                    trySendWithFailureLogging(
+                                        hasEnrollments,
+                                        TAG,
+                                        "update fpEnrollment"
+                                    )
+                                }
+                            }
+                        }
+                    authController.addCallback(callback)
+                    awaitClose { authController.removeCallback(callback) }
+                }
+            }
+            .stateIn(
+                scope,
+                started = SharingStarted.Eagerly,
+                initialValue =
+                    authController.isFingerprintEnrolled(userRepository.getSelectedUserInfo().id)
+            )
+
+    override val isFaceEnrolled: Flow<Boolean> =
+        selectedUserId.flatMapLatest { selectedUserId: Int ->
+            conflatedCallbackFlow {
+                val callback =
+                    object : AuthController.Callback {
+                        override fun onEnrollmentsChanged(
+                            sensorBiometricType: BiometricType,
+                            userId: Int,
+                            hasEnrollments: Boolean
+                        ) {
+                            // TODO(b/242022358), use authController.isFaceAuthEnrolled after
+                            //  ag/20176811 is available.
+                            if (
+                                sensorBiometricType == BiometricType.FACE &&
+                                    userId == selectedUserId
+                            ) {
+                                trySendWithFailureLogging(
+                                    hasEnrollments,
+                                    TAG,
+                                    "Face enrollment changed"
+                                )
+                            }
+                        }
+                    }
+                authController.addCallback(callback)
+                trySendWithFailureLogging(
+                    authController.isFaceAuthEnrolled(selectedUserId),
+                    TAG,
+                    "Initial value of face auth enrollment"
+                )
+                awaitClose { authController.removeCallback(callback) }
+            }
+        }
+
+    override val isFaceAuthenticationEnabled: Flow<Boolean>
+        get() =
+            combine(isFaceEnabledByBiometricsManager, isFaceEnabledByDevicePolicy) {
+                biometricsManagerSetting,
+                devicePolicySetting ->
+                biometricsManagerSetting && devicePolicySetting
+            }
+
+    private val isFaceEnabledByDevicePolicy: Flow<Boolean> =
+        combine(selectedUserId, devicePolicyChangedForAllUsers) { userId, _ ->
+                devicePolicyManager.isFaceDisabled(userId)
+            }
+            .onStart {
+                emit(devicePolicyManager.isFaceDisabled(userRepository.getSelectedUserInfo().id))
+            }
+            .flowOn(backgroundDispatcher)
+            .distinctUntilChanged()
+
+    private val isFaceEnabledByBiometricsManager =
+        conflatedCallbackFlow {
+                val callback =
+                    object : IBiometricEnabledOnKeyguardCallback.Stub() {
+                        override fun onChanged(enabled: Boolean, userId: Int) {
+                            trySendWithFailureLogging(
+                                enabled,
+                                TAG,
+                                "biometricsEnabled state changed"
+                            )
+                        }
+                    }
+                biometricManager?.registerEnabledOnKeyguardCallback(callback)
+                awaitClose {}
+            }
+            // This is because the callback is binder-based and we want to avoid multiple callbacks
+            // being registered.
+            .stateIn(scope, SharingStarted.Eagerly, false)
+
+    override val isStrongBiometricAllowed: StateFlow<Boolean> =
+        selectedUserId
+            .flatMapLatest { currUserId ->
+                conflatedCallbackFlow {
+                    val callback =
+                        object : LockPatternUtils.StrongAuthTracker(context, looper) {
+                            override fun onStrongAuthRequiredChanged(userId: Int) {
+                                if (currUserId != userId) {
+                                    return
+                                }
+
+                                trySendWithFailureLogging(
+                                    isBiometricAllowedForUser(true, currUserId),
+                                    TAG
+                                )
+                            }
+
+                            override fun onIsNonStrongBiometricAllowedChanged(userId: Int) {
+                                // no-op
+                            }
+                        }
+                    lockPatternUtils.registerStrongAuthTracker(callback)
+                    awaitClose { lockPatternUtils.unregisterStrongAuthTracker(callback) }
+                }
+            }
+            .stateIn(
+                scope,
+                started = SharingStarted.Eagerly,
+                initialValue =
+                    lockPatternUtils.isBiometricAllowedForUser(
+                        userRepository.getSelectedUserInfo().id
+                    )
+            )
+
+    override val isFingerprintEnabledByDevicePolicy: StateFlow<Boolean> =
+        selectedUserId
+            .flatMapLatest { userId ->
+                devicePolicyChangedForAllUsers
+                    .transformLatest { emit(devicePolicyManager.isFingerprintDisabled(userId)) }
+                    .flowOn(backgroundDispatcher)
+                    .distinctUntilChanged()
+            }
+            .stateIn(
+                scope,
+                started = SharingStarted.Eagerly,
+                initialValue =
+                    devicePolicyManager.isFingerprintDisabled(
+                        userRepository.getSelectedUserInfo().id
+                    )
+            )
+
+    companion object {
+        private const val TAG = "BiometricsRepositoryImpl"
+    }
+}
+
+private fun DevicePolicyManager.isFaceDisabled(userId: Int): Boolean =
+    isNotActive(userId, DevicePolicyManager.KEYGUARD_DISABLE_FACE)
+
+private fun DevicePolicyManager.isFingerprintDisabled(userId: Int): Boolean =
+    isNotActive(userId, DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT)
+
+private fun DevicePolicyManager.isNotActive(userId: Int, policy: Int): Boolean =
+    (getKeyguardDisabledFeatures(null, userId) and policy) == 0
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
index 08edbc6..7c46684 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
@@ -19,17 +19,24 @@
 import android.hardware.biometrics.BiometricSourceType
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.systemui.Dumpable
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dump.DumpManager
+import java.io.PrintWriter
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.stateIn
 
 /** Encapsulates state about device entry fingerprint auth mechanism. */
 interface DeviceEntryFingerprintAuthRepository {
     /** Whether the device entry fingerprint auth is locked out. */
-    val isLockedOut: Flow<Boolean>
+    val isLockedOut: StateFlow<Boolean>
 }
 
 /**
@@ -44,30 +51,44 @@
 @Inject
 constructor(
     val keyguardUpdateMonitor: KeyguardUpdateMonitor,
-) : DeviceEntryFingerprintAuthRepository {
+    @Application scope: CoroutineScope,
+    dumpManager: DumpManager,
+) : DeviceEntryFingerprintAuthRepository, Dumpable {
 
-    override val isLockedOut: Flow<Boolean> = conflatedCallbackFlow {
-        val sendLockoutUpdate =
-            fun() {
-                trySendWithFailureLogging(
-                    keyguardUpdateMonitor.isFingerprintLockedOut,
-                    TAG,
-                    "onLockedOutStateChanged"
-                )
-            }
-        val callback =
-            object : KeyguardUpdateMonitorCallback() {
-                override fun onLockedOutStateChanged(biometricSourceType: BiometricSourceType?) {
-                    if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
-                        sendLockoutUpdate()
-                    }
-                }
-            }
-        keyguardUpdateMonitor.registerCallback(callback)
-        sendLockoutUpdate()
-        awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
+    init {
+        dumpManager.registerDumpable(this)
     }
 
+    override fun dump(pw: PrintWriter, args: Array<String?>) {
+        pw.println("isLockedOut=${isLockedOut.value}")
+    }
+
+    override val isLockedOut: StateFlow<Boolean> =
+        conflatedCallbackFlow {
+                val sendLockoutUpdate =
+                    fun() {
+                        trySendWithFailureLogging(
+                            keyguardUpdateMonitor.isFingerprintLockedOut,
+                            TAG,
+                            "onLockedOutStateChanged"
+                        )
+                    }
+                val callback =
+                    object : KeyguardUpdateMonitorCallback() {
+                        override fun onLockedOutStateChanged(
+                            biometricSourceType: BiometricSourceType?
+                        ) {
+                            if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
+                                sendLockoutUpdate()
+                            }
+                        }
+                    }
+                keyguardUpdateMonitor.registerCallback(callback)
+                sendLockoutUpdate()
+                awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
+            }
+            .stateIn(scope, started = SharingStarted.Eagerly, initialValue = false)
+
     companion object {
         const val TAG = "DeviceEntryFingerprintAuthRepositoryImpl"
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
index 41574d1..4331fe6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
@@ -26,7 +26,6 @@
 import com.android.systemui.log.dagger.BouncerLog
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.log.table.logDiffsForTable
-import com.android.systemui.statusbar.phone.KeyguardBouncer
 import com.android.systemui.util.time.SystemClock
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -42,31 +41,96 @@
  *
  * Make sure to add newly added flows to the logger.
  */
+interface KeyguardBouncerRepository {
+    /** Values associated with the PrimaryBouncer (pin/pattern/password) input. */
+    val primaryBouncerVisible: StateFlow<Boolean>
+    val primaryBouncerShow: StateFlow<KeyguardBouncerModel?>
+    val primaryBouncerShowingSoon: StateFlow<Boolean>
+    val primaryBouncerHide: StateFlow<Boolean>
+    val primaryBouncerStartingToHide: StateFlow<Boolean>
+    val primaryBouncerStartingDisappearAnimation: StateFlow<Runnable?>
+    /** Determines if we want to instantaneously show the primary bouncer instead of translating. */
+    val primaryBouncerScrimmed: StateFlow<Boolean>
+    /**
+     * Set how much of the notification panel is showing on the screen.
+     * ```
+     *      0f = panel fully hidden = bouncer fully showing
+     *      1f = panel fully showing = bouncer fully hidden
+     * ```
+     */
+    val panelExpansionAmount: StateFlow<Float>
+    val keyguardPosition: StateFlow<Float>
+    val onScreenTurnedOff: StateFlow<Boolean>
+    val isBackButtonEnabled: StateFlow<Boolean?>
+    /** Determines if user is already unlocked */
+    val keyguardAuthenticated: StateFlow<Boolean?>
+    val showMessage: StateFlow<BouncerShowMessageModel?>
+    val resourceUpdateRequests: StateFlow<Boolean>
+    val bouncerPromptReason: Int
+    val bouncerErrorMessage: CharSequence?
+    val alternateBouncerVisible: StateFlow<Boolean>
+    val alternateBouncerUIAvailable: StateFlow<Boolean>
+    var lastAlternateBouncerVisibleTime: Long
+
+    fun setPrimaryScrimmed(isScrimmed: Boolean)
+
+    fun setPrimaryVisible(isVisible: Boolean)
+
+    fun setPrimaryShow(keyguardBouncerModel: KeyguardBouncerModel?)
+
+    fun setPrimaryShowingSoon(showingSoon: Boolean)
+
+    fun setPrimaryHide(hide: Boolean)
+
+    fun setPrimaryStartingToHide(startingToHide: Boolean)
+
+    fun setPrimaryStartDisappearAnimation(runnable: Runnable?)
+
+    fun setPanelExpansion(panelExpansion: Float)
+
+    fun setKeyguardPosition(keyguardPosition: Float)
+
+    fun setResourceUpdateRequests(willUpdateResources: Boolean)
+
+    fun setShowMessage(bouncerShowMessageModel: BouncerShowMessageModel?)
+
+    fun setKeyguardAuthenticated(keyguardAuthenticated: Boolean?)
+
+    fun setIsBackButtonEnabled(isBackButtonEnabled: Boolean)
+
+    fun setOnScreenTurnedOff(onScreenTurnedOff: Boolean)
+
+    fun setAlternateVisible(isVisible: Boolean)
+
+    fun setAlternateBouncerUIAvailable(isAvailable: Boolean)
+}
+
 @SysUISingleton
-class KeyguardBouncerRepository
+class KeyguardBouncerRepositoryImpl
 @Inject
 constructor(
     private val viewMediatorCallback: ViewMediatorCallback,
     private val clock: SystemClock,
     @Application private val applicationScope: CoroutineScope,
     @BouncerLog private val buffer: TableLogBuffer,
-) {
+) : KeyguardBouncerRepository {
     /** Values associated with the PrimaryBouncer (pin/pattern/password) input. */
     private val _primaryBouncerVisible = MutableStateFlow(false)
-    val primaryBouncerVisible = _primaryBouncerVisible.asStateFlow()
+    override val primaryBouncerVisible = _primaryBouncerVisible.asStateFlow()
     private val _primaryBouncerShow = MutableStateFlow<KeyguardBouncerModel?>(null)
-    val primaryBouncerShow = _primaryBouncerShow.asStateFlow()
+    override val primaryBouncerShow = _primaryBouncerShow.asStateFlow()
     private val _primaryBouncerShowingSoon = MutableStateFlow(false)
-    val primaryBouncerShowingSoon = _primaryBouncerShowingSoon.asStateFlow()
+    override val primaryBouncerShowingSoon = _primaryBouncerShowingSoon.asStateFlow()
     private val _primaryBouncerHide = MutableStateFlow(false)
-    val primaryBouncerHide = _primaryBouncerHide.asStateFlow()
+    override val primaryBouncerHide = _primaryBouncerHide.asStateFlow()
     private val _primaryBouncerStartingToHide = MutableStateFlow(false)
-    val primaryBouncerStartingToHide = _primaryBouncerStartingToHide.asStateFlow()
+    override val primaryBouncerStartingToHide = _primaryBouncerStartingToHide.asStateFlow()
     private val _primaryBouncerDisappearAnimation = MutableStateFlow<Runnable?>(null)
-    val primaryBouncerStartingDisappearAnimation = _primaryBouncerDisappearAnimation.asStateFlow()
+    override val primaryBouncerStartingDisappearAnimation =
+        _primaryBouncerDisappearAnimation.asStateFlow()
     /** Determines if we want to instantaneously show the primary bouncer instead of translating. */
     private val _primaryBouncerScrimmed = MutableStateFlow(false)
-    val primaryBouncerScrimmed = _primaryBouncerScrimmed.asStateFlow()
+    override val primaryBouncerScrimmed = _primaryBouncerScrimmed.asStateFlow()
     /**
      * Set how much of the notification panel is showing on the screen.
      * ```
@@ -75,103 +139,103 @@
      * ```
      */
     private val _panelExpansionAmount = MutableStateFlow(EXPANSION_HIDDEN)
-    val panelExpansionAmount = _panelExpansionAmount.asStateFlow()
+    override val panelExpansionAmount = _panelExpansionAmount.asStateFlow()
     private val _keyguardPosition = MutableStateFlow(0f)
-    val keyguardPosition = _keyguardPosition.asStateFlow()
+    override val keyguardPosition = _keyguardPosition.asStateFlow()
     private val _onScreenTurnedOff = MutableStateFlow(false)
-    val onScreenTurnedOff = _onScreenTurnedOff.asStateFlow()
+    override val onScreenTurnedOff = _onScreenTurnedOff.asStateFlow()
     private val _isBackButtonEnabled = MutableStateFlow<Boolean?>(null)
-    val isBackButtonEnabled = _isBackButtonEnabled.asStateFlow()
+    override val isBackButtonEnabled = _isBackButtonEnabled.asStateFlow()
     private val _keyguardAuthenticated = MutableStateFlow<Boolean?>(null)
     /** Determines if user is already unlocked */
-    val keyguardAuthenticated = _keyguardAuthenticated.asStateFlow()
+    override val keyguardAuthenticated = _keyguardAuthenticated.asStateFlow()
     private val _showMessage = MutableStateFlow<BouncerShowMessageModel?>(null)
-    val showMessage = _showMessage.asStateFlow()
+    override val showMessage = _showMessage.asStateFlow()
     private val _resourceUpdateRequests = MutableStateFlow(false)
-    val resourceUpdateRequests = _resourceUpdateRequests.asStateFlow()
-    val bouncerPromptReason: Int
+    override val resourceUpdateRequests = _resourceUpdateRequests.asStateFlow()
+    override val bouncerPromptReason: Int
         get() = viewMediatorCallback.bouncerPromptReason
-    val bouncerErrorMessage: CharSequence?
+    override val bouncerErrorMessage: CharSequence?
         get() = viewMediatorCallback.consumeCustomMessage()
 
+    /** Values associated with the AlternateBouncer */
+    private val _alternateBouncerVisible = MutableStateFlow(false)
+    override val alternateBouncerVisible = _alternateBouncerVisible.asStateFlow()
+    override var lastAlternateBouncerVisibleTime: Long = NOT_VISIBLE
+    private val _alternateBouncerUIAvailable = MutableStateFlow(false)
+    override val alternateBouncerUIAvailable: StateFlow<Boolean> =
+        _alternateBouncerUIAvailable.asStateFlow()
+
     init {
         setUpLogging()
     }
 
-    /** Values associated with the AlternateBouncer */
-    private val _isAlternateBouncerVisible = MutableStateFlow(false)
-    val isAlternateBouncerVisible = _isAlternateBouncerVisible.asStateFlow()
-    var lastAlternateBouncerVisibleTime: Long = NOT_VISIBLE
-    private val _isAlternateBouncerUIAvailable = MutableStateFlow<Boolean>(false)
-    val isAlternateBouncerUIAvailable: StateFlow<Boolean> =
-        _isAlternateBouncerUIAvailable.asStateFlow()
-
-    fun setPrimaryScrimmed(isScrimmed: Boolean) {
+    override fun setPrimaryScrimmed(isScrimmed: Boolean) {
         _primaryBouncerScrimmed.value = isScrimmed
     }
 
-    fun setPrimaryVisible(isVisible: Boolean) {
+    override fun setPrimaryVisible(isVisible: Boolean) {
         _primaryBouncerVisible.value = isVisible
     }
 
-    fun setAlternateVisible(isVisible: Boolean) {
-        if (isVisible && !_isAlternateBouncerVisible.value) {
+    override fun setAlternateVisible(isVisible: Boolean) {
+        if (isVisible && !_alternateBouncerVisible.value) {
             lastAlternateBouncerVisibleTime = clock.uptimeMillis()
         } else if (!isVisible) {
             lastAlternateBouncerVisibleTime = NOT_VISIBLE
         }
-        _isAlternateBouncerVisible.value = isVisible
+        _alternateBouncerVisible.value = isVisible
     }
 
-    fun setAlternateBouncerUIAvailable(isAvailable: Boolean) {
-        _isAlternateBouncerUIAvailable.value = isAvailable
+    override fun setAlternateBouncerUIAvailable(isAvailable: Boolean) {
+        _alternateBouncerUIAvailable.value = isAvailable
     }
 
-    fun setPrimaryShow(keyguardBouncerModel: KeyguardBouncerModel?) {
+    override fun setPrimaryShow(keyguardBouncerModel: KeyguardBouncerModel?) {
         _primaryBouncerShow.value = keyguardBouncerModel
     }
 
-    fun setPrimaryShowingSoon(showingSoon: Boolean) {
+    override fun setPrimaryShowingSoon(showingSoon: Boolean) {
         _primaryBouncerShowingSoon.value = showingSoon
     }
 
-    fun setPrimaryHide(hide: Boolean) {
+    override fun setPrimaryHide(hide: Boolean) {
         _primaryBouncerHide.value = hide
     }
 
-    fun setPrimaryStartingToHide(startingToHide: Boolean) {
+    override fun setPrimaryStartingToHide(startingToHide: Boolean) {
         _primaryBouncerStartingToHide.value = startingToHide
     }
 
-    fun setPrimaryStartDisappearAnimation(runnable: Runnable?) {
+    override fun setPrimaryStartDisappearAnimation(runnable: Runnable?) {
         _primaryBouncerDisappearAnimation.value = runnable
     }
 
-    fun setPanelExpansion(panelExpansion: Float) {
+    override fun setPanelExpansion(panelExpansion: Float) {
         _panelExpansionAmount.value = panelExpansion
     }
 
-    fun setKeyguardPosition(keyguardPosition: Float) {
+    override fun setKeyguardPosition(keyguardPosition: Float) {
         _keyguardPosition.value = keyguardPosition
     }
 
-    fun setResourceUpdateRequests(willUpdateResources: Boolean) {
+    override fun setResourceUpdateRequests(willUpdateResources: Boolean) {
         _resourceUpdateRequests.value = willUpdateResources
     }
 
-    fun setShowMessage(bouncerShowMessageModel: BouncerShowMessageModel?) {
+    override fun setShowMessage(bouncerShowMessageModel: BouncerShowMessageModel?) {
         _showMessage.value = bouncerShowMessageModel
     }
 
-    fun setKeyguardAuthenticated(keyguardAuthenticated: Boolean?) {
+    override fun setKeyguardAuthenticated(keyguardAuthenticated: Boolean?) {
         _keyguardAuthenticated.value = keyguardAuthenticated
     }
 
-    fun setIsBackButtonEnabled(isBackButtonEnabled: Boolean) {
+    override fun setIsBackButtonEnabled(isBackButtonEnabled: Boolean) {
         _isBackButtonEnabled.value = isBackButtonEnabled
     }
 
-    fun setOnScreenTurnedOff(onScreenTurnedOff: Boolean) {
+    override fun setOnScreenTurnedOff(onScreenTurnedOff: Boolean) {
         _onScreenTurnedOff.value = onScreenTurnedOff
     }
 
@@ -226,6 +290,9 @@
         resourceUpdateRequests
             .logDiffsForTable(buffer, "", "ResourceUpdateRequests", false)
             .launchIn(applicationScope)
+        alternateBouncerUIAvailable
+            .logDiffsForTable(buffer, "", "IsAlternateBouncerUIAvailable", false)
+            .launchIn(applicationScope)
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManager.kt
new file mode 100644
index 0000000..2069891
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManager.kt
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import android.app.StatusBarManager
+import android.content.Context
+import android.hardware.face.FaceManager
+import android.os.CancellationSignal
+import com.android.internal.logging.InstanceId
+import com.android.internal.logging.UiEventLogger
+import com.android.keyguard.FaceAuthUiEvent
+import com.android.systemui.Dumpable
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.shared.model.AcquiredAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.AuthenticationStatus
+import com.android.systemui.keyguard.shared.model.DetectionStatus
+import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FailedAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.HelpAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus
+import com.android.systemui.log.FaceAuthenticationLogger
+import com.android.systemui.log.SessionTracker
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.user.data.repository.UserRepository
+import java.io.PrintWriter
+import java.util.Arrays
+import java.util.stream.Collectors
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * API to run face authentication and detection for device entry / on keyguard (as opposed to the
+ * biometric prompt).
+ */
+interface KeyguardFaceAuthManager {
+    /**
+     * Trigger face authentication.
+     *
+     * [uiEvent] provided should be logged whenever face authentication runs. Invocation should be
+     * ignored if face authentication is already running. Results should be propagated through
+     * [authenticationStatus]
+     */
+    suspend fun authenticate(uiEvent: FaceAuthUiEvent)
+
+    /**
+     * Trigger face detection.
+     *
+     * Invocation should be ignored if face authentication is currently running.
+     */
+    suspend fun detect()
+
+    /** Stop currently running face authentication or detection. */
+    fun cancel()
+
+    /** Provide the current status of face authentication. */
+    val authenticationStatus: Flow<AuthenticationStatus>
+
+    /** Provide the current status of face detection. */
+    val detectionStatus: Flow<DetectionStatus>
+
+    /** Current state of whether face authentication is locked out or not. */
+    val isLockedOut: Flow<Boolean>
+
+    /** Current state of whether face authentication is running. */
+    val isAuthRunning: Flow<Boolean>
+
+    /** Is face detection supported. */
+    val isDetectionSupported: Boolean
+}
+
+@SysUISingleton
+class KeyguardFaceAuthManagerImpl
+@Inject
+constructor(
+    context: Context,
+    private val faceManager: FaceManager? = null,
+    private val userRepository: UserRepository,
+    private val keyguardBypassController: KeyguardBypassController? = null,
+    @Application private val applicationScope: CoroutineScope,
+    @Main private val mainDispatcher: CoroutineDispatcher,
+    private val sessionTracker: SessionTracker,
+    private val uiEventsLogger: UiEventLogger,
+    private val faceAuthLogger: FaceAuthenticationLogger,
+    dumpManager: DumpManager,
+) : KeyguardFaceAuthManager, Dumpable {
+    private var cancellationSignal: CancellationSignal? = null
+    private val lockscreenBypassEnabled: Boolean
+        get() = keyguardBypassController?.bypassEnabled ?: false
+    private var faceAcquiredInfoIgnoreList: Set<Int>
+
+    private val faceLockoutResetCallback =
+        object : FaceManager.LockoutResetCallback() {
+            override fun onLockoutReset(sensorId: Int) {
+                _isLockedOut.value = false
+            }
+        }
+
+    init {
+        faceManager?.addLockoutResetCallback(faceLockoutResetCallback)
+        faceAcquiredInfoIgnoreList =
+            Arrays.stream(
+                    context.resources.getIntArray(
+                        R.array.config_face_acquire_device_entry_ignorelist
+                    )
+                )
+                .boxed()
+                .collect(Collectors.toSet())
+        dumpManager.registerCriticalDumpable("KeyguardFaceAuthManagerImpl", this)
+    }
+
+    private val faceAuthCallback =
+        object : FaceManager.AuthenticationCallback() {
+            override fun onAuthenticationFailed() {
+                _authenticationStatus.value = FailedAuthenticationStatus
+                faceAuthLogger.authenticationFailed()
+                onFaceAuthRequestCompleted()
+            }
+
+            override fun onAuthenticationAcquired(acquireInfo: Int) {
+                _authenticationStatus.value = AcquiredAuthenticationStatus(acquireInfo)
+                faceAuthLogger.authenticationAcquired(acquireInfo)
+            }
+
+            override fun onAuthenticationError(errorCode: Int, errString: CharSequence?) {
+                val errorStatus = ErrorAuthenticationStatus(errorCode, errString.toString())
+                if (errorStatus.isLockoutError()) {
+                    _isLockedOut.value = true
+                }
+                _authenticationStatus.value = errorStatus
+                if (errorStatus.isCancellationError()) {
+                    cancelNotReceivedHandlerJob?.cancel()
+                    applicationScope.launch {
+                        faceAuthLogger.launchingQueuedFaceAuthRequest(
+                            faceAuthRequestedWhileCancellation
+                        )
+                        faceAuthRequestedWhileCancellation?.let { authenticate(it) }
+                        faceAuthRequestedWhileCancellation = null
+                    }
+                }
+                faceAuthLogger.authenticationError(
+                    errorCode,
+                    errString,
+                    errorStatus.isLockoutError(),
+                    errorStatus.isCancellationError()
+                )
+                onFaceAuthRequestCompleted()
+            }
+
+            override fun onAuthenticationHelp(code: Int, helpStr: CharSequence?) {
+                if (faceAcquiredInfoIgnoreList.contains(code)) {
+                    return
+                }
+                _authenticationStatus.value = HelpAuthenticationStatus(code, helpStr.toString())
+            }
+
+            override fun onAuthenticationSucceeded(result: FaceManager.AuthenticationResult) {
+                _authenticationStatus.value = SuccessAuthenticationStatus(result)
+                faceAuthLogger.faceAuthSuccess(result)
+                onFaceAuthRequestCompleted()
+            }
+        }
+
+    private fun onFaceAuthRequestCompleted() {
+        cancellationInProgress = false
+        _isAuthRunning.value = false
+        cancellationSignal = null
+    }
+
+    private val detectionCallback =
+        FaceManager.FaceDetectionCallback { sensorId, userId, isStrong ->
+            faceAuthLogger.faceDetected()
+            _detectionStatus.value = DetectionStatus(sensorId, userId, isStrong)
+        }
+
+    private var cancellationInProgress = false
+    private var faceAuthRequestedWhileCancellation: FaceAuthUiEvent? = null
+
+    override suspend fun authenticate(uiEvent: FaceAuthUiEvent) {
+        if (_isAuthRunning.value) {
+            faceAuthLogger.ignoredFaceAuthTrigger(uiEvent)
+            return
+        }
+
+        if (cancellationInProgress) {
+            faceAuthLogger.queuingRequestWhileCancelling(
+                faceAuthRequestedWhileCancellation,
+                uiEvent
+            )
+            faceAuthRequestedWhileCancellation = uiEvent
+            return
+        } else {
+            faceAuthRequestedWhileCancellation = null
+        }
+
+        withContext(mainDispatcher) {
+            // We always want to invoke face auth in the main thread.
+            cancellationSignal = CancellationSignal()
+            _isAuthRunning.value = true
+            uiEventsLogger.logWithInstanceIdAndPosition(
+                uiEvent,
+                0,
+                null,
+                keyguardSessionId,
+                uiEvent.extraInfo
+            )
+            faceAuthLogger.authenticating(uiEvent)
+            faceManager?.authenticate(
+                null,
+                cancellationSignal,
+                faceAuthCallback,
+                null,
+                currentUserId,
+                lockscreenBypassEnabled
+            )
+        }
+    }
+
+    override suspend fun detect() {
+        if (!isDetectionSupported) {
+            faceAuthLogger.detectionNotSupported(faceManager, faceManager?.sensorPropertiesInternal)
+            return
+        }
+        if (_isAuthRunning.value) {
+            faceAuthLogger.skippingBecauseAlreadyRunning("detection")
+            return
+        }
+
+        cancellationSignal = CancellationSignal()
+        withContext(mainDispatcher) {
+            // We always want to invoke face detect in the main thread.
+            faceAuthLogger.faceDetectionStarted()
+            faceManager?.detectFace(cancellationSignal, detectionCallback, currentUserId)
+        }
+    }
+
+    private val currentUserId: Int
+        get() = userRepository.getSelectedUserInfo().id
+
+    override fun cancel() {
+        if (cancellationSignal == null) return
+
+        cancellationSignal?.cancel()
+        cancelNotReceivedHandlerJob =
+            applicationScope.launch {
+                delay(DEFAULT_CANCEL_SIGNAL_TIMEOUT)
+                faceAuthLogger.cancelSignalNotReceived(
+                    _isAuthRunning.value,
+                    _isLockedOut.value,
+                    cancellationInProgress,
+                    faceAuthRequestedWhileCancellation
+                )
+                onFaceAuthRequestCompleted()
+            }
+        cancellationInProgress = true
+        _isAuthRunning.value = false
+    }
+
+    private var cancelNotReceivedHandlerJob: Job? = null
+
+    private val _authenticationStatus: MutableStateFlow<AuthenticationStatus?> =
+        MutableStateFlow(null)
+    override val authenticationStatus: Flow<AuthenticationStatus>
+        get() = _authenticationStatus.filterNotNull()
+
+    private val _detectionStatus = MutableStateFlow<DetectionStatus?>(null)
+    override val detectionStatus: Flow<DetectionStatus>
+        get() = _detectionStatus.filterNotNull()
+
+    private val _isLockedOut = MutableStateFlow(false)
+    override val isLockedOut: Flow<Boolean> = _isLockedOut
+
+    override val isDetectionSupported =
+        faceManager?.sensorPropertiesInternal?.firstOrNull()?.supportsFaceDetection ?: false
+
+    private val _isAuthRunning = MutableStateFlow(false)
+    override val isAuthRunning: Flow<Boolean>
+        get() = _isAuthRunning
+
+    private val keyguardSessionId: InstanceId?
+        get() = sessionTracker.getSessionId(StatusBarManager.SESSION_KEYGUARD)
+
+    companion object {
+        const val TAG = "KeyguardFaceAuthManager"
+
+        /**
+         * If no cancel signal has been received after this amount of time, assume that it is
+         * cancelled.
+         */
+        const val DEFAULT_CANCEL_SIGNAL_TIMEOUT = 3000L
+    }
+
+    override fun dump(pw: PrintWriter, args: Array<out String>) {
+        pw.println("KeyguardFaceAuthManagerImpl state:")
+        pw.println("  cancellationInProgress: $cancellationInProgress")
+        pw.println("  _isLockedOut.value: ${_isLockedOut.value}")
+        pw.println("  _isAuthRunning.value: ${_isAuthRunning.value}")
+        pw.println("  isDetectionSupported: $isDetectionSupported")
+        pw.println("  FaceManager state:")
+        pw.println("    faceManager: $faceManager")
+        pw.println("    sensorPropertiesInternal: ${faceManager?.sensorPropertiesInternal}")
+        pw.println(
+            "    supportsFaceDetection: " +
+                "${faceManager?.sensorPropertiesInternal?.firstOrNull()?.supportsFaceDetection}"
+        )
+        pw.println(
+            "  faceAuthRequestedWhileCancellation: ${faceAuthRequestedWhileCancellation?.reason}"
+        )
+        pw.println("  cancellationSignal: $cancellationSignal")
+        pw.println("  faceAcquiredInfoIgnoreList: $faceAcquiredInfoIgnoreList")
+        pw.println("  _authenticationStatus: ${_authenticationStatus.value}")
+        pw.println("  _detectionStatus: ${_detectionStatus.value}")
+        pw.println("  currentUserId: $currentUserId")
+        pw.println("  keyguardSessionId: $keyguardSessionId")
+        pw.println("  lockscreenBypassEnabled: $lockscreenBypassEnabled")
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
index 2b2b9d0..8ece318 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
@@ -146,7 +146,7 @@
      * Returns a snapshot of the [KeyguardQuickAffordanceConfig] instances of the affordances at the
      * slot with the given ID. The configs are sorted in descending priority order.
      */
-    fun getSelections(slotId: String): List<KeyguardQuickAffordanceConfig> {
+    fun getCurrentSelections(slotId: String): List<KeyguardQuickAffordanceConfig> {
         val selections = selectionManager.value.getSelections().getOrDefault(slotId, emptyList())
         return configs.filter { selections.contains(it.key) }
     }
@@ -155,7 +155,7 @@
      * Returns a snapshot of the IDs of the selected affordances, indexed by slot ID. The configs
      * are sorted in descending priority order.
      */
-    fun getSelections(): Map<String, List<String>> {
+    fun getCurrentSelections(): Map<String, List<String>> {
         return selectionManager.value.getSelections()
     }
 
@@ -217,7 +217,7 @@
     private inner class Dumpster : Dumpable {
         override fun dump(pw: PrintWriter, args: Array<out String>) {
             val slotPickerRepresentations = getSlotPickerRepresentations()
-            val selectionsBySlotId = getSelections()
+            val selectionsBySlotId = getCurrentSelections()
             pw.println("Slots & selections:")
             slotPickerRepresentations.forEach { slotPickerRepresentation ->
                 val slotId = slotPickerRepresentation.id
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 d99af90..a3b3d0f 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
@@ -86,9 +86,6 @@
     /** Observable for the signal that keyguard is about to go away. */
     val isKeyguardGoingAway: Flow<Boolean>
 
-    /** Observable for whether the bouncer is showing. */
-    val isBouncerShowing: Flow<Boolean>
-
     /** Is the always-on display available to be used? */
     val isAodAvailable: Flow<Boolean>
 
@@ -148,6 +145,9 @@
     /** Source of the most recent biometric unlock, such as fingerprint or face. */
     val biometricUnlockSource: Flow<BiometricUnlockSource?>
 
+    /** Whether quick settings or quick-quick settings is visible. */
+    val isQuickSettingsVisible: Flow<Boolean>
+
     /**
      * Returns `true` if the keyguard is showing; `false` otherwise.
      *
@@ -172,6 +172,9 @@
      * Returns whether the keyguard bottom area should be constrained to the top of the lock icon
      */
     fun isUdfpsSupported(): Boolean
+
+    /** Sets whether quick settings or quick-quick settings is visible. */
+    fun setQuickSettingsVisible(isVisible: Boolean)
 }
 
 /** Encapsulates application state for the keyguard. */
@@ -298,29 +301,6 @@
         awaitClose { keyguardStateController.removeCallback(callback) }
     }
 
-    override val isBouncerShowing: Flow<Boolean> = conflatedCallbackFlow {
-        val callback =
-            object : KeyguardStateController.Callback {
-                override fun onBouncerShowingChanged() {
-                    trySendWithFailureLogging(
-                        keyguardStateController.isBouncerShowing,
-                        TAG,
-                        "updated isBouncerShowing"
-                    )
-                }
-            }
-
-        keyguardStateController.addCallback(callback)
-        // Adding the callback does not send an initial update.
-        trySendWithFailureLogging(
-            keyguardStateController.isBouncerShowing,
-            TAG,
-            "initial isBouncerShowing"
-        )
-
-        awaitClose { keyguardStateController.removeCallback(callback) }
-    }
-
     override val isDozing: Flow<Boolean> =
         conflatedCallbackFlow {
                 val callback =
@@ -581,6 +561,9 @@
         awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
     }
 
+    private val _isQuickSettingsVisible = MutableStateFlow(false)
+    override val isQuickSettingsVisible: Flow<Boolean> = _isQuickSettingsVisible.asStateFlow()
+
     override fun setAnimateDozingTransitions(animate: Boolean) {
         _animateBottomAreaDozingTransitions.value = animate
     }
@@ -595,6 +578,10 @@
 
     override fun isUdfpsSupported(): Boolean = keyguardUpdateMonitor.isUdfpsSupported
 
+    override fun setQuickSettingsVisible(isVisible: Boolean) {
+        _isQuickSettingsVisible.value = isVisible
+    }
+
     private fun statusBarStateIntToObject(value: Int): StatusBarState {
         return when (value) {
             0 -> StatusBarState.SHADE
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
index 4639597..4a262f5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
@@ -31,5 +31,16 @@
     @Binds
     fun lightRevealScrimRepository(impl: LightRevealScrimRepositoryImpl): LightRevealScrimRepository
 
-    @Binds fun biometricRepository(impl: BiometricRepositoryImpl): BiometricRepository
+    @Binds
+    fun biometricSettingsRepository(
+        impl: BiometricSettingsRepositoryImpl
+    ): BiometricSettingsRepository
+
+    @Binds
+    fun deviceEntryFingerprintAuthRepository(
+        impl: DeviceEntryFingerprintAuthRepositoryImpl
+    ): DeviceEntryFingerprintAuthRepository
+
+    @Binds
+    fun keyguardBouncerRepository(impl: KeyguardBouncerRepositoryImpl): KeyguardBouncerRepository
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/backup/KeyguardQuickAffordanceBackupHelper.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/backup/KeyguardQuickAffordanceBackupHelper.kt
index 0e865ce..fa6efa5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/backup/KeyguardQuickAffordanceBackupHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/backup/KeyguardQuickAffordanceBackupHelper.kt
@@ -29,16 +29,9 @@
 ) :
     SharedPreferencesBackupHelper(
         context,
-        if (UserFileManagerImpl.isPrimaryUser(userId)) {
-            KeyguardQuickAffordanceSelectionManager.FILE_NAME
-        } else {
-            UserFileManagerImpl.secondaryUserFile(
-                    context = context,
-                    fileName = KeyguardQuickAffordanceSelectionManager.FILE_NAME,
-                    directoryName = UserFileManagerImpl.SHARED_PREFS,
-                    userId = userId,
-                )
-                .also { UserFileManagerImpl.ensureParentDirExists(it) }
-                .toString()
-        }
+        UserFileManagerImpl.createFile(
+                userId = userId,
+                fileName = KeyguardQuickAffordanceSelectionManager.FILE_NAME,
+            )
+            .getPath()
     )
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt
index 28c0b28..dfe1038 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt
@@ -20,7 +20,8 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.data.repository.BiometricRepository
+import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.LegacyAlternateBouncer
 import com.android.systemui.util.time.SystemClock
@@ -33,7 +34,8 @@
 @Inject
 constructor(
     private val bouncerRepository: KeyguardBouncerRepository,
-    private val biometricRepository: BiometricRepository,
+    private val biometricSettingsRepository: BiometricSettingsRepository,
+    private val deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
     private val systemClock: SystemClock,
     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
     featureFlags: FeatureFlags,
@@ -42,7 +44,7 @@
     var legacyAlternateBouncer: LegacyAlternateBouncer? = null
     var legacyAlternateBouncerVisibleTime: Long = NOT_VISIBLE
 
-    val isVisible: Flow<Boolean> = bouncerRepository.isAlternateBouncerVisible
+    val isVisible: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
 
     /**
      * Sets the correct bouncer states to show the alternate bouncer if it can show.
@@ -84,7 +86,7 @@
 
     fun isVisibleState(): Boolean {
         return if (isModernAlternateBouncerEnabled) {
-            bouncerRepository.isAlternateBouncerVisible.value
+            bouncerRepository.alternateBouncerVisible.value
         } else {
             legacyAlternateBouncer?.isShowingAlternateBouncer ?: false
         }
@@ -96,10 +98,11 @@
 
     fun canShowAlternateBouncerForFingerprint(): Boolean {
         return if (isModernAlternateBouncerEnabled) {
-            bouncerRepository.isAlternateBouncerUIAvailable.value &&
-                biometricRepository.isFingerprintEnrolled.value &&
-                biometricRepository.isStrongBiometricAllowed.value &&
-                biometricRepository.isFingerprintEnabledByDevicePolicy.value
+            bouncerRepository.alternateBouncerUIAvailable.value &&
+                biometricSettingsRepository.isFingerprintEnrolled.value &&
+                biometricSettingsRepository.isStrongBiometricAllowed.value &&
+                biometricSettingsRepository.isFingerprintEnabledByDevicePolicy.value &&
+                !deviceEntryFingerprintAuthRepository.isLockedOut.value
         } else {
             legacyAlternateBouncer != null &&
                 keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(true)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
new file mode 100644
index 0000000..310f44d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.animation.ValueAnimator
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.keyguard.shared.model.WakefulnessState
+import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+
+@SysUISingleton
+class FromAlternateBouncerTransitionInteractor
+@Inject
+constructor(
+    @Application private val scope: CoroutineScope,
+    private val keyguardInteractor: KeyguardInteractor,
+    private val keyguardTransitionRepository: KeyguardTransitionRepository,
+    private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
+) : TransitionInteractor(FromAlternateBouncerTransitionInteractor::class.simpleName!!) {
+
+    override fun start() {
+        listenForAlternateBouncerToGone()
+        listenForAlternateBouncerToLockscreenAodOrDozing()
+        listenForAlternateBouncerToPrimaryBouncer()
+    }
+
+    private fun listenForAlternateBouncerToLockscreenAodOrDozing() {
+        scope.launch {
+            keyguardInteractor.alternateBouncerShowing
+                // Add a slight delay, as alternateBouncer and primaryBouncer showing event changes
+                // will arrive with a small gap in time. This prevents a transition to LOCKSCREEN
+                // happening prematurely.
+                .onEach { delay(50) }
+                .sample(
+                    combine(
+                        keyguardInteractor.primaryBouncerShowing,
+                        keyguardTransitionInteractor.startedKeyguardTransitionStep,
+                        keyguardInteractor.wakefulnessModel,
+                        keyguardInteractor.isAodAvailable,
+                        ::toQuad
+                    ),
+                    ::toQuint
+                )
+                .collect {
+                    (
+                        isAlternateBouncerShowing,
+                        isPrimaryBouncerShowing,
+                        lastStartedTransitionStep,
+                        wakefulnessState,
+                        isAodAvailable
+                    ) ->
+                    if (
+                        !isAlternateBouncerShowing &&
+                            !isPrimaryBouncerShowing &&
+                            lastStartedTransitionStep.to == KeyguardState.ALTERNATE_BOUNCER
+                    ) {
+                        val to =
+                            if (
+                                wakefulnessState.state == WakefulnessState.STARTING_TO_SLEEP ||
+                                    wakefulnessState.state == WakefulnessState.ASLEEP
+                            ) {
+                                if (isAodAvailable) {
+                                    KeyguardState.AOD
+                                } else {
+                                    KeyguardState.DOZING
+                                }
+                            } else {
+                                KeyguardState.LOCKSCREEN
+                            }
+                        keyguardTransitionRepository.startTransition(
+                            TransitionInfo(
+                                ownerName = name,
+                                from = KeyguardState.ALTERNATE_BOUNCER,
+                                to = to,
+                                animator = getAnimator(),
+                            )
+                        )
+                    }
+                }
+        }
+    }
+
+    private fun listenForAlternateBouncerToGone() {
+        scope.launch {
+            keyguardInteractor.isKeyguardGoingAway
+                .sample(keyguardTransitionInteractor.finishedKeyguardState, ::Pair)
+                .collect { (isKeyguardGoingAway, keyguardState) ->
+                    if (isKeyguardGoingAway && keyguardState == KeyguardState.ALTERNATE_BOUNCER) {
+                        keyguardTransitionRepository.startTransition(
+                            TransitionInfo(
+                                ownerName = name,
+                                from = KeyguardState.ALTERNATE_BOUNCER,
+                                to = KeyguardState.GONE,
+                                animator = getAnimator(),
+                            )
+                        )
+                    }
+                }
+        }
+    }
+
+    private fun listenForAlternateBouncerToPrimaryBouncer() {
+        scope.launch {
+            keyguardInteractor.primaryBouncerShowing
+                .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
+                .collect { (isPrimaryBouncerShowing, startedKeyguardState) ->
+                    if (
+                        isPrimaryBouncerShowing &&
+                            startedKeyguardState.to == KeyguardState.ALTERNATE_BOUNCER
+                    ) {
+                        keyguardTransitionRepository.startTransition(
+                            TransitionInfo(
+                                ownerName = name,
+                                from = KeyguardState.ALTERNATE_BOUNCER,
+                                to = KeyguardState.PRIMARY_BOUNCER,
+                                animator = getAnimator(),
+                            )
+                        )
+                    }
+                }
+        }
+    }
+
+    private fun getAnimator(): ValueAnimator {
+        return ValueAnimator().apply {
+            interpolator = Interpolators.LINEAR
+            duration = TRANSITION_DURATION_MS
+        }
+    }
+
+    companion object {
+        private const val TRANSITION_DURATION_MS = 300L
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index ce61f2f..86f65dde 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel.Companion.isWakeAndUnlock
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionInfo
 import com.android.systemui.keyguard.shared.model.WakefulnessModel.Companion.isWakingOrStartingToWake
@@ -44,6 +45,7 @@
 
     override fun start() {
         listenForDozingToLockscreen()
+        listenForDozingToGone()
     }
 
     private fun listenForDozingToLockscreen() {
@@ -68,6 +70,28 @@
         }
     }
 
+    private fun listenForDozingToGone() {
+        scope.launch {
+            keyguardInteractor.biometricUnlockState
+                .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
+                .collect { (biometricUnlockState, lastStartedTransition) ->
+                    if (
+                        lastStartedTransition.to == KeyguardState.DOZING &&
+                            isWakeAndUnlock(biometricUnlockState)
+                    ) {
+                        keyguardTransitionRepository.startTransition(
+                            TransitionInfo(
+                                name,
+                                KeyguardState.DOZING,
+                                KeyguardState.GONE,
+                                getAnimator(),
+                            )
+                        )
+                    }
+                }
+        }
+    }
+
     private fun getAnimator(duration: Duration = DEFAULT_DURATION): ValueAnimator {
         return ValueAnimator().apply {
             setInterpolator(Interpolators.LINEAR)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index 81a5828..8715d1f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -34,6 +34,7 @@
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.launch
 
@@ -56,9 +57,14 @@
 
     private fun listenForDreamingToLockscreen() {
         scope.launch {
-            // Using isDreamingWithOverlay provides an optimized path to LOCKSCREEN state, which
-            // otherwise would have gone through OCCLUDED first
-            keyguardInteractor.isAbleToDream
+            // Dependending on the dream, either dream state or occluded change will change first,
+            // so listen for both
+            combine(keyguardInteractor.isAbleToDream, keyguardInteractor.isKeyguardOccluded) {
+                    isAbleToDream,
+                    isKeyguardOccluded ->
+                    isAbleToDream && isKeyguardOccluded
+                }
+                .distinctUntilChanged()
                 .sample(
                     combine(
                         keyguardInteractor.dozeTransitionModel,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index 14f918d..b5bcd45 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -45,6 +45,27 @@
     override fun start() {
         listenForGoneToAodOrDozing()
         listenForGoneToDreaming()
+        listenForGoneToLockscreen()
+    }
+
+    // Primarily for when the user chooses to lock down the device
+    private fun listenForGoneToLockscreen() {
+        scope.launch {
+            keyguardInteractor.isKeyguardShowing
+                .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
+                .collect { (isKeyguardShowing, lastStartedStep) ->
+                    if (isKeyguardShowing && lastStartedStep.to == KeyguardState.GONE) {
+                        keyguardTransitionRepository.startTransition(
+                            TransitionInfo(
+                                name,
+                                KeyguardState.GONE,
+                                KeyguardState.LOCKSCREEN,
+                                getAnimator(),
+                            )
+                        )
+                    }
+                }
+        }
     }
 
     private fun listenForGoneToDreaming() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 5674e2a..d01f489 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -33,7 +33,6 @@
 import kotlin.time.Duration
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.launch
 
@@ -53,9 +52,10 @@
         listenForLockscreenToOccluded()
         listenForLockscreenToCamera()
         listenForLockscreenToAodOrDozing()
-        listenForLockscreenToBouncer()
+        listenForLockscreenToPrimaryBouncer()
         listenForLockscreenToDreaming()
-        listenForLockscreenToBouncerDragging()
+        listenForLockscreenToPrimaryBouncerDragging()
+        listenForLockscreenToAlternateBouncer()
     }
 
     private fun listenForLockscreenToDreaming() {
@@ -78,9 +78,9 @@
         }
     }
 
-    private fun listenForLockscreenToBouncer() {
+    private fun listenForLockscreenToPrimaryBouncer() {
         scope.launch {
-            keyguardInteractor.isBouncerShowing
+            keyguardInteractor.primaryBouncerShowing
                 .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
                 .collect { pair ->
                     val (isBouncerShowing, lastStartedTransitionStep) = pair
@@ -91,7 +91,30 @@
                             TransitionInfo(
                                 ownerName = name,
                                 from = KeyguardState.LOCKSCREEN,
-                                to = KeyguardState.BOUNCER,
+                                to = KeyguardState.PRIMARY_BOUNCER,
+                                animator = getAnimator(),
+                            )
+                        )
+                    }
+                }
+        }
+    }
+
+    private fun listenForLockscreenToAlternateBouncer() {
+        scope.launch {
+            keyguardInteractor.alternateBouncerShowing
+                .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
+                .collect { pair ->
+                    val (isAlternateBouncerShowing, lastStartedTransitionStep) = pair
+                    if (
+                        isAlternateBouncerShowing &&
+                            lastStartedTransitionStep.to == KeyguardState.LOCKSCREEN
+                    ) {
+                        keyguardTransitionRepository.startTransition(
+                            TransitionInfo(
+                                ownerName = name,
+                                from = KeyguardState.LOCKSCREEN,
+                                to = KeyguardState.ALTERNATE_BOUNCER,
                                 animator = getAnimator(),
                             )
                         )
@@ -101,7 +124,7 @@
     }
 
     /* Starts transitions when manually dragging up the bouncer from the lockscreen. */
-    private fun listenForLockscreenToBouncerDragging() {
+    private fun listenForLockscreenToPrimaryBouncerDragging() {
         var transitionId: UUID? = null
         scope.launch {
             shadeRepository.shadeModel
@@ -144,7 +167,7 @@
                             keyguardTransitionRepository.startTransition(
                                 TransitionInfo(
                                     ownerName = name,
-                                    from = KeyguardState.BOUNCER,
+                                    from = KeyguardState.PRIMARY_BOUNCER,
                                     to = KeyguardState.LOCKSCREEN,
                                     animator = getAnimator(0.milliseconds)
                                 )
@@ -163,7 +186,7 @@
                                     TransitionInfo(
                                         ownerName = name,
                                         from = KeyguardState.LOCKSCREEN,
-                                        to = KeyguardState.BOUNCER,
+                                        to = KeyguardState.PRIMARY_BOUNCER,
                                         animator = null,
                                     )
                                 )
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
similarity index 75%
rename from packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromBouncerTransitionInteractor.kt
rename to packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index 0e9c447..b59b413 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -24,62 +24,63 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionInfo
 import com.android.systemui.keyguard.shared.model.WakefulnessState
-import com.android.systemui.shade.data.repository.ShadeRepository
 import com.android.systemui.util.kotlin.sample
-import java.util.UUID
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.launch
 
 @SysUISingleton
-class FromBouncerTransitionInteractor
+class FromPrimaryBouncerTransitionInteractor
 @Inject
 constructor(
     @Application private val scope: CoroutineScope,
     private val keyguardInteractor: KeyguardInteractor,
-    private val shadeRepository: ShadeRepository,
     private val keyguardTransitionRepository: KeyguardTransitionRepository,
     private val keyguardTransitionInteractor: KeyguardTransitionInteractor
-) : TransitionInteractor(FromBouncerTransitionInteractor::class.simpleName!!) {
-
-    private var transitionId: UUID? = null
+) : TransitionInteractor(FromPrimaryBouncerTransitionInteractor::class.simpleName!!) {
 
     override fun start() {
-        listenForBouncerToGone()
-        listenForBouncerToLockscreenOrAod()
+        listenForPrimaryBouncerToGone()
+        listenForPrimaryBouncerToLockscreenAodOrDozing()
     }
 
-    private fun listenForBouncerToLockscreenOrAod() {
+    private fun listenForPrimaryBouncerToLockscreenAodOrDozing() {
         scope.launch {
-            keyguardInteractor.isBouncerShowing
+            keyguardInteractor.primaryBouncerShowing
                 .sample(
                     combine(
                         keyguardInteractor.wakefulnessModel,
                         keyguardTransitionInteractor.startedKeyguardTransitionStep,
-                        ::Pair
+                        keyguardInteractor.isAodAvailable,
+                        ::toTriple
                     ),
-                    ::toTriple
+                    ::toQuad
                 )
-                .collect { triple ->
-                    val (isBouncerShowing, wakefulnessState, lastStartedTransitionStep) = triple
+                .collect {
+                    (isBouncerShowing, wakefulnessState, lastStartedTransitionStep, isAodAvailable)
+                    ->
                     if (
-                        !isBouncerShowing && lastStartedTransitionStep.to == KeyguardState.BOUNCER
+                        !isBouncerShowing &&
+                            lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER
                     ) {
                         val to =
                             if (
                                 wakefulnessState.state == WakefulnessState.STARTING_TO_SLEEP ||
                                     wakefulnessState.state == WakefulnessState.ASLEEP
                             ) {
-                                KeyguardState.AOD
+                                if (isAodAvailable) {
+                                    KeyguardState.AOD
+                                } else {
+                                    KeyguardState.DOZING
+                                }
                             } else {
                                 KeyguardState.LOCKSCREEN
                             }
                         keyguardTransitionRepository.startTransition(
                             TransitionInfo(
                                 ownerName = name,
-                                from = KeyguardState.BOUNCER,
+                                from = KeyguardState.PRIMARY_BOUNCER,
                                 to = to,
                                 animator = getAnimator(),
                             )
@@ -89,17 +90,17 @@
         }
     }
 
-    private fun listenForBouncerToGone() {
+    private fun listenForPrimaryBouncerToGone() {
         scope.launch {
             keyguardInteractor.isKeyguardGoingAway
-                .sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
+                .sample(keyguardTransitionInteractor.finishedKeyguardState) { a, b -> Pair(a, b) }
                 .collect { pair ->
                     val (isKeyguardGoingAway, keyguardState) = pair
-                    if (isKeyguardGoingAway && keyguardState == KeyguardState.BOUNCER) {
+                    if (isKeyguardGoingAway && keyguardState == KeyguardState.PRIMARY_BOUNCER) {
                         keyguardTransitionRepository.startTransition(
                             TransitionInfo(
                                 ownerName = name,
-                                from = KeyguardState.BOUNCER,
+                                from = KeyguardState.PRIMARY_BOUNCER,
                                 to = KeyguardState.GONE,
                                 animator = getAnimator(),
                             )
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 4cf56fe..d25aff0a 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
@@ -22,6 +22,9 @@
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
 import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
 import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel
@@ -31,7 +34,6 @@
 import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.keyguard.shared.model.WakefulnessModel
 import com.android.systemui.statusbar.CommandQueue
-import com.android.systemui.statusbar.CommandQueue.Callbacks
 import javax.inject.Inject
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.delay
@@ -41,7 +43,9 @@
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onStart
 
 /**
  * Encapsulates business-logic related to the keyguard but not to a more specific part within it.
@@ -52,6 +56,8 @@
 constructor(
     private val repository: KeyguardRepository,
     private val commandQueue: CommandQueue,
+    featureFlags: FeatureFlags,
+    bouncerRepository: KeyguardBouncerRepository,
 ) {
     /**
      * The amount of doze the system is in, where `1.0` is fully dozing and `0.0` is not dozing at
@@ -117,8 +123,10 @@
     val isKeyguardOccluded: Flow<Boolean> = repository.isKeyguardOccluded
     /** Whether the keyguard is going away. */
     val isKeyguardGoingAway: Flow<Boolean> = repository.isKeyguardGoingAway
-    /** Whether the bouncer is showing or not. */
-    val isBouncerShowing: Flow<Boolean> = repository.isBouncerShowing
+    /** Whether the primary bouncer is showing or not. */
+    val primaryBouncerShowing: Flow<Boolean> = bouncerRepository.primaryBouncerVisible
+    /** Whether the alternate bouncer is showing or not. */
+    val alternateBouncerShowing: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
     /** The device wake/sleep state */
     val wakefulnessModel: Flow<WakefulnessModel> = repository.wakefulness
     /** Observable for the [StatusBarState] */
@@ -129,6 +137,29 @@
      */
     val biometricUnlockState: Flow<BiometricUnlockModel> = repository.biometricUnlockState
 
+    /** Keyguard is present and is not occluded. */
+    val isKeyguardVisible: Flow<Boolean> =
+        combine(isKeyguardShowing, isKeyguardOccluded) { showing, occluded -> showing && !occluded }
+
+    /** Whether camera is launched over keyguard. */
+    var isSecureCameraActive =
+        if (featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR)) {
+            combine(
+                    isKeyguardVisible,
+                    bouncerRepository.primaryBouncerVisible,
+                    onCameraLaunchDetected,
+                ) { isKeyguardVisible, isPrimaryBouncerShowing, cameraLaunchEvent ->
+                    when {
+                        isKeyguardVisible -> false
+                        isPrimaryBouncerShowing -> false
+                        else -> cameraLaunchEvent == CameraLaunchSourceModel.POWER_DOUBLE_TAP
+                    }
+                }
+                .onStart { emit(false) }
+        } else {
+            flowOf(false)
+        }
+
     /** The approximate location on the screen of the fingerprint sensor, if one is available. */
     val fingerprintSensorLocation: Flow<Point?> = repository.fingerprintSensorLocation
 
@@ -155,6 +186,11 @@
         }
     }
 
+    /** Sets whether quick settings or quick-quick settings is visible. */
+    fun setQuickSettingsVisible(isVisible: Boolean) {
+        repository.setQuickSettingsVisible(isVisible)
+    }
+
     companion object {
         private const val TAG = "KeyguardInteractor"
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt
new file mode 100644
index 0000000..6525a13
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.R
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.shared.model.Position
+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.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.keyguard.domain.model.KeyguardSettingsPopupMenuModel
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.plugins.ActivityStarter
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+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.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.stateIn
+
+/** Business logic for use-cases related to the keyguard long-press feature. */
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class KeyguardLongPressInteractor
+@Inject
+constructor(
+    @Application unsafeContext: Context,
+    @Application scope: CoroutineScope,
+    transitionInteractor: KeyguardTransitionInteractor,
+    repository: KeyguardRepository,
+    private val activityStarter: ActivityStarter,
+    private val logger: UiEventLogger,
+    private val featureFlags: FeatureFlags,
+    broadcastDispatcher: BroadcastDispatcher,
+) {
+    private val appContext = unsafeContext.applicationContext
+
+    private val _isLongPressHandlingEnabled: StateFlow<Boolean> =
+        if (isFeatureEnabled()) {
+                combine(
+                    transitionInteractor.finishedKeyguardState.map {
+                        it == KeyguardState.LOCKSCREEN
+                    },
+                    repository.isQuickSettingsVisible,
+                ) { isFullyTransitionedToLockScreen, isQuickSettingsVisible ->
+                    isFullyTransitionedToLockScreen && !isQuickSettingsVisible
+                }
+            } else {
+                flowOf(false)
+            }
+            .stateIn(
+                scope = scope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = false,
+            )
+
+    /** Whether the long-press handling feature should be enabled. */
+    val isLongPressHandlingEnabled: Flow<Boolean> = _isLongPressHandlingEnabled
+
+    private val _menu = MutableStateFlow<KeyguardSettingsPopupMenuModel?>(null)
+    /** Model for a menu that should be shown; `null` when no menu should be shown. */
+    val menu: Flow<KeyguardSettingsPopupMenuModel?> =
+        isLongPressHandlingEnabled.flatMapLatest { isEnabled ->
+            if (isEnabled) {
+                _menu
+            } else {
+                flowOf(null)
+            }
+        }
+
+    init {
+        if (isFeatureEnabled()) {
+            broadcastDispatcher
+                .broadcastFlow(
+                    IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS),
+                )
+                .onEach { hideMenu() }
+                .launchIn(scope)
+        }
+    }
+
+    /** Notifies that the user has long-pressed on the lock screen. */
+    fun onLongPress(x: Int, y: Int) {
+        if (!_isLongPressHandlingEnabled.value) {
+            return
+        }
+
+        showMenu(
+            x = x,
+            y = y,
+        )
+    }
+
+    private fun isFeatureEnabled(): Boolean {
+        return featureFlags.isEnabled(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED) &&
+            featureFlags.isEnabled(Flags.REVAMPED_WALLPAPER_UI)
+    }
+
+    /** Updates application state to ask to show the menu at the given coordinates. */
+    private fun showMenu(
+        x: Int,
+        y: Int,
+    ) {
+        _menu.value =
+            KeyguardSettingsPopupMenuModel(
+                position =
+                    Position(
+                        x = x,
+                        y = y,
+                    ),
+                onClicked = {
+                    hideMenu()
+                    navigateToLockScreenSettings()
+                },
+                onDismissed = { hideMenu() },
+            )
+        logger.log(LogEvents.LOCK_SCREEN_LONG_PRESS_POPUP_SHOWN)
+    }
+
+    /** Updates application state to ask to hide the menu. */
+    private fun hideMenu() {
+        _menu.value = null
+    }
+
+    /** Opens the wallpaper picker screen after the device is unlocked by the user. */
+    private fun navigateToLockScreenSettings() {
+        logger.log(LogEvents.LOCK_SCREEN_LONG_PRESS_POPUP_CLICKED)
+        activityStarter.dismissKeyguardThenExecute(
+            /* action= */ {
+                appContext.startActivity(
+                    Intent(Intent.ACTION_SET_WALLPAPER).apply {
+                        flags = Intent.FLAG_ACTIVITY_NEW_TASK
+                        appContext
+                            .getString(R.string.config_wallpaperPickerPackage)
+                            .takeIf { it.isNotEmpty() }
+                            ?.let { packageName -> setPackage(packageName) }
+                    }
+                )
+                true
+            },
+            /* cancel= */ {},
+            /* afterKeyguardGone= */ true,
+        )
+    }
+
+    enum class LogEvents(
+        private val _id: Int,
+    ) : UiEventLogger.UiEventEnum {
+        @UiEvent(doc = "The lock screen was long-pressed and we showed the settings popup menu.")
+        LOCK_SCREEN_LONG_PRESS_POPUP_SHOWN(1292),
+        @UiEvent(doc = "The lock screen long-press popup menu was clicked.")
+        LOCK_SCREEN_LONG_PRESS_POPUP_CLICKED(1293),
+        ;
+
+        override fun getId() = _id
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index 9ddc575..dfbe1c2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -18,12 +18,14 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import android.app.AlertDialog
+import android.app.admin.DevicePolicyManager
 import android.content.Intent
 import android.util.Log
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.animation.DialogLaunchAnimator
 import com.android.systemui.animation.Expandable
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
@@ -41,13 +43,17 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import dagger.Lazy
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.withContext
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class KeyguardQuickAffordanceInteractor
 @Inject
@@ -61,6 +67,8 @@
     private val featureFlags: FeatureFlags,
     private val repository: Lazy<KeyguardQuickAffordanceRepository>,
     private val launchAnimator: DialogLaunchAnimator,
+    private val devicePolicyManager: DevicePolicyManager,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
 ) {
     private val isUsingRepository: Boolean
         get() = featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES)
@@ -74,9 +82,13 @@
         get() = featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES)
 
     /** Returns an observable for the quick affordance at the given position. */
-    fun quickAffordance(
+    suspend fun quickAffordance(
         position: KeyguardQuickAffordancePosition
     ): Flow<KeyguardQuickAffordanceModel> {
+        if (isFeatureDisabledByDevicePolicy()) {
+            return flowOf(KeyguardQuickAffordanceModel.Hidden)
+        }
+
         return combine(
             quickAffordanceAlwaysVisible(position),
             keyguardInteractor.isDozing,
@@ -148,13 +160,20 @@
      *
      * @return `true` if the affordance was selected successfully; `false` otherwise.
      */
-    fun select(slotId: String, affordanceId: String): Boolean {
+    suspend fun select(slotId: String, affordanceId: String): Boolean {
         check(isUsingRepository)
+        if (isFeatureDisabledByDevicePolicy()) {
+            return false
+        }
 
         val slots = repository.get().getSlotPickerRepresentations()
         val slot = slots.find { it.id == slotId } ?: return false
         val selections =
-            repository.get().getSelections().getOrDefault(slotId, emptyList()).toMutableList()
+            repository
+                .get()
+                .getCurrentSelections()
+                .getOrDefault(slotId, emptyList())
+                .toMutableList()
         val alreadySelected = selections.remove(affordanceId)
         if (!alreadySelected) {
             while (selections.size > 0 && selections.size >= slot.maxSelectedAffordances) {
@@ -183,8 +202,11 @@
      * @return `true` if the affordance was successfully removed; `false` otherwise (for example, if
      * the affordance was not on the slot to begin with).
      */
-    fun unselect(slotId: String, affordanceId: String?): Boolean {
+    suspend fun unselect(slotId: String, affordanceId: String?): Boolean {
         check(isUsingRepository)
+        if (isFeatureDisabledByDevicePolicy()) {
+            return false
+        }
 
         val slots = repository.get().getSlotPickerRepresentations()
         if (slots.find { it.id == slotId } == null) {
@@ -193,7 +215,7 @@
 
         if (affordanceId.isNullOrEmpty()) {
             return if (
-                repository.get().getSelections().getOrDefault(slotId, emptyList()).isEmpty()
+                repository.get().getCurrentSelections().getOrDefault(slotId, emptyList()).isEmpty()
             ) {
                 false
             } else {
@@ -203,7 +225,11 @@
         }
 
         val selections =
-            repository.get().getSelections().getOrDefault(slotId, emptyList()).toMutableList()
+            repository
+                .get()
+                .getCurrentSelections()
+                .getOrDefault(slotId, emptyList())
+                .toMutableList()
         return if (selections.remove(affordanceId)) {
             repository
                 .get()
@@ -219,8 +245,12 @@
 
     /** Returns affordance IDs indexed by slot ID, for all known slots. */
     suspend fun getSelections(): Map<String, List<KeyguardQuickAffordancePickerRepresentation>> {
+        if (isFeatureDisabledByDevicePolicy()) {
+            return emptyMap()
+        }
+
         val slots = repository.get().getSlotPickerRepresentations()
-        val selections = repository.get().getSelections()
+        val selections = repository.get().getCurrentSelections()
         val affordanceById =
             getAffordancePickerRepresentations().associateBy { affordance -> affordance.id }
         return slots.associate { slot ->
@@ -343,13 +373,17 @@
         return repository.get().getAffordancePickerRepresentations()
     }
 
-    fun getSlotPickerRepresentations(): List<KeyguardSlotPickerRepresentation> {
+    suspend fun getSlotPickerRepresentations(): List<KeyguardSlotPickerRepresentation> {
         check(isUsingRepository)
 
+        if (isFeatureDisabledByDevicePolicy()) {
+            return emptyList()
+        }
+
         return repository.get().getSlotPickerRepresentations()
     }
 
-    fun getPickerFlags(): List<KeyguardPickerFlag> {
+    suspend fun getPickerFlags(): List<KeyguardPickerFlag> {
         return listOf(
             KeyguardPickerFlag(
                 name = Contract.FlagsTable.FLAG_NAME_REVAMPED_WALLPAPER_UI,
@@ -357,7 +391,9 @@
             ),
             KeyguardPickerFlag(
                 name = Contract.FlagsTable.FLAG_NAME_CUSTOM_LOCK_SCREEN_QUICK_AFFORDANCES_ENABLED,
-                value = featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES),
+                value =
+                    !isFeatureDisabledByDevicePolicy() &&
+                        featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES),
             ),
             KeyguardPickerFlag(
                 name = Contract.FlagsTable.FLAG_NAME_CUSTOM_CLOCKS_ENABLED,
@@ -367,9 +403,24 @@
                 name = Contract.FlagsTable.FLAG_NAME_WALLPAPER_FULLSCREEN_PREVIEW,
                 value = featureFlags.isEnabled(Flags.WALLPAPER_FULLSCREEN_PREVIEW),
             ),
+            KeyguardPickerFlag(
+                name = Contract.FlagsTable.FLAG_NAME_MONOCHROMATIC_THEME,
+                value = featureFlags.isEnabled(Flags.MONOCHROMATIC_THEME)
+            )
         )
     }
 
+    private suspend fun isFeatureDisabledByDevicePolicy(): Boolean {
+        val flags =
+            withContext(backgroundDispatcher) {
+                devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId)
+            }
+        val flagsToCheck =
+            DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL or
+                DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL
+        return flagsToCheck and flags != 0
+    }
+
     companion object {
         private const val TAG = "KeyguardQuickAffordanceInteractor"
         private const val DELIMITER = "::"
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
index d4e23499..51b0277 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
@@ -22,7 +22,6 @@
 import com.android.systemui.plugins.log.LogLevel.VERBOSE
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.launch
 
 private val TAG = KeyguardTransitionAuditLogger::class.simpleName!!
@@ -46,8 +45,14 @@
         }
 
         scope.launch {
-            keyguardInteractor.isBouncerShowing.collect {
-                logger.log(TAG, VERBOSE, "Bouncer showing", it)
+            keyguardInteractor.primaryBouncerShowing.collect {
+                logger.log(TAG, VERBOSE, "Primary bouncer showing", it)
+            }
+        }
+
+        scope.launch {
+            keyguardInteractor.alternateBouncerShowing.collect {
+                logger.log(TAG, VERBOSE, "Alternate bouncer showing", it)
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
index fbed446..efc1bd0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
@@ -37,13 +37,14 @@
             // exhaustive
             val ret =
                 when (it) {
-                    is FromBouncerTransitionInteractor -> Log.d(TAG, "Started $it")
+                    is FromPrimaryBouncerTransitionInteractor -> Log.d(TAG, "Started $it")
                     is FromAodTransitionInteractor -> Log.d(TAG, "Started $it")
                     is FromGoneTransitionInteractor -> Log.d(TAG, "Started $it")
                     is FromLockscreenTransitionInteractor -> Log.d(TAG, "Started $it")
                     is FromDreamingTransitionInteractor -> Log.d(TAG, "Started $it")
                     is FromOccludedTransitionInteractor -> Log.d(TAG, "Started $it")
                     is FromDozingTransitionInteractor -> Log.d(TAG, "Started $it")
+                    is FromAlternateBouncerTransitionInteractor -> Log.d(TAG, "Started $it")
                 }
             it.start()
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index ad6dbea..1b7da5b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -19,21 +19,16 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
-import com.android.systemui.keyguard.shared.model.AnimationParams
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
-import com.android.systemui.keyguard.shared.model.KeyguardState.BOUNCER
 import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.shared.model.TransitionState
-import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import javax.inject.Inject
-import kotlin.math.max
-import kotlin.math.min
-import kotlin.time.Duration
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.map
@@ -46,6 +41,10 @@
 constructor(
     repository: KeyguardTransitionRepository,
 ) {
+    /** (any)->GONE transition information */
+    val anyStateToGoneTransition: Flow<TransitionStep> =
+        repository.transitions.filter { step -> step.to == KeyguardState.GONE }
+
     /** (any)->AOD transition information */
     val anyStateToAodTransition: Flow<TransitionStep> =
         repository.transitions.filter { step -> step.to == KeyguardState.AOD }
@@ -63,9 +62,9 @@
     /** LOCKSCREEN->AOD transition information. */
     val lockscreenToAodTransition: Flow<TransitionStep> = repository.transition(LOCKSCREEN, AOD)
 
-    /** LOCKSCREEN->BOUNCER transition information. */
-    val lockscreenToBouncerTransition: Flow<TransitionStep> =
-        repository.transition(LOCKSCREEN, BOUNCER)
+    /** LOCKSCREEN->PRIMARY_BOUNCER transition information. */
+    val mLockscreenToPrimaryBouncerTransition: Flow<TransitionStep> =
+        repository.transition(LOCKSCREEN, PRIMARY_BOUNCER)
 
     /** LOCKSCREEN->DREAMING transition information. */
     val lockscreenToDreamingTransition: Flow<TransitionStep> =
@@ -104,38 +103,4 @@
     /* The last completed [KeyguardState] transition */
     val finishedKeyguardState: Flow<KeyguardState> =
         finishedKeyguardTransitionStep.map { step -> step.to }
-
-    /**
-     * Transitions will occur over a [totalDuration] with [TransitionStep]s being emitted in the
-     * range of [0, 1]. View animations should begin and end within a subset of this range. This
-     * function maps the [startTime] and [duration] into [0, 1], when this subset is valid.
-     */
-    fun transitionStepAnimation(
-        flow: Flow<TransitionStep>,
-        params: AnimationParams,
-        totalDuration: Duration,
-    ): Flow<Float> {
-        val start = (params.startTime / totalDuration).toFloat()
-        val chunks = (totalDuration / params.duration).toFloat()
-        var isRunning = false
-        return flow
-            .map { step ->
-                val value = (step.value - start) * chunks
-                if (step.transitionState == STARTED) {
-                    // When starting, make sure to always emit. If a transition is started from the
-                    // middle, it is possible this animation is being skipped but we need to inform
-                    // the ViewModels of the last update
-                    isRunning = true
-                    max(0f, min(1f, value))
-                } else if (isRunning && value >= 1f) {
-                    // Always send a final value of 1. Because of rounding, [value] may never be
-                    // exactly 1.
-                    isRunning = false
-                    1f
-                } else {
-                    value
-                }
-            }
-            .filter { value -> value >= 0f && value <= 1f }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
index a59c407..833eda7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
@@ -86,7 +86,8 @@
                 KeyguardState.DOZING -> false
                 KeyguardState.AOD -> false
                 KeyguardState.DREAMING -> true
-                KeyguardState.BOUNCER -> true
+                KeyguardState.ALTERNATE_BOUNCER -> true
+                KeyguardState.PRIMARY_BOUNCER -> true
                 KeyguardState.LOCKSCREEN -> true
                 KeyguardState.GONE -> true
                 KeyguardState.OCCLUDED -> true
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
index a92540d..6610983 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
@@ -113,6 +113,8 @@
                 0f
             }
         }
+    /** Allow for interaction when just about fully visible */
+    val isInteractable: Flow<Boolean> = bouncerExpansion.map { it > 0.9 }
 
     // TODO(b/243685699): Move isScrimmed logic to data layer.
     // TODO(b/243695312): Encapsulate all of the show logic for the bouncer.
@@ -162,7 +164,7 @@
         } else {
             DejankUtils.postAfterTraversal(showRunnable)
         }
-        keyguardStateController.notifyBouncerShowing(true)
+        keyguardStateController.notifyPrimaryBouncerShowing(true)
         primaryBouncerCallbackInteractor.dispatchStartingToShow()
         Trace.endSection()
     }
@@ -179,7 +181,7 @@
         }
 
         falsingCollector.onBouncerHidden()
-        keyguardStateController.notifyBouncerShowing(false /* showing */)
+        keyguardStateController.notifyPrimaryBouncerShowing(false /* showing */)
         cancelShowRunnable()
         repository.setPrimaryShowingSoon(false)
         repository.setPrimaryVisible(false)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
index 81fa233..d9690b7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
@@ -32,7 +32,9 @@
 
     @Binds
     @IntoSet
-    abstract fun fromBouncer(impl: FromBouncerTransitionInteractor): TransitionInteractor
+    abstract fun fromPrimaryBouncer(
+        impl: FromPrimaryBouncerTransitionInteractor
+    ): TransitionInteractor
 
     @Binds
     @IntoSet
@@ -53,4 +55,10 @@
     @Binds
     @IntoSet
     abstract fun fromDozing(impl: FromDozingTransitionInteractor): TransitionInteractor
+
+    @Binds
+    @IntoSet
+    abstract fun fromAlternateBouncer(
+        impl: FromAlternateBouncerTransitionInteractor
+    ): TransitionInteractor
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
index 4d24c14..e3e3527 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
@@ -30,7 +30,26 @@
 
     abstract fun start()
 
+    fun <A, B, C> toTriple(a: A, b: B, c: C) = Triple(a, b, c)
+
     fun <A, B, C> toTriple(a: A, bc: Pair<B, C>) = Triple(a, bc.first, bc.second)
 
     fun <A, B, C> toTriple(ab: Pair<A, B>, c: C) = Triple(ab.first, ab.second, c)
+
+    fun <A, B, C, D> toQuad(a: A, b: B, c: C, d: D) = Quad(a, b, c, d)
+
+    fun <A, B, C, D> toQuad(a: A, bcd: Triple<B, C, D>) = Quad(a, bcd.first, bcd.second, bcd.third)
+
+    fun <A, B, C, D, E> toQuint(a: A, bcde: Quad<B, C, D, E>) =
+        Quint(a, bcde.first, bcde.second, bcde.third, bcde.fourth)
 }
+
+data class Quad<A, B, C, D>(val first: A, val second: B, val third: C, val fourth: D)
+
+data class Quint<A, B, C, D, E>(
+    val first: A,
+    val second: B,
+    val third: C,
+    val fourth: D,
+    val fifth: E
+)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardSettingsPopupMenuModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardSettingsPopupMenuModel.kt
new file mode 100644
index 0000000..7c61e71
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardSettingsPopupMenuModel.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.keyguard.domain.model
+
+import com.android.systemui.common.shared.model.Position
+
+/** Models a settings popup menu for the lock screen. */
+data class KeyguardSettingsPopupMenuModel(
+    /** Where the menu should be anchored, roughly in screen space. */
+    val position: Position,
+    /** Callback to invoke when the menu gets clicked by the user. */
+    val onClicked: () -> Unit,
+    /** Callback to invoke when the menu gets dismissed by the user. */
+    val onDismissed: () -> Unit,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/constants/KeyguardBouncerConstants.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/constants/KeyguardBouncerConstants.kt
index bb5ac84..8222dd5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/constants/KeyguardBouncerConstants.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/constants/KeyguardBouncerConstants.kt
@@ -25,4 +25,5 @@
      */
     const val EXPANSION_HIDDEN = 1f
     const val EXPANSION_VISIBLE = 0f
+    const val ALPHA_EXPANSION_THRESHOLD = 0.95f
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
new file mode 100644
index 0000000..b1c5f8f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.shared.model
+
+import android.hardware.face.FaceManager
+
+/** Authentication status provided by [com.android.keyguard.faceauth.KeyguardFaceAuthManager] */
+sealed class AuthenticationStatus
+
+/** Success authentication status. */
+data class SuccessAuthenticationStatus(val successResult: FaceManager.AuthenticationResult) :
+    AuthenticationStatus()
+
+/** Face authentication help message. */
+data class HelpAuthenticationStatus(val msgId: Int, val msg: String?) : AuthenticationStatus()
+
+/** Face acquired message. */
+data class AcquiredAuthenticationStatus(val acquiredInfo: Int) : AuthenticationStatus()
+
+/** Face authentication failed message. */
+object FailedAuthenticationStatus : AuthenticationStatus()
+
+/** Face authentication error message */
+data class ErrorAuthenticationStatus(val msgId: Int, val msg: String?) : AuthenticationStatus() {
+    /**
+     * Method that checks if [msgId] is a lockout error. A lockout error means that face
+     * authentication is locked out.
+     */
+    fun isLockoutError() = msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT
+
+    /**
+     * Method that checks if [msgId] is a cancellation error. This means that face authentication
+     * was cancelled before it completed.
+     */
+    fun isCancellationError() = msgId == FaceManager.FACE_ERROR_CANCELED
+}
+
+/** Face detection success message. */
+data class DetectionStatus(val sensorId: Int, val userId: Int, val isStrongBiometric: Boolean)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
index c757986..87b4321 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
@@ -42,10 +42,15 @@
      */
     AOD,
     /*
-     * The security screen prompt UI, containing PIN, Password, Pattern, and all FPS
-     * (Fingerprint Sensor) variations, for the user to verify their credentials
+     * The security screen prompt containing UI to prompt the user to use a biometric credential
+     * (ie: fingerprint). When supported, this may show before showing the primary bouncer.
      */
-    BOUNCER,
+    ALTERNATE_BOUNCER,
+    /*
+     * The security screen prompt UI, containing PIN, Password, Pattern for the user to verify their
+     * credentials.
+     */
+    PRIMARY_BOUNCER,
     /*
      * Device is actively displaying keyguard UI and is not in low-power mode. Device may be
      * unlocked if SWIPE security method is used, or if face lockscreen bypass is false.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
new file mode 100644
index 0000000..ca1e27c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.keyguard.ui
+
+import android.view.animation.Interpolator
+import com.android.systemui.animation.Interpolators.LINEAR
+import com.android.systemui.keyguard.shared.model.TransitionState.CANCELED
+import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
+import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
+import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import kotlin.math.max
+import kotlin.math.min
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
+
+/**
+ * For the given transition params, construct a flow using [createFlow] for the specified portion of
+ * the overall transition.
+ */
+class KeyguardTransitionAnimationFlow(
+    private val transitionDuration: Duration,
+    private val transitionFlow: Flow<TransitionStep>,
+) {
+    /**
+     * Transitions will occur over a [transitionDuration] with [TransitionStep]s being emitted in
+     * the range of [0, 1]. View animations should begin and end within a subset of this range. This
+     * function maps the [startTime] and [duration] into [0, 1], when this subset is valid.
+     */
+    fun createFlow(
+        duration: Duration,
+        onStep: (Float) -> Float,
+        startTime: Duration = 0.milliseconds,
+        onCancel: (() -> Float)? = null,
+        onFinish: (() -> Float)? = null,
+        interpolator: Interpolator = LINEAR,
+    ): Flow<Float> {
+        if (!duration.isPositive()) {
+            throw IllegalArgumentException("duration must be a positive number: $duration")
+        }
+        if ((startTime + duration).compareTo(transitionDuration) > 0) {
+            throw IllegalArgumentException(
+                "startTime($startTime) + duration($duration) must be" +
+                    " <= transitionDuration($transitionDuration)"
+            )
+        }
+
+        val start = (startTime / transitionDuration).toFloat()
+        val chunks = (transitionDuration / duration).toFloat()
+        var isComplete = true
+
+        fun stepToValue(step: TransitionStep): Float? {
+            val value = (step.value - start) * chunks
+            return when (step.transitionState) {
+                // When starting, make sure to always emit. If a transition is started from the
+                // middle, it is possible this animation is being skipped but we need to inform
+                // the ViewModels of the last update
+                STARTED -> {
+                    isComplete = false
+                    max(0f, min(1f, value))
+                }
+                // Always send a final value of 1. Because of rounding, [value] may never be
+                // exactly 1.
+                RUNNING ->
+                    if (isComplete) {
+                        null
+                    } else if (value >= 1f) {
+                        isComplete = true
+                        1f
+                    } else if (value >= 0f) {
+                        value
+                    } else {
+                        null
+                    }
+                else -> null
+            }?.let { onStep(interpolator.getInterpolation(it)) }
+        }
+
+        return transitionFlow
+            .map { step ->
+                when (step.transitionState) {
+                    STARTED -> stepToValue(step)
+                    RUNNING -> stepToValue(step)
+                    CANCELED -> onCancel?.invoke()
+                    FINISHED -> onFinish?.invoke()
+                }
+            }
+            .filterNotNull()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index d020529..ab009f4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -49,6 +49,7 @@
 import kotlin.math.sqrt
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.flatMapLatest
@@ -66,6 +67,8 @@
 object KeyguardBottomAreaViewBinder {
 
     private const val EXIT_DOZE_BUTTON_REVEAL_ANIMATION_DURATION_MS = 250L
+    private const val SCALE_SELECTED_BUTTON = 1.23f
+    private const val DIM_ALPHA = 0.3f
 
     /**
      * Defines interface for an object that acts as the binding between the view and its view-model.
@@ -161,12 +164,26 @@
 
                         ambientIndicationArea?.alpha = alpha
                         indicationArea.alpha = alpha
-                        startButton.alpha = alpha
-                        endButton.alpha = alpha
                     }
                 }
 
                 launch {
+                    updateButtonAlpha(
+                        view = startButton,
+                        viewModel = viewModel.startButton,
+                        alphaFlow = viewModel.alpha,
+                    )
+                }
+
+                launch {
+                    updateButtonAlpha(
+                        view = endButton,
+                        viewModel = viewModel.endButton,
+                        alphaFlow = viewModel.alpha,
+                    )
+                }
+
+                launch {
                     viewModel.indicationAreaTranslationX.collect { translationX ->
                         indicationArea.translationX = translationX
                         ambientIndicationArea?.translationX = translationX
@@ -315,6 +332,11 @@
             } else {
                 null
             }
+        view
+            .animate()
+            .scaleX(if (viewModel.isSelected) SCALE_SELECTED_BUTTON else 1f)
+            .scaleY(if (viewModel.isSelected) SCALE_SELECTED_BUTTON else 1f)
+            .start()
 
         view.isClickable = viewModel.isClickable
         if (viewModel.isClickable) {
@@ -333,6 +355,17 @@
         view.isSelected = viewModel.isSelected
     }
 
+    private suspend fun updateButtonAlpha(
+        view: View,
+        viewModel: Flow<KeyguardQuickAffordanceViewModel>,
+        alphaFlow: Flow<Float>,
+    ) {
+        combine(viewModel.map { it.isDimmed }, alphaFlow) { isDimmed, alpha ->
+                if (isDimmed) DIM_ALPHA else alpha
+            }
+            .collect { view.alpha = it }
+    }
+
     private class OnTouchListener(
         private val view: View,
         private val viewModel: KeyguardQuickAffordanceViewModel,
@@ -348,82 +381,87 @@
             return when (event?.actionMasked) {
                 MotionEvent.ACTION_DOWN ->
                     if (viewModel.configKey != null) {
-                        longPressAnimator =
-                            view
-                                .animate()
-                                .scaleX(PRESSED_SCALE)
-                                .scaleY(PRESSED_SCALE)
-                                .setDuration(longPressDurationMs)
-                                .withEndAction {
-                                    view.setOnClickListener {
-                                        vibratorHelper?.vibrate(
-                                            if (viewModel.isActivated) {
-                                                Vibrations.Activated
-                                            } else {
-                                                Vibrations.Deactivated
-                                            }
-                                        )
-                                        viewModel.onClicked(
-                                            KeyguardQuickAffordanceViewModel.OnClickedParameters(
-                                                configKey = viewModel.configKey,
-                                                expandable = Expandable.fromView(view),
-                                            )
-                                        )
+                        if (isUsingAccurateTool(event)) {
+                            // For accurate tool types (stylus, mouse, etc.), we don't require a
+                            // long-press.
+                        } else {
+                            // When not using a stylus, we require a long-press to activate the
+                            // quick affordance, mostly to do "falsing" (e.g. protect from false
+                            // clicks in the pocket/bag).
+                            longPressAnimator =
+                                view
+                                    .animate()
+                                    .scaleX(PRESSED_SCALE)
+                                    .scaleY(PRESSED_SCALE)
+                                    .setDuration(longPressDurationMs)
+                                    .withEndAction {
+                                        dispatchClick(viewModel.configKey)
+                                        cancel()
                                     }
-                                    view.performClick()
-                                    view.setOnClickListener(null)
-                                    cancel()
-                                }
+                        }
                         true
                     } else {
                         false
                     }
                 MotionEvent.ACTION_MOVE -> {
-                    if (event.historySize > 0) {
-                        val distance =
-                            sqrt(
-                                (event.y - event.getHistoricalY(0)).pow(2) +
-                                    (event.x - event.getHistoricalX(0)).pow(2)
-                            )
-                        if (distance > ViewConfiguration.getTouchSlop()) {
+                    if (!isUsingAccurateTool(event)) {
+                        // Moving too far while performing a long-press gesture cancels that
+                        // gesture.
+                        val distanceMoved = distanceMoved(event)
+                        if (distanceMoved > ViewConfiguration.getTouchSlop()) {
                             cancel()
                         }
                     }
                     true
                 }
                 MotionEvent.ACTION_UP -> {
-                    cancel(
-                        onAnimationEnd =
-                            if (event.eventTime - event.downTime < longPressDurationMs) {
-                                Runnable {
-                                    messageDisplayer.invoke(
-                                        R.string.keyguard_affordance_press_too_short
-                                    )
-                                    val amplitude =
-                                        view.context.resources
-                                            .getDimensionPixelSize(
-                                                R.dimen.keyguard_affordance_shake_amplitude
-                                            )
-                                            .toFloat()
-                                    val shakeAnimator =
-                                        ObjectAnimator.ofFloat(
-                                            view,
-                                            "translationX",
-                                            -amplitude / 2,
-                                            amplitude / 2,
+                    if (isUsingAccurateTool(event)) {
+                        // When using an accurate tool type (stylus, mouse, etc.), we don't require
+                        // a long-press gesture to activate the quick affordance. Therefore, lifting
+                        // the pointer performs a click.
+                        if (
+                            viewModel.configKey != null &&
+                                distanceMoved(event) <= ViewConfiguration.getTouchSlop()
+                        ) {
+                            dispatchClick(viewModel.configKey)
+                        }
+                    } else {
+                        // When not using a stylus, lifting the finger/pointer will actually cancel
+                        // the long-press gesture. Calling cancel after the quick affordance was
+                        // already long-press activated is a no-op, so it's safe to call from here.
+                        cancel(
+                            onAnimationEnd =
+                                if (event.eventTime - event.downTime < longPressDurationMs) {
+                                    Runnable {
+                                        messageDisplayer.invoke(
+                                            R.string.keyguard_affordance_press_too_short
                                         )
-                                    shakeAnimator.duration =
-                                        ShakeAnimationDuration.inWholeMilliseconds
-                                    shakeAnimator.interpolator =
-                                        CycleInterpolator(ShakeAnimationCycles)
-                                    shakeAnimator.start()
+                                        val amplitude =
+                                            view.context.resources
+                                                .getDimensionPixelSize(
+                                                    R.dimen.keyguard_affordance_shake_amplitude
+                                                )
+                                                .toFloat()
+                                        val shakeAnimator =
+                                            ObjectAnimator.ofFloat(
+                                                view,
+                                                "translationX",
+                                                -amplitude / 2,
+                                                amplitude / 2,
+                                            )
+                                        shakeAnimator.duration =
+                                            ShakeAnimationDuration.inWholeMilliseconds
+                                        shakeAnimator.interpolator =
+                                            CycleInterpolator(ShakeAnimationCycles)
+                                        shakeAnimator.start()
 
-                                    vibratorHelper?.vibrate(Vibrations.Shake)
+                                        vibratorHelper?.vibrate(Vibrations.Shake)
+                                    }
+                                } else {
+                                    null
                                 }
-                            } else {
-                                null
-                            }
-                    )
+                        )
+                    }
                     true
                 }
                 MotionEvent.ACTION_CANCEL -> {
@@ -434,6 +472,28 @@
             }
         }
 
+        private fun dispatchClick(
+            configKey: String,
+        ) {
+            view.setOnClickListener {
+                vibratorHelper?.vibrate(
+                    if (viewModel.isActivated) {
+                        Vibrations.Activated
+                    } else {
+                        Vibrations.Deactivated
+                    }
+                )
+                viewModel.onClicked(
+                    KeyguardQuickAffordanceViewModel.OnClickedParameters(
+                        configKey = configKey,
+                        expandable = Expandable.fromView(view),
+                    )
+                )
+            }
+            view.performClick()
+            view.setOnClickListener(null)
+        }
+
         private fun cancel(onAnimationEnd: Runnable? = null) {
             longPressAnimator?.cancel()
             longPressAnimator = null
@@ -442,6 +502,40 @@
 
         companion object {
             private const val PRESSED_SCALE = 1.5f
+
+            /**
+             * Returns `true` if the tool type at the given pointer index is an accurate tool (like
+             * stylus or mouse), which means we can trust it to not be a false click; `false`
+             * otherwise.
+             */
+            private fun isUsingAccurateTool(
+                event: MotionEvent,
+                pointerIndex: Int = 0,
+            ): Boolean {
+                return when (event.getToolType(pointerIndex)) {
+                    MotionEvent.TOOL_TYPE_STYLUS -> true
+                    MotionEvent.TOOL_TYPE_MOUSE -> true
+                    else -> false
+                }
+            }
+
+            /**
+             * Returns the amount of distance the pointer moved since the historical record at the
+             * [since] index.
+             */
+            private fun distanceMoved(
+                event: MotionEvent,
+                since: Int = 0,
+            ): Float {
+                return if (event.historySize > 0) {
+                    sqrt(
+                        (event.y - event.getHistoricalY(since)).pow(2) +
+                            (event.x - event.getHistoricalX(since)).pow(2)
+                    )
+                } else {
+                    0f
+                }
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
index 5e46c5d..56f911f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
@@ -22,11 +22,12 @@
 import android.window.OnBackAnimationCallback
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
-import com.android.internal.policy.SystemBarUtils
-import com.android.keyguard.KeyguardHostViewController
+import com.android.keyguard.KeyguardSecurityContainerController
 import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardSecurityView
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.dagger.KeyguardBouncerComponent
+import com.android.settingslib.Utils
 import com.android.systemui.keyguard.data.BouncerViewDelegate
 import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
@@ -44,52 +45,54 @@
         viewModel: KeyguardBouncerViewModel,
         componentFactory: KeyguardBouncerComponent.Factory
     ) {
-        // Builds the KeyguardHostViewController from bouncer view group.
-        val hostViewController: KeyguardHostViewController =
-            componentFactory.create(view).keyguardHostViewController
-        hostViewController.init()
+        // Builds the KeyguardSecurityContainerController from bouncer view group.
+        val securityContainerController: KeyguardSecurityContainerController =
+            componentFactory.create(view).securityContainerController
+        securityContainerController.init()
         val delegate =
             object : BouncerViewDelegate {
                 override fun isFullScreenBouncer(): Boolean {
-                    val mode = hostViewController.currentSecurityMode
+                    val mode = securityContainerController.currentSecurityMode
                     return mode == KeyguardSecurityModel.SecurityMode.SimPin ||
                         mode == KeyguardSecurityModel.SecurityMode.SimPuk
                 }
 
                 override fun getBackCallback(): OnBackAnimationCallback {
-                    return hostViewController.backCallback
+                    return securityContainerController.backCallback
                 }
 
                 override fun shouldDismissOnMenuPressed(): Boolean {
-                    return hostViewController.shouldEnableMenuKey()
+                    return securityContainerController.shouldEnableMenuKey()
                 }
 
                 override fun interceptMediaKey(event: KeyEvent?): Boolean {
-                    return hostViewController.interceptMediaKey(event)
+                    return securityContainerController.interceptMediaKey(event)
                 }
 
                 override fun dispatchBackKeyEventPreIme(): Boolean {
-                    return hostViewController.dispatchBackKeyEventPreIme()
+                    return securityContainerController.dispatchBackKeyEventPreIme()
                 }
 
                 override fun showNextSecurityScreenOrFinish(): Boolean {
-                    return hostViewController.dismiss(KeyguardUpdateMonitor.getCurrentUser())
+                    return securityContainerController.dismiss(
+                        KeyguardUpdateMonitor.getCurrentUser()
+                    )
                 }
 
                 override fun resume() {
-                    hostViewController.showPrimarySecurityScreen()
-                    hostViewController.onResume()
+                    securityContainerController.showPrimarySecurityScreen(/* isTurningOff= */ false)
+                    securityContainerController.onResume(KeyguardSecurityView.SCREEN_ON)
                 }
 
                 override fun setDismissAction(
                     onDismissAction: ActivityStarter.OnDismissAction?,
                     cancelAction: Runnable?
                 ) {
-                    hostViewController.setOnDismissAction(onDismissAction, cancelAction)
+                    securityContainerController.setOnDismissAction(onDismissAction, cancelAction)
                 }
 
                 override fun willDismissWithActions(): Boolean {
-                    return hostViewController.hasDismissActions()
+                    return securityContainerController.hasDismissActions()
                 }
             }
         view.repeatWhenAttached {
@@ -98,46 +101,46 @@
                     viewModel.setBouncerViewDelegate(delegate)
                     launch {
                         viewModel.show.collect {
-                            hostViewController.showPromptReason(it.promptReason)
+                            // Reset Security Container entirely.
+                            securityContainerController.reinflateViewFlipper()
+                            securityContainerController.showPromptReason(it.promptReason)
                             it.errorMessage?.let { errorMessage ->
-                                hostViewController.showErrorMessage(errorMessage)
+                                securityContainerController.showMessage(
+                                    errorMessage,
+                                    Utils.getColorError(view.context)
+                                )
                             }
-                            hostViewController.showPrimarySecurityScreen()
-                            hostViewController.appear(
-                                SystemBarUtils.getStatusBarHeight(view.context)
+                            securityContainerController.showPrimarySecurityScreen(
+                                /* turningOff= */ false
                             )
-                        }
-                    }
-
-                    launch {
-                        viewModel.showWithFullExpansion.collect { model ->
-                            hostViewController.resetSecurityContainer()
-                            hostViewController.showPromptReason(model.promptReason)
-                            hostViewController.onResume()
+                            securityContainerController.appear()
+                            securityContainerController.onResume(KeyguardSecurityView.SCREEN_ON)
                         }
                     }
 
                     launch {
                         viewModel.hide.collect {
-                            hostViewController.cancelDismissAction()
-                            hostViewController.cleanUp()
-                            hostViewController.resetSecurityContainer()
+                            securityContainerController.cancelDismissAction()
+                            securityContainerController.onPause()
+                            securityContainerController.reset()
                         }
                     }
 
                     launch {
-                        viewModel.startingToHide.collect { hostViewController.onStartingToHide() }
+                        viewModel.startingToHide.collect {
+                            securityContainerController.onStartingToHide()
+                        }
                     }
 
                     launch {
                         viewModel.startDisappearAnimation.collect {
-                            hostViewController.startDisappearAnimation(it)
+                            securityContainerController.startDisappearAnimation(it)
                         }
                     }
 
                     launch {
                         viewModel.bouncerExpansionAmount.collect { expansion ->
-                            hostViewController.setExpansion(expansion)
+                            securityContainerController.setExpansion(expansion)
                         }
                     }
 
@@ -145,10 +148,8 @@
                         viewModel.bouncerExpansionAmount
                             .filter { it == EXPANSION_VISIBLE }
                             .collect {
-                                hostViewController.onResume()
-                                view.announceForAccessibility(
-                                    hostViewController.accessibilityTitleForCurrentMode
-                                )
+                                securityContainerController.onResume(KeyguardSecurityView.SCREEN_ON)
+                                view.announceForAccessibility(securityContainerController.title)
                             }
                     }
 
@@ -156,42 +157,42 @@
                         viewModel.isBouncerVisible.collect { isVisible ->
                             val visibility = if (isVisible) View.VISIBLE else View.INVISIBLE
                             view.visibility = visibility
-                            hostViewController.onBouncerVisibilityChanged(visibility)
+                            securityContainerController.onBouncerVisibilityChanged(visibility)
                         }
                     }
 
                     launch {
-                        viewModel.isBouncerVisible
-                            .filter { !it }
-                            .collect {
-                                // Remove existing input for security reasons.
-                                hostViewController.resetSecurityContainer()
-                            }
+                        viewModel.isInteractable.collect { isInteractable ->
+                            securityContainerController.setInteractable(isInteractable)
+                        }
                     }
 
                     launch {
                         viewModel.keyguardPosition.collect { position ->
-                            hostViewController.updateKeyguardPosition(position)
+                            securityContainerController.updateKeyguardPosition(position)
                         }
                     }
 
                     launch {
                         viewModel.updateResources.collect {
-                            hostViewController.updateResources()
+                            securityContainerController.updateResources()
                             viewModel.notifyUpdateResources()
                         }
                     }
 
                     launch {
                         viewModel.bouncerShowMessage.collect {
-                            hostViewController.showMessage(it.message, it.colorStateList)
+                            securityContainerController.showMessage(it.message, it.colorStateList)
                             viewModel.onMessageShown()
                         }
                     }
 
                     launch {
                         viewModel.keyguardAuthenticated.collect {
-                            hostViewController.finish(it, KeyguardUpdateMonitor.getCurrentUser())
+                            securityContainerController.finish(
+                                it,
+                                KeyguardUpdateMonitor.getCurrentUser()
+                            )
                             viewModel.notifyKeyguardAuthenticated()
                         }
                     }
@@ -205,7 +206,7 @@
                     launch {
                         viewModel.screenTurnedOff.collect {
                             if (view.visibility == View.VISIBLE) {
-                                hostViewController.onPause()
+                                securityContainerController.onPause()
                             }
                         }
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressPopupViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressPopupViewBinder.kt
new file mode 100644
index 0000000..d85682b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressPopupViewBinder.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.binder
+
+import android.annotation.SuppressLint
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.View
+import android.view.WindowManager
+import android.widget.PopupWindow
+import com.android.systemui.R
+import com.android.systemui.common.ui.binder.IconViewBinder
+import com.android.systemui.common.ui.binder.TextViewBinder
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardSettingsPopupMenuViewModel
+
+object KeyguardLongPressPopupViewBinder {
+    @SuppressLint("InflateParams") // We don't care that the parent is null.
+    fun createAndShow(
+        container: View,
+        viewModel: KeyguardSettingsPopupMenuViewModel,
+        onDismissed: () -> Unit,
+    ): () -> Unit {
+        val contentView: View =
+            LayoutInflater.from(container.context)
+                .inflate(
+                    R.layout.keyguard_settings_popup_menu,
+                    null,
+                )
+
+        contentView.setOnClickListener { viewModel.onClicked() }
+        IconViewBinder.bind(
+            icon = viewModel.icon,
+            view = contentView.requireViewById(R.id.icon),
+        )
+        TextViewBinder.bind(
+            view = contentView.requireViewById(R.id.text),
+            viewModel = viewModel.text,
+        )
+
+        val popupWindow =
+            PopupWindow(container.context).apply {
+                windowLayoutType = WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG
+                setBackgroundDrawable(null)
+                animationStyle = com.android.internal.R.style.Animation_Dialog
+                isOutsideTouchable = true
+                isFocusable = true
+                setContentView(contentView)
+                setOnDismissListener { onDismissed() }
+                contentView.measure(
+                    View.MeasureSpec.makeMeasureSpec(
+                        0,
+                        View.MeasureSpec.UNSPECIFIED,
+                    ),
+                    View.MeasureSpec.makeMeasureSpec(
+                        0,
+                        View.MeasureSpec.UNSPECIFIED,
+                    ),
+                )
+                showAtLocation(
+                    container,
+                    Gravity.NO_GRAVITY,
+                    viewModel.position.x - contentView.measuredWidth / 2,
+                    viewModel.position.y -
+                        contentView.measuredHeight -
+                        container.context.resources.getDimensionPixelSize(
+                            R.dimen.keyguard_long_press_settings_popup_vertical_offset
+                        ),
+                )
+            }
+
+        return { popupWindow.dismiss() }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt
new file mode 100644
index 0000000..ef3f242
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.binder
+
+import android.view.View
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.common.ui.view.LongPressHandlingView
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.plugins.FalsingManager
+import kotlinx.coroutines.launch
+
+object KeyguardLongPressViewBinder {
+    /**
+     * Drives UI for the lock screen long-press feature.
+     *
+     * @param view The view that listens for long-presses.
+     * @param viewModel The view-model that models the UI state.
+     * @param onSingleTap A callback to invoke when the system decides that there was a single tap.
+     * @param falsingManager [FalsingManager] for making sure the long-press didn't just happen in
+     * the user's pocket.
+     */
+    @JvmStatic
+    fun bind(
+        view: LongPressHandlingView,
+        viewModel: KeyguardLongPressViewModel,
+        onSingleTap: () -> Unit,
+        falsingManager: FalsingManager,
+    ) {
+        view.listener =
+            object : LongPressHandlingView.Listener {
+                override fun onLongPressDetected(view: View, x: Int, y: Int) {
+                    if (falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) {
+                        return
+                    }
+
+                    viewModel.onLongPress(
+                        x = x,
+                        y = y,
+                    )
+                }
+
+                override fun onSingleTapDetected(view: View) {
+                    if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+                        return
+                    }
+
+                    onSingleTap()
+                }
+            }
+
+        view.repeatWhenAttached {
+            repeatOnLifecycle(Lifecycle.State.STARTED) {
+                launch {
+                    viewModel.isLongPressHandlingEnabled.collect { isEnabled ->
+                        view.setLongPressHandlingEnabled(isEnabled)
+                    }
+                }
+
+                launch {
+                    var dismissMenu: (() -> Unit)? = null
+
+                    viewModel.menu.collect { menuOrNull ->
+                        if (menuOrNull != null) {
+                            dismissMenu =
+                                KeyguardLongPressPopupViewBinder.createAndShow(
+                                    container = view,
+                                    viewModel = menuOrNull,
+                                    onDismissed = menuOrNull.onDismissed,
+                                )
+                        } else {
+                            dismissMenu?.invoke()
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index a5ae8ba5..72b317c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -24,7 +24,6 @@
 import android.hardware.display.DisplayManager
 import android.os.Bundle
 import android.os.IBinder
-import android.view.Gravity
 import android.view.LayoutInflater
 import android.view.SurfaceControlViewHost
 import android.view.View
@@ -39,6 +38,7 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
 import com.android.systemui.shared.clocks.ClockRegistry
+import com.android.systemui.shared.clocks.shared.model.ClockPreviewConstants
 import com.android.systemui.shared.quickaffordance.shared.model.KeyguardQuickAffordancePreviewConstants
 import com.android.systemui.statusbar.phone.KeyguardBottomAreaView
 import dagger.assisted.Assisted
@@ -65,6 +65,13 @@
     val hostToken: IBinder? = bundle.getBinder(KEY_HOST_TOKEN)
     private val width: Int = bundle.getInt(KEY_VIEW_WIDTH)
     private val height: Int = bundle.getInt(KEY_VIEW_HEIGHT)
+    private val shouldHighlightSelectedAffordance: Boolean =
+        bundle.getBoolean(
+            KeyguardQuickAffordancePreviewConstants.KEY_HIGHLIGHT_QUICK_AFFORDANCES,
+            false,
+        )
+    private val shouldHideClock: Boolean =
+        bundle.getBoolean(ClockPreviewConstants.KEY_HIDE_CLOCK, false)
 
     private var host: SurfaceControlViewHost
 
@@ -82,6 +89,7 @@
                 bundle.getString(
                     KeyguardQuickAffordancePreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID,
                 ),
+            shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance,
         )
         runBlocking(mainDispatcher) {
             host =
@@ -99,7 +107,9 @@
             val rootView = FrameLayout(context)
 
             setUpBottomArea(rootView)
-            setUpClock(rootView)
+            if (!shouldHideClock) {
+                setUpClock(rootView)
+            }
 
             rootView.measure(
                 View.MeasureSpec.makeMeasureSpec(
@@ -154,14 +164,18 @@
             bottomAreaView,
             FrameLayout.LayoutParams(
                 FrameLayout.LayoutParams.MATCH_PARENT,
-                FrameLayout.LayoutParams.WRAP_CONTENT,
-                Gravity.BOTTOM,
+                FrameLayout.LayoutParams.MATCH_PARENT,
             ),
         )
     }
 
     private fun setUpClock(parentView: ViewGroup) {
-        val clockChangeListener = ClockRegistry.ClockChangeListener { onClockChanged(parentView) }
+        val clockChangeListener =
+            object : ClockRegistry.ClockChangeListener {
+                override fun onCurrentClockChanged() {
+                    onClockChanged(parentView)
+                }
+            }
         clockRegistry.registerClockChangeListener(clockChangeListener)
         disposables.add(
             DisposableHandle { clockRegistry.unregisterClockChangeListener(clockChangeListener) }
@@ -173,7 +187,8 @@
         val receiver =
             object : BroadcastReceiver() {
                 override fun onReceive(context: Context?, intent: Intent?) {
-                    clockController.clock?.events?.onTimeTick()
+                    clockController.clock?.smallClock?.events?.onTimeTick()
+                    clockController.clock?.largeClock?.events?.onTimeTick()
                 }
             }
         broadcastDispatcher.registerReceiver(
@@ -195,7 +210,13 @@
             ?.events
             ?.onTargetRegionChanged(KeyguardClockSwitch.getLargeClockRegion(parentView))
         clockView?.let { parentView.removeView(it) }
-        clockView = clockController.clock?.largeClock?.view?.apply { parentView.addView(this) }
+        clockView =
+            clockController.clock?.largeClock?.view?.apply {
+                if (shouldHighlightSelectedAffordance) {
+                    alpha = DIM_ALPHA
+                }
+                parentView.addView(this)
+            }
     }
 
     companion object {
@@ -203,5 +224,7 @@
         private const val KEY_VIEW_WIDTH = "width"
         private const val KEY_VIEW_HEIGHT = "height"
         private const val KEY_DISPLAY_ID = "display_id"
+
+        private const val DIM_ALPHA = 0.3f
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
index 6627865..8d6545a4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
@@ -21,15 +21,10 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.AnimationParams
-import com.android.systemui.keyguard.shared.model.TransitionState.CANCELED
-import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.merge
 
 /**
  * Breaks down DREAMING->LOCKSCREEN transition into discrete steps for corresponding views to
@@ -41,39 +36,46 @@
 constructor(
     private val interactor: KeyguardTransitionInteractor,
 ) {
+    private val transitionAnimation =
+        KeyguardTransitionAnimationFlow(
+            transitionDuration = TO_LOCKSCREEN_DURATION,
+            transitionFlow = interactor.dreamingToLockscreenTransition,
+        )
 
     /** Dream overlay y-translation on exit */
     fun dreamOverlayTranslationY(translatePx: Int): Flow<Float> {
-        return flowForAnimation(DREAM_OVERLAY_TRANSLATION_Y).map { value ->
-            EMPHASIZED_ACCELERATE.getInterpolation(value) * translatePx
-        }
+        return transitionAnimation.createFlow(
+            duration = 600.milliseconds,
+            onStep = { it * translatePx },
+            interpolator = EMPHASIZED_ACCELERATE,
+        )
     }
     /** Dream overlay views alpha - fade out */
-    val dreamOverlayAlpha: Flow<Float> = flowForAnimation(DREAM_OVERLAY_ALPHA).map { 1f - it }
+    val dreamOverlayAlpha: Flow<Float> =
+        transitionAnimation.createFlow(
+            duration = 250.milliseconds,
+            onStep = { 1f - it },
+        )
 
     /** Lockscreen views y-translation */
     fun lockscreenTranslationY(translatePx: Int): Flow<Float> {
-        return merge(
-            flowForAnimation(LOCKSCREEN_TRANSLATION_Y).map { value ->
-                -translatePx + (EMPHASIZED_DECELERATE.getInterpolation(value) * translatePx)
-            },
-            // On end, reset the translation to 0
-            interactor.dreamingToLockscreenTransition
-                .filter { it.transitionState == FINISHED || it.transitionState == CANCELED }
-                .map { 0f }
+        return transitionAnimation.createFlow(
+            duration = TO_LOCKSCREEN_DURATION,
+            onStep = { value -> -translatePx + value * translatePx },
+            // Reset on cancel or finish
+            onFinish = { 0f },
+            onCancel = { 0f },
+            interpolator = EMPHASIZED_DECELERATE,
         )
     }
 
     /** Lockscreen views alpha */
-    val lockscreenAlpha: Flow<Float> = flowForAnimation(LOCKSCREEN_ALPHA)
-
-    private fun flowForAnimation(params: AnimationParams): Flow<Float> {
-        return interactor.transitionStepAnimation(
-            interactor.dreamingToLockscreenTransition,
-            params,
-            totalDuration = TO_LOCKSCREEN_DURATION
+    val lockscreenAlpha: Flow<Float> =
+        transitionAnimation.createFlow(
+            startTime = 233.milliseconds,
+            duration = 250.milliseconds,
+            onStep = { it },
         )
-    }
 
     companion object {
         /* Length of time before ending the dream activity, in order to start unoccluding */
@@ -81,11 +83,5 @@
         @JvmField
         val LOCKSCREEN_ANIMATION_DURATION_MS =
             (TO_LOCKSCREEN_DURATION - DREAM_ANIMATION_DURATION).inWholeMilliseconds
-
-        val DREAM_OVERLAY_TRANSLATION_Y = AnimationParams(duration = 600.milliseconds)
-        val DREAM_OVERLAY_ALPHA = AnimationParams(duration = 250.milliseconds)
-        val LOCKSCREEN_TRANSLATION_Y = AnimationParams(duration = TO_LOCKSCREEN_DURATION)
-        val LOCKSCREEN_ALPHA =
-            AnimationParams(startTime = 233.milliseconds, duration = 250.milliseconds)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt
index 5a47960..f16827d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt
@@ -20,15 +20,10 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DREAMING_DURATION
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.AnimationParams
-import com.android.systemui.keyguard.shared.model.TransitionState.CANCELED
-import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.merge
 
 /** Breaks down GONE->DREAMING transition into discrete steps for corresponding views to consume. */
 @SysUISingleton
@@ -38,32 +33,28 @@
     private val interactor: KeyguardTransitionInteractor,
 ) {
 
+    private val transitionAnimation =
+        KeyguardTransitionAnimationFlow(
+            transitionDuration = TO_DREAMING_DURATION,
+            transitionFlow = interactor.goneToDreamingTransition,
+        )
+
     /** Lockscreen views y-translation */
     fun lockscreenTranslationY(translatePx: Int): Flow<Float> {
-        return merge(
-            flowForAnimation(LOCKSCREEN_TRANSLATION_Y).map { value ->
-                (EMPHASIZED_ACCELERATE.getInterpolation(value) * translatePx)
-            },
-            // On end, reset the translation to 0
-            interactor.goneToDreamingTransition
-                .filter { it.transitionState == FINISHED || it.transitionState == CANCELED }
-                .map { 0f }
+        return transitionAnimation.createFlow(
+            duration = 500.milliseconds,
+            onStep = { it * translatePx },
+            // Reset on cancel or finish
+            onFinish = { 0f },
+            onCancel = { 0f },
+            interpolator = EMPHASIZED_ACCELERATE,
         )
     }
 
     /** Lockscreen views alpha */
-    val lockscreenAlpha: Flow<Float> = flowForAnimation(LOCKSCREEN_ALPHA).map { 1f - it }
-
-    private fun flowForAnimation(params: AnimationParams): Flow<Float> {
-        return interactor.transitionStepAnimation(
-            interactor.goneToDreamingTransition,
-            params,
-            totalDuration = TO_DREAMING_DURATION
+    val lockscreenAlpha: Flow<Float> =
+        transitionAnimation.createFlow(
+            duration = 250.milliseconds,
+            onStep = { 1f - it },
         )
-    }
-
-    companion object {
-        val LOCKSCREEN_TRANSLATION_Y = AnimationParams(duration = 500.milliseconds)
-        val LOCKSCREEN_ALPHA = AnimationParams(duration = 250.milliseconds)
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
index 5d85680..1e3b60c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
@@ -45,12 +45,17 @@
     private val bottomAreaInteractor: KeyguardBottomAreaInteractor,
     private val burnInHelperWrapper: BurnInHelperWrapper,
 ) {
+    data class PreviewMode(
+        val isInPreviewMode: Boolean = false,
+        val shouldHighlightSelectedAffordance: Boolean = false,
+    )
+
     /**
      * Whether this view-model instance is powering the preview experience that renders exclusively
      * in the wallpaper picker application. This should _always_ be `false` for the real lock screen
      * experience.
      */
-    private val isInPreviewMode = MutableStateFlow(false)
+    private val previewMode = MutableStateFlow(PreviewMode())
 
     /**
      * ID of the slot that's currently selected in the preview that renders exclusively in the
@@ -87,8 +92,8 @@
         keyguardInteractor.isDozing.map { !it }.distinctUntilChanged()
     /** An observable for the alpha level for the entire bottom area. */
     val alpha: Flow<Float> =
-        isInPreviewMode.flatMapLatest { isInPreviewMode ->
-            if (isInPreviewMode) {
+        previewMode.flatMapLatest {
+            if (it.isInPreviewMode) {
                 flowOf(1f)
             } else {
                 bottomAreaInteractor.alpha.distinctUntilChanged()
@@ -129,9 +134,18 @@
      * lock screen.
      *
      * @param initiallySelectedSlotId The ID of the initial slot to render as the selected one.
+     * @param shouldHighlightSelectedAffordance Whether the selected quick affordance should be
+     * highlighted (while all others are dimmed to make the selected one stand out).
      */
-    fun enablePreviewMode(initiallySelectedSlotId: String?) {
-        isInPreviewMode.value = true
+    fun enablePreviewMode(
+        initiallySelectedSlotId: String?,
+        shouldHighlightSelectedAffordance: Boolean,
+    ) {
+        previewMode.value =
+            PreviewMode(
+                isInPreviewMode = true,
+                shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance,
+            )
         onPreviewSlotSelected(
             initiallySelectedSlotId ?: KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START
         )
@@ -150,9 +164,9 @@
     private fun button(
         position: KeyguardQuickAffordancePosition
     ): Flow<KeyguardQuickAffordanceViewModel> {
-        return isInPreviewMode.flatMapLatest { isInPreviewMode ->
+        return previewMode.flatMapLatest { previewMode ->
             combine(
-                    if (isInPreviewMode) {
+                    if (previewMode.isInPreviewMode) {
                         quickAffordanceInteractor.quickAffordanceAlwaysVisible(position = position)
                     } else {
                         quickAffordanceInteractor.quickAffordance(position = position)
@@ -161,11 +175,18 @@
                     areQuickAffordancesFullyOpaque,
                     selectedPreviewSlotId,
                 ) { model, animateReveal, isFullyOpaque, selectedPreviewSlotId ->
+                    val isSelected = selectedPreviewSlotId == position.toSlotId()
                     model.toViewModel(
-                        animateReveal = !isInPreviewMode && animateReveal,
-                        isClickable = isFullyOpaque && !isInPreviewMode,
+                        animateReveal = !previewMode.isInPreviewMode && animateReveal,
+                        isClickable = isFullyOpaque && !previewMode.isInPreviewMode,
                         isSelected =
-                            (isInPreviewMode && selectedPreviewSlotId == position.toSlotId()),
+                            previewMode.isInPreviewMode &&
+                                previewMode.shouldHighlightSelectedAffordance &&
+                                isSelected,
+                        isDimmed =
+                            previewMode.isInPreviewMode &&
+                                previewMode.shouldHighlightSelectedAffordance &&
+                                !isSelected,
                     )
                 }
                 .distinctUntilChanged()
@@ -176,6 +197,7 @@
         animateReveal: Boolean,
         isClickable: Boolean,
         isSelected: Boolean,
+        isDimmed: Boolean,
     ): KeyguardQuickAffordanceViewModel {
         return when (this) {
             is KeyguardQuickAffordanceModel.Visible ->
@@ -194,6 +216,7 @@
                     isActivated = activationState is ActivationState.Active,
                     isSelected = isSelected,
                     useLongPress = quickAffordanceInteractor.useLongPress,
+                    isDimmed = isDimmed,
                 )
             is KeyguardQuickAffordanceModel.Hidden -> KeyguardQuickAffordanceViewModel()
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
index c6002d6..b8b3a8e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
@@ -20,12 +20,10 @@
 import com.android.systemui.keyguard.data.BouncerView
 import com.android.systemui.keyguard.data.BouncerViewDelegate
 import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
-import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE
 import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
 import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.map
 
 /** Models UI state for the lock screen bouncer; handles user input. */
@@ -41,13 +39,12 @@
     /** Observe on bouncer visibility. */
     val isBouncerVisible: Flow<Boolean> = interactor.isVisible
 
+    /** Can the user interact with the view? */
+    val isInteractable: Flow<Boolean> = interactor.isInteractable
+
     /** Observe whether bouncer is showing. */
     val show: Flow<KeyguardBouncerModel> = interactor.show
 
-    /** Observe visible expansion when bouncer is showing. */
-    val showWithFullExpansion: Flow<KeyguardBouncerModel> =
-        interactor.show.filter { it.expansionAmount == EXPANSION_VISIBLE }
-
     /** Observe whether bouncer is hiding. */
     val hide: Flow<Unit> = interactor.hide
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardLongPressViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardLongPressViewModel.kt
new file mode 100644
index 0000000..d896390
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardLongPressViewModel.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.R
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.common.shared.model.Text
+import com.android.systemui.keyguard.domain.interactor.KeyguardLongPressInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+/** Models UI state to support the lock screen long-press feature. */
+class KeyguardLongPressViewModel
+@Inject
+constructor(
+    private val interactor: KeyguardLongPressInteractor,
+) {
+
+    /** Whether the long-press handling feature should be enabled. */
+    val isLongPressHandlingEnabled: Flow<Boolean> = interactor.isLongPressHandlingEnabled
+
+    /** View-model for a menu that should be shown; `null` when no menu should be shown. */
+    val menu: Flow<KeyguardSettingsPopupMenuViewModel?> =
+        interactor.menu.map { model ->
+            model?.let {
+                KeyguardSettingsPopupMenuViewModel(
+                    icon =
+                        Icon.Resource(
+                            res = R.drawable.ic_settings,
+                            contentDescription = null,
+                        ),
+                    text =
+                        Text.Resource(
+                            res = R.string.lock_screen_settings,
+                        ),
+                    position = model.position,
+                    onClicked = model.onClicked,
+                    onDismissed = model.onDismissed,
+                )
+            }
+        }
+
+    /** Notifies that the user has long-pressed on the lock screen. */
+    fun onLongPress(
+        x: Int,
+        y: Int,
+    ) {
+        interactor.onLongPress(
+            x = x,
+            y = y,
+        )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
index cf3a6da..cb68a82 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
@@ -31,6 +31,7 @@
     val isActivated: Boolean = false,
     val isSelected: Boolean = false,
     val useLongPress: Boolean = false,
+    val isDimmed: Boolean = false,
 ) {
     data class OnClickedParameters(
         val configKey: String,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSettingsPopupMenuViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSettingsPopupMenuViewModel.kt
new file mode 100644
index 0000000..0571b05
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSettingsPopupMenuViewModel.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.common.shared.model.Position
+import com.android.systemui.common.shared.model.Text
+
+/** Models the UI state of a keyguard settings popup menu. */
+data class KeyguardSettingsPopupMenuViewModel(
+    val icon: Icon,
+    val text: Text,
+    /** Where the menu should be anchored, roughly in screen space. */
+    val position: Position,
+    /** Callback to invoke when the menu gets clicked by the user. */
+    val onClicked: () -> Unit,
+    /** Callback to invoke when the menu gets dismissed by the user. */
+    val onDismissed: () -> Unit,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
index e05adbd..bc9dc4f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
@@ -20,15 +20,10 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_DURATION
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.AnimationParams
-import com.android.systemui.keyguard.shared.model.TransitionState.CANCELED
-import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.merge
 
 /**
  * Breaks down LOCKSCREEN->DREAMING transition into discrete steps for corresponding views to
@@ -40,35 +35,32 @@
 constructor(
     private val interactor: KeyguardTransitionInteractor,
 ) {
+    private val transitionAnimation =
+        KeyguardTransitionAnimationFlow(
+            transitionDuration = TO_DREAMING_DURATION,
+            transitionFlow = interactor.lockscreenToDreamingTransition,
+        )
 
     /** Lockscreen views y-translation */
     fun lockscreenTranslationY(translatePx: Int): Flow<Float> {
-        return merge(
-            flowForAnimation(LOCKSCREEN_TRANSLATION_Y).map { value ->
-                (EMPHASIZED_ACCELERATE.getInterpolation(value) * translatePx)
-            },
-            // On end, reset the translation to 0
-            interactor.lockscreenToDreamingTransition
-                .filter { it.transitionState == FINISHED || it.transitionState == CANCELED }
-                .map { 0f }
+        return transitionAnimation.createFlow(
+            duration = 500.milliseconds,
+            onStep = { it * translatePx },
+            // Reset on cancel or finish
+            onFinish = { 0f },
+            onCancel = { 0f },
+            interpolator = EMPHASIZED_ACCELERATE,
         )
     }
 
     /** Lockscreen views alpha */
-    val lockscreenAlpha: Flow<Float> = flowForAnimation(LOCKSCREEN_ALPHA).map { 1f - it }
-
-    private fun flowForAnimation(params: AnimationParams): Flow<Float> {
-        return interactor.transitionStepAnimation(
-            interactor.lockscreenToDreamingTransition,
-            params,
-            totalDuration = TO_DREAMING_DURATION
+    val lockscreenAlpha: Flow<Float> =
+        transitionAnimation.createFlow(
+            duration = 250.milliseconds,
+            onStep = { 1f - it },
         )
-    }
 
     companion object {
         @JvmField val DREAMING_ANIMATION_DURATION_MS = TO_DREAMING_DURATION.inWholeMilliseconds
-
-        val LOCKSCREEN_TRANSLATION_Y = AnimationParams(duration = 500.milliseconds)
-        val LOCKSCREEN_ALPHA = AnimationParams(duration = 250.milliseconds)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
index 22d292e..a60665a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
@@ -20,14 +20,10 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_OCCLUDED_DURATION
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.AnimationParams
-import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.merge
 
 /**
  * Breaks down LOCKSCREEN->OCCLUDED transition into discrete steps for corresponding views to
@@ -39,33 +35,28 @@
 constructor(
     private val interactor: KeyguardTransitionInteractor,
 ) {
+    private val transitionAnimation =
+        KeyguardTransitionAnimationFlow(
+            transitionDuration = TO_OCCLUDED_DURATION,
+            transitionFlow = interactor.lockscreenToOccludedTransition,
+        )
+
+    /** Lockscreen views alpha */
+    val lockscreenAlpha: Flow<Float> =
+        transitionAnimation.createFlow(
+            duration = 250.milliseconds,
+            onStep = { 1f - it },
+        )
 
     /** Lockscreen views y-translation */
     fun lockscreenTranslationY(translatePx: Int): Flow<Float> {
-        return merge(
-            flowForAnimation(LOCKSCREEN_TRANSLATION_Y).map { value ->
-                (EMPHASIZED_ACCELERATE.getInterpolation(value) * translatePx)
-            },
-            // On end, reset the translation to 0
-            interactor.lockscreenToOccludedTransition
-                .filter { step -> step.transitionState == TransitionState.FINISHED }
-                .map { 0f }
+        return transitionAnimation.createFlow(
+            duration = TO_OCCLUDED_DURATION,
+            onStep = { value -> value * translatePx },
+            // Reset on cancel or finish
+            onFinish = { 0f },
+            onCancel = { 0f },
+            interpolator = EMPHASIZED_ACCELERATE,
         )
     }
-
-    /** Lockscreen views alpha */
-    val lockscreenAlpha: Flow<Float> = flowForAnimation(LOCKSCREEN_ALPHA).map { 1f - it }
-
-    private fun flowForAnimation(params: AnimationParams): Flow<Float> {
-        return interactor.transitionStepAnimation(
-            interactor.lockscreenToOccludedTransition,
-            params,
-            totalDuration = TO_OCCLUDED_DURATION
-        )
-    }
-
-    companion object {
-        val LOCKSCREEN_TRANSLATION_Y = AnimationParams(duration = TO_OCCLUDED_DURATION)
-        val LOCKSCREEN_ALPHA = AnimationParams(duration = 250.milliseconds)
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
index e804562..5770f3e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
@@ -20,11 +20,10 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.AnimationParams
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.map
 
 /**
  * Breaks down OCCLUDED->LOCKSCREEN transition into discrete steps for corresponding views to
@@ -36,28 +35,26 @@
 constructor(
     private val interactor: KeyguardTransitionInteractor,
 ) {
+    private val transitionAnimation =
+        KeyguardTransitionAnimationFlow(
+            transitionDuration = TO_LOCKSCREEN_DURATION,
+            transitionFlow = interactor.occludedToLockscreenTransition,
+        )
+
     /** Lockscreen views y-translation */
     fun lockscreenTranslationY(translatePx: Int): Flow<Float> {
-        return flowForAnimation(LOCKSCREEN_TRANSLATION_Y).map { value ->
-            -translatePx + (EMPHASIZED_DECELERATE.getInterpolation(value) * translatePx)
-        }
-    }
-
-    /** Lockscreen views alpha */
-    val lockscreenAlpha: Flow<Float> = flowForAnimation(LOCKSCREEN_ALPHA)
-
-    private fun flowForAnimation(params: AnimationParams): Flow<Float> {
-        return interactor.transitionStepAnimation(
-            interactor.occludedToLockscreenTransition,
-            params,
-            totalDuration = TO_LOCKSCREEN_DURATION
+        return transitionAnimation.createFlow(
+            duration = TO_LOCKSCREEN_DURATION,
+            onStep = { value -> -translatePx + value * translatePx },
+            interpolator = EMPHASIZED_DECELERATE,
         )
     }
 
-    companion object {
-        @JvmField val LOCKSCREEN_ANIMATION_DURATION_MS = TO_LOCKSCREEN_DURATION.inWholeMilliseconds
-        val LOCKSCREEN_TRANSLATION_Y = AnimationParams(duration = TO_LOCKSCREEN_DURATION)
-        val LOCKSCREEN_ALPHA =
-            AnimationParams(startTime = 233.milliseconds, duration = 250.milliseconds)
-    }
+    /** Lockscreen views alpha */
+    val lockscreenAlpha: Flow<Float> =
+        transitionAnimation.createFlow(
+            startTime = 233.milliseconds,
+            duration = 250.milliseconds,
+            onStep = { it },
+        )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
new file mode 100644
index 0000000..f7349a2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
@@ -0,0 +1,181 @@
+package com.android.systemui.log
+
+import android.hardware.face.FaceManager
+import android.hardware.face.FaceSensorPropertiesInternal
+import com.android.keyguard.FaceAuthUiEvent
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.dagger.FaceAuthLog
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel.DEBUG
+import com.google.errorprone.annotations.CompileTimeConstant
+import javax.inject.Inject
+
+private const val TAG = "KeyguardFaceAuthManagerLog"
+
+/**
+ * Helper class for logging for [com.android.keyguard.faceauth.KeyguardFaceAuthManager]
+ *
+ * To enable logcat echoing for an entire buffer:
+ *
+ * ```
+ *   adb shell settings put global systemui/buffer/KeyguardFaceAuthManagerLog <logLevel>
+ *
+ * ```
+ */
+@SysUISingleton
+class FaceAuthenticationLogger
+@Inject
+constructor(
+    @FaceAuthLog private val logBuffer: LogBuffer,
+) {
+    fun ignoredFaceAuthTrigger(uiEvent: FaceAuthUiEvent) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            { str1 = uiEvent.reason },
+            {
+                "Ignoring trigger because face auth is currently running. " +
+                    "Trigger reason: $str1"
+            }
+        )
+    }
+
+    fun queuingRequestWhileCancelling(
+        alreadyQueuedRequest: FaceAuthUiEvent?,
+        newRequest: FaceAuthUiEvent
+    ) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = alreadyQueuedRequest?.reason
+                str2 = newRequest.reason
+            },
+            {
+                "Face auth requested while previous request is being cancelled, " +
+                    "already queued request: $str1 queueing the new request: $str2"
+            }
+        )
+    }
+
+    fun authenticating(uiEvent: FaceAuthUiEvent) {
+        logBuffer.log(TAG, DEBUG, { str1 = uiEvent.reason }, { "Running authenticate for $str1" })
+    }
+
+    fun detectionNotSupported(
+        faceManager: FaceManager?,
+        sensorPropertiesInternal: MutableList<FaceSensorPropertiesInternal>?
+    ) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                bool1 = faceManager == null
+                bool2 = sensorPropertiesInternal.isNullOrEmpty()
+                bool2 = sensorPropertiesInternal?.firstOrNull()?.supportsFaceDetection ?: false
+            },
+            {
+                "skipping detection request because it is not supported, " +
+                    "faceManager isNull: $bool1, " +
+                    "sensorPropertiesInternal isNullOrEmpty: $bool2, " +
+                    "supportsFaceDetection: $bool3"
+            }
+        )
+    }
+
+    fun skippingBecauseAlreadyRunning(@CompileTimeConstant operation: String) {
+        logBuffer.log(TAG, DEBUG, "isAuthRunning is true, skipping $operation")
+    }
+
+    fun faceDetectionStarted() {
+        logBuffer.log(TAG, DEBUG, "Face detection started.")
+    }
+
+    fun faceDetected() {
+        logBuffer.log(TAG, DEBUG, "Face detected")
+    }
+
+    fun cancelSignalNotReceived(
+        isAuthRunning: Boolean,
+        isLockedOut: Boolean,
+        cancellationInProgress: Boolean,
+        faceAuthRequestedWhileCancellation: FaceAuthUiEvent?
+    ) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                bool1 = isAuthRunning
+                bool2 = isLockedOut
+                bool3 = cancellationInProgress
+                str1 = "${faceAuthRequestedWhileCancellation?.reason}"
+            },
+            {
+                "Cancel signal was not received, running timeout handler to reset state. " +
+                    "State before reset: " +
+                    "isAuthRunning: $bool1, " +
+                    "isLockedOut: $bool2, " +
+                    "cancellationInProgress: $bool3, " +
+                    "faceAuthRequestedWhileCancellation: $str1"
+            }
+        )
+    }
+
+    fun authenticationFailed() {
+        logBuffer.log(TAG, DEBUG, "Face authentication failed")
+    }
+
+    fun authenticationAcquired(acquireInfo: Int) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            { int1 = acquireInfo },
+            { "Face acquired during face authentication: acquireInfo: $int1 " }
+        )
+    }
+
+    fun authenticationError(
+        errorCode: Int,
+        errString: CharSequence?,
+        lockoutError: Boolean,
+        cancellationError: Boolean
+    ) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                int1 = errorCode
+                str1 = "$errString"
+                bool1 = lockoutError
+                bool2 = cancellationError
+            },
+            {
+                "Received authentication error: errorCode: $int1, " +
+                    "errString: $str1, " +
+                    "isLockoutError: $bool1, " +
+                    "isCancellationError: $bool2"
+            }
+        )
+    }
+
+    fun launchingQueuedFaceAuthRequest(faceAuthRequestedWhileCancellation: FaceAuthUiEvent?) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            { str1 = "${faceAuthRequestedWhileCancellation?.reason}" },
+            { "Received cancellation error and starting queued face auth request: $str1" }
+        )
+    }
+
+    fun faceAuthSuccess(result: FaceManager.AuthenticationResult) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                int1 = result.userId
+                bool1 = result.isStrongBiometric
+            },
+            { "Face authenticated successfully: userId: $int1, isStrongBiometric: $bool1" }
+        )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt b/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt
new file mode 100644
index 0000000..5acaa46
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt
@@ -0,0 +1,135 @@
+/*
+ * 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.log
+
+import android.graphics.Point
+import android.graphics.Rect
+import android.graphics.RectF
+import androidx.core.graphics.toRectF
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.dagger.ScreenDecorationsLog
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel.DEBUG
+import com.android.systemui.plugins.log.LogLevel.ERROR
+import javax.inject.Inject
+
+private const val TAG = "ScreenDecorationsLog"
+
+/**
+ * Helper class for logging for [com.android.systemui.ScreenDecorations]
+ *
+ * To enable logcat echoing for an entire buffer:
+ *
+ * ```
+ *   adb shell settings put global systemui/buffer/ScreenDecorationsLog <logLevel>
+ *
+ * ```
+ */
+@SysUISingleton
+class ScreenDecorationsLogger
+@Inject
+constructor(
+    @ScreenDecorationsLog private val logBuffer: LogBuffer,
+) {
+    fun cameraProtectionBoundsForScanningOverlay(bounds: Rect) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            { str1 = bounds.toShortString() },
+            { "Face scanning overlay present camera protection bounds: $str1" }
+        )
+    }
+
+    fun hwcLayerCameraProtectionBounds(bounds: Rect) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            { str1 = bounds.toShortString() },
+            { "Hwc layer present camera protection bounds: $str1" }
+        )
+    }
+
+    fun dcvCameraBounds(id: Int, bounds: Rect) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = bounds.toShortString()
+                int1 = id
+            },
+            { "DisplayCutoutView id=$int1 present, camera protection bounds: $str1" }
+        )
+    }
+
+    fun cutoutViewNotInitialized() {
+        logBuffer.log(TAG, ERROR, "CutoutView not initialized showCameraProtection")
+    }
+
+    fun boundingRect(boundingRectangle: RectF, context: String) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = context
+                str2 = boundingRectangle.toShortString()
+            },
+            { "Bounding rect $str1 : $str2" }
+        )
+    }
+
+    fun boundingRect(boundingRectangle: Rect, context: String) {
+        boundingRect(boundingRectangle.toRectF(), context)
+    }
+
+    fun onMeasureDimensions(
+        widthMeasureSpec: Int,
+        heightMeasureSpec: Int,
+        measuredWidth: Int,
+        measuredHeight: Int
+    ) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                long1 = widthMeasureSpec.toLong()
+                long2 = heightMeasureSpec.toLong()
+                int1 = measuredWidth
+                int2 = measuredHeight
+            },
+            {
+                "Face scanning animation: widthMeasureSpec: $long1 measuredWidth: $int1, " +
+                    "heightMeasureSpec: $long2 measuredHeight: $int2"
+            }
+        )
+    }
+
+    fun faceSensorLocation(faceSensorLocation: Point?) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                int1 = faceSensorLocation?.y?.times(2) ?: 0
+                str1 = "$faceSensorLocation"
+            },
+            { "Reinflating view: Face sensor location: $str1, faceScanningHeight: $int1" }
+        )
+    }
+
+    fun onSensorLocationChanged() {
+        logBuffer.log(TAG, DEBUG, "AuthControllerCallback in ScreenDecorations triggered")
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/FaceAuthLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/FaceAuthLog.kt
new file mode 100644
index 0000000..b97e3a7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/FaceAuthLog.kt
@@ -0,0 +1,6 @@
+package com.android.systemui.log.dagger
+
+import javax.inject.Qualifier
+
+/** A [com.android.systemui.log.LogBuffer] for Face authentication triggered by SysUI. */
+@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class FaceAuthLog()
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 15306f9..817de79 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -200,28 +200,6 @@
     }
 
     /**
-     * Provides a logging buffer for logs related to the media tap-to-transfer chip on the sender
-     * device. See {@link com.android.systemui.media.taptotransfer.sender.MediaTttSenderLogger}.
-     */
-    @Provides
-    @SysUISingleton
-    @MediaTttSenderLogBuffer
-    public static LogBuffer provideMediaTttSenderLogBuffer(LogBufferFactory factory) {
-        return factory.create("MediaTttSender", 20);
-    }
-
-    /**
-     * Provides a logging buffer for logs related to the media tap-to-transfer chip on the receiver
-     * device. See {@link com.android.systemui.media.taptotransfer.receiver.MediaTttReceiverLogger}.
-     */
-    @Provides
-    @SysUISingleton
-    @MediaTttReceiverLogBuffer
-    public static LogBuffer provideMediaTttReceiverLogBuffer(LogBufferFactory factory) {
-        return factory.create("MediaTttReceiver", 20);
-    }
-
-    /**
      * Provides a logging buffer for logs related to the media mute-await connections. See
      * {@link com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManager}.
      */
@@ -287,16 +265,6 @@
         return factory.create("MediaCarouselCtlrLog", 20);
     }
 
-    /**
-     * Provides a {@link LogBuffer} for use in the status bar connectivity pipeline
-     */
-    @Provides
-    @SysUISingleton
-    @StatusBarConnectivityLog
-    public static LogBuffer provideStatusBarConnectivityBuffer(LogBufferFactory factory) {
-        return factory.create("SbConnectivity", 64);
-    }
-
     /** Allows logging buffers to be tweaked via adb on debug builds but not on prod builds. */
     @Provides
     @SysUISingleton
@@ -372,6 +340,27 @@
     }
 
     /**
+     * Provides a {@link LogBuffer} for use by {@link com.android.systemui.ScreenDecorations}.
+     */
+    @Provides
+    @SysUISingleton
+    @ScreenDecorationsLog
+    public static LogBuffer provideScreenDecorationsLog(LogBufferFactory factory) {
+        return factory.create("ScreenDecorationsLog", 200);
+    }
+
+    /**
+     * Provides a {@link LogBuffer} for use by
+     *  {@link com.android.keyguard.faceauth.KeyguardFaceAuthManagerImpl}.
+     */
+    @Provides
+    @SysUISingleton
+    @FaceAuthLog
+    public static LogBuffer provideFaceAuthLog(LogBufferFactory factory) {
+        return factory.create("KeyguardFaceAuthManagerLog", 300);
+    }
+
+    /**
      * Provides a {@link LogBuffer} for bluetooth-related logs.
      */
     @Provides
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AnimationParams.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/ScreenDecorationsLog.kt
similarity index 60%
copy from packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AnimationParams.kt
copy to packages/SystemUI/src/com/android/systemui/log/dagger/ScreenDecorationsLog.kt
index 67733e9..de2a8b6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AnimationParams.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/ScreenDecorationsLog.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -11,15 +11,15 @@
  * 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
+ * limitations under the License.
  */
-package com.android.systemui.keyguard.shared.model
 
-import kotlin.time.Duration
-import kotlin.time.Duration.Companion.milliseconds
+package com.android.systemui.log.dagger
 
-/** Animation parameters */
-data class AnimationParams(
-    val startTime: Duration = 0.milliseconds,
-    val duration: Duration,
-)
+import javax.inject.Qualifier
+
+/** A [com.android.systemui.log.LogBuffer] for ScreenDecorations added by SysUI. */
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class ScreenDecorationsLog
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/StatusBarConnectivityLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/StatusBarConnectivityLog.java
deleted file mode 100644
index 67cdb72..0000000
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/StatusBarConnectivityLog.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2022 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.log.dagger;
-
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import com.android.systemui.plugins.log.LogBuffer;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-
-import javax.inject.Qualifier;
-
-/**
- * A {@link LogBuffer} for status bar connectivity events.
- */
-@Qualifier
-@Documented
-@Retention(RUNTIME)
-public @interface StatusBarConnectivityLog {
-}
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt b/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt
index 348d941..ccd4060 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt
@@ -79,10 +79,10 @@
     }
 }
 
-/**
- * Each time the boolean flow is updated with a new value that's different from the previous value,
- * logs the new value to the given [tableLogBuffer].
- */
+// Here and below: Various Flow<SomeType> extension functions that are effectively equivalent to the
+// above [logDiffsForTable] method.
+
+/** See [logDiffsForTable(TableLogBuffer, String, T)]. */
 fun Flow<Boolean>.logDiffsForTable(
     tableLogBuffer: TableLogBuffer,
     columnPrefix: String,
@@ -100,10 +100,8 @@
         newVal
     }
 }
-/**
- * Each time the Int flow is updated with a new value that's different from the previous value, logs
- * the new value to the given [tableLogBuffer].
- */
+
+/** See [logDiffsForTable(TableLogBuffer, String, T)]. */
 fun Flow<Int>.logDiffsForTable(
     tableLogBuffer: TableLogBuffer,
     columnPrefix: String,
@@ -122,10 +120,26 @@
     }
 }
 
-/**
- * Each time the String? flow is updated with a new value that's different from the previous value,
- * logs the new value to the given [tableLogBuffer].
- */
+/** See [logDiffsForTable(TableLogBuffer, String, T)]. */
+fun Flow<Int?>.logDiffsForTable(
+    tableLogBuffer: TableLogBuffer,
+    columnPrefix: String,
+    columnName: String,
+    initialValue: Int?,
+): Flow<Int?> {
+    val initialValueFun = {
+        tableLogBuffer.logChange(columnPrefix, columnName, initialValue)
+        initialValue
+    }
+    return this.pairwiseBy(initialValueFun) { prevVal, newVal: Int? ->
+        if (prevVal != newVal) {
+            tableLogBuffer.logChange(columnPrefix, columnName, newVal)
+        }
+        newVal
+    }
+}
+
+/** See [logDiffsForTable(TableLogBuffer, String, T)]. */
 fun Flow<String?>.logDiffsForTable(
     tableLogBuffer: TableLogBuffer,
     columnPrefix: String,
@@ -143,3 +157,23 @@
         newVal
     }
 }
+
+/** See [logDiffsForTable(TableLogBuffer, String, T)]. */
+fun <T> Flow<List<T>>.logDiffsForTable(
+    tableLogBuffer: TableLogBuffer,
+    columnPrefix: String,
+    columnName: String,
+    initialValue: List<T>,
+): Flow<List<T>> {
+    val initialValueFun = {
+        tableLogBuffer.logChange(columnPrefix, columnName, initialValue.toString())
+        initialValue
+    }
+    return this.pairwiseBy(initialValueFun) { prevVal, newVal: List<T> ->
+        if (prevVal != newVal) {
+            // TODO(b/267761156): Can we log list changes without using toString?
+            tableLogBuffer.logChange(columnPrefix, columnName, newVal.toString())
+        }
+        newVal
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableChange.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableChange.kt
index 68c297f..4880f80 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/TableChange.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/TableChange.kt
@@ -27,7 +27,7 @@
     var columnName: String = "",
     var type: DataType = DataType.EMPTY,
     var bool: Boolean = false,
-    var int: Int = 0,
+    var int: Int? = null,
     var str: String? = null,
 ) {
     /** Resets to default values so that the object can be recycled. */
@@ -54,7 +54,7 @@
     }
 
     /** Sets this to store an int change. */
-    fun set(value: Int) {
+    fun set(value: Int?) {
         type = DataType.INT
         int = value
     }
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
index 2c299d6..1712dab 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
@@ -138,7 +138,7 @@
     }
 
     /** Logs a Int change. */
-    fun logChange(prefix: String, columnName: String, value: Int) {
+    fun logChange(prefix: String, columnName: String, value: Int?) {
         logChange(systemClock.currentTimeMillis(), prefix, columnName, value)
     }
 
@@ -155,7 +155,7 @@
         change.set(value)
     }
 
-    private fun logChange(timestamp: Long, prefix: String, columnName: String, value: Int) {
+    private fun logChange(timestamp: Long, prefix: String, columnName: String, value: Int?) {
         val change = obtain(timestamp, prefix, columnName)
         change.set(value)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
index a692ad7..52d4171 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
@@ -28,12 +28,15 @@
 import android.os.UserHandle
 import android.view.ViewGroup
 import com.android.internal.annotations.VisibleForTesting
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyStateProvider
 import com.android.internal.app.AbstractMultiProfilePagerAdapter.MyUserIdProvider
 import com.android.internal.app.ChooserActivity
 import com.android.internal.app.ResolverListController
 import com.android.internal.app.chooser.NotSelectableTargetInfo
 import com.android.internal.app.chooser.TargetInfo
 import com.android.systemui.R
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
 import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorComponent
 import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorController
 import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorResultHandler
@@ -47,6 +50,7 @@
 class MediaProjectionAppSelectorActivity(
     private val componentFactory: MediaProjectionAppSelectorComponent.Factory,
     private val activityLauncher: AsyncActivityLauncher,
+    private val featureFlags: FeatureFlags,
     /** This is used to override the dependency in a screenshot test */
     @VisibleForTesting
     private val listControllerFactory: ((userHandle: UserHandle) -> ResolverListController)?
@@ -56,7 +60,8 @@
     constructor(
         componentFactory: MediaProjectionAppSelectorComponent.Factory,
         activityLauncher: AsyncActivityLauncher,
-    ) : this(componentFactory, activityLauncher, null)
+        featureFlags: FeatureFlags
+    ) : this(componentFactory, activityLauncher, featureFlags, listControllerFactory = null)
 
     private lateinit var configurationController: ConfigurationController
     private lateinit var controller: MediaProjectionAppSelectorController
@@ -91,6 +96,13 @@
 
     override fun appliedThemeResId(): Int = R.style.Theme_SystemUI_MediaProjectionAppSelector
 
+    override fun createBlockerEmptyStateProvider(): EmptyStateProvider =
+        if (featureFlags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES)) {
+            component.emptyStateProvider
+        } else {
+            super.createBlockerEmptyStateProvider()
+        }
+
     override fun createListController(userHandle: UserHandle): ResolverListController =
         listControllerFactory?.invoke(userHandle) ?: super.createListController(userHandle)
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
index d830fc4..c4e76b2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
@@ -45,33 +45,46 @@
 import android.util.Log;
 import android.view.Window;
 
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
+import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
+import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
 import com.android.systemui.screenrecord.MediaProjectionPermissionDialog;
 import com.android.systemui.screenrecord.ScreenShareOption;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.util.Utils;
 
+import javax.inject.Inject;
+
+import dagger.Lazy;
+
 public class MediaProjectionPermissionActivity extends Activity
         implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
     private static final String TAG = "MediaProjectionPermissionActivity";
     private static final float MAX_APP_NAME_SIZE_PX = 500f;
     private static final String ELLIPSIS = "\u2026";
 
+    private final FeatureFlags mFeatureFlags;
+    private final Lazy<ScreenCaptureDevicePolicyResolver> mScreenCaptureDevicePolicyResolver;
+
     private String mPackageName;
     private int mUid;
     private IMediaProjectionManager mService;
-    private FeatureFlags mFeatureFlags;
 
     private AlertDialog mDialog;
 
+    @Inject
+    public MediaProjectionPermissionActivity(FeatureFlags featureFlags,
+            Lazy<ScreenCaptureDevicePolicyResolver> screenCaptureDevicePolicyResolver) {
+        mFeatureFlags = featureFlags;
+        mScreenCaptureDevicePolicyResolver = screenCaptureDevicePolicyResolver;
+    }
+
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
-        mFeatureFlags = Dependency.get(FeatureFlags.class);
         mPackageName = getCallingPackage();
         IBinder b = ServiceManager.getService(MEDIA_PROJECTION_SERVICE);
         mService = IMediaProjectionManager.Stub.asInterface(b);
@@ -104,6 +117,12 @@
             return;
         }
 
+        if (mFeatureFlags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES)) {
+            if (showScreenCaptureDisabledDialogIfNeeded()) {
+                return;
+            }
+        }
+
         TextPaint paint = new TextPaint();
         paint.setTextSize(42);
 
@@ -171,16 +190,7 @@
             mDialog = dialogBuilder.create();
         }
 
-        SystemUIDialog.registerDismissListener(mDialog);
-        SystemUIDialog.applyFlags(mDialog);
-        SystemUIDialog.setDialogSize(mDialog);
-
-        mDialog.setOnCancelListener(this);
-        mDialog.create();
-        mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setFilterTouchesWhenObscured(true);
-
-        final Window w = mDialog.getWindow();
-        w.addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+        setUpDialog(mDialog);
 
         mDialog.show();
     }
@@ -200,6 +210,32 @@
         }
     }
 
+    private void setUpDialog(AlertDialog dialog) {
+        SystemUIDialog.registerDismissListener(dialog);
+        SystemUIDialog.applyFlags(dialog);
+        SystemUIDialog.setDialogSize(dialog);
+
+        dialog.setOnCancelListener(this);
+        dialog.create();
+        dialog.getButton(DialogInterface.BUTTON_POSITIVE).setFilterTouchesWhenObscured(true);
+
+        final Window w = dialog.getWindow();
+        w.addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+    }
+
+    private boolean showScreenCaptureDisabledDialogIfNeeded() {
+        final UserHandle hostUserHandle = getHostUserHandle();
+        if (mScreenCaptureDevicePolicyResolver.get()
+                .isScreenCaptureCompletelyDisabled(hostUserHandle)) {
+            AlertDialog dialog = new ScreenCaptureDisabledDialog(this);
+            setUpDialog(dialog);
+            dialog.show();
+            return true;
+        }
+
+        return false;
+    }
+
     private void grantMediaProjectionPermission(int screenShareMode) {
         try {
             if (screenShareMode == ENTIRE_SCREEN) {
@@ -211,7 +247,7 @@
                 intent.putExtra(MediaProjectionManager.EXTRA_MEDIA_PROJECTION,
                         projection.asBinder());
                 intent.putExtra(MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_USER_HANDLE,
-                        UserHandle.getUserHandleForUid(getLaunchedFromUid()));
+                        getHostUserHandle());
                 intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
 
                 // Start activity from the current foreground user to avoid creating a separate
@@ -230,6 +266,10 @@
         }
     }
 
+    private UserHandle getHostUserHandle() {
+        return UserHandle.getUserHandleForUid(getLaunchedFromUid());
+    }
+
     private IMediaProjection createProjection(int uid, String packageName) throws RemoteException {
         return mService.createProjection(uid, packageName,
                 MediaProjectionManager.TYPE_SCREEN_CAPTURE, false /* permanentGrant */);
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaData.kt
index be18cbe..b7a2522 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaData.kt
@@ -92,6 +92,9 @@
 
     /** Whether explicit indicator exists */
     val isExplicit: Boolean = false,
+
+    /** Track progress (0 - 1) to display for players where [resumption] is true */
+    val resumeProgress: Double? = null,
 ) {
     companion object {
         /** Media is playing on the local device */
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarViewModel.kt
index bba5f35..a057c9f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarViewModel.kt
@@ -238,6 +238,24 @@
     }
 
     /**
+     * Set the progress to a fixed percentage value that cannot be changed by the user.
+     *
+     * @param percent value between 0 and 1
+     */
+    fun updateStaticProgress(percent: Double) {
+        val position = (percent * 100).toInt()
+        _data =
+            Progress(
+                enabled = true,
+                seekAvailable = false,
+                playing = false,
+                scrubbing = false,
+                elapsedTime = position,
+                duration = 100,
+            )
+    }
+
+    /**
      * Puts the seek bar into a resumption state.
      *
      * This should be called when the media session behind the controller has been destroyed.
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt
index 1a10b18..70f2dee 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt
@@ -20,7 +20,9 @@
 import android.view.View
 import android.view.ViewGroup
 import android.widget.ImageView
+import android.widget.SeekBar
 import android.widget.TextView
+import com.android.internal.widget.CachingIconView
 import com.android.systemui.R
 import com.android.systemui.media.controls.models.GutsViewHolder
 import com.android.systemui.media.controls.ui.IlluminationDrawable
@@ -29,18 +31,16 @@
 private const val TAG = "RecommendationViewHolder"
 
 /** ViewHolder for a Smartspace media recommendation. */
-class RecommendationViewHolder private constructor(itemView: View) {
+class RecommendationViewHolder private constructor(itemView: View, updatedView: Boolean) {
 
     val recommendations = itemView as TransitionLayout
 
     // Recommendation screen
-    val cardIcon = itemView.requireViewById<ImageView>(R.id.recommendation_card_icon)
-    val mediaCoverItems =
-        listOf<ImageView>(
-            itemView.requireViewById(R.id.media_cover1),
-            itemView.requireViewById(R.id.media_cover2),
-            itemView.requireViewById(R.id.media_cover3)
-        )
+    lateinit var cardIcon: ImageView
+    lateinit var mediaAppIcons: List<CachingIconView>
+    lateinit var mediaProgressBars: List<SeekBar>
+    lateinit var cardTitle: TextView
+
     val mediaCoverContainers =
         listOf<ViewGroup>(
             itemView.requireViewById(R.id.media_cover1_container),
@@ -48,21 +48,52 @@
             itemView.requireViewById(R.id.media_cover3_container)
         )
     val mediaTitles: List<TextView> =
-        listOf(
-            itemView.requireViewById(R.id.media_title1),
-            itemView.requireViewById(R.id.media_title2),
-            itemView.requireViewById(R.id.media_title3)
-        )
+        if (updatedView) {
+            mediaCoverContainers.map { it.requireViewById(R.id.media_title) }
+        } else {
+            listOf(
+                itemView.requireViewById(R.id.media_title1),
+                itemView.requireViewById(R.id.media_title2),
+                itemView.requireViewById(R.id.media_title3)
+            )
+        }
     val mediaSubtitles: List<TextView> =
-        listOf(
-            itemView.requireViewById(R.id.media_subtitle1),
-            itemView.requireViewById(R.id.media_subtitle2),
-            itemView.requireViewById(R.id.media_subtitle3)
-        )
+        if (updatedView) {
+            mediaCoverContainers.map { it.requireViewById(R.id.media_subtitle) }
+        } else {
+            listOf(
+                itemView.requireViewById(R.id.media_subtitle1),
+                itemView.requireViewById(R.id.media_subtitle2),
+                itemView.requireViewById(R.id.media_subtitle3)
+            )
+        }
 
+    val mediaCoverItems: List<ImageView> =
+        if (updatedView) {
+            mediaCoverContainers.map { it.requireViewById(R.id.media_cover) }
+        } else {
+            listOf(
+                itemView.requireViewById(R.id.media_cover1),
+                itemView.requireViewById(R.id.media_cover2),
+                itemView.requireViewById(R.id.media_cover3)
+            )
+        }
     val gutsViewHolder = GutsViewHolder(itemView)
 
     init {
+        if (updatedView) {
+            mediaAppIcons = mediaCoverContainers.map { it.requireViewById(R.id.media_rec_app_icon) }
+            cardTitle = itemView.requireViewById(R.id.media_rec_title)
+            mediaProgressBars =
+                mediaCoverContainers.map {
+                    it.requireViewById<SeekBar?>(R.id.media_progress_bar).apply {
+                        // Media playback is in the direction of tape, not time, so it stays LTR
+                        layoutDirection = View.LAYOUT_DIRECTION_LTR
+                    }
+                }
+        } else {
+            cardIcon = itemView.requireViewById<ImageView>(R.id.recommendation_card_icon)
+        }
         (recommendations.background as IlluminationDrawable).let { background ->
             mediaCoverContainers.forEach { background.registerLightSource(it) }
             background.registerLightSource(gutsViewHolder.cancel)
@@ -83,36 +114,52 @@
          * @param parent Parent of inflated view.
          */
         @JvmStatic
-        fun create(inflater: LayoutInflater, parent: ViewGroup): RecommendationViewHolder {
+        fun create(
+            inflater: LayoutInflater,
+            parent: ViewGroup,
+            updatedView: Boolean,
+        ): RecommendationViewHolder {
             val itemView =
-                inflater.inflate(
-                    R.layout.media_smartspace_recommendations,
-                    parent,
-                    false /* attachToRoot */
-                )
+                if (updatedView) {
+                    inflater.inflate(
+                        R.layout.media_recommendations,
+                        parent,
+                        false /* attachToRoot */
+                    )
+                } else {
+                    inflater.inflate(
+                        R.layout.media_smartspace_recommendations,
+                        parent,
+                        false /* attachToRoot */
+                    )
+                }
             // Because this media view (a TransitionLayout) is used to measure and layout the views
             // in various states before being attached to its parent, we can't depend on the default
             // LAYOUT_DIRECTION_INHERIT to correctly resolve the ltr direction.
             itemView.layoutDirection = View.LAYOUT_DIRECTION_LOCALE
-            return RecommendationViewHolder(itemView)
+            return RecommendationViewHolder(itemView, updatedView)
         }
 
         // Res Ids for the control components on the recommendation view.
         val controlsIds =
             setOf(
                 R.id.recommendation_card_icon,
+                R.id.media_rec_title,
                 R.id.media_cover1,
                 R.id.media_cover2,
                 R.id.media_cover3,
+                R.id.media_cover,
                 R.id.media_cover1_container,
                 R.id.media_cover2_container,
                 R.id.media_cover3_container,
                 R.id.media_title1,
                 R.id.media_title2,
                 R.id.media_title3,
+                R.id.media_title,
                 R.id.media_subtitle1,
                 R.id.media_subtitle2,
-                R.id.media_subtitle3
+                R.id.media_subtitle3,
+                R.id.media_subtitle,
             )
 
         val mediaTitlesAndSubtitlesIds =
@@ -120,9 +167,11 @@
                 R.id.media_title1,
                 R.id.media_title2,
                 R.id.media_title3,
+                R.id.media_title,
                 R.id.media_subtitle1,
                 R.id.media_subtitle2,
-                R.id.media_subtitle3
+                R.id.media_subtitle3,
+                R.id.media_subtitle,
             )
 
         val mediaContainersIds =
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaData.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaData.kt
index 1df42c6..dc7a4f1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaData.kt
@@ -41,10 +41,12 @@
     val recommendations: List<SmartspaceAction>,
     /** Intent for the user's initiated dismissal. */
     val dismissIntent: Intent?,
-    /** The timestamp in milliseconds that headphone is connected. */
+    /** The timestamp in milliseconds that the card was generated */
     val headphoneConnectionTimeMillis: Long,
     /** Instance ID for [MediaUiEventLogger] */
-    val instanceId: InstanceId
+    val instanceId: InstanceId,
+    /** The timestamp in milliseconds indicating when the card should be removed */
+    val expiryTimeMs: Long,
 ) {
     /**
      * Indicates if all the data is valid.
@@ -86,5 +88,12 @@
     }
 }
 
+/** Key for extras [SmartspaceMediaData.cardAction] indicating why the card was sent */
+const val EXTRA_KEY_TRIGGER_SOURCE = "MEDIA_RECOMMENDATION_TRIGGER_SOURCE"
+/** Value for [EXTRA_KEY_TRIGGER_SOURCE] when the card is sent on headphone connection */
+const val EXTRA_VALUE_TRIGGER_HEADPHONE = "HEADPHONE_CONNECTION"
+/** Value for key [EXTRA_KEY_TRIGGER_SOURCE] when the card is sent as a regular update */
+const val EXTRA_VALUE_TRIGGER_PERIODIC = "PERIODIC_TRIGGER"
+
 const val NUM_REQUIRED_RECOMMENDATIONS = 3
 private val TAG = SmartspaceMediaData::class.simpleName!!
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt
index cf71d67..27f7b97 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.media.controls.models.player.MediaData
 import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
+import com.android.systemui.media.controls.util.MediaFlags
 import com.android.systemui.media.controls.util.MediaUiEventLogger
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
@@ -66,7 +67,8 @@
     private val lockscreenUserManager: NotificationLockscreenUserManager,
     @Main private val executor: Executor,
     private val systemClock: SystemClock,
-    private val logger: MediaUiEventLogger
+    private val logger: MediaUiEventLogger,
+    private val mediaFlags: MediaFlags,
 ) : MediaDataManager.Listener {
     private val _listeners: MutableSet<MediaDataManager.Listener> = mutableSetOf()
     internal val listeners: Set<MediaDataManager.Listener>
@@ -121,7 +123,9 @@
         data: SmartspaceMediaData,
         shouldPrioritize: Boolean
     ) {
-        if (!data.isActive) {
+        // With persistent recommendation card, we could get a background update while inactive
+        // Otherwise, consider it an invalid update
+        if (!data.isActive && !mediaFlags.isPersistentSsCardEnabled()) {
             Log.d(TAG, "Inactive recommendation data. Skip triggering.")
             return
         }
@@ -141,7 +145,7 @@
             }
         }
 
-        val shouldReactivate = !hasActiveMedia() && hasAnyMedia()
+        val shouldReactivate = !hasActiveMedia() && hasAnyMedia() && data.isActive
 
         if (timeSinceActive < smartspaceMaxAgeMillis) {
             // It could happen there are existing active media resume cards, then we don't need to
@@ -169,7 +173,7 @@
                     )
                 }
             }
-        } else {
+        } else if (data.isActive) {
             // Mark to prioritize Smartspace card if no recent media.
             shouldPrioritizeMutable = true
         }
@@ -252,7 +256,7 @@
             if (dismissIntent == null) {
                 Log.w(
                     TAG,
-                    "Cannot create dismiss action click action: " + "extras missing dismiss_intent."
+                    "Cannot create dismiss action click action: extras missing dismiss_intent."
                 )
             } else if (
                 dismissIntent.getComponent() != null &&
@@ -264,15 +268,21 @@
             } else {
                 broadcastSender.sendBroadcast(dismissIntent)
             }
-            smartspaceMediaData =
-                EMPTY_SMARTSPACE_MEDIA_DATA.copy(
-                    targetId = smartspaceMediaData.targetId,
-                    instanceId = smartspaceMediaData.instanceId
+
+            if (mediaFlags.isPersistentSsCardEnabled()) {
+                smartspaceMediaData = smartspaceMediaData.copy(isActive = false)
+                mediaDataManager.setRecommendationInactive(smartspaceMediaData.targetId)
+            } else {
+                smartspaceMediaData =
+                    EMPTY_SMARTSPACE_MEDIA_DATA.copy(
+                        targetId = smartspaceMediaData.targetId,
+                        instanceId = smartspaceMediaData.instanceId,
+                    )
+                mediaDataManager.dismissSmartspaceRecommendation(
+                    smartspaceMediaData.targetId,
+                    delay = 0L,
                 )
-            mediaDataManager.dismissSmartspaceRecommendation(
-                smartspaceMediaData.targetId,
-                delay = 0L
-            )
+            }
         }
     }
 
@@ -283,8 +293,15 @@
                 (smartspaceMediaData.isValid() || reactivatedKey != null))
 
     /** Are there any media entries we should display? */
-    fun hasAnyMediaOrRecommendation() =
-        userEntries.isNotEmpty() || (smartspaceMediaData.isActive && smartspaceMediaData.isValid())
+    fun hasAnyMediaOrRecommendation(): Boolean {
+        val hasSmartspace =
+            if (mediaFlags.isPersistentSsCardEnabled()) {
+                smartspaceMediaData.isValid()
+            } else {
+                smartspaceMediaData.isActive && smartspaceMediaData.isValid()
+            }
+        return userEntries.isNotEmpty() || hasSmartspace
+    }
 
     /** Are there any media notifications active (excluding the recommendation)? */
     fun hasActiveMedia() = userEntries.any { it.value.active }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
index b11f628..f5558a2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
@@ -49,8 +49,8 @@
 import android.text.TextUtils
 import android.util.Log
 import androidx.media.utils.MediaConstants
-import com.android.internal.annotations.VisibleForTesting
 import com.android.internal.logging.InstanceId
+import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.Dumpable
 import com.android.systemui.R
 import com.android.systemui.broadcast.BroadcastDispatcher
@@ -63,10 +63,14 @@
 import com.android.systemui.media.controls.models.player.MediaData
 import com.android.systemui.media.controls.models.player.MediaDeviceData
 import com.android.systemui.media.controls.models.player.MediaViewHolder
+import com.android.systemui.media.controls.models.recommendation.EXTRA_KEY_TRIGGER_SOURCE
+import com.android.systemui.media.controls.models.recommendation.EXTRA_VALUE_TRIGGER_PERIODIC
 import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
 import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaDataProvider
 import com.android.systemui.media.controls.resume.MediaResumeListener
+import com.android.systemui.media.controls.resume.ResumeMediaBrowser
 import com.android.systemui.media.controls.util.MediaControllerFactory
+import com.android.systemui.media.controls.util.MediaDataUtils
 import com.android.systemui.media.controls.util.MediaFlags
 import com.android.systemui.media.controls.util.MediaUiEventLogger
 import com.android.systemui.plugins.ActivityStarter
@@ -118,7 +122,6 @@
         appUid = Process.INVALID_UID
     )
 
-@VisibleForTesting
 internal val EMPTY_SMARTSPACE_MEDIA_DATA =
     SmartspaceMediaData(
         targetId = "INVALID",
@@ -128,7 +131,8 @@
         recommendations = emptyList(),
         dismissIntent = null,
         headphoneConnectionTimeMillis = 0,
-        instanceId = InstanceId.fakeInstanceId(-1)
+        instanceId = InstanceId.fakeInstanceId(-1),
+        expiryTimeMs = 0,
     )
 
 fun isMediaNotification(sbn: StatusBarNotification): Boolean {
@@ -174,6 +178,7 @@
     private val mediaFlags: MediaFlags,
     private val logger: MediaUiEventLogger,
     private val smartspaceManager: SmartspaceManager,
+    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
 ) : Dumpable, BcSmartspaceDataPlugin.SmartspaceTargetListener {
 
     companion object {
@@ -238,6 +243,7 @@
         mediaFlags: MediaFlags,
         logger: MediaUiEventLogger,
         smartspaceManager: SmartspaceManager,
+        keyguardUpdateMonitor: KeyguardUpdateMonitor,
     ) : this(
         context,
         backgroundExecutor,
@@ -261,6 +267,7 @@
         mediaFlags,
         logger,
         smartspaceManager,
+        keyguardUpdateMonitor,
     )
 
     private val appChangeReceiver =
@@ -547,6 +554,11 @@
             if (DEBUG) Log.d(TAG, "Updating $key timedOut: $timedOut")
             onMediaDataLoaded(key, key, it)
         }
+
+        if (key == smartspaceMediaData.targetId) {
+            if (DEBUG) Log.d(TAG, "smartspace card expired")
+            dismissSmartspaceRecommendation(key, delay = 0L)
+        }
     }
 
     /** Called when the player's [PlaybackState] has been updated with new actions and/or state */
@@ -604,8 +616,8 @@
     }
 
     /**
-     * Called whenever the recommendation has been expired, or swiped from QQS. This will make the
-     * recommendation view to not be shown anymore during this headphone connection session.
+     * Called whenever the recommendation has been expired or removed by the user. This will remove
+     * the recommendation card entirely from the carousel.
      */
     fun dismissSmartspaceRecommendation(key: String, delay: Long) {
         if (smartspaceMediaData.targetId != key || !smartspaceMediaData.isValid()) {
@@ -627,6 +639,23 @@
         )
     }
 
+    /** Called when the recommendation card should no longer be visible in QQS or lockscreen */
+    fun setRecommendationInactive(key: String) {
+        if (!mediaFlags.isPersistentSsCardEnabled()) {
+            Log.e(TAG, "Only persistent recommendation can be inactive!")
+            return
+        }
+        if (DEBUG) Log.d(TAG, "Setting smartspace recommendation inactive")
+
+        if (smartspaceMediaData.targetId != key || !smartspaceMediaData.isValid()) {
+            // If this doesn't match, or we've already invalidated the data, no action needed
+            return
+        }
+
+        smartspaceMediaData = smartspaceMediaData.copy(isActive = false)
+        notifySmartspaceMediaDataLoaded(smartspaceMediaData.targetId, smartspaceMediaData)
+    }
+
     private fun loadMediaDataInBgForResumption(
         userId: Int,
         desc: MediaDescription,
@@ -667,6 +696,11 @@
                 MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT &&
                 mediaFlags.isExplicitIndicatorEnabled()
 
+        val progress =
+            if (mediaFlags.isResumeProgressEnabled()) {
+                MediaDataUtils.getDescriptionProgress(desc.extras)
+            } else null
+
         val mediaAction = getResumeMediaAction(resumeAction)
         val lastActive = systemClock.elapsedRealtime()
         foregroundExecutor.execute {
@@ -697,6 +731,7 @@
                     instanceId = instanceId,
                     appUid = appUid,
                     isExplicit = isExplicit,
+                    resumeProgress = progress,
                 )
             )
         }
@@ -1258,12 +1293,25 @@
                 if (DEBUG) {
                     Log.d(TAG, "Set Smartspace media to be inactive for the data update")
                 }
-                smartspaceMediaData =
-                    EMPTY_SMARTSPACE_MEDIA_DATA.copy(
-                        targetId = smartspaceMediaData.targetId,
-                        instanceId = smartspaceMediaData.instanceId
+                if (mediaFlags.isPersistentSsCardEnabled()) {
+                    // Smartspace uses this signal to hide the card (e.g. when it expires or user
+                    // disconnects headphones), so treat as setting inactive when flag is on
+                    smartspaceMediaData = smartspaceMediaData.copy(isActive = false)
+                    notifySmartspaceMediaDataLoaded(
+                        smartspaceMediaData.targetId,
+                        smartspaceMediaData,
                     )
-                notifySmartspaceMediaDataRemoved(smartspaceMediaData.targetId, immediately = false)
+                } else {
+                    smartspaceMediaData =
+                        EMPTY_SMARTSPACE_MEDIA_DATA.copy(
+                            targetId = smartspaceMediaData.targetId,
+                            instanceId = smartspaceMediaData.instanceId,
+                        )
+                    notifySmartspaceMediaDataRemoved(
+                        smartspaceMediaData.targetId,
+                        immediately = false,
+                    )
+                }
             }
             1 -> {
                 val newMediaTarget = mediaTargets.get(0)
@@ -1272,7 +1320,7 @@
                     return
                 }
                 if (DEBUG) Log.d(TAG, "Forwarding Smartspace media update.")
-                smartspaceMediaData = toSmartspaceMediaData(newMediaTarget, isActive = true)
+                smartspaceMediaData = toSmartspaceMediaData(newMediaTarget)
                 notifySmartspaceMediaDataLoaded(smartspaceMediaData.targetId, smartspaceMediaData)
             }
             else -> {
@@ -1281,7 +1329,7 @@
                 Log.wtf(TAG, "More than 1 Smartspace Media Update. Resetting the status...")
                 notifySmartspaceMediaDataRemoved(
                     smartspaceMediaData.targetId,
-                    false /* immediately */
+                    immediately = false,
                 )
                 smartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA
             }
@@ -1292,10 +1340,12 @@
         Assert.isMainThread()
         val removed = mediaEntries.remove(key) ?: return
 
-        if (useMediaResumption && removed.resumeAction != null && removed.isLocalSession()) {
-            convertToResumePlayer(removed)
+        if (keyguardUpdateMonitor.isUserInLockdown(removed.userId)) {
+            logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId)
+        } else if (useMediaResumption && removed.resumeAction != null && removed.isLocalSession()) {
+            convertToResumePlayer(key, removed)
         } else if (mediaFlags.isRetainingPlayersEnabled()) {
-            handlePossibleRemoval(removed, notificationRemoved = true)
+            handlePossibleRemoval(key, removed, notificationRemoved = true)
         } else {
             notifyMediaDataRemoved(key)
             logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId)
@@ -1309,7 +1359,7 @@
         val entry = mediaEntries.remove(key) ?: return
         // Clear token since the session is no longer valid
         val updated = entry.copy(token = null)
-        handlePossibleRemoval(updated)
+        handlePossibleRemoval(key, updated)
     }
 
     /**
@@ -1318,8 +1368,11 @@
      * if it was removed before becoming inactive. (Assumes that [removed] was removed from
      * [mediaEntries] before this function was called)
      */
-    private fun handlePossibleRemoval(removed: MediaData, notificationRemoved: Boolean = false) {
-        val key = removed.notificationKey!!
+    private fun handlePossibleRemoval(
+        key: String,
+        removed: MediaData,
+        notificationRemoved: Boolean = false
+    ) {
         val hasSession = removed.token != null
         if (hasSession && removed.semanticActions != null) {
             // The app was using session actions, and the session is still valid: keep player
@@ -1345,13 +1398,12 @@
                         "($hasSession) gone for inactive player $key"
                 )
             }
-            convertToResumePlayer(removed)
+            convertToResumePlayer(key, removed)
         }
     }
 
     /** Set the given [MediaData] as a resume state player and notify listeners */
-    private fun convertToResumePlayer(data: MediaData) {
-        val key = data.notificationKey!!
+    private fun convertToResumePlayer(key: String, data: MediaData) {
         if (DEBUG) Log.d(TAG, "Converting $key to resume")
         // Move to resume key (aka package name) if that key doesn't already exist.
         val resumeAction = data.resumeAction?.let { getResumeMediaAction(it) }
@@ -1388,6 +1440,22 @@
             notifyMediaDataLoaded(key = pkg, oldKey = pkg, info = updated)
         }
         logger.logActiveConvertedToResume(updated.appUid, pkg, updated.instanceId)
+
+        // Limit total number of resume controls
+        val resumeEntries = mediaEntries.filter { (key, data) -> data.resumption }
+        val numResume = resumeEntries.size
+        if (numResume > ResumeMediaBrowser.MAX_RESUMPTION_CONTROLS) {
+            resumeEntries
+                .toList()
+                .sortedBy { (key, data) -> data.lastActive }
+                .subList(0, numResume - ResumeMediaBrowser.MAX_RESUMPTION_CONTROLS)
+                .forEach { (key, data) ->
+                    Log.d(TAG, "Removing excess control $key")
+                    mediaEntries.remove(key)
+                    notifyMediaDataRemoved(key)
+                    logger.logMediaRemoved(data.appUid, data.packageName, data.instanceId)
+                }
+        }
     }
 
     fun setMediaResumptionEnabled(isEnabled: Boolean) {
@@ -1487,21 +1555,28 @@
     }
 
     /**
-     * Converts the pass-in SmartspaceTarget to SmartspaceMediaData with the pass-in active status.
+     * Converts the pass-in SmartspaceTarget to SmartspaceMediaData
      *
      * @return An empty SmartspaceMediaData with the valid target Id is returned if the
      * SmartspaceTarget's data is invalid.
      */
-    private fun toSmartspaceMediaData(
-        target: SmartspaceTarget,
-        isActive: Boolean
-    ): SmartspaceMediaData {
+    private fun toSmartspaceMediaData(target: SmartspaceTarget): SmartspaceMediaData {
         var dismissIntent: Intent? = null
         if (target.baseAction != null && target.baseAction.extras != null) {
             dismissIntent =
                 target.baseAction.extras.getParcelable(EXTRAS_SMARTSPACE_DISMISS_INTENT_KEY)
                     as Intent?
         }
+
+        val isActive =
+            when {
+                !mediaFlags.isPersistentSsCardEnabled() -> true
+                target.baseAction == null -> true
+                else ->
+                    target.baseAction.extras.getString(EXTRA_KEY_TRIGGER_SOURCE) !=
+                        EXTRA_VALUE_TRIGGER_PERIODIC
+            }
+
         packageName(target)?.let {
             return SmartspaceMediaData(
                 targetId = target.smartspaceTargetId,
@@ -1511,7 +1586,8 @@
                 recommendations = target.iconGrid,
                 dismissIntent = dismissIntent,
                 headphoneConnectionTimeMillis = target.creationTimeMillis,
-                instanceId = logger.getNewInstanceId()
+                instanceId = logger.getNewInstanceId(),
+                expiryTimeMs = target.expiryTimeMillis,
             )
         }
         return EMPTY_SMARTSPACE_MEDIA_DATA.copy(
@@ -1519,7 +1595,8 @@
             isActive = isActive,
             dismissIntent = dismissIntent,
             headphoneConnectionTimeMillis = target.creationTimeMillis,
-            instanceId = logger.getNewInstanceId()
+            instanceId = logger.getNewInstanceId(),
+            expiryTimeMs = target.expiryTimeMillis,
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt
index a898b00..878962d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt
@@ -23,7 +23,9 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.media.controls.models.player.MediaData
+import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
 import com.android.systemui.media.controls.util.MediaControllerFactory
+import com.android.systemui.media.controls.util.MediaFlags
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState
 import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -38,7 +40,7 @@
 
 @VisibleForTesting
 val RESUME_MEDIA_TIMEOUT =
-    SystemProperties.getLong("debug.sysui.media_timeout_resume", TimeUnit.DAYS.toMillis(3))
+    SystemProperties.getLong("debug.sysui.media_timeout_resume", TimeUnit.DAYS.toMillis(2))
 
 /** Controller responsible for keeping track of playback states and expiring inactive streams. */
 @SysUISingleton
@@ -49,10 +51,12 @@
     @Main private val mainExecutor: DelayableExecutor,
     private val logger: MediaTimeoutLogger,
     statusBarStateController: SysuiStatusBarStateController,
-    private val systemClock: SystemClock
+    private val systemClock: SystemClock,
+    private val mediaFlags: MediaFlags,
 ) : MediaDataManager.Listener {
 
     private val mediaListeners: MutableMap<String, PlaybackStateListener> = mutableMapOf()
+    private val recommendationListeners: MutableMap<String, RecommendationListener> = mutableMapOf()
 
     /**
      * Callback representing that a media object is now expired:
@@ -93,6 +97,16 @@
                                 listener.doTimeout()
                             }
                         }
+
+                        recommendationListeners.forEach { (key, listener) ->
+                            if (
+                                listener.cancellation != null &&
+                                    listener.expiration <= systemClock.currentTimeMillis()
+                            ) {
+                                logger.logTimeoutCancelled(key, "Timed out while dozing")
+                                listener.doTimeout()
+                            }
+                        }
                     }
                 }
             }
@@ -155,6 +169,30 @@
         mediaListeners.remove(key)?.destroy()
     }
 
+    override fun onSmartspaceMediaDataLoaded(
+        key: String,
+        data: SmartspaceMediaData,
+        shouldPrioritize: Boolean
+    ) {
+        if (!mediaFlags.isPersistentSsCardEnabled()) return
+
+        // First check if we already have a listener
+        recommendationListeners.get(key)?.let {
+            if (!it.destroyed) {
+                it.recommendationData = data
+                return
+            }
+        }
+
+        // Otherwise, create a new one
+        recommendationListeners[key] = RecommendationListener(key, data)
+    }
+
+    override fun onSmartspaceMediaDataRemoved(key: String, immediately: Boolean) {
+        if (!mediaFlags.isPersistentSsCardEnabled()) return
+        recommendationListeners.remove(key)?.destroy()
+    }
+
     fun isTimedOut(key: String): Boolean {
         return mediaListeners[key]?.timedOut ?: false
     }
@@ -335,4 +373,53 @@
         }
         return true
     }
+
+    /** Listens to changes in recommendation card data and schedules a timeout for its expiration */
+    private inner class RecommendationListener(var key: String, data: SmartspaceMediaData) {
+        private var timedOut = false
+        var destroyed = false
+        var expiration = Long.MAX_VALUE
+            private set
+        var cancellation: Runnable? = null
+            private set
+
+        var recommendationData: SmartspaceMediaData = data
+            set(value) {
+                destroyed = false
+                field = value
+                processUpdate()
+            }
+
+        init {
+            recommendationData = data
+        }
+
+        fun destroy() {
+            cancellation?.run()
+            cancellation = null
+            destroyed = true
+        }
+
+        private fun processUpdate() {
+            if (recommendationData.expiryTimeMs != expiration) {
+                // The expiry time changed - cancel and reschedule
+                val timeout =
+                    recommendationData.expiryTimeMs -
+                        recommendationData.headphoneConnectionTimeMillis
+                logger.logRecommendationTimeoutScheduled(key, timeout)
+                cancellation?.run()
+                cancellation = mainExecutor.executeDelayed({ doTimeout() }, timeout)
+                expiration = recommendationData.expiryTimeMs
+            }
+        }
+
+        fun doTimeout() {
+            cancellation?.run()
+            cancellation = null
+            logger.logTimeout(key)
+            timedOut = true
+            expiration = Long.MAX_VALUE
+            timeoutCallback(key, timedOut)
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutLogger.kt
index 8f3f054..f731dc0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutLogger.kt
@@ -107,6 +107,17 @@
                 str1 = key
                 str2 = reason
             },
-            { "media timeout cancelled for $str1, reason: $str2" }
+            { "timeout cancelled for $str1, reason: $str2" }
+        )
+
+    fun logRecommendationTimeoutScheduled(key: String, timeout: Long) =
+        buffer.log(
+            TAG,
+            LogLevel.VERBOSE,
+            {
+                str1 = key
+                long1 = timeout
+            },
+            { "recommendation timeout scheduled for $str1 in $long1 ms" }
         )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
index e7f7647..68d2c5c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
@@ -30,19 +30,27 @@
 import android.view.animation.PathInterpolator
 import android.widget.LinearLayout
 import androidx.annotation.VisibleForTesting
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
 import com.android.internal.logging.InstanceId
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.systemui.Dumpable
 import com.android.systemui.R
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.media.controls.models.player.MediaData
 import com.android.systemui.media.controls.models.player.MediaViewHolder
 import com.android.systemui.media.controls.models.recommendation.RecommendationViewHolder
 import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
 import com.android.systemui.media.controls.pipeline.MediaDataManager
 import com.android.systemui.media.controls.ui.MediaControlPanel.SMARTSPACE_CARD_DISMISS_EVENT
+import com.android.systemui.media.controls.util.MediaFlags
 import com.android.systemui.media.controls.util.MediaUiEventLogger
 import com.android.systemui.media.controls.util.SmallHash
 import com.android.systemui.plugins.ActivityStarter
@@ -62,6 +70,10 @@
 import java.util.TreeMap
 import javax.inject.Inject
 import javax.inject.Provider
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.launch
 
 private const val TAG = "MediaCarouselController"
 private val settingsIntent = Intent().setAction(ACTION_MEDIA_CONTROLS_SETTINGS)
@@ -88,7 +100,10 @@
     falsingManager: FalsingManager,
     dumpManager: DumpManager,
     private val logger: MediaUiEventLogger,
-    private val debugLogger: MediaCarouselControllerLogger
+    private val debugLogger: MediaCarouselControllerLogger,
+    private val mediaFlags: MediaFlags,
+    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+    private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
 ) : Dumpable {
     /** The current width of the carousel */
     private var currentCarouselWidth: Int = 0
@@ -149,13 +164,13 @@
                 mediaCarouselScrollHandler.scrollToStart()
             }
         }
-    private var currentlyExpanded = true
+
+    @VisibleForTesting
+    var currentlyExpanded = true
         set(value) {
             if (field != value) {
                 field = value
-                for (player in MediaPlayerData.players()) {
-                    player.setListening(field)
-                }
+                updateSeekbarListening(mediaCarouselScrollHandler.visibleToUser)
             }
         }
 
@@ -211,6 +226,17 @@
             }
         }
 
+    private val keyguardUpdateMonitorCallback =
+        object : KeyguardUpdateMonitorCallback() {
+            override fun onStrongAuthStateChanged(userId: Int) {
+                if (keyguardUpdateMonitor.isUserInLockdown(userId)) {
+                    hideMediaCarousel()
+                } else if (keyguardUpdateMonitor.isUserUnlocked(userId)) {
+                    showMediaCarousel()
+                }
+            }
+        }
+
     /**
      * Update MediaCarouselScrollHandler.visibleToUser to reflect media card container visibility.
      * It will be called when the container is out of view.
@@ -233,6 +259,7 @@
                 executor,
                 this::onSwipeToDismiss,
                 this::updatePageIndicatorLocation,
+                this::updateSeekbarListening,
                 this::closeGuts,
                 falsingCollector,
                 falsingManager,
@@ -366,7 +393,7 @@
                     data: SmartspaceMediaData,
                     shouldPrioritize: Boolean
                 ) {
-                    debugLogger.logRecommendationLoaded(key)
+                    debugLogger.logRecommendationLoaded(key, data.isActive)
                     // Log the case where the hidden media carousel with the existed inactive resume
                     // media is shown by the Smartspace signal.
                     if (data.isActive) {
@@ -440,7 +467,12 @@
                             logSmartspaceImpression(mediaCarouselScrollHandler.qsExpanded)
                         }
                     } else {
-                        onSmartspaceMediaDataRemoved(data.targetId, immediately = true)
+                        if (!mediaFlags.isPersistentSsCardEnabled()) {
+                            // Handle update to inactive as a removal
+                            onSmartspaceMediaDataRemoved(data.targetId, immediately = true)
+                        } else {
+                            addSmartspaceMediaRecommendations(key, data, shouldPrioritize)
+                        }
                     }
                 }
 
@@ -480,6 +512,13 @@
                 }
             }
         )
+        keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
+        mediaCarousel.repeatWhenAttached {
+            repeatOnLifecycle(Lifecycle.State.STARTED) {
+                // A backup to show media carousel (if available) once the keyguard is gone.
+                listenForAnyStateToGoneKeyguardTransition(this)
+            }
+        }
     }
 
     private fun inflateSettingsButton() {
@@ -509,6 +548,23 @@
         return mediaCarousel
     }
 
+    private fun hideMediaCarousel() {
+        mediaCarousel.visibility = View.GONE
+    }
+
+    private fun showMediaCarousel() {
+        mediaCarousel.visibility = View.VISIBLE
+    }
+
+    @VisibleForTesting
+    internal fun listenForAnyStateToGoneKeyguardTransition(scope: CoroutineScope): Job {
+        return scope.launch {
+            keyguardTransitionInteractor.anyStateToGoneTransition
+                .filter { it.transitionState == TransitionState.FINISHED }
+                .collect { showMediaCarousel() }
+        }
+    }
+
     private fun reorderAllPlayers(
         previousVisiblePlayerKey: MediaPlayerData.MediaSortKey?,
         key: String? = null
@@ -535,6 +591,17 @@
                     ?: mediaCarouselScrollHandler.scrollToPlayer(destIndex = mediaIndex)
             }
         }
+        // Check postcondition: mediaContent should have the same number of children as there
+        // are
+        // elements in mediaPlayers.
+        if (MediaPlayerData.players().size != mediaContent.childCount) {
+            Log.e(
+                TAG,
+                "Size of players list and number of views in carousel are out of sync. " +
+                    "Players size is ${MediaPlayerData.players().size}. " +
+                    "View count is ${mediaContent.childCount}."
+            )
+        }
     }
 
     // Returns true if new player is added
@@ -563,7 +630,9 @@
                     )
                 newPlayer.mediaViewHolder?.player?.setLayoutParams(lp)
                 newPlayer.bindPlayer(data, key)
-                newPlayer.setListening(currentlyExpanded)
+                newPlayer.setListening(
+                    mediaCarouselScrollHandler.visibleToUser && currentlyExpanded
+                )
                 MediaPlayerData.addMediaPlayer(
                     key,
                     data,
@@ -610,17 +679,6 @@
             updatePageIndicator()
             mediaCarouselScrollHandler.onPlayersChanged()
             mediaFrame.requiresRemeasuring = true
-            // Check postcondition: mediaContent should have the same number of children as there
-            // are
-            // elements in mediaPlayers.
-            if (MediaPlayerData.players().size != mediaContent.childCount) {
-                Log.e(
-                    TAG,
-                    "Size of players list and number of views in carousel are out of sync. " +
-                        "Players size is ${MediaPlayerData.players().size}. " +
-                        "View count is ${mediaContent.childCount}."
-                )
-            }
             return existingPlayer == null
         }
 
@@ -631,7 +689,19 @@
     ) =
         traceSection("MediaCarouselController#addSmartspaceMediaRecommendations") {
             if (DEBUG) Log.d(TAG, "Updating smartspace target in carousel")
-            if (MediaPlayerData.getMediaPlayer(key) != null) {
+            MediaPlayerData.getMediaPlayer(key)?.let {
+                if (mediaFlags.isPersistentSsCardEnabled()) {
+                    // The card exists, but could have changed active state, so update for sorting
+                    MediaPlayerData.addMediaRecommendation(
+                        key,
+                        data,
+                        it,
+                        shouldPrioritize,
+                        systemClock,
+                        debugLogger,
+                        update = true,
+                    )
+                }
                 Log.w(TAG, "Skip adding smartspace target in carousel")
                 return
             }
@@ -647,7 +717,11 @@
 
             val newRecs = mediaControlPanelFactory.get()
             newRecs.attachRecommendation(
-                RecommendationViewHolder.create(LayoutInflater.from(context), mediaContent)
+                RecommendationViewHolder.create(
+                    LayoutInflater.from(context),
+                    mediaContent,
+                    mediaFlags.isRecommendationCardUpdateEnabled()
+                )
             )
             newRecs.mediaViewController.sizeChangedListener = this::updateCarouselDimensions
             val lp =
@@ -666,7 +740,7 @@
                 newRecs,
                 shouldPrioritize,
                 systemClock,
-                debugLogger
+                debugLogger,
             )
             updatePlayerToState(newRecs, noAnimation = true)
             reorderAllPlayers(curVisibleMediaKey)
@@ -843,6 +917,13 @@
                 .toFloat()
     }
 
+    /** Update listening to seekbar. */
+    private fun updateSeekbarListening(visibleToUser: Boolean) {
+        for (player in MediaPlayerData.players()) {
+            player.setListening(visibleToUser && currentlyExpanded)
+        }
+    }
+
     /** Update the dimension of this carousel. */
     private fun updateCarouselDimensions() {
         var width = 0
@@ -1219,17 +1300,18 @@
         player: MediaControlPanel,
         shouldPrioritize: Boolean,
         clock: SystemClock,
-        debugLogger: MediaCarouselControllerLogger? = null
+        debugLogger: MediaCarouselControllerLogger? = null,
+        update: Boolean = false
     ) {
         shouldPrioritizeSs = shouldPrioritize
         val removedPlayer = removeMediaPlayer(key)
-        if (removedPlayer != null && removedPlayer != player) {
+        if (!update && removedPlayer != null && removedPlayer != player) {
             debugLogger?.logPotentialMemoryLeak(key)
         }
         val sortKey =
             MediaSortKey(
                 isSsMediaRec = true,
-                EMPTY.copy(isPlaying = false),
+                EMPTY.copy(active = data.isActive, isPlaying = false),
                 key,
                 clock.currentTimeMillis(),
                 isSsReactivated = true
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselControllerLogger.kt
index eed1bd7..35bda15 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselControllerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselControllerLogger.kt
@@ -48,8 +48,16 @@
     fun logMediaRemoved(key: String) =
         buffer.log(TAG, LogLevel.DEBUG, { str1 = key }, { "removing player $str1" })
 
-    fun logRecommendationLoaded(key: String) =
-        buffer.log(TAG, LogLevel.DEBUG, { str1 = key }, { "add recommendation $str1" })
+    fun logRecommendationLoaded(key: String, isActive: Boolean) =
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            {
+                str1 = key
+                bool1 = isActive
+            },
+            { "add recommendation $str1, active $bool1" }
+        )
 
     fun logRecommendationRemoved(key: String, immediately: Boolean) =
         buffer.log(
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt
index 36b2eda..1ace316 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt
@@ -57,6 +57,7 @@
     private val mainExecutor: DelayableExecutor,
     val dismissCallback: () -> Unit,
     private var translationChangedListener: () -> Unit,
+    private var seekBarUpdateListener: (visibleToUser: Boolean) -> Unit,
     private val closeGuts: (immediate: Boolean) -> Unit,
     private val falsingCollector: FalsingCollector,
     private val falsingManager: FalsingManager,
@@ -177,6 +178,12 @@
 
     /** Whether the media card is visible to user if any */
     var visibleToUser: Boolean = false
+        set(value) {
+            if (field != value) {
+                field = value
+                seekBarUpdateListener.invoke(field)
+            }
+        }
 
     /** Whether the quick setting is expanded or not */
     var qsExpanded: Boolean = false
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
index 45d50f0..7677062 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
@@ -56,11 +56,13 @@
 import android.view.animation.Interpolator;
 import android.widget.ImageButton;
 import android.widget.ImageView;
+import android.widget.SeekBar;
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
+import androidx.appcompat.content.res.AppCompatResources;
 import androidx.constraintlayout.widget.ConstraintSet;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -114,8 +116,6 @@
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.time.SystemClock;
 
-import dagger.Lazy;
-
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.List;
@@ -123,6 +123,7 @@
 
 import javax.inject.Inject;
 
+import dagger.Lazy;
 import kotlin.Triple;
 import kotlin.Unit;
 
@@ -345,6 +346,11 @@
         mSeekBarViewModel.setListening(listening);
     }
 
+    @VisibleForTesting
+    public boolean getListening() {
+        return mSeekBarViewModel.getListening();
+    }
+
     /** Sets whether the user is touching the seek bar to change the track position. */
     private void setIsScrubbing(boolean isScrubbing) {
         if (mMediaData == null || mMediaData.getSemanticActions() == null) {
@@ -522,8 +528,13 @@
         }
 
         // Seek Bar
-        final MediaController controller = getController();
-        mBackgroundExecutor.execute(() -> mSeekBarViewModel.updateController(controller));
+        if (data.getResumption() && data.getResumeProgress() != null) {
+            double progress = data.getResumeProgress();
+            mSeekBarViewModel.updateStaticProgress(progress);
+        } else {
+            final MediaController controller = getController();
+            mBackgroundExecutor.execute(() -> mSeekBarViewModel.updateController(controller));
+        }
 
         // Show the broadcast dialog button only when the le audio is enabled.
         mShowBroadcastDialogButton =
@@ -728,9 +739,14 @@
             contentDescription =
                     mRecommendationViewHolder.getGutsViewHolder().getGutsText().getText();
         } else if (data != null) {
-            contentDescription = mContext.getString(
-                    R.string.controls_media_smartspace_rec_description,
-                    data.getAppName(mContext));
+            if (mFeatureFlags.isEnabled(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE)) {
+                contentDescription = mContext.getString(
+                        R.string.controls_media_smartspace_rec_header);
+            } else {
+                contentDescription = mContext.getString(
+                        R.string.controls_media_smartspace_rec_description,
+                        data.getAppName(mContext));
+            }
         } else {
             contentDescription = null;
         }
@@ -752,43 +768,16 @@
         int width = mMediaViewHolder.getAlbumView().getMeasuredWidth();
         int height = mMediaViewHolder.getAlbumView().getMeasuredHeight();
 
-        // WallpaperColors.fromBitmap takes a good amount of time. We do that work
-        // on the background executor to avoid stalling animations on the UI Thread.
         mBackgroundExecutor.execute(() -> {
             // Album art
             ColorScheme mutableColorScheme = null;
             Drawable artwork;
             boolean isArtworkBound;
             Icon artworkIcon = data.getArtwork();
-            WallpaperColors wallpaperColors = null;
-            if (artworkIcon != null) {
-                if (artworkIcon.getType() == Icon.TYPE_BITMAP
-                        || artworkIcon.getType() == Icon.TYPE_ADAPTIVE_BITMAP) {
-                    // Avoids extra processing if this is already a valid bitmap
-                    wallpaperColors = WallpaperColors
-                            .fromBitmap(artworkIcon.getBitmap());
-                } else {
-                    Drawable artworkDrawable = artworkIcon.loadDrawable(mContext);
-                    if (artworkDrawable != null) {
-                        wallpaperColors = WallpaperColors
-                                .fromDrawable(artworkIcon.loadDrawable(mContext));
-                    }
-                }
-            }
+            WallpaperColors wallpaperColors = getWallpaperColor(artworkIcon);
             if (wallpaperColors != null) {
                 mutableColorScheme = new ColorScheme(wallpaperColors, true, Style.CONTENT);
-                Drawable albumArt = getScaledBackground(artworkIcon, width, height);
-                GradientDrawable gradient = (GradientDrawable) mContext
-                        .getDrawable(R.drawable.qs_media_scrim);
-                gradient.setColors(new int[] {
-                        ColorUtilKt.getColorWithAlpha(
-                                MediaColorSchemesKt.backgroundStartFromScheme(mutableColorScheme),
-                                0.25f),
-                        ColorUtilKt.getColorWithAlpha(
-                                MediaColorSchemesKt.backgroundEndFromScheme(mutableColorScheme),
-                                0.9f),
-                });
-                artwork = new LayerDrawable(new Drawable[] { albumArt, gradient });
+                artwork = addGradientToIcon(artworkIcon, mutableColorScheme, width, height);
                 isArtworkBound = true;
             } else {
                 // If there's no artwork, use colors from the app icon
@@ -867,6 +856,96 @@
         });
     }
 
+    private void bindRecommendationArtwork(
+            SmartspaceAction recommendation,
+            String packageName,
+            int itemIndex
+    ) {
+        final int traceCookie = recommendation.hashCode();
+        final String traceName =
+                "MediaControlPanel#bindRecommendationArtwork<" + packageName + ">";
+        Trace.beginAsyncSection(traceName, traceCookie);
+
+        // Capture width & height from views in foreground for artwork scaling in background
+        int width = mRecommendationViewHolder.getMediaCoverContainers().get(0).getMeasuredWidth();
+        int height = mRecommendationViewHolder.getMediaCoverContainers().get(0).getMeasuredHeight();
+
+        mBackgroundExecutor.execute(() -> {
+            // Album art
+            ColorScheme mutableColorScheme = null;
+            Drawable artwork;
+            Icon artworkIcon = recommendation.getIcon();
+            WallpaperColors wallpaperColors = getWallpaperColor(artworkIcon);
+            if (wallpaperColors != null) {
+                mutableColorScheme = new ColorScheme(wallpaperColors, true, Style.CONTENT);
+                artwork = addGradientToIcon(artworkIcon, mutableColorScheme, width, height);
+            } else {
+                artwork = new ColorDrawable(Color.TRANSPARENT);
+            }
+
+            mMainExecutor.execute(() -> {
+                // Bind the artwork drawable to media cover.
+                ImageView mediaCover =
+                        mRecommendationViewHolder.getMediaCoverItems().get(itemIndex);
+                mediaCover.setImageDrawable(artwork);
+
+                // Set up the app icon.
+                ImageView appIconView = mRecommendationViewHolder.getMediaAppIcons().get(itemIndex);
+                appIconView.clearColorFilter();
+                try {
+                    Drawable icon = mContext.getPackageManager()
+                            .getApplicationIcon(packageName);
+                    appIconView.setImageDrawable(icon);
+                } catch (PackageManager.NameNotFoundException e) {
+                    Log.w(TAG, "Cannot find icon for package " + packageName, e);
+                    appIconView.setImageResource(R.drawable.ic_music_note);
+                }
+                Trace.endAsyncSection(traceName, traceCookie);
+            });
+        });
+    }
+
+    // This method should be called from a background thread. WallpaperColors.fromBitmap takes a
+    // good amount of time. We do that work on the background executor to avoid stalling animations
+    // on the UI Thread.
+    private WallpaperColors getWallpaperColor(Icon artworkIcon) {
+        if (artworkIcon != null) {
+            if (artworkIcon.getType() == Icon.TYPE_BITMAP
+                    || artworkIcon.getType() == Icon.TYPE_ADAPTIVE_BITMAP) {
+                // Avoids extra processing if this is already a valid bitmap
+                return WallpaperColors
+                        .fromBitmap(artworkIcon.getBitmap());
+            } else {
+                Drawable artworkDrawable = artworkIcon.loadDrawable(mContext);
+                if (artworkDrawable != null) {
+                    return WallpaperColors
+                            .fromDrawable(artworkIcon.loadDrawable(mContext));
+                }
+            }
+        }
+        return null;
+    }
+
+    private LayerDrawable addGradientToIcon(
+            Icon artworkIcon,
+            ColorScheme mutableColorScheme,
+            int width,
+            int height
+    ) {
+        Drawable albumArt = getScaledBackground(artworkIcon, width, height);
+        GradientDrawable gradient = (GradientDrawable) AppCompatResources
+                .getDrawable(mContext, R.drawable.qs_media_scrim);
+        gradient.setColors(new int[] {
+                ColorUtilKt.getColorWithAlpha(
+                        MediaColorSchemesKt.backgroundStartFromScheme(mutableColorScheme),
+                        0.25f),
+                ColorUtilKt.getColorWithAlpha(
+                        MediaColorSchemesKt.backgroundEndFromScheme(mutableColorScheme),
+                        0.9f),
+        });
+        return new LayerDrawable(new Drawable[] { albumArt, gradient });
+    }
+
     private void scaleTransitionDrawableLayer(TransitionDrawable transitionDrawable, int layer,
             int targetWidth, int targetHeight) {
         Drawable drawable = transitionDrawable.getDrawable(layer);
@@ -1062,8 +1141,10 @@
                         /* pixelDensity= */ getContext().getResources().getDisplayMetrics().density,
                         mColorSchemeTransition.getAccentPrimary().getCurrentColor(),
                         /* opacity= */ 100,
-                        /* shouldFillRipple= */ false,
                         /* sparkleStrength= */ 0f,
+                        /* baseRingFadeParams= */ null,
+                        /* sparkleRingFadeParams= */ null,
+                        /* centerFillFadeParams= */ null,
                         /* shouldDistort= */ false
                 )
         );
@@ -1224,8 +1305,10 @@
         PackageManager packageManager = mContext.getPackageManager();
         // Set up media source app's logo.
         Drawable icon = packageManager.getApplicationIcon(applicationInfo);
-        ImageView headerLogoImageView = mRecommendationViewHolder.getCardIcon();
-        headerLogoImageView.setImageDrawable(icon);
+        if (!mFeatureFlags.isEnabled(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE)) {
+            ImageView headerLogoImageView = mRecommendationViewHolder.getCardIcon();
+            headerLogoImageView.setImageDrawable(icon);
+        }
         fetchAndUpdateRecommendationColors(icon);
 
         // Set up media rec card's tap action if applicable.
@@ -1245,7 +1328,15 @@
 
             // Set up media item cover.
             ImageView mediaCoverImageView = mediaCoverItems.get(itemIndex);
-            mediaCoverImageView.setImageIcon(recommendation.getIcon());
+            if (mFeatureFlags.isEnabled(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE)) {
+                bindRecommendationArtwork(
+                        recommendation,
+                        data.getPackageName(),
+                        itemIndex
+                );
+            } else {
+                mediaCoverImageView.setImageIcon(recommendation.getIcon());
+            }
 
             // Set up the media item's click listener if applicable.
             ViewGroup mediaCoverContainer = mediaCoverContainers.get(itemIndex);
@@ -1275,7 +1366,6 @@
                                 recommendation.getTitle(), artistName, appName));
             }
 
-
             // Set up title
             CharSequence title = recommendation.getTitle();
             hasTitle |= !TextUtils.isEmpty(title);
@@ -1289,6 +1379,24 @@
             hasSubtitle |= !TextUtils.isEmpty(subtitle);
             TextView subtitleView = mRecommendationViewHolder.getMediaSubtitles().get(itemIndex);
             subtitleView.setText(subtitle);
+
+            // Set up progress bar
+            if (mFeatureFlags.isEnabled(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE)) {
+                SeekBar mediaProgressBar =
+                        mRecommendationViewHolder.getMediaProgressBars().get(itemIndex);
+                TextView mediaSubtitle =
+                        mRecommendationViewHolder.getMediaSubtitles().get(itemIndex);
+                // show progress bar if the recommended album is played.
+                Double progress = MediaDataUtils.getDescriptionProgress(recommendation.getExtras());
+                if (progress == null || progress <= 0.0) {
+                    mediaProgressBar.setVisibility(View.GONE);
+                    mediaSubtitle.setVisibility(View.VISIBLE);
+                } else {
+                    mediaProgressBar.setProgress((int) (progress * 100));
+                    mediaProgressBar.setVisibility(View.VISIBLE);
+                    mediaSubtitle.setVisibility(View.GONE);
+                }
+            }
         }
         mSmartspaceMediaItemsCount = NUM_REQUIRED_RECOMMENDATIONS;
 
@@ -1353,12 +1461,22 @@
         int textPrimaryColor = MediaColorSchemesKt.textPrimaryFromScheme(colorScheme);
         int textSecondaryColor = MediaColorSchemesKt.textSecondaryFromScheme(colorScheme);
 
+        if (mFeatureFlags.isEnabled(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE)) {
+            mRecommendationViewHolder.getCardTitle().setTextColor(textPrimaryColor);
+        }
+
         mRecommendationViewHolder.getRecommendations()
                 .setBackgroundTintList(ColorStateList.valueOf(backgroundColor));
         mRecommendationViewHolder.getMediaTitles().forEach(
                 (title) -> title.setTextColor(textPrimaryColor));
         mRecommendationViewHolder.getMediaSubtitles().forEach(
                 (subtitle) -> subtitle.setTextColor(textSecondaryColor));
+        if (mFeatureFlags.isEnabled(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE)) {
+            mRecommendationViewHolder.getMediaProgressBars().forEach(
+                    (progressBar) -> progressBar.setProgressTintList(
+                            ColorStateList.valueOf(textPrimaryColor))
+            );
+        }
 
         mRecommendationViewHolder.getGutsViewHolder().setColors(colorScheme);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
index 2ec7be6..b9b0459 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.media.controls.models.player.MediaViewHolder
 import com.android.systemui.media.controls.models.recommendation.RecommendationViewHolder
 import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.calculateAlpha
+import com.android.systemui.media.controls.util.MediaFlags
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.animation.MeasurementOutput
 import com.android.systemui.util.animation.TransitionLayout
@@ -45,7 +46,8 @@
     private val context: Context,
     private val configurationController: ConfigurationController,
     private val mediaHostStatesManager: MediaHostStatesManager,
-    private val logger: MediaViewLogger
+    private val logger: MediaViewLogger,
+    private val mediaFlags: MediaFlags,
 ) {
 
     /**
@@ -309,16 +311,15 @@
         }
 
         // media player
-        val controlsTop =
-            calculateWidgetGroupAlphaForSquishiness(
-                controlIds,
-                squishedViewState.measureHeight.toFloat(),
-                squishedViewState,
-                squishFraction
-            )
+        calculateWidgetGroupAlphaForSquishiness(
+            controlIds,
+            squishedViewState.measureHeight.toFloat(),
+            squishedViewState,
+            squishFraction
+        )
         calculateWidgetGroupAlphaForSquishiness(
             detailIds,
-            controlsTop,
+            squishedViewState.measureHeight.toFloat(),
             squishedViewState,
             squishFraction
         )
@@ -646,8 +647,13 @@
                 expandedLayout.load(context, R.xml.media_session_expanded)
             }
             TYPE.RECOMMENDATION -> {
-                collapsedLayout.load(context, R.xml.media_recommendation_collapsed)
-                expandedLayout.load(context, R.xml.media_recommendation_expanded)
+                if (mediaFlags.isRecommendationCardUpdateEnabled()) {
+                    collapsedLayout.load(context, R.xml.media_recommendations_view_collapsed)
+                    expandedLayout.load(context, R.xml.media_recommendations_view_expanded)
+                } else {
+                    collapsedLayout.load(context, R.xml.media_recommendation_collapsed)
+                    expandedLayout.load(context, R.xml.media_recommendation_expanded)
+                }
             }
         }
         refreshState()
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaDataUtils.java b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaDataUtils.java
index bcfceaa..e95106e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaDataUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaDataUtils.java
@@ -16,11 +16,16 @@
 
 package com.android.systemui.media.controls.util;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.os.Bundle;
 import android.text.TextUtils;
 
+import androidx.core.math.MathUtils;
+import androidx.media.utils.MediaConstants;
+
 /**
  * Utility class with common methods for media controls
  */
@@ -50,4 +55,36 @@
                         : unknownName);
         return applicationName;
     }
+
+    /**
+     * Check the bundle for extras indicating the progress percentage
+     *
+     * @param extras
+     * @return the progress value between 0-1 inclusive if prsent, otherwise null
+     */
+    public static Double getDescriptionProgress(@Nullable Bundle extras) {
+        if (extras == null
+                || !extras.containsKey(MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS)) {
+            return null;
+        }
+
+        int status = extras.getInt(MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS);
+        switch (status) {
+            case MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_NOT_PLAYED:
+                return 0.0;
+            case MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_FULLY_PLAYED:
+                return 1.0;
+            case MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED: {
+                if (extras
+                        .containsKey(MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE)) {
+                    double percent = extras
+                            .getDouble(MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE);
+                    return MathUtils.clamp(percent, 0.0, 1.0);
+                } else {
+                    return 0.5;
+                }
+            }
+        }
+        return null;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
index ab03930..c3fa76e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
@@ -51,4 +51,14 @@
      * whether the underlying notification was dismissed
      */
     fun isRetainingPlayersEnabled() = featureFlags.isEnabled(Flags.MEDIA_RETAIN_SESSIONS)
+
+    /** Check whether we show the updated recommendation card. */
+    fun isRecommendationCardUpdateEnabled() =
+        featureFlags.isEnabled(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE)
+
+    /** Check whether to get progress information for resume players */
+    fun isResumeProgressEnabled() = featureFlags.isEnabled(Flags.MEDIA_RESUME_PROGRESS)
+
+    /** If true, do not automatically dismiss the recommendation card */
+    fun isPersistentSsCardEnabled() = featureFlags.isEnabled(Flags.MEDIA_RETAIN_RECOMMENDATIONS)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index bb833df..9ae4577 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -17,8 +17,7 @@
 package com.android.systemui.media.dagger;
 
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.log.dagger.MediaTttReceiverLogBuffer;
-import com.android.systemui.log.dagger.MediaTttSenderLogBuffer;
+import com.android.systemui.log.LogBufferFactory;
 import com.android.systemui.media.controls.pipeline.MediaDataManager;
 import com.android.systemui.media.controls.ui.MediaHierarchyManager;
 import com.android.systemui.media.controls.ui.MediaHost;
@@ -29,12 +28,9 @@
 import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
 import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
 import com.android.systemui.media.taptotransfer.MediaTttFlags;
-import com.android.systemui.media.taptotransfer.common.MediaTttLogger;
-import com.android.systemui.media.taptotransfer.receiver.ChipReceiverInfo;
-import com.android.systemui.media.taptotransfer.receiver.MediaTttReceiverLogger;
-import com.android.systemui.media.taptotransfer.sender.MediaTttSenderLogger;
+import com.android.systemui.media.taptotransfer.receiver.MediaTttReceiverLogBuffer;
+import com.android.systemui.media.taptotransfer.sender.MediaTttSenderLogBuffer;
 import com.android.systemui.plugins.log.LogBuffer;
-import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo;
 
 import java.util.Optional;
 
@@ -94,22 +90,22 @@
         return new MediaHost(stateHolder, hierarchyManager, dataManager, statesManager);
     }
 
+    /** Provides a logging buffer related to the media tap-to-transfer chip on the sender device. */
     @Provides
     @SysUISingleton
-    @MediaTttSenderLogger
-    static MediaTttLogger<ChipbarInfo> providesMediaTttSenderLogger(
-            @MediaTttSenderLogBuffer LogBuffer buffer
-    ) {
-        return new MediaTttLogger<>("Sender", buffer);
+    @MediaTttSenderLogBuffer
+    static LogBuffer provideMediaTttSenderLogBuffer(LogBufferFactory factory) {
+        return factory.create("MediaTttSender", 30);
     }
 
+    /**
+     * Provides a logging buffer related to the media tap-to-transfer chip on the receiver device.
+     */
     @Provides
     @SysUISingleton
-    @MediaTttReceiverLogger
-    static MediaTttLogger<ChipReceiverInfo> providesMediaTttReceiverLogger(
-            @MediaTttReceiverLogBuffer LogBuffer buffer
-    ) {
-        return new MediaTttLogger<>("Receiver", buffer);
+    @MediaTttReceiverLogBuffer
+    static LogBuffer provideMediaTttReceiverLogBuffer(LogBufferFactory factory) {
+        return factory.create("MediaTttReceiver", 20);
     }
 
     /** */
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 51b5a3d..769e0c8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -16,17 +16,21 @@
 
 package com.android.systemui.media.dialog;
 
+import android.content.Context;
 import android.content.res.ColorStateList;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.CheckBox;
 import android.widget.TextView;
 
+import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
 import androidx.core.widget.CompoundButtonCompat;
 import androidx.recyclerview.widget.RecyclerView;
 
@@ -186,6 +190,17 @@
                     mCurrentActivePosition = position;
                     updateFullItemClickListener(v -> onItemClick(v, device));
                     setSingleLineLayout(getItemTitle(device));
+                } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
+                        && mController.isSubStatusSupported() && device.hasDisabledReason()) {
+                    //update to subtext with device status
+                    setUpDeviceIcon(device);
+                    mSubTitleText.setText(
+                            Api34Impl.composeDisabledReason(device.getDisableReason(), mContext));
+                    updateConnectionFailedStatusIcon();
+                    updateFullItemClickListener(null);
+                    setTwoLineLayout(device, false /* bFocused */, false /* showSeekBar */,
+                            false /* showProgressBar */, true /* showSubtitle */,
+                            true /* showStatus */);
                 } else if (device.getState() == MediaDeviceState.STATE_CONNECTING_FAILED) {
                     setUpDeviceIcon(device);
                     updateConnectionFailedStatusIcon();
@@ -389,4 +404,12 @@
             mTitleText.setText(groupDividerTitle);
         }
     }
+
+    @RequiresApi(34)
+    private static class Api34Impl {
+        @DoNotInline
+        static String composeDisabledReason(int reason, Context context) {
+            return "";
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 4e08050..dc75538 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -45,6 +45,7 @@
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
 import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.settingslib.Utils;
@@ -142,11 +143,12 @@
         final TextView mVolumeValueText;
         final ImageView mTitleIcon;
         final ProgressBar mProgressBar;
-        final MediaOutputSeekbar mSeekBar;
         final LinearLayout mTwoLineLayout;
         final ImageView mStatusIcon;
         final CheckBox mCheckBox;
         final ViewGroup mEndTouchArea;
+        @VisibleForTesting
+        MediaOutputSeekbar mSeekBar;
         private String mDeviceId;
         private ValueAnimator mCornerAnimator;
         private ValueAnimator mVolumeAnimator;
@@ -390,6 +392,7 @@
                         mTitleIcon.setVisibility(View.VISIBLE);
                         mVolumeValueText.setVisibility(View.GONE);
                     }
+                    mController.logInteractionAdjustVolume(device);
                     mIsDragging = false;
                 }
             });
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index f95da27..5f5c686 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -141,7 +141,7 @@
     @VisibleForTesting
     LocalMediaManager mLocalMediaManager;
     @VisibleForTesting
-    private MediaOutputMetricLogger mMetricLogger;
+    MediaOutputMetricLogger mMetricLogger;
     private int mCurrentState;
 
     private int mColorItemContent;
@@ -757,6 +757,10 @@
         return mFeatureFlags.isEnabled(Flags.OUTPUT_SWITCHER_ROUTES_PROCESSING);
     }
 
+    public boolean isSubStatusSupported() {
+        return mFeatureFlags.isEnabled(Flags.OUTPUT_SWITCHER_DEVICE_STATUS);
+    }
+
     List<MediaDevice> getGroupMediaDevices() {
         final List<MediaDevice> selectedDevices = getSelectedMediaDevice();
         final List<MediaDevice> selectableDevices = getSelectableMediaDevice();
@@ -866,12 +870,15 @@
     }
 
     void adjustVolume(MediaDevice device, int volume) {
-        mMetricLogger.logInteractionAdjustVolume(device);
         ThreadUtils.postOnBackgroundThread(() -> {
             device.requestSetVolume(volume);
         });
     }
 
+    void logInteractionAdjustVolume(MediaDevice device) {
+        mMetricLogger.logInteractionAdjustVolume(device);
+    }
+
     String getPackageName() {
         return mPackageName;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLogger.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLogger.kt
deleted file mode 100644
index 8aef938..0000000
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLogger.kt
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.media.taptotransfer.common
-
-import com.android.systemui.plugins.log.LogBuffer
-import com.android.systemui.plugins.log.LogLevel
-import com.android.systemui.temporarydisplay.TemporaryViewInfo
-import com.android.systemui.temporarydisplay.TemporaryViewLogger
-
-/**
- * A logger for media tap-to-transfer events.
- *
- * @param deviceTypeTag the type of device triggering the logs -- "Sender" or "Receiver".
- *
- * TODO(b/245610654): We should de-couple the sender and receiver loggers, since they're vastly
- * different experiences.
- */
-class MediaTttLogger<T : TemporaryViewInfo>(
-    deviceTypeTag: String,
-    buffer: LogBuffer
-) : TemporaryViewLogger<T>(buffer, BASE_TAG + deviceTypeTag) {
-    /** Logs a change in the chip state for the given [mediaRouteId]. */
-    fun logStateChange(stateName: String, mediaRouteId: String, packageName: String?) {
-        buffer.log(
-            tag,
-            LogLevel.DEBUG,
-            {
-                str1 = stateName
-                str2 = mediaRouteId
-                str3 = packageName
-            },
-            { "State changed to $str1 for ID=$str2 package=$str3" }
-        )
-    }
-
-    /**
-     * Logs an error in trying to update to [displayState].
-     *
-     * [displayState] is either a [android.app.StatusBarManager.MediaTransferSenderState] or
-     * a [android.app.StatusBarManager.MediaTransferReceiverState].
-     */
-    fun logStateChangeError(displayState: Int) {
-        buffer.log(
-            tag,
-            LogLevel.ERROR,
-            { int1 = displayState },
-            { "Cannot display state=$int1; aborting" }
-        )
-    }
-
-    /**
-     * Logs an invalid sender state transition error in trying to update to [desiredState].
-     *
-     * @param currentState the previous state of the chip.
-     * @param desiredState the new state of the chip.
-     */
-    fun logInvalidStateTransitionError(
-        currentState: String,
-        desiredState: String
-    ) {
-        buffer.log(
-                tag,
-                LogLevel.ERROR,
-                {
-                    str1 = currentState
-                    str2 = desiredState
-                },
-                { "Cannot display state=$str2 after state=$str1; invalid transition" }
-        )
-    }
-
-    /** Logs that we couldn't find information for [packageName]. */
-    fun logPackageNotFound(packageName: String) {
-        buffer.log(
-            tag,
-            LogLevel.DEBUG,
-            { str1 = packageName },
-            { "Package $str1 could not be found" }
-        )
-    }
-
-    /**
-     * Logs that a removal request has been bypassed (ignored).
-     *
-     * @param removalReason the reason that the chip removal was requested.
-     * @param bypassReason the reason that the request was bypassed.
-     */
-    fun logRemovalBypass(removalReason: String, bypassReason: String) {
-        buffer.log(
-            tag,
-            LogLevel.DEBUG,
-            {
-                str1 = removalReason
-                str2 = bypassReason
-            },
-            { "Chip removal requested due to $str1; however, removal was ignored because $str2" })
-    }
-}
-
-private const val BASE_TAG = "MediaTtt"
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtils.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtils.kt
new file mode 100644
index 0000000..0e839c6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtils.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.taptotransfer.common
+
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel
+
+/** A helper for logging media tap-to-transfer events. */
+object MediaTttLoggerUtils {
+    fun logStateChange(
+        buffer: LogBuffer,
+        tag: String,
+        stateName: String,
+        mediaRouteId: String,
+        packageName: String?,
+    ) {
+        buffer.log(
+            tag,
+            LogLevel.DEBUG,
+            {
+                str1 = stateName
+                str2 = mediaRouteId
+                str3 = packageName
+            },
+            { "State changed to $str1 for ID=$str2 package=$str3" }
+        )
+    }
+
+    fun logStateChangeError(buffer: LogBuffer, tag: String, displayState: Int) {
+        buffer.log(
+            tag,
+            LogLevel.ERROR,
+            { int1 = displayState },
+            { "Cannot display state=$int1; aborting" }
+        )
+    }
+
+    fun logPackageNotFound(buffer: LogBuffer, tag: String, packageName: String) {
+        buffer.log(
+            tag,
+            LogLevel.DEBUG,
+            { str1 = packageName },
+            { "Package $str1 could not be found" }
+        )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
index 066c185..720c44a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
@@ -25,7 +25,6 @@
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.shared.model.TintedIcon
-import com.android.systemui.temporarydisplay.TemporaryViewInfo
 
 /** Utility methods for media tap-to-transfer. */
 class MediaTttUtils {
@@ -43,26 +42,39 @@
          * default name and icon if we can't find the app name/icon.
          *
          * @param appPackageName the package name of the app playing the media.
-         * @param logger the logger to use for any errors.
+         * @param onPackageNotFoundException a function run if a
+         * [PackageManager.NameNotFoundException] occurs.
+         * @param isReceiver indicates whether the icon is displayed in a receiver view.
          */
         fun getIconInfoFromPackageName(
             context: Context,
             appPackageName: String?,
-            logger: MediaTttLogger<out TemporaryViewInfo>
+            isReceiver: Boolean,
+            onPackageNotFoundException: () -> Unit,
         ): IconInfo {
             if (appPackageName != null) {
                 val packageManager = context.packageManager
                 try {
+                    val appName =
+                        packageManager
+                            .getApplicationInfo(
+                                appPackageName,
+                                PackageManager.ApplicationInfoFlags.of(0),
+                            )
+                            .loadLabel(packageManager)
+                            .toString()
                     val contentDescription =
-                        ContentDescription.Loaded(
-                            packageManager
-                                .getApplicationInfo(
-                                    appPackageName,
-                                    PackageManager.ApplicationInfoFlags.of(0)
+                        if (isReceiver) {
+                            ContentDescription.Loaded(
+                                context.getString(
+                                    R.string
+                                        .media_transfer_receiver_content_description_with_app_name,
+                                    appName
                                 )
-                                .loadLabel(packageManager)
-                                .toString()
-                        )
+                            )
+                        } else {
+                            ContentDescription.Loaded(appName)
+                        }
                     return IconInfo(
                         contentDescription,
                         MediaTttIcon.Loaded(packageManager.getApplicationIcon(appPackageName)),
@@ -70,11 +82,19 @@
                         isAppIcon = true
                     )
                 } catch (e: PackageManager.NameNotFoundException) {
-                    logger.logPackageNotFound(appPackageName)
+                    onPackageNotFoundException.invoke()
                 }
             }
             return IconInfo(
-                ContentDescription.Resource(R.string.media_output_dialog_unknown_launch_app_name),
+                if (isReceiver) {
+                    ContentDescription.Resource(
+                        R.string.media_transfer_receiver_content_description_unknown_app
+                    )
+                } else {
+                    ContentDescription.Resource(
+                        R.string.media_output_dialog_unknown_launch_app_name
+                    )
+                },
                 MediaTttIcon.Resource(R.drawable.ic_cast),
                 tintAttr = android.R.attr.textColorPrimary,
                 isAppIcon = false
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 60dd5da..fab8c06 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -16,7 +16,9 @@
 
 package com.android.systemui.media.taptotransfer.receiver
 
+import android.animation.TimeInterpolator
 import android.annotation.SuppressLint
+import android.animation.ValueAnimator
 import android.app.StatusBarManager
 import android.content.Context
 import android.graphics.Rect
@@ -31,8 +33,10 @@
 import android.view.WindowManager
 import android.view.accessibility.AccessibilityManager
 import android.view.View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE
+import android.view.View.ACCESSIBILITY_LIVE_REGION_NONE
 import com.android.internal.widget.CachingIconView
 import com.android.systemui.R
+import com.android.systemui.animation.Interpolators
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.ui.binder.TintedIconViewBinder
 import com.android.systemui.dagger.SysUISingleton
@@ -40,7 +44,6 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.media.taptotransfer.MediaTttFlags
 import com.android.systemui.media.taptotransfer.common.MediaTttIcon
-import com.android.systemui.media.taptotransfer.common.MediaTttLogger
 import com.android.systemui.media.taptotransfer.common.MediaTttUtils
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.policy.ConfigurationController
@@ -65,7 +68,7 @@
 open class MediaTttChipControllerReceiver @Inject constructor(
         private val commandQueue: CommandQueue,
         context: Context,
-        @MediaTttReceiverLogger logger: MediaTttLogger<ChipReceiverInfo>,
+        logger: MediaTttReceiverLogger,
         windowManager: WindowManager,
         mainExecutor: DelayableExecutor,
         accessibilityManager: AccessibilityManager,
@@ -79,7 +82,7 @@
         wakeLockBuilder: WakeLock.Builder,
         systemClock: SystemClock,
         private val rippleController: MediaTttReceiverRippleController,
-) : TemporaryViewDisplayController<ChipReceiverInfo, MediaTttLogger<ChipReceiverInfo>>(
+) : TemporaryViewDisplayController<ChipReceiverInfo, MediaTttReceiverLogger>(
         context,
         logger,
         windowManager,
@@ -102,6 +105,13 @@
         fitInsetsTypes = 0 // Ignore insets from all system bars
     }
 
+    // Value animator that controls the bouncing animation of views.
+    private val bounceAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
+        repeatCount = ValueAnimator.INFINITE
+        repeatMode = ValueAnimator.REVERSE
+        duration = ICON_BOUNCE_ANIM_DURATION
+    }
+
     private val commandQueueCallbacks = object : CommandQueue.Callbacks {
         override fun updateMediaTapToTransferReceiverDisplay(
             @StatusBarManager.MediaTransferReceiverState displayState: Int,
@@ -173,9 +183,14 @@
     }
 
     override fun updateView(newInfo: ChipReceiverInfo, currentView: ViewGroup) {
+        val packageName = newInfo.routeInfo.clientPackageName
         var iconInfo = MediaTttUtils.getIconInfoFromPackageName(
-            context, newInfo.routeInfo.clientPackageName, logger
-        )
+            context,
+            packageName,
+            isReceiver = true,
+        ) {
+            logger.logPackageNotFound(packageName)
+        }
 
         if (newInfo.appNameOverride != null) {
             iconInfo = iconInfo.copy(
@@ -199,44 +214,52 @@
 
         val iconView = currentView.getAppIconView()
         iconView.setPadding(iconPadding, iconPadding, iconPadding, iconPadding)
-        iconView.accessibilityLiveRegion = ACCESSIBILITY_LIVE_REGION_ASSERTIVE
         TintedIconViewBinder.bind(iconInfo.toTintedIcon(), iconView)
+
+        val iconContainerView = currentView.getIconContainerView()
+        iconContainerView.accessibilityLiveRegion = ACCESSIBILITY_LIVE_REGION_ASSERTIVE
     }
 
     override fun animateViewIn(view: ViewGroup) {
-        val appIconView = view.getAppIconView()
+        val iconContainerView = view.getIconContainerView()
         val iconRippleView: ReceiverChipRippleView = view.requireViewById(R.id.icon_glow_ripple)
         val rippleView: ReceiverChipRippleView = view.requireViewById(R.id.ripple)
-        animateViewTranslationAndFade(appIconView, -1 * getTranslationAmount(), 1f)
-        animateViewTranslationAndFade(iconRippleView, -1 * getTranslationAmount(), 1f)
+        val translationYBy = getTranslationAmount()
+        // Make the icon container view starts animation from bottom of the screen.
+        iconContainerView.translationY += rippleController.getReceiverIconSize()
+        animateViewTranslationAndFade(
+            iconContainerView,
+            translationYBy = -1 * translationYBy,
+            alphaEndValue = 1f,
+            Interpolators.EMPHASIZED_DECELERATE,
+        ) {
+            animateBouncingView(iconContainerView, translationYBy * BOUNCE_TRANSLATION_RATIO)
+        }
         rippleController.expandToInProgressState(rippleView, iconRippleView)
     }
 
     override fun animateViewOut(view: ViewGroup, removalReason: String?, onAnimationEnd: Runnable) {
-        val appIconView = view.getAppIconView()
-        val iconRippleView: ReceiverChipRippleView = view.requireViewById(R.id.icon_glow_ripple)
+        val iconContainerView = view.getIconContainerView()
         val rippleView: ReceiverChipRippleView = view.requireViewById(R.id.ripple)
+        val translationYBy = getTranslationAmount()
+
+        // Remove update listeners from bounce animator to prevent any conflict with
+        // translation animation.
+        bounceAnimator.removeAllUpdateListeners()
+        bounceAnimator.cancel()
         if (removalReason == ChipStateReceiver.TRANSFER_TO_RECEIVER_SUCCEEDED.name &&
                 mediaTttFlags.isMediaTttReceiverSuccessRippleEnabled()) {
             rippleController.expandToSuccessState(rippleView, onAnimationEnd)
             animateViewTranslationAndFade(
-                iconRippleView,
-                -1 * getTranslationAmount(),
-                0f,
-                translationDuration = ICON_TRANSLATION_SUCCEEDED_DURATION,
-                alphaDuration = ICON_TRANSLATION_SUCCEEDED_DURATION,
-            )
-            animateViewTranslationAndFade(
-                appIconView,
-                -1 * getTranslationAmount(),
+                iconContainerView,
+                -1 * translationYBy,
                 0f,
                 translationDuration = ICON_TRANSLATION_SUCCEEDED_DURATION,
                 alphaDuration = ICON_TRANSLATION_SUCCEEDED_DURATION,
             )
         } else {
             rippleController.collapseRipple(rippleView, onAnimationEnd)
-            animateViewTranslationAndFade(iconRippleView, getTranslationAmount(), 0f)
-            animateViewTranslationAndFade(appIconView, getTranslationAmount(), 0f)
+            animateViewTranslationAndFade(iconContainerView, translationYBy, 0f)
         }
     }
 
@@ -248,15 +271,19 @@
 
     /** Animation of view translation and fading. */
     private fun animateViewTranslationAndFade(
-        view: View,
+        view: ViewGroup,
         translationYBy: Float,
         alphaEndValue: Float,
+        interpolator: TimeInterpolator? = null,
         translationDuration: Long = ICON_TRANSLATION_ANIM_DURATION,
         alphaDuration: Long = ICON_ALPHA_ANIM_DURATION,
+        onAnimationEnd: Runnable? = null,
     ) {
         view.animate()
             .translationYBy(translationYBy)
+            .setInterpolator(interpolator)
             .setDuration(translationDuration)
+            .withEndAction { onAnimationEnd?.run() }
             .start()
         view.animate()
             .alpha(alphaEndValue)
@@ -266,17 +293,42 @@
 
     /** Returns the amount that the chip will be translated by in its intro animation. */
     private fun getTranslationAmount(): Float {
-        return rippleController.getRippleSize() * 0.5f -
-            rippleController.getReceiverIconSize()
+        return rippleController.getRippleSize() * 0.5f
     }
 
     private fun View.getAppIconView(): CachingIconView {
         return this.requireViewById(R.id.app_icon)
     }
 
+    private fun View.getIconContainerView(): ViewGroup {
+        return this.requireViewById(R.id.icon_container_view)
+    }
+
+    private fun animateBouncingView(iconContainerView: ViewGroup, translationYBy: Float) {
+        if (bounceAnimator.isStarted) {
+            return
+        }
+
+        addViewToBounceAnimation(iconContainerView, translationYBy)
+
+        // In order not to announce description every time the view animate.
+        iconContainerView.accessibilityLiveRegion = ACCESSIBILITY_LIVE_REGION_NONE
+        bounceAnimator.start()
+    }
+
+    private fun addViewToBounceAnimation(view: View, translationYBy: Float) {
+        val prevTranslationY = view.translationY
+        bounceAnimator.addUpdateListener { updateListener ->
+            val progress = updateListener.animatedValue as Float
+            view.translationY = prevTranslationY + translationYBy * progress
+        }
+    }
+
     companion object {
         private const val ICON_TRANSLATION_ANIM_DURATION = 500L
+        private const val ICON_BOUNCE_ANIM_DURATION = 750L
         private const val ICON_TRANSLATION_SUCCEEDED_DURATION = 167L
+        private const val BOUNCE_TRANSLATION_RATIO = 0.15f
         private val ICON_ALPHA_ANIM_DURATION = 5.frames
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttReceiverLogBuffer.java b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogBuffer.java
similarity index 85%
rename from packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttReceiverLogBuffer.java
rename to packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogBuffer.java
index 1570d43..67e464c 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttReceiverLogBuffer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogBuffer.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.log.dagger;
+package com.android.systemui.media.taptotransfer.receiver;
 
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
@@ -26,8 +26,7 @@
 import javax.inject.Qualifier;
 
 /**
- * A {@link LogBuffer} for
- * {@link com.android.systemui.media.taptotransfer.receiver.MediaTttReceiverLogger}.
+ * A {@link LogBuffer} for receiver logs.
  */
 @Qualifier
 @Documented
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt
index 54fc48d..b0c6257 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt
@@ -13,14 +13,44 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.systemui.media.taptotransfer.receiver
 
-import java.lang.annotation.Documented
-import java.lang.annotation.Retention
-import java.lang.annotation.RetentionPolicy
-import javax.inject.Qualifier
+import android.app.StatusBarManager
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.media.taptotransfer.common.MediaTttLoggerUtils
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.temporarydisplay.TemporaryViewLogger
+import javax.inject.Inject
 
-@Qualifier
-@Documented
-@Retention(RetentionPolicy.RUNTIME)
-annotation class MediaTttReceiverLogger
+/** A logger for all events related to the media tap-to-transfer receiver experience. */
+@SysUISingleton
+class MediaTttReceiverLogger
+@Inject
+constructor(
+    @MediaTttReceiverLogBuffer buffer: LogBuffer,
+) : TemporaryViewLogger<ChipReceiverInfo>(buffer, TAG) {
+
+    /** Logs a change in the chip state for the given [mediaRouteId]. */
+    fun logStateChange(
+        stateName: String,
+        mediaRouteId: String,
+        packageName: String?,
+    ) {
+        MediaTttLoggerUtils.logStateChange(buffer, TAG, stateName, mediaRouteId, packageName)
+    }
+
+    /** Logs an error in trying to update to [displayState]. */
+    fun logStateChangeError(@StatusBarManager.MediaTransferReceiverState displayState: Int) {
+        MediaTttLoggerUtils.logStateChangeError(buffer, TAG, displayState)
+    }
+
+    /** Logs that we couldn't find information for [packageName]. */
+    fun logPackageNotFound(packageName: String) {
+        MediaTttLoggerUtils.logPackageNotFound(buffer, TAG, packageName)
+    }
+
+    companion object {
+        private const val TAG = "MediaTttReceiver"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
index f8785fc..4ff082a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
@@ -34,7 +34,7 @@
 
     init {
         setupShader(RippleShader.RippleShape.CIRCLE)
-        setRippleFill(true)
+        setupRippleFadeParams()
         setSparkleStrength(0f)
         isStarted = false
     }
@@ -72,16 +72,16 @@
         animator.removeAllUpdateListeners()
 
         // Only show the outline as ripple expands and disappears when animation ends.
-        setRippleFill(false)
+        removeRippleFill()
 
         val startingPercentage = calculateStartingPercentage(newHeight)
         animator.duration = EXPAND_TO_FULL_DURATION
         animator.addUpdateListener { updateListener ->
             val now = updateListener.currentPlayTime
             val progress = updateListener.animatedValue as Float
-            rippleShader.progress = startingPercentage + (progress * (1 - startingPercentage))
-            rippleShader.distortionStrength = 1 - rippleShader.progress
-            rippleShader.pixelDensity = 1 - rippleShader.progress
+            rippleShader.rawProgress = startingPercentage + (progress * (1 - startingPercentage))
+            rippleShader.distortionStrength = 1 - rippleShader.rawProgress
+            rippleShader.pixelDensity = 1 - rippleShader.rawProgress
             rippleShader.time = now.toFloat()
             invalidate()
         }
@@ -103,6 +103,38 @@
         return 1 - remainingPercentage
     }
 
+    private fun setupRippleFadeParams() {
+        with(rippleShader) {
+            // No fade out for the base ring.
+            baseRingFadeParams.fadeOutStart = 1f
+            baseRingFadeParams.fadeOutEnd = 1f
+
+            // No fade in and outs for the center fill, as we always draw it.
+            centerFillFadeParams.fadeInStart = 0f
+            centerFillFadeParams.fadeInEnd = 0f
+            centerFillFadeParams.fadeOutStart = 1f
+            centerFillFadeParams.fadeOutEnd = 1f
+        }
+    }
+
+    private fun removeRippleFill() {
+        with(rippleShader) {
+            // Set back to default because we modified them in [setupRippleFadeParams].
+            baseRingFadeParams.fadeOutStart = RippleShader.DEFAULT_BASE_RING_FADE_OUT_START
+            baseRingFadeParams.fadeOutEnd = RippleShader.DEFAULT_FADE_OUT_END
+
+            centerFillFadeParams.fadeInStart = RippleShader.DEFAULT_FADE_IN_START
+            centerFillFadeParams.fadeInEnd = RippleShader.DEFAULT_CENTER_FILL_FADE_IN_END
+
+            // To avoid a seam showing up, we should match either:
+            // 1. baseRingFadeParams#fadeInEnd and centerFillFadeParams#fadeOutStart
+            // 2. baseRingFadeParams#fadeOutStart and centerFillFadeOutStart
+            // Here we go with 1 to fade in the centerFill faster.
+            centerFillFadeParams.fadeOutStart = baseRingFadeParams.fadeInEnd
+            centerFillFadeParams.fadeOutEnd = RippleShader.DEFAULT_FADE_OUT_END
+        }
+    }
+
     companion object {
         const val DEFAULT_DURATION = 333L
         const val EXPAND_TO_FULL_DURATION = 1000L
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
index 1f27582..537dbb9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
@@ -136,9 +136,9 @@
         ),
     ) {
         override fun isValidNextState(nextState: ChipStateSender): Boolean {
-            return nextState == FAR_FROM_RECEIVER ||
-                    nextState == ALMOST_CLOSE_TO_START_CAST ||
-                    nextState == TRANSFER_TO_THIS_DEVICE_TRIGGERED
+            // Since _SUCCEEDED is the end of a transfer sequence, we should be able to move to any
+            // state that represents the beginning of a new sequence.
+            return stateIsStartOfSequence(nextState)
         }
     },
 
@@ -158,9 +158,9 @@
         ),
     ) {
         override fun isValidNextState(nextState: ChipStateSender): Boolean {
-            return nextState == FAR_FROM_RECEIVER ||
-                    nextState == ALMOST_CLOSE_TO_END_CAST ||
-                    nextState == TRANSFER_TO_RECEIVER_TRIGGERED
+            // Since _SUCCEEDED is the end of a transfer sequence, we should be able to move to any
+            // state that represents the beginning of a new sequence.
+            return stateIsStartOfSequence(nextState)
         }
     },
 
@@ -173,9 +173,9 @@
         endItem = SenderEndItem.Error,
     ) {
         override fun isValidNextState(nextState: ChipStateSender): Boolean {
-            return nextState == FAR_FROM_RECEIVER ||
-                    nextState == ALMOST_CLOSE_TO_START_CAST ||
-                    nextState == TRANSFER_TO_THIS_DEVICE_TRIGGERED
+            // Since _FAILED is the end of a transfer sequence, we should be able to move to any
+            // state that represents the beginning of a new sequence.
+            return stateIsStartOfSequence(nextState)
         }
     },
 
@@ -188,9 +188,9 @@
         endItem = SenderEndItem.Error,
     ) {
         override fun isValidNextState(nextState: ChipStateSender): Boolean {
-            return nextState == FAR_FROM_RECEIVER ||
-                    nextState == ALMOST_CLOSE_TO_END_CAST ||
-                    nextState == TRANSFER_TO_RECEIVER_TRIGGERED
+            // Since _FAILED is the end of a transfer sequence, we should be able to move to any
+            // state that represents the beginning of a new sequence.
+            return stateIsStartOfSequence(nextState)
         }
     },
 
@@ -210,9 +210,9 @@
         }
 
         override fun isValidNextState(nextState: ChipStateSender): Boolean {
-            return nextState == FAR_FROM_RECEIVER ||
-                    nextState.transferStatus == TransferStatus.NOT_STARTED ||
-                    nextState.transferStatus == TransferStatus.IN_PROGRESS
+            // When far away, we can go to any state that represents the start of a transfer
+            // sequence.
+            return stateIsStartOfSequence(nextState)
         }
     };
 
@@ -227,6 +227,20 @@
         return Text.Loaded(context.getString(stringResId!!, otherDeviceName))
     }
 
+    /**
+     * Returns true if moving from this state to [nextState] is a valid transition.
+     *
+     * In general, we expect a media transfer go to through a sequence of states:
+     * For push-to-receiver:
+     *   - ALMOST_CLOSE_TO_START_CAST => TRANSFER_TO_RECEIVER_TRIGGERED =>
+     *     TRANSFER_TO_RECEIVER_(SUCCEEDED|FAILED)
+     *   - ALMOST_CLOSE_TO_END_CAST => TRANSFER_TO_THIS_DEVICE_TRIGGERED =>
+     *     TRANSFER_TO_THIS_DEVICE_(SUCCEEDED|FAILED)
+     *
+     * This method should validate that the states go through approximately that sequence.
+     *
+     * See b/221265848 for more details.
+     */
     abstract fun isValidNextState(nextState: ChipStateSender): Boolean
 
     companion object {
@@ -276,6 +290,18 @@
 
             return currentState.isValidNextState(desiredState)
         }
+
+        /**
+         * Returns true if [state] represents a state at the beginning of a sequence and false
+         * otherwise.
+         */
+        private fun stateIsStartOfSequence(state: ChipStateSender): Boolean {
+            return state == FAR_FROM_RECEIVER ||
+                state.transferStatus == TransferStatus.NOT_STARTED ||
+                // It's possible to skip the NOT_STARTED phase and go immediately into the
+                // IN_PROGRESS phase.
+                state.transferStatus == TransferStatus.IN_PROGRESS
+        }
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
index 902a10a0..6bb6906 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
@@ -23,11 +23,12 @@
 import com.android.internal.logging.UiEventLogger
 import com.android.internal.statusbar.IUndoMediaTransferCallback
 import com.android.systemui.CoreStartable
+import com.android.systemui.Dumpable
 import com.android.systemui.R
 import com.android.systemui.common.shared.model.Text
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
 import com.android.systemui.media.taptotransfer.MediaTttFlags
-import com.android.systemui.media.taptotransfer.common.MediaTttLogger
 import com.android.systemui.media.taptotransfer.common.MediaTttUtils
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
@@ -35,6 +36,7 @@
 import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
 import com.android.systemui.temporarydisplay.chipbar.ChipbarEndItem
 import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo
+import java.io.PrintWriter
 import javax.inject.Inject
 
 /**
@@ -48,14 +50,13 @@
     private val chipbarCoordinator: ChipbarCoordinator,
     private val commandQueue: CommandQueue,
     private val context: Context,
-    @MediaTttSenderLogger private val logger: MediaTttLogger<ChipbarInfo>,
+    private val dumpManager: DumpManager,
+    private val logger: MediaTttSenderLogger,
     private val mediaTttFlags: MediaTttFlags,
     private val uiEventLogger: MediaTttSenderUiEventLogger,
-) : CoreStartable {
+) : CoreStartable, Dumpable {
 
-    private var displayedState: ChipStateSender? = null
     // A map to store current chip state per id.
-    // TODO(b/265455911): Log whenever we add or remove from the store.
     private var stateMap: MutableMap<String, ChipStateSender> = mutableMapOf()
 
     private val commandQueueCallbacks =
@@ -76,6 +77,7 @@
     override fun start() {
         if (mediaTttFlags.isMediaTttEnabled()) {
             commandQueue.addCallback(commandQueueCallbacks)
+            dumpManager.registerNormalDumpable(this)
         }
     }
 
@@ -93,11 +95,11 @@
             return
         }
 
-        val currentState = stateMap[routeInfo.id]
-        if (!ChipStateSender.isValidStateTransition(currentState, chipState)) {
+        val currentStateForId: ChipStateSender? = stateMap[routeInfo.id]
+        if (!ChipStateSender.isValidStateTransition(currentStateForId, chipState)) {
             // ChipStateSender.FAR_FROM_RECEIVER is the default state when there is no state.
             logger.logInvalidStateTransitionError(
-                currentState = currentState?.name ?: ChipStateSender.FAR_FROM_RECEIVER.name,
+                currentState = currentStateForId?.name ?: ChipStateSender.FAR_FROM_RECEIVER.name,
                 chipState.name
             )
             return
@@ -105,30 +107,29 @@
         uiEventLogger.logSenderStateChange(chipState)
 
         if (chipState == ChipStateSender.FAR_FROM_RECEIVER) {
-            // No need to store the state since it is the default state
-            removeIdFromStore(routeInfo.id)
-            // Return early if we're not displaying a chip anyway
-            val currentDisplayedState = displayedState ?: return
+            // Return early if we're not displaying a chip for this ID anyway
+            if (currentStateForId == null) return
 
             val removalReason = ChipStateSender.FAR_FROM_RECEIVER.name
             if (
-                currentDisplayedState.transferStatus == TransferStatus.IN_PROGRESS ||
-                    currentDisplayedState.transferStatus == TransferStatus.SUCCEEDED
+                currentStateForId.transferStatus == TransferStatus.IN_PROGRESS ||
+                    currentStateForId.transferStatus == TransferStatus.SUCCEEDED
             ) {
                 // Don't remove the chip if we're in progress or succeeded, since the user should
                 // still be able to see the status of the transfer.
                 logger.logRemovalBypass(
                     removalReason,
-                    bypassReason = "transferStatus=${currentDisplayedState.transferStatus.name}"
+                    bypassReason = "transferStatus=${currentStateForId.transferStatus.name}"
                 )
                 return
             }
 
-            displayedState = null
+            // No need to store the state since it is the default state
+            removeIdFromStore(routeInfo.id, reason = removalReason)
             chipbarCoordinator.removeView(routeInfo.id, removalReason)
         } else {
             stateMap[routeInfo.id] = chipState
-            displayedState = chipState
+            logger.logStateMap(stateMap)
             chipbarCoordinator.registerListener(displayListener)
             chipbarCoordinator.displayView(
                 createChipbarInfo(
@@ -150,7 +151,7 @@
         routeInfo: MediaRoute2Info,
         undoCallback: IUndoMediaTransferCallback?,
         context: Context,
-        logger: MediaTttLogger<ChipbarInfo>,
+        logger: MediaTttSenderLogger,
     ): ChipbarInfo {
         val packageName = routeInfo.clientPackageName
         val otherDeviceName =
@@ -159,12 +160,14 @@
             } else {
                 routeInfo.name.toString()
             }
+        val icon =
+            MediaTttUtils.getIconInfoFromPackageName(context, packageName, isReceiver = false) {
+                logger.logPackageNotFound(packageName)
+            }
 
         return ChipbarInfo(
             // Display the app's icon as the start icon
-            startIcon =
-                MediaTttUtils.getIconInfoFromPackageName(context, packageName, logger)
-                    .toTintedIcon(),
+            startIcon = icon.toTintedIcon(),
             text = chipStateSender.getChipTextString(context, otherDeviceName),
             endItem =
                 when (chipStateSender.endItem) {
@@ -231,12 +234,19 @@
     }
 
     private val displayListener =
-        TemporaryViewDisplayController.Listener { id -> removeIdFromStore(id) }
+        TemporaryViewDisplayController.Listener { id, reason -> removeIdFromStore(id, reason) }
 
-    private fun removeIdFromStore(id: String) {
+    private fun removeIdFromStore(id: String, reason: String) {
+        logger.logStateMapRemoval(id, reason)
         stateMap.remove(id)
+        logger.logStateMap(stateMap)
         if (stateMap.isEmpty()) {
             chipbarCoordinator.unregisterListener(displayListener)
         }
     }
+
+    override fun dump(pw: PrintWriter, args: Array<out String>) {
+        pw.println("Current sender states:")
+        pw.println(stateMap.toString())
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttSenderLogBuffer.java b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogBuffer.java
similarity index 86%
rename from packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttSenderLogBuffer.java
rename to packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogBuffer.java
index bf216c6..a262e97 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttSenderLogBuffer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogBuffer.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.log.dagger;
+package com.android.systemui.media.taptotransfer.sender;
 
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
@@ -26,8 +26,7 @@
 import javax.inject.Qualifier;
 
 /**
- * A {@link LogBuffer} for
- * {@link com.android.systemui.media.taptotransfer.sender.MediaTttSenderLogger}.
+ * A {@link LogBuffer} for sender logs.
  */
 @Qualifier
 @Documented
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt
index 4393af9..964a95b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt
@@ -13,14 +13,102 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.systemui.media.taptotransfer.sender
 
-import java.lang.annotation.Documented
-import java.lang.annotation.Retention
-import java.lang.annotation.RetentionPolicy
-import javax.inject.Qualifier
+import android.app.StatusBarManager
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.media.taptotransfer.common.MediaTttLoggerUtils
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel
+import javax.inject.Inject
 
-@Qualifier
-@Documented
-@Retention(RetentionPolicy.RUNTIME)
-annotation class MediaTttSenderLogger
+/** A logger for all events related to the media tap-to-transfer sender experience. */
+@SysUISingleton
+class MediaTttSenderLogger
+@Inject
+constructor(
+    @MediaTttSenderLogBuffer private val buffer: LogBuffer,
+) {
+    /** Logs a change in the chip state for the given [mediaRouteId]. */
+    fun logStateChange(
+        stateName: String,
+        mediaRouteId: String,
+        packageName: String?,
+    ) {
+        MediaTttLoggerUtils.logStateChange(buffer, TAG, stateName, mediaRouteId, packageName)
+    }
+
+    /** Logs an error in trying to update to [displayState]. */
+    fun logStateChangeError(@StatusBarManager.MediaTransferSenderState displayState: Int) {
+        MediaTttLoggerUtils.logStateChangeError(buffer, TAG, displayState)
+    }
+
+    /** Logs that we couldn't find information for [packageName]. */
+    fun logPackageNotFound(packageName: String) {
+        MediaTttLoggerUtils.logPackageNotFound(buffer, TAG, packageName)
+    }
+
+    /**
+     * Logs an invalid sender state transition error in trying to update to [desiredState].
+     *
+     * @param currentState the previous state of the chip.
+     * @param desiredState the new state of the chip.
+     */
+    fun logInvalidStateTransitionError(currentState: String, desiredState: String) {
+        buffer.log(
+            TAG,
+            LogLevel.ERROR,
+            {
+                str1 = currentState
+                str2 = desiredState
+            },
+            { "Cannot display state=$str2 after state=$str1; invalid transition" }
+        )
+    }
+
+    /**
+     * Logs that a removal request has been bypassed (ignored).
+     *
+     * @param removalReason the reason that the chip removal was requested.
+     * @param bypassReason the reason that the request was bypassed.
+     */
+    fun logRemovalBypass(removalReason: String, bypassReason: String) {
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            {
+                str1 = removalReason
+                str2 = bypassReason
+            },
+            { "Chip removal requested due to $str1; however, removal was ignored because $str2" }
+        )
+    }
+
+    /** Logs the current contents of the state map. */
+    fun logStateMap(map: Map<String, ChipStateSender>) {
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            { str1 = map.toString() },
+            { "Current sender states: $str1" }
+        )
+    }
+
+    /** Logs that [id] has been removed from the state map due to [reason]. */
+    fun logStateMapRemoval(id: String, reason: String) {
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            {
+                str1 = id
+                str2 = reason
+            },
+            { "State removal: id=$str1 reason=$str2" }
+        )
+    }
+
+    companion object {
+        private const val TAG = "MediaTttSender"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
index 1edb837..3088d8b 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.media.MediaProjectionAppSelectorActivity
 import com.android.systemui.media.MediaProjectionAppSelectorActivity.Companion.EXTRA_HOST_APP_USER_HANDLE
+import com.android.systemui.media.MediaProjectionPermissionActivity
 import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerLabelLoader
 import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerThumbnailLoader
 import com.android.systemui.mediaprojection.appselector.data.AppIconLoader
@@ -34,8 +35,8 @@
 import com.android.systemui.mediaprojection.appselector.data.ShellRecentTaskListProvider
 import com.android.systemui.mediaprojection.appselector.view.MediaProjectionRecentsViewController
 import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider
-import com.android.systemui.settings.UserTracker
-import com.android.systemui.shared.system.ActivityManagerWrapper
+import com.android.systemui.mediaprojection.devicepolicy.MediaProjectionDevicePolicyModule
+import com.android.systemui.mediaprojection.devicepolicy.PersonalProfile
 import com.android.systemui.statusbar.phone.ConfigurationControllerImpl
 import com.android.systemui.statusbar.policy.ConfigurationController
 import dagger.Binds
@@ -54,13 +55,12 @@
 
 @Qualifier @Retention(AnnotationRetention.BINARY) annotation class HostUserHandle
 
-@Qualifier @Retention(AnnotationRetention.BINARY) annotation class PersonalProfile
-
-@Qualifier @Retention(AnnotationRetention.BINARY) annotation class WorkProfile
-
 @Retention(AnnotationRetention.RUNTIME) @Scope annotation class MediaProjectionAppSelectorScope
 
-@Module(subcomponents = [MediaProjectionAppSelectorComponent::class])
+@Module(
+    subcomponents = [MediaProjectionAppSelectorComponent::class],
+    includes = [MediaProjectionDevicePolicyModule::class]
+)
 interface MediaProjectionModule {
     @Binds
     @IntoMap
@@ -68,6 +68,11 @@
     fun provideMediaProjectionAppSelectorActivity(
         activity: MediaProjectionAppSelectorActivity
     ): Activity
+
+    @Binds
+    @IntoMap
+    @ClassKey(MediaProjectionPermissionActivity::class)
+    fun bindsMediaProjectionPermissionActivity(impl: MediaProjectionPermissionActivity): Activity
 }
 
 /**
@@ -105,25 +110,17 @@
         @Provides
         @MediaProjectionAppSelector
         @MediaProjectionAppSelectorScope
+        fun provideCallerPackageName(activity: MediaProjectionAppSelectorActivity): String? =
+            activity.callingPackage
+
+        @Provides
+        @MediaProjectionAppSelector
+        @MediaProjectionAppSelectorScope
         fun bindConfigurationController(
             activity: MediaProjectionAppSelectorActivity
         ): ConfigurationController = ConfigurationControllerImpl(activity)
 
         @Provides
-        @PersonalProfile
-        @MediaProjectionAppSelectorScope
-        fun personalUserHandle(activityManagerWrapper: ActivityManagerWrapper): UserHandle {
-            // Current foreground user is the 'personal' profile
-            return UserHandle.of(activityManagerWrapper.currentUserId)
-        }
-
-        @Provides
-        @WorkProfile
-        @MediaProjectionAppSelectorScope
-        fun workProfileUserHandle(userTracker: UserTracker): UserHandle? =
-            userTracker.userProfiles.find { it.isManagedProfile }?.userHandle
-
-        @Provides
         @HostUserHandle
         @MediaProjectionAppSelectorScope
         fun hostUserHandle(activity: MediaProjectionAppSelectorActivity): UserHandle {
@@ -164,6 +161,7 @@
 
     val controller: MediaProjectionAppSelectorController
     val recentsViewController: MediaProjectionRecentsViewController
+    val emptyStateProvider: MediaProjectionBlockerEmptyStateProvider
     @get:HostUserHandle val hostUserHandle: UserHandle
     @get:PersonalProfile val personalProfileUserHandle: UserHandle
 
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
index 52c7ca3..219629b 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
@@ -36,16 +36,16 @@
     private val flags: FeatureFlags,
     @HostUserHandle private val hostUserHandle: UserHandle,
     @MediaProjectionAppSelector private val scope: CoroutineScope,
-    @MediaProjectionAppSelector private val appSelectorComponentName: ComponentName
+    @MediaProjectionAppSelector private val appSelectorComponentName: ComponentName,
+    @MediaProjectionAppSelector private val callerPackageName: String?
 ) {
 
     fun init() {
         scope.launch {
             val recentTasks = recentTaskListProvider.loadRecentTasks()
 
-            val tasks = recentTasks
-                .filterDevicePolicyRestrictedTasks()
-                .sortedTasks()
+            val tasks =
+                recentTasks.filterDevicePolicyRestrictedTasks().filterAppSelector().sortedTasks()
 
             view.bind(tasks)
         }
@@ -67,8 +67,13 @@
             filter { UserHandle.of(it.userId) == hostUserHandle }
         }
 
+    private fun List<RecentTask>.filterAppSelector(): List<RecentTask> = filter {
+        // Only take tasks that is not the app selector
+        it.topActivityComponent != appSelectorComponentName
+    }
+
     private fun List<RecentTask>.sortedTasks(): List<RecentTask> = sortedBy {
         // Show normal tasks first and only then tasks with opened app selector
-        it.topActivityComponent == appSelectorComponentName
+        it.topActivityComponent?.packageName == callerPackageName
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionBlockerEmptyStateProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionBlockerEmptyStateProvider.kt
new file mode 100644
index 0000000..829b0dd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionBlockerEmptyStateProvider.kt
@@ -0,0 +1,70 @@
+/*
+ * 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.mediaprojection.appselector
+
+import android.content.Context
+import android.os.UserHandle
+import com.android.internal.R as AndroidR
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyState
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyStateProvider
+import com.android.internal.app.ResolverListAdapter
+import com.android.systemui.R
+import com.android.systemui.mediaprojection.devicepolicy.PersonalProfile
+import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver
+import javax.inject.Inject
+
+@MediaProjectionAppSelectorScope
+class MediaProjectionBlockerEmptyStateProvider
+@Inject
+constructor(
+    @HostUserHandle private val hostAppHandle: UserHandle,
+    @PersonalProfile private val personalProfileHandle: UserHandle,
+    private val policyResolver: ScreenCaptureDevicePolicyResolver,
+    private val context: Context
+) : EmptyStateProvider {
+
+    override fun getEmptyState(resolverListAdapter: ResolverListAdapter): EmptyState? {
+        val screenCaptureAllowed =
+            policyResolver.isScreenCaptureAllowed(
+                targetAppUserHandle = resolverListAdapter.userHandle,
+                hostAppUserHandle = hostAppHandle
+            )
+
+        val isHostAppInPersonalProfile = hostAppHandle == personalProfileHandle
+
+        val subtitle =
+            if (isHostAppInPersonalProfile) {
+                AndroidR.string.resolver_cant_share_with_personal_apps_explanation
+            } else {
+                AndroidR.string.resolver_cant_share_with_work_apps_explanation
+            }
+
+        if (!screenCaptureAllowed) {
+            return object : EmptyState {
+                override fun getSubtitle(): String = context.resources.getString(subtitle)
+                override fun getTitle(): String =
+                    context.resources.getString(
+                        R.string.screen_capturing_disabled_by_policy_dialog_title
+                    )
+                override fun onEmptyStateShown() {
+                    // TODO(b/237397740) report analytics
+                }
+                override fun shouldSkipDataRebuild(): Boolean = true
+            }
+        }
+        return null
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/MediaProjectionDevicePolicyModule.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/MediaProjectionDevicePolicyModule.kt
new file mode 100644
index 0000000..13b71a7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/MediaProjectionDevicePolicyModule.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.mediaprojection.devicepolicy
+
+import android.os.UserHandle
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.shared.system.ActivityManagerWrapper
+import dagger.Module
+import dagger.Provides
+import javax.inject.Qualifier
+
+@Qualifier @Retention(AnnotationRetention.BINARY) annotation class WorkProfile
+
+@Qualifier @Retention(AnnotationRetention.BINARY) annotation class PersonalProfile
+
+/** Module for media projection device policy related dependencies */
+@Module
+class MediaProjectionDevicePolicyModule {
+    @Provides
+    @PersonalProfile
+    fun personalUserHandle(activityManagerWrapper: ActivityManagerWrapper): UserHandle {
+        // Current foreground user is the 'personal' profile
+        return UserHandle.of(activityManagerWrapper.currentUserId)
+    }
+
+    @Provides
+    @WorkProfile
+    fun workProfileUserHandle(userTracker: UserTracker): UserHandle? =
+        userTracker.userProfiles.find { it.isManagedProfile }?.userHandle
+}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolver.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolver.kt
new file mode 100644
index 0000000..6bd33e7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolver.kt
@@ -0,0 +1,125 @@
+/*
+ * 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.mediaprojection.devicepolicy
+
+import android.app.admin.DevicePolicyManager
+import android.os.UserHandle
+import android.os.UserManager
+import javax.inject.Inject
+
+/**
+ * Utility class to resolve if screen capture allowed for a particular target app/host app pair. It
+ * caches the state of the policies, so you need to create a new instance of this class if you want
+ * to react to updated policies state.
+ */
+class ScreenCaptureDevicePolicyResolver
+@Inject
+constructor(
+    private val devicePolicyManager: DevicePolicyManager,
+    private val userManager: UserManager,
+    @PersonalProfile private val personalProfileUserHandle: UserHandle,
+    @WorkProfile private val workProfileUserHandle: UserHandle?
+) {
+
+    /**
+     * Returns true if [hostAppUserHandle] is allowed to perform screen capture of
+     * [targetAppUserHandle]
+     */
+    fun isScreenCaptureAllowed(
+        targetAppUserHandle: UserHandle,
+        hostAppUserHandle: UserHandle,
+    ): Boolean {
+        if (hostAppUserHandle.isWorkProfile() && workProfileScreenCaptureDisabled) {
+            // Disable screen capturing as host apps should not capture the screen
+            return false
+        }
+
+        if (!hostAppUserHandle.isWorkProfile() && personalProfileScreenCaptureDisabled) {
+            // Disable screen capturing as personal apps should not capture the screen
+            return false
+        }
+
+        if (targetAppUserHandle.isWorkProfile()) {
+            // Work profile target
+            if (workProfileScreenCaptureDisabled) {
+                // Do not allow sharing work profile apps as work profile capturing is disabled
+                return false
+            }
+        } else {
+            // Personal profile target
+            if (hostAppUserHandle.isWorkProfile() && disallowSharingIntoManagedProfile) {
+                // Do not allow sharing of personal apps into work profile apps
+                return false
+            }
+
+            if (personalProfileScreenCaptureDisabled) {
+                // Disable screen capturing as personal apps should not be captured
+                return false
+            }
+        }
+
+        return true
+    }
+
+    /**
+     * Returns true if [hostAppUserHandle] is NOT allowed to capture an app from any profile,
+     * could be useful to finish the screen capture flow as soon as possible when the screen
+     * could not be captured at all.
+     */
+    fun isScreenCaptureCompletelyDisabled(hostAppUserHandle: UserHandle): Boolean {
+        val isWorkAppsCaptureDisabled =
+                if (workProfileUserHandle != null) {
+                    !isScreenCaptureAllowed(
+                            targetAppUserHandle = workProfileUserHandle,
+                            hostAppUserHandle = hostAppUserHandle
+                    )
+                } else true
+
+        val isPersonalAppsCaptureDisabled =
+                !isScreenCaptureAllowed(
+                        targetAppUserHandle = personalProfileUserHandle,
+                        hostAppUserHandle = hostAppUserHandle
+                )
+
+        return isWorkAppsCaptureDisabled && isPersonalAppsCaptureDisabled
+    }
+
+    private val personalProfileScreenCaptureDisabled: Boolean by lazy {
+        devicePolicyManager.getScreenCaptureDisabled(
+            /* admin */ null,
+            personalProfileUserHandle.identifier
+        )
+    }
+
+    private val workProfileScreenCaptureDisabled: Boolean by lazy {
+        workProfileUserHandle?.let {
+            devicePolicyManager.getScreenCaptureDisabled(/* admin */ null, it.identifier)
+        }
+            ?: false
+    }
+
+    private val disallowSharingIntoManagedProfile: Boolean by lazy {
+        workProfileUserHandle?.let {
+            userManager.hasUserRestrictionForUser(
+                UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE,
+                it
+            )
+        }
+            ?: false
+    }
+
+    private fun UserHandle?.isWorkProfile(): Boolean = this == workProfileUserHandle
+}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDisabledDialog.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDisabledDialog.kt
new file mode 100644
index 0000000..a6b3da0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDisabledDialog.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.mediaprojection.devicepolicy
+
+import android.content.Context
+import com.android.systemui.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
+
+/** Dialog that shows that screen capture is disabled on this device. */
+class ScreenCaptureDisabledDialog(context: Context) : SystemUIDialog(context) {
+
+    init {
+        setTitle(context.getString(R.string.screen_capturing_disabled_by_policy_dialog_title))
+        setMessage(
+            context.getString(R.string.screen_capturing_disabled_by_policy_dialog_description)
+        )
+        setIcon(R.drawable.ic_cast)
+        setButton(BUTTON_POSITIVE, context.getString(android.R.string.ok)) { _, _ -> cancel() }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
index 3ecf154..8d80990 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
+++ b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
@@ -16,13 +16,12 @@
 
 package com.android.systemui.model;
 
-import static android.view.Display.DEFAULT_DISPLAY;
-
 import android.annotation.NonNull;
 import android.util.Log;
 
 import com.android.systemui.Dumpable;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.shared.system.QuickStepContract;
 
 import java.io.PrintWriter;
@@ -39,11 +38,16 @@
     private static final String TAG = SysUiState.class.getSimpleName();
     public static final boolean DEBUG = false;
 
+    private final DisplayTracker mDisplayTracker;
     private @QuickStepContract.SystemUiStateFlags int mFlags;
     private final List<SysUiStateCallback> mCallbacks = new ArrayList<>();
     private int mFlagsToSet = 0;
     private int mFlagsToClear = 0;
 
+    public SysUiState(DisplayTracker displayTracker) {
+        mDisplayTracker = displayTracker;
+    }
+
     /**
      * Add listener to be notified of changes made to SysUI state.
      * The callback will also be called as part of this function.
@@ -81,7 +85,7 @@
     }
 
     private void updateFlags(int displayId) {
-        if (displayId != DEFAULT_DISPLAY) {
+        if (displayId != mDisplayTracker.getDefaultDisplayId()) {
             // Ignore non-default displays for now
             Log.w(TAG, "Ignoring flag update for display: " + displayId, new Throwable());
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/motiontool/MotionToolModule.kt b/packages/SystemUI/src/com/android/systemui/motiontool/MotionToolModule.kt
index 1324d2c..c4a1ed4 100644
--- a/packages/SystemUI/src/com/android/systemui/motiontool/MotionToolModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/motiontool/MotionToolModule.kt
@@ -19,7 +19,6 @@
 import android.view.WindowManagerGlobal
 import com.android.app.motiontool.DdmHandleMotionTool
 import com.android.app.motiontool.MotionToolManager
-import com.android.app.viewcapture.ViewCapture
 import com.android.systemui.CoreStartable
 import dagger.Binds
 import dagger.Module
@@ -38,17 +37,12 @@
         }
 
         @Provides
-        fun provideMotionToolManager(
-            viewCapture: ViewCapture,
-            windowManagerGlobal: WindowManagerGlobal
-        ): MotionToolManager {
-            return MotionToolManager.getInstance(viewCapture, windowManagerGlobal)
+        fun provideMotionToolManager(windowManagerGlobal: WindowManagerGlobal): MotionToolManager {
+            return MotionToolManager.getInstance(windowManagerGlobal)
         }
 
         @Provides
         fun provideWindowManagerGlobal(): WindowManagerGlobal = WindowManagerGlobal.getInstance()
-
-        @Provides fun provideViewCapture(): ViewCapture = ViewCapture.getInstance()
     }
 
     @Binds
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 5993e2e..97c290d 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -25,7 +25,6 @@
 import static android.app.StatusBarManager.WindowVisibleState;
 import static android.app.StatusBarManager.windowStateToString;
 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
-import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.containsType;
 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
@@ -130,6 +129,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.recents.Recents;
+import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shade.ShadeController;
@@ -218,6 +218,7 @@
     private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener;
     private final UserContextProvider mUserContextProvider;
     private final WakefulnessLifecycle mWakefulnessLifecycle;
+    private final DisplayTracker mDisplayTracker;
     private final RegionSamplingHelper mRegionSamplingHelper;
     private final int mNavColorSampleMargin;
     private NavigationBarFrame mFrame;
@@ -461,7 +462,8 @@
                 @Override
                 public void onStartedWakingUp() {
                     notifyScreenStateChanged(true);
-                    if (isGesturalModeOnDefaultDisplay(getContext(), mNavBarMode)) {
+                    if (isGesturalModeOnDefaultDisplay(getContext(), mDisplayTracker,
+                            mNavBarMode)) {
                         mRegionSamplingHelper.start(mSamplingBounds);
                     }
                 }
@@ -545,7 +547,8 @@
             Optional<BackAnimation> backAnimation,
             UserContextProvider userContextProvider,
             WakefulnessLifecycle wakefulnessLifecycle,
-            TaskStackChangeListeners taskStackChangeListeners) {
+            TaskStackChangeListeners taskStackChangeListeners,
+            DisplayTracker displayTracker) {
         super(navigationBarView);
         mFrame = navigationBarFrame;
         mContext = context;
@@ -585,6 +588,7 @@
         mUserContextProvider = userContextProvider;
         mWakefulnessLifecycle = wakefulnessLifecycle;
         mTaskStackChangeListeners = taskStackChangeListeners;
+        mDisplayTracker = displayTracker;
 
         mNavColorSampleMargin = getResources()
                 .getDimensionPixelSize(R.dimen.navigation_handle_sample_horizontal_margin);
@@ -632,12 +636,14 @@
 
                     @Override
                     public boolean isSamplingEnabled() {
-                        return isGesturalModeOnDefaultDisplay(getContext(), mNavBarMode);
+                        return isGesturalModeOnDefaultDisplay(getContext(), mDisplayTracker,
+                                mNavBarMode);
                     }
                 }, mainExecutor, bgExecutor);
 
         mView.setBackgroundExecutor(bgExecutor);
         mView.setEdgeBackGestureHandler(mEdgeBackGestureHandler);
+        mView.setDisplayTracker(mDisplayTracker);
         mNavBarMode = mNavigationModeController.addListener(mModeChangedListener);
     }
 
@@ -665,7 +671,7 @@
                 getBarLayoutParams(mContext.getResources().getConfiguration().windowConfiguration
                         .getRotation()));
         mDisplayId = mContext.getDisplayId();
-        mIsOnDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
+        mIsOnDefaultDisplay = mDisplayId == mDisplayTracker.getDefaultDisplayId();
 
         // Ensure we try to get currentSysuiState from navBarHelper before command queue callbacks
         // start firing, since the latter is source of truth
@@ -1468,7 +1474,7 @@
     private void onAccessibilityClick(View v) {
         final Display display = v.getDisplay();
         mAccessibilityManager.notifyAccessibilityButtonClicked(
-                display != null ? display.getDisplayId() : DEFAULT_DISPLAY);
+                display != null ? display.getDisplayId() : mDisplayTracker.getDefaultDisplayId());
     }
 
     private boolean onAccessibilityLongClick(View v) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index dce69bb..8c19111 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -19,7 +19,6 @@
 import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
 import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
 import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
-import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler.DEBUG_MISSING_GESTURE_TAG;
 import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
@@ -55,6 +54,7 @@
 import com.android.systemui.flags.Flags;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.statusbar.CommandQueue;
@@ -87,6 +87,7 @@
     private final NavigationBarComponent.Factory mNavigationBarComponentFactory;
     private FeatureFlags mFeatureFlags;
     private final SecureSettings mSecureSettings;
+    private final DisplayTracker mDisplayTracker;
     private final DisplayManager mDisplayManager;
     private final TaskbarDelegate mTaskbarDelegate;
     private int mNavMode;
@@ -119,12 +120,14 @@
             Optional<Pip> pipOptional,
             Optional<BackAnimation> backAnimation,
             FeatureFlags featureFlags,
-            SecureSettings secureSettings) {
+            SecureSettings secureSettings,
+            DisplayTracker displayTracker) {
         mContext = context;
         mHandler = mainHandler;
         mNavigationBarComponentFactory = navigationBarComponentFactory;
         mFeatureFlags = featureFlags;
         mSecureSettings = secureSettings;
+        mDisplayTracker = displayTracker;
         mDisplayManager = mContext.getSystemService(DisplayManager.class);
         commandQueue.addCallback(this);
         configurationController.addCallback(this);
@@ -296,9 +299,10 @@
         // Don't need to create nav bar on the default display if we initialize TaskBar.
         final boolean shouldCreateDefaultNavbar = includeDefaultDisplay
                 && !initializeTaskbarIfNecessary();
-        Display[] displays = mDisplayManager.getDisplays();
+        Display[] displays = mDisplayTracker.getAllDisplays();
         for (Display display : displays) {
-            if (shouldCreateDefaultNavbar || display.getDisplayId() != DEFAULT_DISPLAY) {
+            if (shouldCreateDefaultNavbar
+                    || display.getDisplayId() != mDisplayTracker.getDefaultDisplayId()) {
                 createNavigationBar(display, null /* savedState */, result);
             }
         }
@@ -317,7 +321,7 @@
         }
 
         final int displayId = display.getDisplayId();
-        final boolean isOnDefaultDisplay = displayId == DEFAULT_DISPLAY;
+        final boolean isOnDefaultDisplay = displayId == mDisplayTracker.getDefaultDisplayId();
 
         // We may show TaskBar on the default display for large screen device. Don't need to create
         // navigation bar for this case.
@@ -412,7 +416,7 @@
 
     /** @return {@link NavigationBarView} on the default display. */
     public @Nullable NavigationBarView getDefaultNavigationBarView() {
-        return getNavigationBarView(DEFAULT_DISPLAY);
+        return getNavigationBarView(mDisplayTracker.getDefaultDisplayId());
     }
 
     /**
@@ -433,7 +437,8 @@
         final NavigationBarView navBarView = getNavigationBarView(displayId);
         if (navBarView != null) {
             navBarView.showPinningEnterExitToast(entering);
-        } else if (displayId == DEFAULT_DISPLAY && mTaskbarDelegate.isInitialized()) {
+        } else if (displayId == mDisplayTracker.getDefaultDisplayId()
+                && mTaskbarDelegate.isInitialized()) {
             mTaskbarDelegate.showPinningEnterExitToast(entering);
         }
     }
@@ -442,7 +447,8 @@
         final NavigationBarView navBarView = getNavigationBarView(displayId);
         if (navBarView != null) {
             navBarView.showPinningEscapeToast();
-        } else if (displayId == DEFAULT_DISPLAY && mTaskbarDelegate.isInitialized()) {
+        } else if (displayId == mDisplayTracker.getDefaultDisplayId()
+                && mTaskbarDelegate.isInitialized()) {
             mTaskbarDelegate.showPinningEscapeToast();
         }
     }
@@ -459,7 +465,7 @@
     /** @return {@link NavigationBar} on the default display. */
     @Nullable
     public NavigationBar getDefaultNavigationBar() {
-        return mNavigationBars.get(DEFAULT_DISPLAY);
+        return mNavigationBars.get(mDisplayTracker.getDefaultDisplayId());
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
index 6793f01..a4de9ff 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
@@ -24,7 +24,6 @@
 import android.os.Handler;
 import android.os.RemoteException;
 import android.util.SparseArray;
-import android.view.Display;
 import android.view.IWallpaperVisibilityListener;
 import android.view.IWindowManager;
 import android.view.View;
@@ -32,6 +31,7 @@
 import com.android.systemui.R;
 import com.android.systemui.navigationbar.NavigationBarComponent.NavigationBarScope;
 import com.android.systemui.navigationbar.buttons.ButtonDispatcher;
+import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.statusbar.phone.BarTransitions;
 import com.android.systemui.statusbar.phone.LightBarTransitionsController;
 
@@ -65,6 +65,7 @@
     @org.jetbrains.annotations.NotNull
     private final IWindowManager mWindowManagerService;
     private final LightBarTransitionsController mLightTransitionsController;
+    private final DisplayTracker mDisplayTracker;
     private final boolean mAllowAutoDimWallpaperNotVisible;
     private boolean mWallpaperVisible;
 
@@ -89,18 +90,20 @@
     public NavigationBarTransitions(
             NavigationBarView view,
             IWindowManager windowManagerService,
-            LightBarTransitionsController.Factory lightBarTransitionsControllerFactory) {
+            LightBarTransitionsController.Factory lightBarTransitionsControllerFactory,
+            DisplayTracker displayTracker) {
         super(view, R.drawable.nav_background);
         mView = view;
         mWindowManagerService = windowManagerService;
         mLightTransitionsController = lightBarTransitionsControllerFactory.create(this);
+        mDisplayTracker = displayTracker;
         mAllowAutoDimWallpaperNotVisible = view.getContext().getResources()
                 .getBoolean(R.bool.config_navigation_bar_enable_auto_dim_no_visible_wallpaper);
         mDarkIntensityListeners = new ArrayList();
 
         try {
             mWallpaperVisible = mWindowManagerService.registerWallpaperVisibilityListener(
-                    mWallpaperVisibilityListener, Display.DEFAULT_DISPLAY);
+                    mWallpaperVisibilityListener, mDisplayTracker.getDefaultDisplayId());
         } catch (RemoteException e) {
         }
         mView.addOnLayoutChangeListener(
@@ -126,7 +129,7 @@
     public void destroy() {
         try {
             mWindowManagerService.unregisterWallpaperVisibilityListener(mWallpaperVisibilityListener,
-                    Display.DEFAULT_DISPLAY);
+                    mDisplayTracker.getDefaultDisplayId());
         } catch (RemoteException e) {
         }
         mLightTransitionsController.destroy();
@@ -135,7 +138,10 @@
     @Override
     public void setAutoDim(boolean autoDim) {
         // Ensure we aren't in gestural nav if we are triggering auto dim
-        if (autoDim && isGesturalModeOnDefaultDisplay(mView.getContext(), mNavBarMode)) return;
+        if (autoDim && isGesturalModeOnDefaultDisplay(mView.getContext(), mDisplayTracker,
+                mNavBarMode)) {
+            return;
+        }
         if (mAutoDim == autoDim) return;
         mAutoDim = autoDim;
         applyLightsOut(true, false);
@@ -219,7 +225,7 @@
 
     @Override
     public int getTintAnimationDuration() {
-        if (isGesturalModeOnDefaultDisplay(mView.getContext(), mNavBarMode)) {
+        if (isGesturalModeOnDefaultDisplay(mView.getContext(), mDisplayTracker, mNavBarMode)) {
             return Math.max(DEFAULT_COLOR_ADAPT_TRANSITION_TIME, MIN_COLOR_ADAPT_TRANSITION_TIME);
         }
         return LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 88c4fd5..63fb499 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -16,13 +16,11 @@
 
 package com.android.systemui.navigationbar;
 
-import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
 import static android.inputmethodservice.InputMethodService.canImeRenderGesturalNavButtons;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
 
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SEARCH_DISABLED;
 import static com.android.systemui.shared.system.QuickStepContract.isGesturalMode;
 
@@ -75,13 +73,12 @@
 import com.android.systemui.navigationbar.buttons.RotationContextButton;
 import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
 import com.android.systemui.recents.Recents;
+import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.shared.rotation.FloatingRotationButton;
 import com.android.systemui.shared.rotation.RotationButton.RotationButtonUpdatesCallback;
 import com.android.systemui.shared.rotation.RotationButtonController;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.statusbar.phone.AutoHideController;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 import com.android.systemui.statusbar.phone.LightBarTransitionsController;
@@ -127,6 +124,7 @@
     private int mDarkIconColor;
 
     private EdgeBackGestureHandler mEdgeBackGestureHandler;
+    private DisplayTracker mDisplayTracker;
     private final DeadZone mDeadZone;
     private NavigationBarTransitions mBarTransitions;
     @Nullable
@@ -303,7 +301,8 @@
                 R.dimen.floating_rotation_button_taskbar_left_margin,
                 R.dimen.floating_rotation_button_taskbar_bottom_margin,
                 R.dimen.floating_rotation_button_diameter,
-                R.dimen.key_button_ripple_max_width);
+                R.dimen.key_button_ripple_max_width,
+                R.bool.floating_rotation_button_position_left);
         mRotationButtonController = new RotationButtonController(mLightContext, mLightIconColor,
                 mDarkIconColor, R.drawable.ic_sysbar_rotate_button_ccw_start_0,
                 R.drawable.ic_sysbar_rotate_button_ccw_start_90,
@@ -361,6 +360,10 @@
         mBgExecutor = bgExecutor;
     }
 
+    public void setDisplayTracker(DisplayTracker displayTracker) {
+        mDisplayTracker = displayTracker;
+    }
+
     public void setTouchHandler(Gefingerpoken touchHandler) {
         mTouchHandler = touchHandler;
     }
@@ -558,7 +561,8 @@
     }
 
     public void setBehavior(@Behavior int behavior) {
-        mRotationButtonController.onBehaviorChanged(Display.DEFAULT_DISPLAY, behavior);
+        mRotationButtonController.onBehaviorChanged(mDisplayTracker.getDefaultDisplayId(),
+                behavior);
     }
 
     @Override
@@ -678,7 +682,7 @@
     @VisibleForTesting
     boolean isRecentsButtonDisabled() {
         return mUseCarModeUi || !isOverviewEnabled()
-                || getContext().getDisplayId() != Display.DEFAULT_DISPLAY;
+                || getContext().getDisplayId() != mDisplayTracker.getDefaultDisplayId();
     }
 
     private Display getContextDisplay() {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
index 2822435..f335733 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
@@ -1,14 +1,20 @@
 package com.android.systemui.navigationbar.gestural
 
 import android.content.Context
+import android.content.res.Configuration
+import android.content.res.TypedArray
 import android.graphics.Canvas
 import android.graphics.Paint
 import android.graphics.Path
 import android.graphics.RectF
+import android.util.MathUtils.min
+import android.util.TypedValue
 import android.view.View
+import androidx.appcompat.view.ContextThemeWrapper
 import androidx.dynamicanimation.animation.FloatPropertyCompat
 import androidx.dynamicanimation.animation.SpringAnimation
 import androidx.dynamicanimation.animation.SpringForce
+import com.android.internal.R.style.Theme_DeviceDefault
 import com.android.internal.util.LatencyTracker
 import com.android.settingslib.Utils
 import com.android.systemui.navigationbar.gestural.BackPanelController.DelayedOnAnimationEndListener
@@ -16,7 +22,10 @@
 private const val TAG = "BackPanel"
 private const val DEBUG = false
 
-class BackPanel(context: Context, private val latencyTracker: LatencyTracker) : View(context) {
+class BackPanel(
+        context: Context,
+        private val latencyTracker: LatencyTracker
+) : View(context) {
 
     var arrowsPointLeft = false
         set(value) {
@@ -45,52 +54,54 @@
     /**
      * The length of the arrow measured horizontally. Used for animating [arrowPath]
      */
-    private var arrowLength = AnimatedFloat("arrowLength", SpringForce())
+    private var arrowLength = AnimatedFloat(
+            name = "arrowLength",
+            minimumVisibleChange = SpringAnimation.MIN_VISIBLE_CHANGE_PIXELS
+    )
 
     /**
      * The height of the arrow measured vertically from its center to its top (i.e. half the total
      * height). Used for animating [arrowPath]
      */
-    private var arrowHeight = AnimatedFloat("arrowHeight", SpringForce())
-
-    private val backgroundWidth = AnimatedFloat(
-        name = "backgroundWidth",
-        SpringForce().apply {
-            stiffness = 600f
-            dampingRatio = 0.65f
-        }
+    var arrowHeight = AnimatedFloat(
+            name = "arrowHeight",
+            minimumVisibleChange = SpringAnimation.MIN_VISIBLE_CHANGE_ROTATION_DEGREES
     )
 
-    private val backgroundHeight = AnimatedFloat(
-        name = "backgroundHeight",
-        SpringForce().apply {
-            stiffness = 600f
-            dampingRatio = 0.65f
-        }
+    val backgroundWidth = AnimatedFloat(
+            name = "backgroundWidth",
+            minimumVisibleChange = SpringAnimation.MIN_VISIBLE_CHANGE_PIXELS,
+            minimumValue = 0f,
+    )
+
+    val backgroundHeight = AnimatedFloat(
+            name = "backgroundHeight",
+            minimumVisibleChange = SpringAnimation.MIN_VISIBLE_CHANGE_PIXELS,
+            minimumValue = 0f,
     )
 
     /**
      * Corners of the background closer to the edge of the screen (where the arrow appeared from).
      * Used for animating [arrowBackgroundRect]
      */
-    private val backgroundEdgeCornerRadius = AnimatedFloat(
-        name = "backgroundEdgeCornerRadius",
-        SpringForce().apply {
-            stiffness = 400f
-            dampingRatio = SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY
-        }
-    )
+    val backgroundEdgeCornerRadius = AnimatedFloat("backgroundEdgeCornerRadius")
 
     /**
      * Corners of the background further from the edge of the screens (toward the direction the
      * arrow is being dragged). Used for animating [arrowBackgroundRect]
      */
-    private val backgroundFarCornerRadius = AnimatedFloat(
-        name = "backgroundDragCornerRadius",
-        SpringForce().apply {
-            stiffness = 2200f
-            dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
-        }
+    val backgroundFarCornerRadius = AnimatedFloat("backgroundFarCornerRadius")
+
+    var scale = AnimatedFloat(
+            name = "scale",
+            minimumVisibleChange = SpringAnimation.MIN_VISIBLE_CHANGE_SCALE,
+            minimumValue = 0f
+    )
+
+    val scalePivotX = AnimatedFloat(
+            name = "scalePivotX",
+            minimumVisibleChange = SpringAnimation.MIN_VISIBLE_CHANGE_PIXELS,
+            minimumValue = backgroundWidth.pos / 2,
     )
 
     /**
@@ -98,34 +109,40 @@
      * background's margin relative to the screen edge. The arrow will be centered within the
      * background.
      */
-    private var horizontalTranslation = AnimatedFloat("horizontalTranslation", SpringForce())
+    var horizontalTranslation = AnimatedFloat(name = "horizontalTranslation")
 
-    private val currentAlpha: FloatPropertyCompat<BackPanel> =
-        object : FloatPropertyCompat<BackPanel>("currentAlpha") {
-            override fun setValue(panel: BackPanel, value: Float) {
-                panel.alpha = value
-            }
+    var arrowAlpha = AnimatedFloat(
+            name = "arrowAlpha",
+            minimumVisibleChange = SpringAnimation.MIN_VISIBLE_CHANGE_ALPHA,
+            minimumValue = 0f,
+            maximumValue = 1f
+    )
 
-            override fun getValue(panel: BackPanel): Float = panel.alpha
-        }
+    val backgroundAlpha = AnimatedFloat(
+            name = "backgroundAlpha",
+            minimumVisibleChange = SpringAnimation.MIN_VISIBLE_CHANGE_ALPHA,
+            minimumValue = 0f,
+            maximumValue = 1f
+    )
 
-    private val alphaAnimation = SpringAnimation(this, currentAlpha)
-        .setSpring(
-            SpringForce()
-                .setStiffness(60f)
-                .setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY)
-        )
+    private val allAnimatedFloat = setOf(
+            arrowLength,
+            arrowHeight,
+            backgroundWidth,
+            backgroundEdgeCornerRadius,
+            backgroundFarCornerRadius,
+            scalePivotX,
+            scale,
+            horizontalTranslation,
+            arrowAlpha,
+            backgroundAlpha
+    )
 
     /**
      * Canvas vertical translation. How far up/down the arrow and background appear relative to the
      * canvas.
      */
-    private var verticalTranslation: AnimatedFloat = AnimatedFloat(
-        name = "verticalTranslation",
-        SpringForce().apply {
-            stiffness = SpringForce.STIFFNESS_MEDIUM
-        }
-    )
+    var verticalTranslation = AnimatedFloat("verticalTranslation")
 
     /**
      * Use for drawing debug info. Can only be set if [DEBUG]=true
@@ -136,28 +153,67 @@
         }
 
     internal fun updateArrowPaint(arrowThickness: Float) {
-        // Arrow constants
+
         arrowPaint.strokeWidth = arrowThickness
 
-        arrowPaint.color =
-            Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorPrimary)
-        arrowBackgroundPaint.color = Utils.getColorAccentDefaultColor(context)
+        val isDeviceInNightTheme = resources.configuration.uiMode and
+                Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
+
+        val colorControlActivated = ContextThemeWrapper(context, Theme_DeviceDefault)
+                .run {
+                    val typedValue = TypedValue()
+                    val a: TypedArray = obtainStyledAttributes(typedValue.data,
+                            intArrayOf(android.R.attr.colorControlActivated))
+                    val color = a.getColor(0, 0)
+                    a.recycle()
+                    color
+                }
+
+        val colorPrimary =
+                Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorPrimary)
+
+        arrowPaint.color = Utils.getColorAccentDefaultColor(context)
+
+        arrowBackgroundPaint.color = if (isDeviceInNightTheme) {
+            colorPrimary
+        } else {
+            colorControlActivated
+        }
     }
 
-    private inner class AnimatedFloat(name: String, springForce: SpringForce) {
+    inner class AnimatedFloat(
+            name: String,
+            private val minimumVisibleChange: Float? = null,
+            private val minimumValue: Float? = null,
+            private val maximumValue: Float? = null,
+    ) {
+
         // The resting position when not stretched by a touch drag
         private var restingPosition = 0f
 
         // The current position as updated by the SpringAnimation
         var pos = 0f
-            set(v) {
+            private set(v) {
                 if (field != v) {
                     field = v
                     invalidate()
                 }
             }
 
-        val animation: SpringAnimation
+        private val animation: SpringAnimation
+        var spring: SpringForce
+            get() = animation.spring
+            set(value) {
+                animation.cancel()
+                animation.spring = value
+            }
+
+        val isRunning: Boolean
+            get() = animation.isRunning
+
+        fun addEndListener(listener: DelayedOnAnimationEndListener) {
+            animation.addEndListener(listener)
+        }
 
         init {
             val floatProp = object : FloatPropertyCompat<AnimatedFloat>(name) {
@@ -167,8 +223,12 @@
 
                 override fun getValue(animatedFloat: AnimatedFloat): Float = animatedFloat.pos
             }
-            animation = SpringAnimation(this, floatProp)
-            animation.spring = springForce
+            animation = SpringAnimation(this, floatProp).apply {
+                spring = SpringForce()
+                this@AnimatedFloat.minimumValue?.let { setMinValue(it) }
+                this@AnimatedFloat.maximumValue?.let { setMaxValue(it) }
+                this@AnimatedFloat.minimumVisibleChange?.let { minimumVisibleChange = it }
+            }
         }
 
         fun snapTo(newPosition: Float) {
@@ -178,8 +238,24 @@
             pos = newPosition
         }
 
-        fun stretchTo(stretchAmount: Float) {
-            animation.animateToFinalPosition(restingPosition + stretchAmount)
+        fun snapToRestingPosition() {
+            snapTo(restingPosition)
+        }
+
+
+        fun stretchTo(
+                stretchAmount: Float,
+                startingVelocity: Float? = null,
+                springForce: SpringForce? = null
+        ) {
+            animation.apply {
+                startingVelocity?.let {
+                    cancel()
+                    setStartVelocity(it)
+                }
+                springForce?.let { spring = springForce }
+                animateToFinalPosition(restingPosition + stretchAmount)
+            }
         }
 
         /**
@@ -188,18 +264,23 @@
          *
          * The [restingPosition] will remain unchanged. Only the animation is updated.
          */
-        fun stretchBy(finalPosition: Float, amount: Float) {
-            val stretchedAmount = amount * (finalPosition - restingPosition)
+        fun stretchBy(finalPosition: Float?, amount: Float) {
+            val stretchedAmount = amount * ((finalPosition ?: 0f) - restingPosition)
             animation.animateToFinalPosition(restingPosition + stretchedAmount)
         }
 
-        fun updateRestingPosition(pos: Float, animated: Boolean) {
+        fun updateRestingPosition(pos: Float?, animated: Boolean = true) {
+            if (pos == null) return
+
             restingPosition = pos
-            if (animated)
+            if (animated) {
                 animation.animateToFinalPosition(restingPosition)
-            else
+            } else {
                 snapTo(restingPosition)
+            }
         }
+
+        fun cancel() = animation.cancel()
     }
 
     init {
@@ -224,126 +305,203 @@
         return arrowPath
     }
 
-    fun addEndListener(endListener: DelayedOnAnimationEndListener): Boolean {
-        return if (alphaAnimation.isRunning) {
-            alphaAnimation.addEndListener(endListener)
-            true
-        } else if (horizontalTranslation.animation.isRunning) {
-            horizontalTranslation.animation.addEndListener(endListener)
+    fun addAnimationEndListener(
+            animatedFloat: AnimatedFloat,
+            endListener: DelayedOnAnimationEndListener
+    ): Boolean {
+        return if (animatedFloat.isRunning) {
+            animatedFloat.addEndListener(endListener)
             true
         } else {
-            endListener.runNow()
+            endListener.run()
             false
         }
     }
 
+    fun cancelAnimations() {
+        allAnimatedFloat.forEach { it.cancel() }
+    }
+
     fun setStretch(
-        horizontalTranslationStretchAmount: Float,
-        arrowStretchAmount: Float,
-        backgroundWidthStretchAmount: Float,
-        fullyStretchedDimens: EdgePanelParams.BackIndicatorDimens
+            horizontalTranslationStretchAmount: Float,
+            arrowStretchAmount: Float,
+            arrowAlphaStretchAmount: Float,
+            backgroundAlphaStretchAmount: Float,
+            backgroundWidthStretchAmount: Float,
+            backgroundHeightStretchAmount: Float,
+            edgeCornerStretchAmount: Float,
+            farCornerStretchAmount: Float,
+            fullyStretchedDimens: EdgePanelParams.BackIndicatorDimens
     ) {
         horizontalTranslation.stretchBy(
-            finalPosition = fullyStretchedDimens.horizontalTranslation,
-            amount = horizontalTranslationStretchAmount
+                finalPosition = fullyStretchedDimens.horizontalTranslation,
+                amount = horizontalTranslationStretchAmount
         )
         arrowLength.stretchBy(
-            finalPosition = fullyStretchedDimens.arrowDimens.length,
-            amount = arrowStretchAmount
+                finalPosition = fullyStretchedDimens.arrowDimens.length,
+                amount = arrowStretchAmount
         )
         arrowHeight.stretchBy(
-            finalPosition = fullyStretchedDimens.arrowDimens.height,
-            amount = arrowStretchAmount
+                finalPosition = fullyStretchedDimens.arrowDimens.height,
+                amount = arrowStretchAmount
+        )
+        arrowAlpha.stretchBy(
+                finalPosition = fullyStretchedDimens.arrowDimens.alpha,
+                amount = arrowAlphaStretchAmount
+        )
+        backgroundAlpha.stretchBy(
+                finalPosition = fullyStretchedDimens.backgroundDimens.alpha,
+                amount = backgroundAlphaStretchAmount
         )
         backgroundWidth.stretchBy(
-            finalPosition = fullyStretchedDimens.backgroundDimens.width,
-            amount = backgroundWidthStretchAmount
+                finalPosition = fullyStretchedDimens.backgroundDimens.width,
+                amount = backgroundWidthStretchAmount
+        )
+        backgroundHeight.stretchBy(
+                finalPosition = fullyStretchedDimens.backgroundDimens.height,
+                amount = backgroundHeightStretchAmount
+        )
+        backgroundEdgeCornerRadius.stretchBy(
+                finalPosition = fullyStretchedDimens.backgroundDimens.edgeCornerRadius,
+                amount = edgeCornerStretchAmount
+        )
+        backgroundFarCornerRadius.stretchBy(
+                finalPosition = fullyStretchedDimens.backgroundDimens.farCornerRadius,
+                amount = farCornerStretchAmount
         )
     }
 
+    fun popOffEdge(startingVelocity: Float) {
+        val heightStretchAmount = startingVelocity * 50
+        val widthStretchAmount = startingVelocity * 150
+        val scaleStretchAmount = startingVelocity * 0.8f
+        backgroundHeight.stretchTo(stretchAmount = 0f, startingVelocity = -heightStretchAmount)
+        backgroundWidth.stretchTo(stretchAmount = 0f, startingVelocity = widthStretchAmount)
+        scale.stretchTo(stretchAmount = 0f, startingVelocity = -scaleStretchAmount)
+    }
+
+    fun popScale(startingVelocity: Float) {
+        scalePivotX.snapTo(backgroundWidth.pos / 2)
+        scale.stretchTo(stretchAmount = 0f, startingVelocity = startingVelocity)
+    }
+
+    fun popArrowAlpha(startingVelocity: Float, springForce: SpringForce? = null) {
+        arrowAlpha.stretchTo(stretchAmount = 0f, startingVelocity = startingVelocity,
+                springForce = springForce)
+    }
+
     fun resetStretch() {
-        horizontalTranslation.stretchTo(0f)
-        arrowLength.stretchTo(0f)
-        arrowHeight.stretchTo(0f)
-        backgroundWidth.stretchTo(0f)
-        backgroundHeight.stretchTo(0f)
-        backgroundEdgeCornerRadius.stretchTo(0f)
-        backgroundFarCornerRadius.stretchTo(0f)
+        backgroundAlpha.snapTo(1f)
+        verticalTranslation.snapTo(0f)
+        scale.snapTo(1f)
+
+        horizontalTranslation.snapToRestingPosition()
+        arrowLength.snapToRestingPosition()
+        arrowHeight.snapToRestingPosition()
+        arrowAlpha.snapToRestingPosition()
+        backgroundWidth.snapToRestingPosition()
+        backgroundHeight.snapToRestingPosition()
+        backgroundEdgeCornerRadius.snapToRestingPosition()
+        backgroundFarCornerRadius.snapToRestingPosition()
     }
 
     /**
      * Updates resting arrow and background size not accounting for stretch
      */
     internal fun setRestingDimens(
-        restingParams: EdgePanelParams.BackIndicatorDimens,
-        animate: Boolean
+            restingParams: EdgePanelParams.BackIndicatorDimens,
+            animate: Boolean = true
     ) {
-        horizontalTranslation.updateRestingPosition(restingParams.horizontalTranslation, animate)
+        horizontalTranslation.updateRestingPosition(restingParams.horizontalTranslation)
+        scale.updateRestingPosition(restingParams.scale)
+        arrowAlpha.updateRestingPosition(restingParams.arrowDimens.alpha)
+        backgroundAlpha.updateRestingPosition(restingParams.backgroundDimens.alpha)
+
         arrowLength.updateRestingPosition(restingParams.arrowDimens.length, animate)
         arrowHeight.updateRestingPosition(restingParams.arrowDimens.height, animate)
+        scalePivotX.updateRestingPosition(restingParams.backgroundDimens.width, animate)
         backgroundWidth.updateRestingPosition(restingParams.backgroundDimens.width, animate)
         backgroundHeight.updateRestingPosition(restingParams.backgroundDimens.height, animate)
         backgroundEdgeCornerRadius.updateRestingPosition(
-            restingParams.backgroundDimens.edgeCornerRadius,
-            animate
+                restingParams.backgroundDimens.edgeCornerRadius, animate
         )
         backgroundFarCornerRadius.updateRestingPosition(
-            restingParams.backgroundDimens.farCornerRadius,
-            animate
+                restingParams.backgroundDimens.farCornerRadius, animate
         )
     }
 
     fun animateVertically(yPos: Float) = verticalTranslation.stretchTo(yPos)
 
-    fun setArrowStiffness(arrowStiffness: Float, arrowDampingRatio: Float) {
-        arrowLength.animation.spring.apply {
-            stiffness = arrowStiffness
-            dampingRatio = arrowDampingRatio
-        }
-        arrowHeight.animation.spring.apply {
-            stiffness = arrowStiffness
-            dampingRatio = arrowDampingRatio
-        }
+    fun setSpring(
+            horizontalTranslation: SpringForce? = null,
+            verticalTranslation: SpringForce? = null,
+            scale: SpringForce? = null,
+            arrowLength: SpringForce? = null,
+            arrowHeight: SpringForce? = null,
+            arrowAlpha: SpringForce? = null,
+            backgroundAlpha: SpringForce? = null,
+            backgroundFarCornerRadius: SpringForce? = null,
+            backgroundEdgeCornerRadius: SpringForce? = null,
+            backgroundWidth: SpringForce? = null,
+            backgroundHeight: SpringForce? = null,
+    ) {
+        arrowLength?.let { this.arrowLength.spring = it }
+        arrowHeight?.let { this.arrowHeight.spring = it }
+        arrowAlpha?.let { this.arrowAlpha.spring = it }
+        backgroundAlpha?.let { this.backgroundAlpha.spring = it }
+        backgroundFarCornerRadius?.let { this.backgroundFarCornerRadius.spring = it }
+        backgroundEdgeCornerRadius?.let { this.backgroundEdgeCornerRadius.spring = it }
+        scale?.let { this.scale.spring = it }
+        backgroundWidth?.let { this.backgroundWidth.spring = it }
+        backgroundHeight?.let { this.backgroundHeight.spring = it }
+        horizontalTranslation?.let { this.horizontalTranslation.spring = it }
+        verticalTranslation?.let { this.verticalTranslation.spring = it }
     }
 
     override fun hasOverlappingRendering() = false
 
     override fun onDraw(canvas: Canvas) {
-        var edgeCorner = backgroundEdgeCornerRadius.pos
+        val edgeCorner = backgroundEdgeCornerRadius.pos
         val farCorner = backgroundFarCornerRadius.pos
         val halfHeight = backgroundHeight.pos / 2
+        val canvasWidth = width
+        val backgroundWidth = backgroundWidth.pos
+        val scalePivotX = scalePivotX.pos
 
         canvas.save()
 
-        if (!isLeftPanel) canvas.scale(-1f, 1f, width / 2.0f, 0f)
+        if (!isLeftPanel) canvas.scale(-1f, 1f, canvasWidth / 2.0f, 0f)
 
         canvas.translate(
-            horizontalTranslation.pos,
-            height * 0.5f + verticalTranslation.pos
+                horizontalTranslation.pos,
+                height * 0.5f + verticalTranslation.pos
         )
 
+        canvas.scale(scale.pos, scale.pos, scalePivotX, 0f)
+
         val arrowBackground = arrowBackgroundRect.apply {
             left = 0f
             top = -halfHeight
-            right = backgroundWidth.pos
+            right = backgroundWidth
             bottom = halfHeight
         }.toPathWithRoundCorners(
-            topLeft = edgeCorner,
-            bottomLeft = edgeCorner,
-            topRight = farCorner,
-            bottomRight = farCorner
+                topLeft = edgeCorner,
+                bottomLeft = edgeCorner,
+                topRight = farCorner,
+                bottomRight = farCorner
         )
-        canvas.drawPath(arrowBackground, arrowBackgroundPaint)
+        canvas.drawPath(arrowBackground,
+                arrowBackgroundPaint.apply { alpha = (255 * backgroundAlpha.pos).toInt() })
 
         val dx = arrowLength.pos
         val dy = arrowHeight.pos
 
         // How far the arrow bounding box should be from the edge of the screen. Measured from
         // either the tip or the back of the arrow, whichever is closer
-        var arrowOffset = (backgroundWidth.pos - dx) / 2
+        val arrowOffset = (backgroundWidth - dx) / 2
         canvas.translate(
-            /* dx= */ arrowOffset,
-            /* dy= */ 0f /* pass 0 for the y position since the canvas was already translated */
+                /* dx= */ arrowOffset,
+                /* dy= */ 0f /* pass 0 for the y position since the canvas was already translated */
         )
 
         val arrowPointsAwayFromEdge = !arrowsPointLeft.xor(isLeftPanel)
@@ -355,6 +513,8 @@
         }
 
         val arrowPath = calculateArrowPath(dx = dx, dy = dy)
+        val arrowPaint = arrowPaint
+                .apply { alpha = (255 * min(arrowAlpha.pos, backgroundAlpha.pos)).toInt() }
         canvas.drawPath(arrowPath, arrowPaint)
         canvas.restore()
 
@@ -372,26 +532,17 @@
     }
 
     private fun RectF.toPathWithRoundCorners(
-        topLeft: Float = 0f,
-        topRight: Float = 0f,
-        bottomRight: Float = 0f,
-        bottomLeft: Float = 0f
+            topLeft: Float = 0f,
+            topRight: Float = 0f,
+            bottomRight: Float = 0f,
+            bottomLeft: Float = 0f
     ): Path = Path().apply {
         val corners = floatArrayOf(
-            topLeft, topLeft,
-            topRight, topRight,
-            bottomRight, bottomRight,
-            bottomLeft, bottomLeft
+                topLeft, topLeft,
+                topRight, topRight,
+                bottomRight, bottomRight,
+                bottomLeft, bottomLeft
         )
         addRoundRect(this@toPathWithRoundCorners, corners, Path.Direction.CW)
     }
-
-    fun cancelAlphaAnimations() {
-        alphaAnimation.cancel()
-        alpha = 1f
-    }
-
-    fun fadeOut() {
-        alphaAnimation.animateToFinalPosition(0f)
-    }
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index 6e927b0..367d125 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -24,18 +24,16 @@
 import android.os.SystemClock
 import android.os.VibrationEffect
 import android.util.Log
-import android.util.MathUtils.constrain
-import android.util.MathUtils.saturate
+import android.util.MathUtils
 import android.view.Gravity
 import android.view.MotionEvent
 import android.view.VelocityTracker
-import android.view.View
 import android.view.ViewConfiguration
 import android.view.WindowManager
-import android.view.animation.DecelerateInterpolator
-import android.view.animation.PathInterpolator
+import androidx.annotation.VisibleForTesting
+import androidx.core.os.postDelayed
+import androidx.core.view.isVisible
 import androidx.dynamicanimation.animation.DynamicAnimation
-import androidx.dynamicanimation.animation.SpringForce
 import com.android.internal.util.LatencyTracker
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.NavigationEdgeBackPlugin
@@ -50,58 +48,42 @@
 import kotlin.math.sign
 
 private const val TAG = "BackPanelController"
-private const val DEBUG = false
-
 private const val ENABLE_FAILSAFE = true
 
-private const val FAILSAFE_DELAY_MS: Long = 350
+private const val PX_PER_SEC = 1000
+private const val PX_PER_MS = 1
 
-/**
- * The time required between the arrow-appears vibration effect and the back-committed vibration
- * effect. If the arrow is flung quickly, the phone only vibrates once. However, if the arrow is
- * held on the screen for a long time, it will vibrate a second time when the back gesture is
- * committed.
- */
-private const val GESTURE_DURATION_FOR_CLICK_MS = 400
+internal const val MIN_DURATION_ACTIVE_ANIMATION = 300L
+private const val MIN_DURATION_CANCELLED_ANIMATION = 200L
+private const val MIN_DURATION_COMMITTED_ANIMATION = 200L
+private const val MIN_DURATION_INACTIVE_BEFORE_FLUNG_ANIMATION = 50L
+private const val MIN_DURATION_CONSIDERED_AS_FLING = 100L
 
-/**
- * The min duration arrow remains on screen during a fling event.
- */
-private const val FLING_MIN_APPEARANCE_DURATION = 235L
+private const val FAILSAFE_DELAY_MS = 350L
+private const val POP_ON_FLING_DELAY = 160L
 
-/**
- * The min duration arrow remains on screen during a fling event.
- */
-private const val MIN_FLING_VELOCITY = 3000
+internal val VIBRATE_ACTIVATED_EFFECT =
+        VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)
 
-/**
- * The amount of rubber banding we do for the vertical translation
- */
-private const val RUBBER_BAND_AMOUNT = 15
+internal val VIBRATE_DEACTIVATED_EFFECT =
+        VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK)
 
-private const val ARROW_APPEAR_STIFFNESS = 600f
-private const val ARROW_APPEAR_DAMPING_RATIO = 0.4f
-private const val ARROW_DISAPPEAR_STIFFNESS = 1200f
-private const val ARROW_DISAPPEAR_DAMPING_RATIO = SpringForce.DAMPING_RATIO_NO_BOUNCY
+private const val DEBUG = false
 
-/**
- * The interpolator used to rubber band
- */
-private val RUBBER_BAND_INTERPOLATOR = PathInterpolator(1.0f / 5.0f, 1.0f, 1.0f, 1.0f)
-
-private val DECELERATE_INTERPOLATOR = DecelerateInterpolator()
-
-private val DECELERATE_INTERPOLATOR_SLOW = DecelerateInterpolator(0.7f)
-
-class BackPanelController private constructor(
-    context: Context,
-    private val windowManager: WindowManager,
-    private val viewConfiguration: ViewConfiguration,
-    @Main private val mainHandler: Handler,
-    private val vibratorHelper: VibratorHelper,
-    private val configurationController: ConfigurationController,
-    latencyTracker: LatencyTracker
-) : ViewController<BackPanel>(BackPanel(context, latencyTracker)), NavigationEdgeBackPlugin {
+class BackPanelController internal constructor(
+        context: Context,
+        private val windowManager: WindowManager,
+        private val viewConfiguration: ViewConfiguration,
+        @Main private val mainHandler: Handler,
+        private val vibratorHelper: VibratorHelper,
+        private val configurationController: ConfigurationController,
+        private val latencyTracker: LatencyTracker
+) : ViewController<BackPanel>(
+        BackPanel(
+                context,
+                latencyTracker
+        )
+), NavigationEdgeBackPlugin {
 
     /**
      * Injectable instance to create a new BackPanelController.
@@ -110,44 +92,44 @@
      * BackPanelController, and we need to match EdgeBackGestureHandler's context.
      */
     class Factory @Inject constructor(
-        private val windowManager: WindowManager,
-        private val viewConfiguration: ViewConfiguration,
-        @Main private val mainHandler: Handler,
-        private val vibratorHelper: VibratorHelper,
-        private val configurationController: ConfigurationController,
-        private val latencyTracker: LatencyTracker
+            private val windowManager: WindowManager,
+            private val viewConfiguration: ViewConfiguration,
+            @Main private val mainHandler: Handler,
+            private val vibratorHelper: VibratorHelper,
+            private val configurationController: ConfigurationController,
+            private val latencyTracker: LatencyTracker
     ) {
         /** Construct a [BackPanelController].  */
         fun create(context: Context): BackPanelController {
             val backPanelController = BackPanelController(
-                context,
-                windowManager,
-                viewConfiguration,
-                mainHandler,
-                vibratorHelper,
-                configurationController,
-                latencyTracker
+                    context,
+                    windowManager,
+                    viewConfiguration,
+                    mainHandler,
+                    vibratorHelper,
+                    configurationController,
+                    latencyTracker
             )
             backPanelController.init()
             return backPanelController
         }
     }
 
-    private var params: EdgePanelParams = EdgePanelParams(resources)
-    private var currentState: GestureState = GestureState.GONE
+    @VisibleForTesting
+    internal var params: EdgePanelParams = EdgePanelParams(resources)
+    @VisibleForTesting
+    internal var currentState: GestureState = GestureState.GONE
     private var previousState: GestureState = GestureState.GONE
 
-    // Phone should only vibrate the first time the arrow is activated
-    private var hasHapticPlayed = false
-
     // Screen attributes
     private lateinit var layoutParams: WindowManager.LayoutParams
     private val displaySize = Point()
 
     private lateinit var backCallback: NavigationEdgeBackPlugin.BackCallback
-
+    private var previousXTranslationOnActiveOffset = 0f
     private var previousXTranslation = 0f
     private var totalTouchDelta = 0f
+    private var touchDeltaStartX = 0f
     private var velocityTracker: VelocityTracker? = null
         set(value) {
             if (field != value) field?.recycle()
@@ -161,8 +143,18 @@
     // The x,y position of the first touch event
     private var startX = 0f
     private var startY = 0f
+    private var startIsLeft: Boolean? = null
 
-    private var gestureStartTime = 0L
+    private var gestureSinceActionDown = 0L
+    private var gestureEntryTime = 0L
+    private var gestureActiveTime = 0L
+    private var gestureInactiveOrEntryTime = 0L
+    private var gestureArrowStrokeVisibleTime = 0L
+
+    private val elapsedTimeSinceActionDown
+        get() = SystemClock.uptimeMillis() - gestureSinceActionDown
+    private val elapsedTimeSinceEntry
+        get() = SystemClock.uptimeMillis() - gestureEntryTime
 
     // Whether the current gesture has moved a sufficiently large amount,
     // so that we can unambiguously start showing the ENTRY animation
@@ -170,7 +162,7 @@
 
     private val failsafeRunnable = Runnable { onFailsafe() }
 
-    private enum class GestureState {
+    internal enum class GestureState {
         /* Arrow is off the screen and invisible */
         GONE,
 
@@ -191,17 +183,6 @@
 
         /* back action currently cancelling, arrow soon to be GONE */
         CANCELLED;
-
-        /**
-         * @return true if the current state responds to touch move events in some way (e.g. by
-         * stretching the back indicator)
-         */
-        fun isInteractive(): Boolean {
-            return when (this) {
-                ENTRY, ACTIVE, INACTIVE -> true
-                GONE, FLUNG, COMMITTED, CANCELLED -> false
-            }
-        }
     }
 
     /**
@@ -209,50 +190,43 @@
      * runnable is not called if the animation is cancelled
      */
     inner class DelayedOnAnimationEndListener internal constructor(
-        private val handler: Handler,
-        private val runnable: Runnable,
-        private val minDuration: Long
+            private val handler: Handler,
+            private val runnableDelay: Long,
+            val runnable: Runnable,
     ) : DynamicAnimation.OnAnimationEndListener {
+
         override fun onAnimationEnd(
-            animation: DynamicAnimation<*>,
-            canceled: Boolean,
-            value: Float,
-            velocity: Float
+                animation: DynamicAnimation<*>,
+                canceled: Boolean,
+                value: Float,
+                velocity: Float
         ) {
             animation.removeEndListener(this)
+
             if (!canceled) {
-                // Total elapsed time of the gesture and the animation
-                val totalElapsedTime = SystemClock.uptimeMillis() - gestureStartTime
+
                 // The delay between finishing this animation and starting the runnable
-                val delay = max(0, minDuration - totalElapsedTime)
+                val delay = max(0, runnableDelay - elapsedTimeSinceEntry)
+
                 handler.postDelayed(runnable, delay)
             }
         }
 
-        internal fun runNow() {
-            runnable.run()
-        }
+        internal fun run() = runnable.run()
     }
 
-    private val setCommittedEndListener =
-        DelayedOnAnimationEndListener(
-            mainHandler,
-            { updateArrowState(GestureState.COMMITTED) },
-            minDuration = FLING_MIN_APPEARANCE_DURATION
-        )
+    private val onEndSetCommittedStateListener = DelayedOnAnimationEndListener(mainHandler, 0L) {
+        updateArrowState(GestureState.COMMITTED)
+    }
 
-    private val setGoneEndListener =
-        DelayedOnAnimationEndListener(
-            mainHandler,
-            {
+
+    private val onEndSetGoneStateListener =
+            DelayedOnAnimationEndListener(mainHandler, runnableDelay = 0L) {
                 cancelFailsafe()
                 updateArrowState(GestureState.GONE)
-            },
-            minDuration = 0
-        )
+            }
 
-    // Vibration
-    private var vibrationTime: Long = 0
+    private val playAnimationThenSetGoneOnAlphaEnd = Runnable { playAnimationThenSetGoneEnd() }
 
     // Minimum of the screen's width or the predefined threshold
     private var fullyStretchedThreshold = 0f
@@ -279,7 +253,7 @@
         updateConfiguration()
         updateArrowDirection(configurationController.isLayoutRtl)
         updateArrowState(GestureState.GONE, force = true)
-        updateRestingArrowDimens(animated = false, currentState)
+        updateRestingArrowDimens()
         configurationController.addCallback(configurationListener)
     }
 
@@ -296,22 +270,57 @@
         velocityTracker!!.addMovement(event)
         when (event.actionMasked) {
             MotionEvent.ACTION_DOWN -> {
-                resetOnDown()
+                gestureSinceActionDown = SystemClock.uptimeMillis()
+                cancelAllPendingAnimations()
                 startX = event.x
                 startY = event.y
-                gestureStartTime = SystemClock.uptimeMillis()
+
+                updateArrowState(GestureState.GONE)
+                updateYStartPosition(startY)
+
+                // reset animation properties
+                startIsLeft = mView.isLeftPanel
+                hasPassedDragSlop = false
+                mView.resetStretch()
             }
             MotionEvent.ACTION_MOVE -> {
-                // only go to the ENTRY state after some minimum motion has occurred
                 if (dragSlopExceeded(event.x, startX)) {
                     handleMoveEvent(event)
                 }
             }
             MotionEvent.ACTION_UP -> {
-                if (currentState == GestureState.ACTIVE) {
-                    updateArrowState(if (isFlung()) GestureState.FLUNG else GestureState.COMMITTED)
-                } else if (currentState != GestureState.GONE) { // if invisible, skip animation
-                    updateArrowState(GestureState.CANCELLED)
+                when (currentState) {
+                    GestureState.ENTRY -> {
+                        if (isFlungAwayFromEdge(endX = event.x)) {
+                            updateArrowState(GestureState.ACTIVE)
+                            updateArrowState(GestureState.FLUNG)
+                        } else {
+                            updateArrowState(GestureState.CANCELLED)
+                        }
+                    }
+                    GestureState.INACTIVE -> {
+                        if (isFlungAwayFromEdge(endX = event.x)) {
+                            mainHandler.postDelayed(MIN_DURATION_INACTIVE_BEFORE_FLUNG_ANIMATION) {
+                                updateArrowState(GestureState.ACTIVE)
+                                updateArrowState(GestureState.FLUNG)
+                            }
+                        } else {
+                            updateArrowState(GestureState.CANCELLED)
+                        }
+                    }
+                    GestureState.ACTIVE -> {
+                        if (elapsedTimeSinceEntry < MIN_DURATION_CONSIDERED_AS_FLING) {
+                            updateArrowState(GestureState.FLUNG)
+                        } else {
+                            updateArrowState(GestureState.COMMITTED)
+                        }
+                    }
+                    GestureState.GONE,
+                    GestureState.FLUNG,
+                    GestureState.COMMITTED,
+                    GestureState.CANCELLED -> {
+                        updateArrowState(GestureState.CANCELLED)
+                    }
                 }
                 velocityTracker = null
             }
@@ -325,6 +334,14 @@
         }
     }
 
+    private fun cancelAllPendingAnimations() {
+        cancelFailsafe()
+        mView.cancelAnimations()
+        mainHandler.removeCallbacks(onEndSetCommittedStateListener.runnable)
+        mainHandler.removeCallbacks(onEndSetGoneStateListener.runnable)
+        mainHandler.removeCallbacks(playAnimationThenSetGoneOnAlphaEnd)
+    }
+
     /**
      * Returns false until the current gesture exceeds the touch slop threshold,
      * and returns true thereafter (we reset on the subsequent back gesture).
@@ -335,7 +352,7 @@
     private fun dragSlopExceeded(curX: Float, startX: Float): Boolean {
         if (hasPassedDragSlop) return true
 
-        if (abs(curX - startX) > viewConfiguration.scaledTouchSlop) {
+        if (abs(curX - startX) > viewConfiguration.scaledEdgeSlop) {
             // Reset the arrow to the side
             updateArrowState(GestureState.ENTRY)
 
@@ -348,39 +365,46 @@
     }
 
     private fun updateArrowStateOnMove(yTranslation: Float, xTranslation: Float) {
-        if (!currentState.isInteractive())
-            return
+
+        val isWithinYActivationThreshold = xTranslation * 2 >= yTranslation
 
         when (currentState) {
-            // Check if we should transition from ENTRY to ACTIVE
-            GestureState.ENTRY ->
-                if (xTranslation > params.swipeTriggerThreshold) {
+            GestureState.ENTRY -> {
+                if (xTranslation > params.staticTriggerThreshold) {
                     updateArrowState(GestureState.ACTIVE)
                 }
+            }
+            GestureState.ACTIVE -> {
+                val isPastDynamicDeactivationThreshold =
+                        totalTouchDelta <= params.deactivationSwipeTriggerThreshold
+                val isMinDurationElapsed =
+                        elapsedTimeSinceActionDown > MIN_DURATION_ACTIVE_ANIMATION
 
-            // Abort if we had continuous motion toward the edge for a while, OR the direction
-            // in Y is bigger than X * 2
-            GestureState.ACTIVE ->
-                if ((totalTouchDelta < 0 && -totalTouchDelta > params.minDeltaForSwitch) ||
-                    (yTranslation > xTranslation * 2)
+                if (isMinDurationElapsed && (!isWithinYActivationThreshold ||
+                                isPastDynamicDeactivationThreshold)
                 ) {
                     updateArrowState(GestureState.INACTIVE)
                 }
+            }
+            GestureState.INACTIVE -> {
+                val isPastStaticThreshold =
+                        xTranslation > params.staticTriggerThreshold
+                val isPastDynamicReactivationThreshold = totalTouchDelta > 0 &&
+                        abs(totalTouchDelta) >=
+                        params.reactivationTriggerThreshold
 
-            //  Re-activate if we had continuous motion away from the edge for a while
-            GestureState.INACTIVE ->
-                if (totalTouchDelta > 0 && totalTouchDelta > params.minDeltaForSwitch) {
+                if (isPastStaticThreshold &&
+                        isPastDynamicReactivationThreshold &&
+                        isWithinYActivationThreshold
+                ) {
                     updateArrowState(GestureState.ACTIVE)
                 }
-
-            // By default assume the current direction is kept
+            }
             else -> {}
         }
     }
 
     private fun handleMoveEvent(event: MotionEvent) {
-        if (!currentState.isInteractive())
-            return
 
         val x = event.x
         val y = event.y
@@ -400,23 +424,44 @@
         previousXTranslation = xTranslation
 
         if (abs(xDelta) > 0) {
-            if (sign(xDelta) == sign(totalTouchDelta)) {
+            val range =
+                params.run { deactivationSwipeTriggerThreshold..reactivationTriggerThreshold }
+            val isTouchInContinuousDirection =
+                    sign(xDelta) == sign(totalTouchDelta) || totalTouchDelta in range
+
+            if (isTouchInContinuousDirection) {
                 // Direction has NOT changed, so keep counting the delta
                 totalTouchDelta += xDelta
             } else {
                 // Direction has changed, so reset the delta
                 totalTouchDelta = xDelta
+                touchDeltaStartX = x
             }
         }
 
         updateArrowStateOnMove(yTranslation, xTranslation)
+
         when (currentState) {
-            GestureState.ACTIVE ->
-                stretchActiveBackIndicator(fullScreenStretchProgress(xTranslation))
-            GestureState.ENTRY ->
-                stretchEntryBackIndicator(preThresholdStretchProgress(xTranslation))
-            GestureState.INACTIVE ->
-                mView.resetStretch()
+            GestureState.ACTIVE -> {
+                stretchActiveBackIndicator(fullScreenProgress(xTranslation))
+            }
+            GestureState.ENTRY -> {
+                val progress = staticThresholdProgress(xTranslation)
+                stretchEntryBackIndicator(progress)
+
+                params.arrowStrokeAlphaSpring.get(progress).takeIf { it.isNewState }?.let {
+                    mView.popArrowAlpha(0f, it.value)
+                }
+            }
+            GestureState.INACTIVE -> {
+                val progress = reactivationThresholdProgress(totalTouchDelta)
+                stretchInactiveBackIndicator(progress)
+
+                params.arrowStrokeAlphaSpring.get(progress).takeIf { it.isNewState }?.let {
+                    gestureArrowStrokeVisibleTime = SystemClock.uptimeMillis()
+                    mView.popArrowAlpha(0f, it.value)
+                }
+            }
             else -> {}
         }
 
@@ -427,21 +472,22 @@
     private fun setVerticalTranslation(yOffset: Float) {
         val yTranslation = abs(yOffset)
         val maxYOffset = (mView.height - params.entryIndicator.backgroundDimens.height) / 2f
-        val yProgress = saturate(yTranslation / (maxYOffset * RUBBER_BAND_AMOUNT))
-        mView.animateVertically(
-            RUBBER_BAND_INTERPOLATOR.getInterpolation(yProgress) * maxYOffset *
+        val rubberbandAmount = 15f
+        val yProgress = MathUtils.saturate(yTranslation / (maxYOffset * rubberbandAmount))
+        val yPosition = params.translationInterpolator.getInterpolation(yProgress) *
+                maxYOffset *
                 sign(yOffset)
-        )
+        mView.animateVertically(yPosition)
     }
 
     /**
-     * @return the relative position of the drag from the time after the arrow is activated until
+     * Tracks the relative position of the drag from the time after the arrow is activated until
      * the arrow is fully stretched (between 0.0 - 1.0f)
      */
-    private fun fullScreenStretchProgress(xTranslation: Float): Float {
-        return saturate(
-            (xTranslation - params.swipeTriggerThreshold) /
-                (fullyStretchedThreshold - params.swipeTriggerThreshold)
+    private fun fullScreenProgress(xTranslation: Float): Float {
+        return MathUtils.saturate(
+                (xTranslation - previousXTranslationOnActiveOffset) /
+                        (fullyStretchedThreshold - previousXTranslationOnActiveOffset)
         )
     }
 
@@ -449,26 +495,74 @@
      * Tracks the relative position of the drag from the entry until the threshold where the arrow
      * activates (between 0.0 - 1.0f)
      */
-    private fun preThresholdStretchProgress(xTranslation: Float): Float {
-        return saturate(xTranslation / params.swipeTriggerThreshold)
+    private fun staticThresholdProgress(xTranslation: Float): Float {
+        return MathUtils.saturate(xTranslation / params.staticTriggerThreshold)
+    }
+
+    private fun reactivationThresholdProgress(totalTouchDelta: Float): Float {
+        return MathUtils.saturate(totalTouchDelta / params.reactivationTriggerThreshold)
     }
 
     private fun stretchActiveBackIndicator(progress: Float) {
-        val rubberBandIterpolation = RUBBER_BAND_INTERPOLATOR.getInterpolation(progress)
         mView.setStretch(
-            horizontalTranslationStretchAmount = rubberBandIterpolation,
-            arrowStretchAmount = rubberBandIterpolation,
-            backgroundWidthStretchAmount = DECELERATE_INTERPOLATOR_SLOW.getInterpolation(progress),
-            params.fullyStretchedIndicator
+                horizontalTranslationStretchAmount = params.translationInterpolator
+                        .getInterpolation(progress),
+                arrowStretchAmount = params.arrowAngleInterpolator.getInterpolation(progress),
+                backgroundWidthStretchAmount = params.activeWidthInterpolator
+                        .getInterpolation(progress),
+                backgroundAlphaStretchAmount = 1f,
+                backgroundHeightStretchAmount = 1f,
+                arrowAlphaStretchAmount = 1f,
+                edgeCornerStretchAmount = 1f,
+                farCornerStretchAmount = 1f,
+                fullyStretchedDimens = params.fullyStretchedIndicator
         )
     }
 
     private fun stretchEntryBackIndicator(progress: Float) {
         mView.setStretch(
-            horizontalTranslationStretchAmount = 0f,
-            arrowStretchAmount = RUBBER_BAND_INTERPOLATOR.getInterpolation(progress),
-            backgroundWidthStretchAmount = DECELERATE_INTERPOLATOR.getInterpolation(progress),
-            params.preThresholdIndicator
+                horizontalTranslationStretchAmount = 0f,
+                arrowStretchAmount = params.arrowAngleInterpolator
+                        .getInterpolation(progress),
+                backgroundWidthStretchAmount = params.entryWidthInterpolator
+                        .getInterpolation(progress),
+                backgroundHeightStretchAmount = params.heightInterpolator
+                        .getInterpolation(progress),
+                backgroundAlphaStretchAmount = 1f,
+                arrowAlphaStretchAmount = params.arrowStrokeAlphaInterpolator.get(progress).value,
+                edgeCornerStretchAmount = params.edgeCornerInterpolator.getInterpolation(progress),
+                farCornerStretchAmount = params.farCornerInterpolator.getInterpolation(progress),
+                fullyStretchedDimens = params.preThresholdIndicator
+        )
+    }
+
+    private var previousPreThresholdWidthInterpolator = params.entryWidthTowardsEdgeInterpolator
+    fun preThresholdWidthStretchAmount(progress: Float): Float {
+        val interpolator = run {
+            val isPastSlop = abs(totalTouchDelta) > ViewConfiguration.get(context).scaledTouchSlop
+            if (isPastSlop) {
+                if (totalTouchDelta > 0) {
+                    params.entryWidthInterpolator
+                } else params.entryWidthTowardsEdgeInterpolator
+            } else {
+                previousPreThresholdWidthInterpolator
+            }.also { previousPreThresholdWidthInterpolator = it }
+        }
+        return interpolator.getInterpolation(progress).coerceAtLeast(0f)
+    }
+
+    private fun stretchInactiveBackIndicator(progress: Float) {
+        mView.setStretch(
+                horizontalTranslationStretchAmount = 0f,
+                arrowStretchAmount = params.arrowAngleInterpolator.getInterpolation(progress),
+                backgroundWidthStretchAmount = preThresholdWidthStretchAmount(progress),
+                backgroundHeightStretchAmount = params.heightInterpolator
+                        .getInterpolation(progress),
+                backgroundAlphaStretchAmount = 1f,
+                arrowAlphaStretchAmount = params.arrowStrokeAlphaInterpolator.get(progress).value,
+                edgeCornerStretchAmount = params.edgeCornerInterpolator.getInterpolation(progress),
+                farCornerStretchAmount = params.farCornerInterpolator.getInterpolation(progress),
+                fullyStretchedDimens = params.preThresholdIndicator
         )
     }
 
@@ -486,8 +580,7 @@
         }
     }
 
-    override fun setInsets(insetLeft: Int, insetRight: Int) {
-    }
+    override fun setInsets(insetLeft: Int, insetRight: Int) = Unit
 
     override fun setBackCallback(callback: NavigationEdgeBackPlugin.BackCallback) {
         backCallback = callback
@@ -498,62 +591,54 @@
         windowManager.addView(mView, layoutParams)
     }
 
-    private fun isFlung() = velocityTracker!!.run {
-        computeCurrentVelocity(1000)
-        abs(xVelocity) > MIN_FLING_VELOCITY
+    private fun isDragAwayFromEdge(velocityPxPerSecThreshold: Int = 0) = velocityTracker!!.run {
+        computeCurrentVelocity(PX_PER_SEC)
+        val velocity = xVelocity.takeIf { mView.isLeftPanel } ?: (xVelocity * -1)
+        velocity > velocityPxPerSecThreshold
     }
 
-    private fun playFlingBackAnimation() {
-        playAnimation(setCommittedEndListener)
+    private fun isFlungAwayFromEdge(endX: Float, startX: Float = touchDeltaStartX): Boolean {
+        val minDistanceConsideredForFling = ViewConfiguration.get(context).scaledTouchSlop
+        val flingDistance = abs(endX - startX)
+        val isPastFlingVelocity = isDragAwayFromEdge(
+                velocityPxPerSecThreshold =
+                ViewConfiguration.get(context).scaledMinimumFlingVelocity)
+        return flingDistance > minDistanceConsideredForFling && isPastFlingVelocity
     }
 
-    private fun playCommitBackAnimation() {
-        // Check if we should vibrate again
-        if (previousState != GestureState.FLUNG) {
-            velocityTracker!!.computeCurrentVelocity(1000)
-            val isSlow = abs(velocityTracker!!.xVelocity) < 500
-            val hasNotVibratedRecently =
-                SystemClock.uptimeMillis() - vibrationTime >= GESTURE_DURATION_FOR_CLICK_MS
-            if (isSlow || hasNotVibratedRecently) {
-                vibratorHelper.vibrate(VibrationEffect.EFFECT_CLICK)
-            }
-        }
-        // Dispatch the actual back trigger
-        if (DEBUG) Log.d(TAG, "playCommitBackAnimation() invoked triggerBack() on backCallback")
-        backCallback.triggerBack()
-
-        playAnimation(setGoneEndListener)
-    }
-
-    private fun playCancelBackAnimation() {
-        backCallback.cancelBack()
-        playAnimation(setGoneEndListener)
-    }
-
-    /**
-     * @return true if the animation is running, false otherwise. Some transitions don't animate
-     */
-    private fun playAnimation(endListener: DelayedOnAnimationEndListener) {
-        updateRestingArrowDimens(animated = true, currentState)
-
-        if (!mView.addEndListener(endListener)) {
+    private fun playHorizontalAnimationThen(onEnd: DelayedOnAnimationEndListener) {
+        updateRestingArrowDimens()
+        if (!mView.addAnimationEndListener(mView.horizontalTranslation, onEnd)) {
             scheduleFailsafe()
         }
     }
 
-    private fun resetOnDown() {
-        hasPassedDragSlop = false
-        hasHapticPlayed = false
-        totalTouchDelta = 0f
-        vibrationTime = 0
-        cancelFailsafe()
+    private fun playAnimationThenSetGoneEnd() {
+        updateRestingArrowDimens()
+        if (!mView.addAnimationEndListener(mView.backgroundAlpha, onEndSetGoneStateListener)) {
+            scheduleFailsafe()
+        }
     }
 
-    private fun updateYPosition(touchY: Float) {
+    private fun playWithBackgroundWidthAnimation(
+            onEnd: DelayedOnAnimationEndListener,
+            delay: Long = 0L
+    ) {
+        if (delay == 0L) {
+            updateRestingArrowDimens()
+            if (!mView.addAnimationEndListener(mView.backgroundWidth, onEnd)) {
+                scheduleFailsafe()
+            }
+        } else {
+            mainHandler.postDelayed(delay) { playWithBackgroundWidthAnimation(onEnd, delay = 0L) }
+        }
+    }
+
+    private fun updateYStartPosition(touchY: Float) {
         var yPosition = touchY - params.fingerOffset
         yPosition = max(yPosition, params.minArrowYPosition.toFloat())
         yPosition -= layoutParams.height / 2.0f
-        layoutParams.y = constrain(yPosition.toInt(), 0, displaySize.y)
+        layoutParams.y = MathUtils.constrain(yPosition.toInt(), 0, displaySize.y)
     }
 
     override fun setDisplaySize(displaySize: Point) {
@@ -564,53 +649,135 @@
     /**
      * Updates resting arrow and background size not accounting for stretch
      */
-    private fun updateRestingArrowDimens(animated: Boolean, currentState: GestureState) {
-        if (animated) {
-            when (currentState) {
-                GestureState.ENTRY, GestureState.ACTIVE, GestureState.FLUNG ->
-                    mView.setArrowStiffness(ARROW_APPEAR_STIFFNESS, ARROW_APPEAR_DAMPING_RATIO)
-                GestureState.CANCELLED -> mView.fadeOut()
-                else ->
-                    mView.setArrowStiffness(
-                        ARROW_DISAPPEAR_STIFFNESS,
-                        ARROW_DISAPPEAR_DAMPING_RATIO
-                    )
+    private fun updateRestingArrowDimens() {
+        when (currentState) {
+            GestureState.GONE,
+            GestureState.ENTRY -> {
+                mView.setSpring(
+                        arrowLength = params.entryIndicator.arrowDimens.lengthSpring,
+                        arrowHeight = params.entryIndicator.arrowDimens.heightSpring,
+                        arrowAlpha = params.entryIndicator.arrowDimens.alphaSpring,
+                        scale = params.entryIndicator.scaleSpring,
+                        verticalTranslation = params.entryIndicator.verticalTranslationSpring,
+                        horizontalTranslation = params.entryIndicator.horizontalTranslationSpring,
+                        backgroundAlpha = params.entryIndicator.backgroundDimens.alphaSpring,
+                        backgroundWidth = params.entryIndicator.backgroundDimens.widthSpring,
+                        backgroundHeight = params.entryIndicator.backgroundDimens.heightSpring,
+                        backgroundEdgeCornerRadius = params.entryIndicator.backgroundDimens
+                                .edgeCornerRadiusSpring,
+                        backgroundFarCornerRadius = params.entryIndicator.backgroundDimens
+                                .farCornerRadiusSpring,
+                )
             }
+            GestureState.INACTIVE -> {
+                mView.setSpring(
+                        arrowLength = params.preThresholdIndicator.arrowDimens.lengthSpring,
+                        arrowHeight = params.preThresholdIndicator.arrowDimens.heightSpring,
+                        horizontalTranslation = params.preThresholdIndicator
+                                .horizontalTranslationSpring,
+                        scale = params.preThresholdIndicator.scaleSpring,
+                        backgroundWidth = params.preThresholdIndicator.backgroundDimens
+                                .widthSpring,
+                        backgroundHeight = params.preThresholdIndicator.backgroundDimens
+                                .heightSpring,
+                        backgroundEdgeCornerRadius = params.preThresholdIndicator.backgroundDimens
+                                .edgeCornerRadiusSpring,
+                        backgroundFarCornerRadius = params.preThresholdIndicator.backgroundDimens
+                                .farCornerRadiusSpring,
+                )
+            }
+            GestureState.ACTIVE -> {
+                mView.setSpring(
+                        arrowLength = params.activeIndicator.arrowDimens.lengthSpring,
+                        arrowHeight = params.activeIndicator.arrowDimens.heightSpring,
+                        scale = params.activeIndicator.scaleSpring,
+                        horizontalTranslation = params.activeIndicator.horizontalTranslationSpring,
+                        backgroundWidth = params.activeIndicator.backgroundDimens.widthSpring,
+                        backgroundHeight = params.activeIndicator.backgroundDimens.heightSpring,
+                        backgroundEdgeCornerRadius = params.activeIndicator.backgroundDimens
+                                .edgeCornerRadiusSpring,
+                        backgroundFarCornerRadius = params.activeIndicator.backgroundDimens
+                                .farCornerRadiusSpring,
+                )
+            }
+            GestureState.FLUNG -> {
+                mView.setSpring(
+                        arrowLength = params.flungIndicator.arrowDimens.lengthSpring,
+                        arrowHeight = params.flungIndicator.arrowDimens.heightSpring,
+                        backgroundWidth = params.flungIndicator.backgroundDimens.widthSpring,
+                        backgroundHeight = params.flungIndicator.backgroundDimens.heightSpring,
+                        backgroundEdgeCornerRadius = params.flungIndicator.backgroundDimens
+                                .edgeCornerRadiusSpring,
+                        backgroundFarCornerRadius = params.flungIndicator.backgroundDimens
+                                .farCornerRadiusSpring,
+                )
+            }
+            GestureState.COMMITTED -> {
+                mView.setSpring(
+                        arrowLength = params.committedIndicator.arrowDimens.lengthSpring,
+                        arrowHeight = params.committedIndicator.arrowDimens.heightSpring,
+                        scale = params.committedIndicator.scaleSpring,
+                        backgroundWidth = params.committedIndicator.backgroundDimens.widthSpring,
+                        backgroundHeight = params.committedIndicator.backgroundDimens.heightSpring,
+                        backgroundEdgeCornerRadius = params.committedIndicator.backgroundDimens
+                                .edgeCornerRadiusSpring,
+                        backgroundFarCornerRadius = params.committedIndicator.backgroundDimens
+                                .farCornerRadiusSpring,
+                )
+            }
+            else -> {}
         }
+
         mView.setRestingDimens(
-            restingParams = EdgePanelParams.BackIndicatorDimens(
-                horizontalTranslation = when (currentState) {
-                    GestureState.GONE -> -params.activeIndicator.backgroundDimens.width
-                    // Position the committed arrow slightly further off the screen so we  do not
-                    // see part of it bouncing
-                    GestureState.COMMITTED ->
-                        -params.activeIndicator.backgroundDimens.width * 1.5f
-                    GestureState.FLUNG -> params.fullyStretchedIndicator.horizontalTranslation
-                    GestureState.ACTIVE -> params.activeIndicator.horizontalTranslation
-                    GestureState.ENTRY, GestureState.INACTIVE, GestureState.CANCELLED ->
-                        params.entryIndicator.horizontalTranslation
-                },
-                arrowDimens = when (currentState) {
-                    GestureState.ACTIVE, GestureState.INACTIVE,
-                    GestureState.COMMITTED, GestureState.FLUNG -> params.activeIndicator.arrowDimens
-                    GestureState.CANCELLED -> params.cancelledArrowDimens
-                    GestureState.GONE, GestureState.ENTRY -> params.entryIndicator.arrowDimens
-                },
-                backgroundDimens = when (currentState) {
-                    GestureState.GONE, GestureState.ENTRY -> params.entryIndicator.backgroundDimens
-                    else ->
-                        params.activeIndicator.backgroundDimens.copy(
-                            edgeCornerRadius =
-                            if (currentState == GestureState.INACTIVE ||
-                                currentState == GestureState.CANCELLED
-                            )
-                                params.cancelledEdgeCornerRadius
-                            else
-                                params.activeIndicator.backgroundDimens.edgeCornerRadius
-                        )
-                }
-            ),
-            animate = animated
+                animate = !(currentState == GestureState.FLUNG ||
+                        currentState == GestureState.COMMITTED),
+                restingParams = EdgePanelParams.BackIndicatorDimens(
+                        scale = when (currentState) {
+                            GestureState.ACTIVE,
+                            GestureState.FLUNG,
+                            -> params.activeIndicator.scale
+                            GestureState.COMMITTED -> params.committedIndicator.scale
+                            else -> params.preThresholdIndicator.scale
+                        },
+                        scalePivotX = when (currentState) {
+                            GestureState.GONE,
+                            GestureState.ENTRY,
+                            GestureState.INACTIVE,
+                            GestureState.CANCELLED -> params.preThresholdIndicator.scalePivotX
+                            else -> params.committedIndicator.scalePivotX
+                        },
+                        horizontalTranslation = when (currentState) {
+                            GestureState.GONE -> {
+                                params.activeIndicator.backgroundDimens.width?.times(-1)
+                            }
+                            GestureState.ENTRY,
+                            GestureState.INACTIVE -> params.entryIndicator.horizontalTranslation
+                            GestureState.FLUNG -> params.activeIndicator.horizontalTranslation
+                            GestureState.ACTIVE -> params.activeIndicator.horizontalTranslation
+                            GestureState.CANCELLED -> {
+                                params.cancelledIndicator.horizontalTranslation
+                            }
+                            else -> null
+                        },
+                        arrowDimens = when (currentState) {
+                            GestureState.GONE,
+                            GestureState.ENTRY,
+                            GestureState.INACTIVE -> params.entryIndicator.arrowDimens
+                            GestureState.ACTIVE -> params.activeIndicator.arrowDimens
+                            GestureState.FLUNG,
+                            GestureState.COMMITTED -> params.committedIndicator.arrowDimens
+                            GestureState.CANCELLED -> params.cancelledIndicator.arrowDimens
+                        },
+                        backgroundDimens = when (currentState) {
+                            GestureState.GONE,
+                            GestureState.ENTRY,
+                            GestureState.INACTIVE -> params.entryIndicator.backgroundDimens
+                            GestureState.ACTIVE -> params.activeIndicator.backgroundDimens
+                            GestureState.FLUNG -> params.activeIndicator.backgroundDimens
+                            GestureState.COMMITTED -> params.committedIndicator.backgroundDimens
+                            GestureState.CANCELLED -> params.cancelledIndicator.backgroundDimens
+                        }
+                )
         )
     }
 
@@ -623,42 +790,123 @@
     private fun updateArrowState(newState: GestureState, force: Boolean = false) {
         if (!force && currentState == newState) return
 
-        if (DEBUG) Log.d(TAG, "updateArrowState $currentState -> $newState")
         previousState = currentState
         currentState = newState
-        if (currentState == GestureState.GONE) {
-            mView.cancelAlphaAnimations()
-            mView.visibility = View.GONE
-        } else {
-            mView.visibility = View.VISIBLE
+
+        when (currentState) {
+            GestureState.CANCELLED -> {
+                backCallback.cancelBack()
+            }
+            GestureState.FLUNG,
+            GestureState.COMMITTED -> {
+                // When flung, trigger back immediately but don't fire again
+                // once state resolves to committed.
+                if (previousState != GestureState.FLUNG) backCallback.triggerBack()
+            }
+            GestureState.ENTRY,
+            GestureState.INACTIVE -> {
+                backCallback.setTriggerBack(false)
+            }
+            GestureState.ACTIVE -> {
+                backCallback.setTriggerBack(true)
+            }
+            GestureState.GONE -> { }
         }
 
         when (currentState) {
             // Transitioning to GONE never animates since the arrow is (presumably) already off the
             // screen
-            GestureState.GONE -> updateRestingArrowDimens(animated = false, currentState)
+            GestureState.GONE -> {
+                updateRestingArrowDimens()
+                mView.isVisible = false
+            }
             GestureState.ENTRY -> {
-                updateYPosition(startY)
-                updateRestingArrowDimens(animated = true, currentState)
+                mView.isVisible = true
+
+                updateRestingArrowDimens()
+                gestureEntryTime = SystemClock.uptimeMillis()
+                gestureInactiveOrEntryTime = SystemClock.uptimeMillis()
             }
             GestureState.ACTIVE -> {
-                updateRestingArrowDimens(animated = true, currentState)
-                // Vibrate the first time we transition to ACTIVE
-                if (!hasHapticPlayed) {
-                    hasHapticPlayed = true
-                    vibrationTime = SystemClock.uptimeMillis()
-                    vibratorHelper.vibrate(VibrationEffect.EFFECT_TICK)
+                previousXTranslationOnActiveOffset = previousXTranslation
+                gestureActiveTime = SystemClock.uptimeMillis()
+
+                updateRestingArrowDimens()
+
+                vibratorHelper.cancel()
+                mainHandler.postDelayed(10L) {
+                    vibratorHelper.vibrate(VIBRATE_ACTIVATED_EFFECT)
+                }
+
+                val startingVelocity = convertVelocityToSpringStartingVelocity(
+                    valueOnFastVelocity = 0f,
+                    valueOnSlowVelocity = if (previousState == GestureState.ENTRY) 2f else 4.5f
+                )
+
+                when (previousState) {
+                    GestureState.ENTRY,
+                    GestureState.INACTIVE -> {
+                        mView.popOffEdge(startingVelocity)
+                    }
+                    GestureState.COMMITTED -> {
+                        // if previous state was committed then this activation
+                        // was due to a quick second swipe. Don't pop the arrow this time
+                    }
+                    else -> { }
                 }
             }
+
             GestureState.INACTIVE -> {
-                updateRestingArrowDimens(animated = true, currentState)
+                gestureInactiveOrEntryTime = SystemClock.uptimeMillis()
+
+                val startingVelocity = convertVelocityToSpringStartingVelocity(
+                        valueOnFastVelocity = -1.05f,
+                        valueOnSlowVelocity = -1.50f
+                )
+                mView.popOffEdge(startingVelocity)
+
+                vibratorHelper.vibrate(VIBRATE_DEACTIVATED_EFFECT)
+                updateRestingArrowDimens()
             }
-            GestureState.FLUNG -> playFlingBackAnimation()
-            GestureState.COMMITTED -> playCommitBackAnimation()
-            GestureState.CANCELLED -> playCancelBackAnimation()
+            GestureState.FLUNG -> {
+                mainHandler.postDelayed(POP_ON_FLING_DELAY) { mView.popScale(1.9f) }
+                playHorizontalAnimationThen(onEndSetCommittedStateListener)
+            }
+            GestureState.COMMITTED -> {
+                if (previousState == GestureState.FLUNG) {
+                    playAnimationThenSetGoneEnd()
+                } else {
+                    mView.popScale(3f)
+                    mainHandler.postDelayed(
+                            playAnimationThenSetGoneOnAlphaEnd,
+                            MIN_DURATION_COMMITTED_ANIMATION
+                    )
+                }
+            }
+            GestureState.CANCELLED -> {
+                val delay = max(0, MIN_DURATION_CANCELLED_ANIMATION - elapsedTimeSinceEntry)
+                playWithBackgroundWidthAnimation(onEndSetGoneStateListener, delay)
+
+                params.arrowStrokeAlphaSpring.get(0f).takeIf { it.isNewState }?.let {
+                    mView.popArrowAlpha(0f, it.value)
+                }
+                mainHandler.postDelayed(10L) { vibratorHelper.cancel() }
+            }
         }
     }
 
+    private fun convertVelocityToSpringStartingVelocity(
+            valueOnFastVelocity: Float,
+            valueOnSlowVelocity: Float,
+    ): Float {
+        val factor = velocityTracker?.run {
+            computeCurrentVelocity(PX_PER_MS)
+            MathUtils.smoothStep(0f, 3f, abs(xVelocity))
+        } ?: valueOnFastVelocity
+
+        return MathUtils.lerp(valueOnFastVelocity, valueOnSlowVelocity, 1 - factor)
+    }
+
     private fun scheduleFailsafe() {
         if (!ENABLE_FAILSAFE) return
         cancelFailsafe()
@@ -685,24 +933,24 @@
     init {
         if (DEBUG) mView.drawDebugInfo = { canvas ->
             val debugStrings = listOf(
-                "$currentState",
-                "startX=$startX",
-                "startY=$startY",
-                "xDelta=${"%.1f".format(totalTouchDelta)}",
-                "xTranslation=${"%.1f".format(previousXTranslation)}",
-                "pre=${"%.0f".format(preThresholdStretchProgress(previousXTranslation) * 100)}%",
-                "post=${"%.0f".format(fullScreenStretchProgress(previousXTranslation) * 100)}%"
+                    "$currentState",
+                    "startX=$startX",
+                    "startY=$startY",
+                    "xDelta=${"%.1f".format(totalTouchDelta)}",
+                    "xTranslation=${"%.1f".format(previousXTranslation)}",
+                    "pre=${"%.0f".format(staticThresholdProgress(previousXTranslation) * 100)}%",
+                    "post=${"%.0f".format(fullScreenProgress(previousXTranslation) * 100)}%"
             )
             val debugPaint = Paint().apply {
                 color = Color.WHITE
             }
             val debugInfoBottom = debugStrings.size * 32f + 4f
             canvas.drawRect(
-                4f,
-                4f,
-                canvas.width.toFloat(),
-                debugStrings.size * 32f + 4f,
-                debugPaint
+                    4f,
+                    4f,
+                    canvas.width.toFloat(),
+                    debugStrings.size * 32f + 4f,
+                    debugPaint
             )
             debugPaint.apply {
                 color = Color.BLACK
@@ -728,9 +976,71 @@
                 canvas.drawLine(x, debugInfoBottom, x, canvas.height.toFloat(), debugPaint)
             }
 
-            drawVerticalLine(x = params.swipeTriggerThreshold, color = Color.BLUE)
+            drawVerticalLine(x = params.staticTriggerThreshold, color = Color.BLUE)
+            drawVerticalLine(x = params.deactivationSwipeTriggerThreshold, color = Color.BLUE)
             drawVerticalLine(x = startX, color = Color.GREEN)
             drawVerticalLine(x = previousXTranslation, color = Color.DKGRAY)
         }
     }
 }
+
+/**
+ * In addition to a typical step function which returns one or two
+ * values based on a threshold, `Step` also gracefully handles quick
+ * changes in input near the threshold value that would typically
+ * result in the output rapidly changing.
+ *
+ * In the context of Back arrow, the arrow's stroke opacity should
+ * always appear transparent or opaque. Using a typical Step function,
+ * this would resulting in a flickering appearance as the output would
+ * change rapidly. `Step` addresses this by moving the threshold after
+ * it is crossed so it cannot be easily crossed again with small changes
+ * in touch events.
+ */
+class Step<T>(
+        private val threshold: Float,
+        private val factor: Float = 1.1f,
+        private val postThreshold: T,
+        private val preThreshold: T
+) {
+
+    data class Value<T>(val value: T, val isNewState: Boolean)
+
+    private val lowerFactor = 2 - factor
+
+    private lateinit var startValue: Value<T>
+    private lateinit var previousValue: Value<T>
+    private var hasCrossedUpperBoundAtLeastOnce = false
+    private var progress: Float = 0f
+
+    init {
+        reset()
+    }
+
+    fun reset() {
+        hasCrossedUpperBoundAtLeastOnce = false
+        progress = 0f
+        startValue = Value(preThreshold, false)
+        previousValue = startValue
+    }
+
+    fun get(progress: Float): Value<T> {
+        this.progress = progress
+
+        val hasCrossedUpperBound = progress > threshold * factor
+        val hasCrossedLowerBound = progress > threshold * lowerFactor
+
+        return when {
+            hasCrossedUpperBound && !hasCrossedUpperBoundAtLeastOnce -> {
+                hasCrossedUpperBoundAtLeastOnce = true
+                Value(postThreshold, true)
+            }
+            hasCrossedLowerBound -> previousValue.copy(isNewState = false)
+            hasCrossedUpperBoundAtLeastOnce -> {
+                hasCrossedUpperBoundAtLeastOnce = false
+                Value(preThreshold, true)
+            }
+            else -> startValue
+        }.also { previousValue = it }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
index d56537b..0c00022 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
@@ -1,52 +1,82 @@
 package com.android.systemui.navigationbar.gestural
 
 import android.content.res.Resources
+import android.util.TypedValue
+import androidx.core.animation.PathInterpolator
+import androidx.dynamicanimation.animation.SpringForce
 import com.android.systemui.R
 
 data class EdgePanelParams(private var resources: Resources) {
 
     data class ArrowDimens(
-        val length: Float = 0f,
-        val height: Float = 0f
+            val length: Float = 0f,
+            val height: Float = 0f,
+            val alpha: Float = 0f,
+            var alphaSpring: SpringForce? = null,
+            val heightSpring: SpringForce? = null,
+            val lengthSpring: SpringForce? = null,
     )
 
     data class BackgroundDimens(
-        val width: Float = 0f,
-        val height: Float = 0f,
-        val edgeCornerRadius: Float = 0f,
-        val farCornerRadius: Float = 0f
+            val width: Float? = 0f,
+            val height: Float = 0f,
+            val edgeCornerRadius: Float = 0f,
+            val farCornerRadius: Float = 0f,
+            val alpha: Float = 0f,
+            val widthSpring: SpringForce? = null,
+            val heightSpring: SpringForce? = null,
+            val farCornerRadiusSpring: SpringForce? = null,
+            val edgeCornerRadiusSpring: SpringForce? = null,
+            val alphaSpring: SpringForce? = null,
     )
 
     data class BackIndicatorDimens(
-        val horizontalTranslation: Float = 0f,
-        val arrowDimens: ArrowDimens = ArrowDimens(),
-        val backgroundDimens: BackgroundDimens = BackgroundDimens()
+            val horizontalTranslation: Float? = 0f,
+            val scale: Float = 0f,
+            val scalePivotX: Float = 0f,
+            val arrowDimens: ArrowDimens,
+            val backgroundDimens: BackgroundDimens,
+            val verticalTranslationSpring: SpringForce? = null,
+            val horizontalTranslationSpring: SpringForce? = null,
+            val scaleSpring: SpringForce? = null,
     )
 
-    var arrowThickness: Float = 0f
+    lateinit var entryIndicator: BackIndicatorDimens
         private set
-    var entryIndicator = BackIndicatorDimens()
+    lateinit var activeIndicator: BackIndicatorDimens
         private set
-    var activeIndicator = BackIndicatorDimens()
+    lateinit var cancelledIndicator: BackIndicatorDimens
         private set
-    var preThresholdIndicator = BackIndicatorDimens()
+    lateinit var flungIndicator: BackIndicatorDimens
         private set
-    var fullyStretchedIndicator = BackIndicatorDimens()
+    lateinit var committedIndicator: BackIndicatorDimens
         private set
-    var cancelledEdgeCornerRadius: Float = 0f
+    lateinit var preThresholdIndicator: BackIndicatorDimens
         private set
-    var cancelledArrowDimens = ArrowDimens()
+    lateinit var fullyStretchedIndicator: BackIndicatorDimens
+        private set
 
     // navigation bar edge constants
     var arrowPaddingEnd: Int = 0
         private set
+    var arrowThickness: Float = 0f
+        private set
+    lateinit var arrowStrokeAlphaSpring: Step<SpringForce>
+        private set
+    lateinit var arrowStrokeAlphaInterpolator: Step<Float>
+        private set
 
     // The closest to y
     var minArrowYPosition: Int = 0
         private set
     var fingerOffset: Int = 0
         private set
-    var swipeTriggerThreshold: Float = 0f
+    var staticTriggerThreshold: Float = 0f
+        private set
+    var reactivationTriggerThreshold: Float = 0f
+        private set
+    var deactivationSwipeTriggerThreshold: Float = 0f
+        get() = -field
         private set
     var swipeProgressThreshold: Float = 0f
         private set
@@ -55,6 +85,26 @@
     var minDeltaForSwitch: Int = 0
         private set
 
+    var minDragToStartAnimation: Float = 0f
+        private set
+
+    lateinit var entryWidthInterpolator: PathInterpolator
+        private set
+    lateinit var entryWidthTowardsEdgeInterpolator: PathInterpolator
+        private set
+    lateinit var activeWidthInterpolator: PathInterpolator
+        private set
+    lateinit var arrowAngleInterpolator: PathInterpolator
+        private set
+    lateinit var translationInterpolator: PathInterpolator
+        private set
+    lateinit var farCornerInterpolator: PathInterpolator
+        private set
+    lateinit var edgeCornerInterpolator: PathInterpolator
+        private set
+    lateinit var heightInterpolator: PathInterpolator
+        private set
+
     init {
         update(resources)
     }
@@ -63,6 +113,10 @@
         return resources.getDimension(id)
     }
 
+    private fun getDimenFloat(id: Int): Float {
+        return TypedValue().run { resources.getValue(id, this, true); float }
+    }
+
     private fun getPx(id: Int): Int {
         return resources.getDimensionPixelSize(id)
     }
@@ -73,72 +127,200 @@
         arrowPaddingEnd = getPx(R.dimen.navigation_edge_panel_padding)
         minArrowYPosition = getPx(R.dimen.navigation_edge_arrow_min_y)
         fingerOffset = getPx(R.dimen.navigation_edge_finger_offset)
-        swipeTriggerThreshold = getDimen(R.dimen.navigation_edge_action_drag_threshold)
+        staticTriggerThreshold = getDimen(R.dimen.navigation_edge_action_drag_threshold)
+        reactivationTriggerThreshold =
+                getDimen(R.dimen.navigation_edge_action_reactivation_drag_threshold)
+        deactivationSwipeTriggerThreshold =
+                getDimen(R.dimen.navigation_edge_action_deactivation_drag_threshold)
         swipeProgressThreshold = getDimen(R.dimen.navigation_edge_action_progress_threshold)
         minDeltaForSwitch = getPx(R.dimen.navigation_edge_minimum_x_delta_for_switch)
+        minDragToStartAnimation =
+                getDimen(R.dimen.navigation_edge_action_min_distance_to_start_animation)
+
+        entryWidthInterpolator = PathInterpolator(.19f, 1.27f, .71f, .86f)
+        entryWidthTowardsEdgeInterpolator = PathInterpolator(1f, -3f, 1f, 1.2f)
+        activeWidthInterpolator = PathInterpolator(.15f, .48f, .46f, .89f)
+        arrowAngleInterpolator = entryWidthInterpolator
+        translationInterpolator = PathInterpolator(0.2f, 1.0f, 1.0f, 1.0f)
+        farCornerInterpolator = PathInterpolator(.03f, .19f, .14f, 1.09f)
+        edgeCornerInterpolator = PathInterpolator(0f, 1.11f, .85f, .84f)
+        heightInterpolator = PathInterpolator(1f, .05f, .9f, -0.29f)
+
+        val showArrowOnProgressValue = .2f
+        val showArrowOnProgressValueFactor = 1.05f
+
+        val entryActiveHorizontalTranslationSpring = createSpring(675f, 0.8f)
+        val activeCommittedArrowLengthSpring = createSpring(1500f, 0.29f)
+        val activeCommittedArrowHeightSpring = createSpring(1500f, 0.29f)
+        val flungCommittedEdgeCornerSpring = createSpring(10000f, 1f)
+        val flungCommittedFarCornerSpring = createSpring(10000f, 1f)
+        val flungCommittedWidthSpring = createSpring(10000f, 1f)
+        val flungCommittedHeightSpring = createSpring(10000f, 1f)
 
         entryIndicator = BackIndicatorDimens(
-            horizontalTranslation = getDimen(R.dimen.navigation_edge_entry_margin),
-            arrowDimens = ArrowDimens(
-                length = getDimen(R.dimen.navigation_edge_entry_arrow_length),
-                height = getDimen(R.dimen.navigation_edge_entry_arrow_height),
-            ),
-            backgroundDimens = BackgroundDimens(
-                width = getDimen(R.dimen.navigation_edge_entry_background_width),
-                height = getDimen(R.dimen.navigation_edge_entry_background_height),
-                edgeCornerRadius = getDimen(R.dimen.navigation_edge_entry_edge_corners),
-                farCornerRadius = getDimen(R.dimen.navigation_edge_entry_far_corners)
-            )
+                horizontalTranslation = getDimen(R.dimen.navigation_edge_entry_margin),
+                scale = getDimenFloat(R.dimen.navigation_edge_entry_scale),
+                scalePivotX = getDimen(R.dimen.navigation_edge_pre_threshold_background_width),
+                horizontalTranslationSpring = entryActiveHorizontalTranslationSpring,
+                verticalTranslationSpring = createSpring(10000f, 0.9f),
+                scaleSpring = createSpring(120f, 0.8f),
+                arrowDimens = ArrowDimens(
+                        length = getDimen(R.dimen.navigation_edge_entry_arrow_length),
+                        height = getDimen(R.dimen.navigation_edge_entry_arrow_height),
+                        alpha = 0f,
+                        alphaSpring = createSpring(200f, 1f),
+                        lengthSpring = createSpring(600f, 0.4f),
+                        heightSpring = createSpring(600f, 0.4f),
+                ),
+                backgroundDimens = BackgroundDimens(
+                        alpha = 1f,
+                        width = getDimen(R.dimen.navigation_edge_entry_background_width),
+                        height = getDimen(R.dimen.navigation_edge_entry_background_height),
+                        edgeCornerRadius = getDimen(R.dimen.navigation_edge_entry_edge_corners),
+                        farCornerRadius = getDimen(R.dimen.navigation_edge_entry_far_corners),
+                        alphaSpring = createSpring(900f, 1f),
+                        widthSpring = createSpring(450f, 0.65f),
+                        heightSpring = createSpring(1500f, 0.45f),
+                        farCornerRadiusSpring = createSpring(300f, 0.5f),
+                        edgeCornerRadiusSpring = createSpring(150f, 0.5f),
+                )
         )
 
         activeIndicator = BackIndicatorDimens(
-            horizontalTranslation = getDimen(R.dimen.navigation_edge_active_margin),
-            arrowDimens = ArrowDimens(
-                length = getDimen(R.dimen.navigation_edge_active_arrow_length),
-                height = getDimen(R.dimen.navigation_edge_active_arrow_height),
-            ),
-            backgroundDimens = BackgroundDimens(
-                width = getDimen(R.dimen.navigation_edge_active_background_width),
-                height = getDimen(R.dimen.navigation_edge_active_background_height),
-                edgeCornerRadius = getDimen(R.dimen.navigation_edge_active_edge_corners),
-                farCornerRadius = getDimen(R.dimen.navigation_edge_active_far_corners)
-
-            )
+                horizontalTranslation = getDimen(R.dimen.navigation_edge_active_margin),
+                scale = getDimenFloat(R.dimen.navigation_edge_active_scale),
+                horizontalTranslationSpring = entryActiveHorizontalTranslationSpring,
+                scaleSpring = createSpring(450f, 0.415f),
+                arrowDimens = ArrowDimens(
+                        length = getDimen(R.dimen.navigation_edge_active_arrow_length),
+                        height = getDimen(R.dimen.navigation_edge_active_arrow_height),
+                        alpha = 1f,
+                        lengthSpring = activeCommittedArrowLengthSpring,
+                        heightSpring = activeCommittedArrowHeightSpring,
+                ),
+                backgroundDimens = BackgroundDimens(
+                        alpha = 1f,
+                        width = getDimen(R.dimen.navigation_edge_active_background_width),
+                        height = getDimen(R.dimen.navigation_edge_active_background_height),
+                        edgeCornerRadius = getDimen(R.dimen.navigation_edge_active_edge_corners),
+                        farCornerRadius = getDimen(R.dimen.navigation_edge_active_far_corners),
+                        widthSpring = createSpring(375f, 0.675f),
+                        heightSpring = createSpring(10000f, 1f),
+                        edgeCornerRadiusSpring = createSpring(600f, 0.36f),
+                        farCornerRadiusSpring = createSpring(2500f, 0.855f),
+                )
         )
 
         preThresholdIndicator = BackIndicatorDimens(
-            horizontalTranslation = getDimen(R.dimen.navigation_edge_pre_threshold_margin),
-            arrowDimens = ArrowDimens(
-                length = entryIndicator.arrowDimens.length,
-                height = entryIndicator.arrowDimens.height,
-            ),
-            backgroundDimens = BackgroundDimens(
-                width = getDimen(R.dimen.navigation_edge_pre_threshold_background_width),
-                height = getDimen(R.dimen.navigation_edge_pre_threshold_background_height),
-                edgeCornerRadius = getDimen(R.dimen.navigation_edge_pre_threshold_edge_corners),
-                farCornerRadius = getDimen(R.dimen.navigation_edge_pre_threshold_far_corners)
-            )
+                horizontalTranslation = getDimen(R.dimen.navigation_edge_pre_threshold_margin),
+                scale = getDimenFloat(R.dimen.navigation_edge_pre_threshold_scale),
+                scalePivotX = getDimen(R.dimen.navigation_edge_pre_threshold_background_width),
+                scaleSpring = createSpring(120f, 0.8f),
+                horizontalTranslationSpring = createSpring(6000f, 1f),
+                arrowDimens = ArrowDimens(
+                        length = getDimen(R.dimen.navigation_edge_pre_threshold_arrow_length),
+                        height = getDimen(R.dimen.navigation_edge_pre_threshold_arrow_height),
+                        alpha = 1f,
+                        lengthSpring = createSpring(100f, 0.6f),
+                        heightSpring = createSpring(100f, 0.6f),
+                ),
+                backgroundDimens = BackgroundDimens(
+                        alpha = 1f,
+                        width = getDimen(R.dimen.navigation_edge_pre_threshold_background_width),
+                        height = getDimen(R.dimen.navigation_edge_pre_threshold_background_height),
+                        edgeCornerRadius =
+                                getDimen(R.dimen.navigation_edge_pre_threshold_edge_corners),
+                        farCornerRadius =
+                                getDimen(R.dimen.navigation_edge_pre_threshold_far_corners),
+                        widthSpring = createSpring(200f, 0.65f),
+                        heightSpring = createSpring(1500f, 0.45f),
+                        farCornerRadiusSpring = createSpring(200f, 1f),
+                        edgeCornerRadiusSpring = createSpring(150f, 0.5f),
+                )
+        )
+
+        committedIndicator = activeIndicator.copy(
+                horizontalTranslation = null,
+                arrowDimens = activeIndicator.arrowDimens.copy(
+                        lengthSpring = activeCommittedArrowLengthSpring,
+                        heightSpring = activeCommittedArrowHeightSpring,
+                ),
+                backgroundDimens = activeIndicator.backgroundDimens.copy(
+                        alpha = 0f,
+                        // explicitly set to null to preserve previous width upon state change
+                        width = null,
+                        widthSpring = flungCommittedWidthSpring,
+                        heightSpring = flungCommittedHeightSpring,
+                        edgeCornerRadiusSpring = flungCommittedEdgeCornerSpring,
+                        farCornerRadiusSpring = flungCommittedFarCornerSpring,
+                ),
+                scale = 0.85f,
+                scaleSpring = createSpring(650f, 1f),
+        )
+
+        flungIndicator = committedIndicator.copy(
+                arrowDimens = committedIndicator.arrowDimens.copy(
+                        lengthSpring = createSpring(850f, 0.46f),
+                        heightSpring = createSpring(850f, 0.46f),
+                ),
+                backgroundDimens = committedIndicator.backgroundDimens.copy(
+                        widthSpring = flungCommittedWidthSpring,
+                        heightSpring = flungCommittedHeightSpring,
+                        edgeCornerRadiusSpring = flungCommittedEdgeCornerSpring,
+                        farCornerRadiusSpring = flungCommittedFarCornerSpring,
+                )
+        )
+
+        cancelledIndicator = entryIndicator.copy(
+                backgroundDimens = entryIndicator.backgroundDimens.copy(width = 0f)
         )
 
         fullyStretchedIndicator = BackIndicatorDimens(
-            horizontalTranslation = getDimen(R.dimen.navigation_edge_stretch_margin),
-            arrowDimens = ArrowDimens(
-                length = getDimen(R.dimen.navigation_edge_stretched_arrow_length),
-                height = getDimen(R.dimen.navigation_edge_stretched_arrow_height),
-            ),
-            backgroundDimens = BackgroundDimens(
-                width = getDimen(R.dimen.navigation_edge_stretch_background_width),
-                height = getDimen(R.dimen.navigation_edge_stretch_background_height),
-                edgeCornerRadius = getDimen(R.dimen.navigation_edge_stretch_edge_corners),
-                farCornerRadius = getDimen(R.dimen.navigation_edge_stretch_far_corners)
+                horizontalTranslation = getDimen(R.dimen.navigation_edge_stretch_margin),
+                scale = getDimenFloat(R.dimen.navigation_edge_stretch_scale),
+                horizontalTranslationSpring = null,
+                verticalTranslationSpring = null,
+                scaleSpring = null,
+                arrowDimens = ArrowDimens(
+                        length = getDimen(R.dimen.navigation_edge_stretched_arrow_length),
+                        height = getDimen(R.dimen.navigation_edge_stretched_arrow_height),
+                        alpha = 1f,
+                        alphaSpring = null,
+                        heightSpring = null,
+                        lengthSpring = null,
+                ),
+                backgroundDimens = BackgroundDimens(
+                        alpha = 1f,
+                        width = getDimen(R.dimen.navigation_edge_stretch_background_width),
+                        height = getDimen(R.dimen.navigation_edge_stretch_background_height),
+                        edgeCornerRadius = getDimen(R.dimen.navigation_edge_stretch_edge_corners),
+                        farCornerRadius = getDimen(R.dimen.navigation_edge_stretch_far_corners),
+                        alphaSpring = null,
+                        widthSpring = null,
+                        heightSpring = null,
+                        edgeCornerRadiusSpring = null,
+                        farCornerRadiusSpring = null,
+                )
+        )
+
+        arrowStrokeAlphaInterpolator = Step(
+                threshold = showArrowOnProgressValue,
+                factor = showArrowOnProgressValueFactor,
+                postThreshold = 1f,
+                preThreshold = 0f
+        )
+
+        entryIndicator.arrowDimens.alphaSpring?.let { alphaSpring ->
+            arrowStrokeAlphaSpring = Step(
+                    threshold = showArrowOnProgressValue,
+                    factor = showArrowOnProgressValueFactor,
+                    postThreshold = alphaSpring,
+                    preThreshold = SpringForce().setStiffness(2000f).setDampingRatio(1f)
             )
-        )
-
-        cancelledEdgeCornerRadius = getDimen(R.dimen.navigation_edge_cancelled_edge_corners)
-
-        cancelledArrowDimens = ArrowDimens(
-            length = getDimen(R.dimen.navigation_edge_cancelled_arrow_length),
-            height = getDimen(R.dimen.navigation_edge_cancelled_arrow_height)
-        )
+        }
     }
 }
+
+fun createSpring(stiffness: Float, dampingRatio: Float): SpringForce {
+    return SpringForce().setStiffness(stiffness).setDampingRatio(dampingRatio)
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
index 1230708..590efbb 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.navigationbar.gestural;
 
-import static android.view.Display.DEFAULT_DISPLAY;
-
 import static com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler.DEBUG_MISSING_GESTURE;
 import static com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler.DEBUG_MISSING_GESTURE_TAG;
 
@@ -56,6 +54,7 @@
 import com.android.systemui.animation.Interpolators;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.plugins.NavigationEdgeBackPlugin;
+import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
 import com.android.systemui.statusbar.VibratorHelper;
 
@@ -289,7 +288,8 @@
             Context context,
             LatencyTracker latencyTracker,
             VibratorHelper vibratorHelper,
-            @Background Executor backgroundExecutor) {
+            @Background Executor backgroundExecutor,
+            DisplayTracker displayTracker) {
         super(context);
 
         mWindowManager = context.getSystemService(WindowManager.class);
@@ -365,7 +365,7 @@
 
         setVisibility(GONE);
 
-        boolean isPrimaryDisplay = mContext.getDisplayId() == DEFAULT_DISPLAY;
+        boolean isPrimaryDisplay = mContext.getDisplayId() == displayTracker.getDefaultDisplayId();
         mRegionSamplingHelper = new RegionSamplingHelper(this,
                 new RegionSamplingHelper.SamplingCallback() {
                     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index 08d1857..be615d6 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -17,10 +17,15 @@
 package com.android.systemui.notetask
 
 import android.app.KeyguardManager
+import android.content.ActivityNotFoundException
 import android.content.ComponentName
 import android.content.Context
+import android.content.Intent
 import android.content.pm.PackageManager
 import android.os.UserManager
+import android.util.Log
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
 import com.android.systemui.util.kotlin.getOrNull
@@ -40,11 +45,12 @@
 @Inject
 constructor(
     private val context: Context,
-    private val intentResolver: NoteTaskIntentResolver,
+    private val resolver: NoteTaskInfoResolver,
     private val optionalBubbles: Optional<Bubbles>,
     private val optionalKeyguardManager: Optional<KeyguardManager>,
     private val optionalUserManager: Optional<UserManager>,
     @NoteTaskEnabledKey private val isEnabled: Boolean,
+    private val uiEventLogger: UiEventLogger,
 ) {
 
     /**
@@ -57,27 +63,37 @@
      * If the keyguard is locked, notes will open as a full screen experience. A locked device has
      * no contextual information which let us use the whole screen space available.
      *
-     * If no in multi-window or the keyguard is unlocked, notes will open as a bubble OR it will be
+     * If not in multi-window or the keyguard is unlocked, notes will open as a bubble OR it will be
      * collapsed if the notes bubble is already opened.
      *
      * That will let users open other apps in full screen, and take contextual notes.
      */
-    fun showNoteTask(isInMultiWindowMode: Boolean = false) {
+    @JvmOverloads
+    fun showNoteTask(isInMultiWindowMode: Boolean = false, uiEvent: ShowNoteTaskUiEvent? = null) {
+
         if (!isEnabled) return
 
         val bubbles = optionalBubbles.getOrNull() ?: return
         val keyguardManager = optionalKeyguardManager.getOrNull() ?: return
         val userManager = optionalUserManager.getOrNull() ?: return
-        val intent = intentResolver.resolveIntent() ?: return
 
         // TODO(b/249954038): We should handle direct boot (isUserUnlocked). For now, we do nothing.
         if (!userManager.isUserUnlocked) return
 
-        if (isInMultiWindowMode || keyguardManager.isKeyguardLocked) {
-            context.startActivity(intent)
-        } else {
-            // TODO(b/254606432): Should include Intent.EXTRA_FLOATING_WINDOW_MODE parameter.
-            bubbles.showOrHideAppBubble(intent)
+        val noteTaskInfo = resolver.resolveInfo() ?: return
+
+        uiEvent?.let { uiEventLogger.log(it, noteTaskInfo.uid, noteTaskInfo.packageName) }
+
+        // TODO(b/266686199): We should handle when app not available. For now, we log.
+        val intent = noteTaskInfo.toCreateNoteIntent()
+        try {
+            if (isInMultiWindowMode || keyguardManager.isKeyguardLocked) {
+                context.startActivity(intent)
+            } else {
+                bubbles.showOrHideAppBubble(intent)
+            }
+        } catch (e: ActivityNotFoundException) {
+            Log.e(TAG, "Activity not found for action: $ACTION_CREATE_NOTE.", e)
         }
     }
 
@@ -105,8 +121,47 @@
         )
     }
 
+    /** IDs of UI events accepted by [showNoteTask]. */
+    enum class ShowNoteTaskUiEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
+        @UiEvent(doc = "User opened a note by tapping on the lockscreen shortcut.")
+        NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE(1294),
+
+        /* ktlint-disable max-line-length */
+        @UiEvent(
+            doc =
+                "User opened a note by pressing the stylus tail button while the screen was unlocked."
+        )
+        NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON(1295),
+        @UiEvent(
+            doc =
+                "User opened a note by pressing the stylus tail button while the screen was locked."
+        )
+        NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED(1296),
+        @UiEvent(doc = "User opened a note by tapping on an app shortcut.")
+        NOTE_OPENED_VIA_SHORTCUT(1297);
+
+        override fun getId() = _id
+    }
+
     companion object {
+        private val TAG = NoteTaskController::class.simpleName.orEmpty()
+
+        private fun NoteTaskInfoResolver.NoteTaskInfo.toCreateNoteIntent(): Intent {
+            return Intent(ACTION_CREATE_NOTE)
+                .setPackage(packageName)
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                // EXTRA_USE_STYLUS_MODE does not mean a stylus is in-use, but a stylus entrypoint
+                // was used to start it.
+                .putExtra(INTENT_EXTRA_USE_STYLUS_MODE, true)
+        }
+
         // TODO(b/254604589): Use final KeyEvent.KEYCODE_* instead.
         const val NOTE_TASK_KEY_EVENT = 311
+
+        // TODO(b/265912743): Use Intent.ACTION_CREATE_NOTE instead.
+        const val ACTION_CREATE_NOTE = "android.intent.action.CREATE_NOTE"
+
+        // TODO(b/265912743): Use Intent.INTENT_EXTRA_USE_STYLUS_MODE instead.
+        const val INTENT_EXTRA_USE_STYLUS_MODE = "android.intent.extra.USE_STYLUS_MODE"
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt
new file mode 100644
index 0000000..bd822d4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt
@@ -0,0 +1,69 @@
+/*
+ * 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.notetask
+
+import android.app.role.RoleManager
+import android.content.Context
+import android.content.pm.PackageManager
+import android.os.UserHandle
+import android.util.Log
+import javax.inject.Inject
+
+internal class NoteTaskInfoResolver
+@Inject
+constructor(
+    private val context: Context,
+    private val roleManager: RoleManager,
+    private val packageManager: PackageManager,
+) {
+    fun resolveInfo(): NoteTaskInfo? {
+        // TODO(b/267634412): Select UserHandle depending on where the user initiated note-taking.
+        val user = context.user
+        val packageName = roleManager.getRoleHoldersAsUser(ROLE_NOTES, user).firstOrNull()
+
+        if (packageName.isNullOrEmpty()) return null
+
+        return NoteTaskInfo(packageName, packageManager.getUidOf(packageName, user))
+    }
+
+    /** Package name and kernel user-ID of a note-taking app. */
+    data class NoteTaskInfo(val packageName: String, val uid: Int)
+
+    companion object {
+        private val TAG = NoteTaskInfoResolver::class.simpleName.orEmpty()
+
+        private val EMPTY_APPLICATION_INFO_FLAGS = PackageManager.ApplicationInfoFlags.of(0)!!
+
+        /**
+         * Returns the kernel user-ID of [packageName] for a [user]. Returns zero if the app cannot
+         * be found.
+         */
+        private fun PackageManager.getUidOf(packageName: String, user: UserHandle): Int {
+            val applicationInfo =
+                try {
+                    getApplicationInfoAsUser(packageName, EMPTY_APPLICATION_INFO_FLAGS, user)
+                } catch (e: PackageManager.NameNotFoundException) {
+                    Log.e(TAG, "Couldn't find notes app UID", e)
+                    return 0
+                }
+            return applicationInfo.uid
+        }
+
+        // TODO(b/265912743): Use RoleManager.NOTES_ROLE instead.
+        const val ROLE_NOTES = "android.app.role.NOTES"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
index d5f4a5a..d40bf2b 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
@@ -16,8 +16,10 @@
 
 package com.android.systemui.notetask
 
+import android.app.KeyguardManager
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.kotlin.getOrNull
 import com.android.wm.shell.bubbles.Bubbles
 import java.util.Optional
 import javax.inject.Inject
@@ -30,6 +32,7 @@
     private val noteTaskController: NoteTaskController,
     private val commandQueue: CommandQueue,
     @NoteTaskEnabledKey private val isEnabled: Boolean,
+    private val optionalKeyguardManager: Optional<KeyguardManager>,
 ) {
 
     @VisibleForTesting
@@ -37,11 +40,21 @@
         object : CommandQueue.Callbacks {
             override fun handleSystemKey(keyCode: Int) {
                 if (keyCode == NoteTaskController.NOTE_TASK_KEY_EVENT) {
-                    noteTaskController.showNoteTask()
+                    showNoteTask()
                 }
             }
         }
 
+    private fun showNoteTask() {
+        val uiEvent =
+            if (optionalKeyguardManager.isKeyguardLocked) {
+                NoteTaskController.ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED
+            } else {
+                NoteTaskController.ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON
+            }
+        noteTaskController.showNoteTask(uiEvent = uiEvent)
+    }
+
     fun initialize() {
         if (isEnabled && optionalBubbles.isPresent) {
             commandQueue.addCallback(callbacks)
@@ -49,3 +62,7 @@
         noteTaskController.setNoteTaskShortcutEnabled(isEnabled)
     }
 }
+
+private val Optional<KeyguardManager>.isKeyguardLocked: Boolean
+    // If there's no KeyguardManager, assume that the keyguard is not locked.
+    get() = getOrNull()?.isKeyguardLocked ?: false
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskIntentResolver.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskIntentResolver.kt
deleted file mode 100644
index 4b10d69..0000000
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskIntentResolver.kt
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2022 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.notetask
-
-import android.content.ComponentName
-import android.content.Intent
-import android.content.pm.ActivityInfo
-import android.content.pm.PackageManager
-import android.content.pm.PackageManager.ResolveInfoFlags
-import com.android.systemui.notetask.NoteTaskIntentResolver.Companion.ACTION_CREATE_NOTE
-import javax.inject.Inject
-
-/**
- * Class responsible to query all apps and find one that can handle the [ACTION_CREATE_NOTE]. If
- * found, an [Intent] ready for be launched will be returned. Otherwise, returns null.
- *
- * TODO(b/248274123): should be revisited once the notes role is implemented.
- */
-internal class NoteTaskIntentResolver
-@Inject
-constructor(
-    private val packageManager: PackageManager,
-) {
-
-    fun resolveIntent(): Intent? {
-        val intent = Intent(ACTION_CREATE_NOTE)
-        val flags = ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY.toLong())
-        val infoList = packageManager.queryIntentActivities(intent, flags)
-
-        for (info in infoList) {
-            val packageName = info.activityInfo.applicationInfo.packageName ?: continue
-            val activityName = resolveActivityNameForNotesAction(packageName) ?: continue
-
-            return Intent(ACTION_CREATE_NOTE)
-                .setPackage(packageName)
-                .setComponent(ComponentName(packageName, activityName))
-                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-        }
-
-        return null
-    }
-
-    private fun resolveActivityNameForNotesAction(packageName: String): String? {
-        val intent = Intent(ACTION_CREATE_NOTE).setPackage(packageName)
-        val flags = ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY.toLong())
-        val resolveInfo = packageManager.resolveActivity(intent, flags)
-
-        val activityInfo = resolveInfo?.activityInfo ?: return null
-        if (activityInfo.name.isNullOrBlank()) return null
-        if (!activityInfo.exported) return null
-        if (!activityInfo.enabled) return null
-        if (!activityInfo.showWhenLocked) return null
-        if (!activityInfo.turnScreenOn) return null
-
-        return activityInfo.name
-    }
-
-    companion object {
-        // TODO(b/254606432): Use Intent.ACTION_CREATE_NOTE instead.
-        const val ACTION_CREATE_NOTE = "android.intent.action.CREATE_NOTE"
-
-        // TODO(b/265912743): Use RoleManager.NOTES_ROLE instead.
-        const val NOTE_ROLE = "android.app.role.NOTES"
-    }
-}
-
-private val ActivityInfo.showWhenLocked: Boolean
-    get() = flags and ActivityInfo.FLAG_SHOW_WHEN_LOCKED != 0
-
-private val ActivityInfo.turnScreenOn: Boolean
-    get() = flags and ActivityInfo.FLAG_TURN_SCREEN_ON != 0
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
index 22ce121..b8800a2 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
@@ -51,7 +51,7 @@
             featureFlags: FeatureFlags,
             roleManager: RoleManager,
         ): Boolean {
-            val isRoleAvailable = roleManager.isRoleAvailable(NoteTaskIntentResolver.NOTE_ROLE)
+            val isRoleAvailable = roleManager.isRoleAvailable(NoteTaskInfoResolver.ROLE_NOTES)
             val isFeatureEnabled = featureFlags.isEnabled(Flags.NOTE_TASKS)
             return isRoleAvailable && isFeatureEnabled
         }
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
index cfbaa48..43869cc 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnTriggeredResult
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.PickerScreenState
 import com.android.systemui.notetask.NoteTaskController
+import com.android.systemui.notetask.NoteTaskController.ShowNoteTaskUiEvent
 import com.android.systemui.notetask.NoteTaskEnabledKey
 import javax.inject.Inject
 import kotlinx.coroutines.flow.flowOf
@@ -64,7 +65,9 @@
         }
 
     override fun onTriggered(expandable: Expandable?): OnTriggeredResult {
-        noteTaskController.showNoteTask()
+        noteTaskController.showNoteTask(
+            uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE
+        )
         return OnTriggeredResult.Handled
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt
index f203e7a..3ac5bfa 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt
@@ -21,7 +21,7 @@
 import android.os.Bundle
 import androidx.activity.ComponentActivity
 import com.android.systemui.notetask.NoteTaskController
-import com.android.systemui.notetask.NoteTaskIntentResolver
+import com.android.systemui.notetask.NoteTaskController.ShowNoteTaskUiEvent
 import javax.inject.Inject
 
 /** Activity responsible for launching the note experience, and finish. */
@@ -34,7 +34,10 @@
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
 
-        noteTaskController.showNoteTask(isInMultiWindowMode)
+        noteTaskController.showNoteTask(
+            isInMultiWindowMode = isInMultiWindowMode,
+            uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_SHORTCUT,
+        )
 
         finish()
     }
@@ -46,7 +49,7 @@
             return Intent(context, LaunchNoteTaskActivity::class.java).apply {
                 // Intent's action must be set in shortcuts, or an exception will be thrown.
                 // TODO(b/254606432): Use Intent.ACTION_CREATE_NOTE instead.
-                action = NoteTaskIntentResolver.ACTION_CREATE_NOTE
+                action = NoteTaskController.ACTION_CREATE_NOTE
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java
index 146633d..95f1419 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java
@@ -121,6 +121,6 @@
     @Provides
     @Named(PLUGIN_PRIVILEGED)
     static List<String> providesPrivilegedPlugins(Context context) {
-        return Arrays.asList(context.getResources().getStringArray(R.array.config_pluginWhitelist));
+        return Arrays.asList(context.getResources().getStringArray(R.array.config_pluginAllowlist));
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
index 2a6ca1a..8ad2f86 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
@@ -17,11 +17,11 @@
 import android.content.Context
 import android.util.AttributeSet
 import android.view.ViewGroup
-import android.widget.FrameLayout
 import android.widget.ImageView
 import android.widget.LinearLayout
 import com.android.settingslib.Utils
 import com.android.systemui.R
+import com.android.systemui.animation.LaunchableFrameLayout
 import com.android.systemui.statusbar.events.BackgroundAnimatableView
 
 class OngoingPrivacyChip @JvmOverloads constructor(
@@ -29,7 +29,7 @@
     attrs: AttributeSet? = null,
     defStyleAttrs: Int = 0,
     defStyleRes: Int = 0
-) : FrameLayout(context, attrs, defStyleAttrs, defStyleRes), BackgroundAnimatableView {
+) : LaunchableFrameLayout(context, attrs, defStyleAttrs, defStyleRes), BackgroundAnimatableView {
 
     private var iconMargin = 0
     private var iconSize = 0
diff --git a/packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java b/packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java
new file mode 100644
index 0000000..245cf89
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java
@@ -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.process;
+
+import javax.inject.Inject;
+
+/**
+ * A simple wrapper that provides access to process-related details. This facilitates testing by
+ * providing a mockable target around these details.
+ */
+public class ProcessWrapper {
+    @Inject
+    public ProcessWrapper() {}
+
+    public int getUserHandleIdentifier() {
+        return android.os.Process.myUserHandle().getIdentifier();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/process/condition/UserProcessCondition.java b/packages/SystemUI/src/com/android/systemui/process/condition/UserProcessCondition.java
new file mode 100644
index 0000000..5a21ea0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/process/condition/UserProcessCondition.java
@@ -0,0 +1,48 @@
+/*
+ * 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.process.condition;
+
+import com.android.systemui.process.ProcessWrapper;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.shared.condition.Condition;
+
+import javax.inject.Inject;
+
+/**
+ * {@link UserProcessCondition} provides a signal when the process handle belongs to the current
+ * user.
+ */
+public class UserProcessCondition extends Condition {
+    private final ProcessWrapper mProcessWrapper;
+    private final UserTracker mUserTracker;
+
+    @Inject
+    public UserProcessCondition(ProcessWrapper processWrapper, UserTracker userTracker) {
+        mProcessWrapper = processWrapper;
+        mUserTracker = userTracker;
+    }
+
+    @Override
+    protected void start() {
+        updateCondition(mUserTracker.getUserId()
+                == mProcessWrapper.getUserHandleIdentifier());
+    }
+
+    @Override
+    protected void stop() {
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt b/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt
new file mode 100644
index 0000000..62c99da
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.qrcodescanner.dagger
+
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.QRCodeScannerTile
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
+
+@Module
+interface QRCodeScannerModule {
+
+    /**
+     */
+    @Binds
+    @IntoMap
+    @StringKey(QRCodeScannerTile.TILE_SPEC)
+    fun bindQRCodeScannerTile(qrCodeScannerTile: QRCodeScannerTile): QSTileImpl<*>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
index b48ea23..be93550 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
@@ -43,6 +43,7 @@
 import javax.inject.Inject
 
 private const val TAG = "AutoAddTracker"
+private const val DELIMITER = ","
 
 /**
  * Class to track tiles that have been auto-added
@@ -67,7 +68,7 @@
 
     @GuardedBy("autoAdded")
     private val autoAdded = ArraySet<String>()
-    private var restoredTiles: Set<String>? = null
+    private var restoredTiles: Map<String, AutoTile>? = null
 
     override val currentUserId: Int
         get() = userId
@@ -98,25 +99,26 @@
         when (intent.getStringExtra(Intent.EXTRA_SETTING_NAME)) {
             Settings.Secure.QS_TILES -> {
                 restoredTiles = intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)
-                        ?.split(",")
-                        ?.toSet()
+                        ?.split(DELIMITER)
+                        ?.mapIndexed(::AutoTile)
+                        ?.associateBy(AutoTile::tileType)
                         ?: run {
                             Log.w(TAG, "Null restored tiles for user $userId")
-                            emptySet()
+                            emptyMap()
                         }
             }
             Settings.Secure.QS_AUTO_ADDED_TILES -> {
-                restoredTiles?.let { tiles ->
+                restoredTiles?.let { restoredTiles ->
                     val restoredAutoAdded = intent
                             .getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)
-                            ?.split(",")
+                            ?.split(DELIMITER)
                             ?: emptyList()
                     val autoAddedBeforeRestore = intent
                             .getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE)
-                            ?.split(",")
+                            ?.split(DELIMITER)
                             ?: emptyList()
 
-                    val tilesToRemove = restoredAutoAdded.filter { it !in tiles }
+                    val tilesToRemove = restoredAutoAdded.filter { it !in restoredTiles }
                     if (tilesToRemove.isNotEmpty()) {
                         qsHost.removeTiles(tilesToRemove)
                     }
@@ -180,6 +182,9 @@
         registerBroadcastReceiver()
     }
 
+    fun getRestoredTilePosition(tile: String): Int =
+        restoredTiles?.get(tile)?.index ?: QSTileHost.POSITION_AT_END
+
     /**
      * Returns `true` if the tile has been auto-added before
      */
@@ -196,12 +201,12 @@
      */
     fun setTileAdded(tile: String) {
         val tiles = synchronized(autoAdded) {
-                if (autoAdded.add(tile)) {
-                    getTilesFromListLocked()
-                } else {
-                    null
-                }
+            if (autoAdded.add(tile)) {
+                getTilesFromListLocked()
+            } else {
+                null
             }
+        }
         tiles?.let { saveTiles(it) }
     }
 
@@ -222,7 +227,7 @@
     }
 
     private fun getTilesFromListLocked(): String {
-        return TextUtils.join(",", autoAdded)
+        return TextUtils.join(DELIMITER, autoAdded)
     }
 
     private fun saveTiles(tiles: String) {
@@ -245,7 +250,7 @@
 
     private fun getAdded(): Collection<String> {
         val current = secureSettings.getStringForUser(Settings.Secure.QS_AUTO_ADDED_TILES, userId)
-        return current?.split(",") ?: emptySet()
+        return current?.split(DELIMITER) ?: emptySet()
     }
 
     override fun dump(pw: PrintWriter, args: Array<out String>) {
@@ -281,4 +286,6 @@
             )
         }
     }
+
+    private data class AutoTile(val index: Int, val tileType: String)
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 8ad102e..ae965d3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -51,9 +51,7 @@
 import com.android.systemui.animation.ShadeInterpolation;
 import com.android.systemui.compose.ComposeFacade;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.media.controls.ui.MediaHost;
-import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.plugins.qs.QSContainerController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -61,6 +59,7 @@
 import com.android.systemui.qs.dagger.QSFragmentComponent;
 import com.android.systemui.qs.footer.ui.binder.FooterActionsViewBinder;
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel;
+import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -88,14 +87,11 @@
 
     private final Rect mQsBounds = new Rect();
     private final SysuiStatusBarStateController mStatusBarStateController;
-    private final FalsingManager mFalsingManager;
     private final KeyguardBypassController mBypassController;
     private boolean mQsExpanded;
     private boolean mHeaderAnimating;
     private boolean mStackScrollerOverscrolling;
 
-    private long mDelay;
-
     private QSAnimator mQSAnimator;
     private HeightListener mPanelView;
     private QSSquishinessController mQSSquishinessController;
@@ -116,8 +112,7 @@
     private final MediaHost mQqsMediaHost;
     private final QSFragmentComponent.Factory mQsComponentFactory;
     private final QSFragmentDisableFlagsLogger mQsFragmentDisableFlagsLogger;
-    private final QSTileHost mHost;
-    private final FeatureFlags mFeatureFlags;
+    private final QSLogger mLogger;
     private final FooterActionsController mFooterActionsController;
     private final FooterActionsViewModel.Factory mFooterActionsViewModelFactory;
     private final ListeningAndVisibilityLifecycleOwner mListeningAndVisibilityLifecycleOwner;
@@ -150,11 +145,6 @@
      */
     private boolean mTransitioningToFullShade;
 
-    /**
-     * Whether the next Quick settings
-     */
-    private boolean mAnimateNextQsUpdate;
-
     private final DumpManager mDumpManager;
 
     /**
@@ -178,14 +168,13 @@
 
     @Inject
     public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
-            QSTileHost qsTileHost,
             SysuiStatusBarStateController statusBarStateController, CommandQueue commandQueue,
             @Named(QS_PANEL) MediaHost qsMediaHost,
             @Named(QUICK_QS_PANEL) MediaHost qqsMediaHost,
             KeyguardBypassController keyguardBypassController,
             QSFragmentComponent.Factory qsComponentFactory,
             QSFragmentDisableFlagsLogger qsFragmentDisableFlagsLogger,
-            FalsingManager falsingManager, DumpManager dumpManager, FeatureFlags featureFlags,
+            DumpManager dumpManager, QSLogger qsLogger,
             FooterActionsController footerActionsController,
             FooterActionsViewModel.Factory footerActionsViewModelFactory) {
         mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
@@ -193,13 +182,11 @@
         mQqsMediaHost = qqsMediaHost;
         mQsComponentFactory = qsComponentFactory;
         mQsFragmentDisableFlagsLogger = qsFragmentDisableFlagsLogger;
+        mLogger = qsLogger;
         commandQueue.observe(getLifecycle(), this);
-        mHost = qsTileHost;
-        mFalsingManager = falsingManager;
         mBypassController = keyguardBypassController;
         mStatusBarStateController = statusBarStateController;
         mDumpManager = dumpManager;
-        mFeatureFlags = featureFlags;
         mFooterActionsController = footerActionsController;
         mFooterActionsViewModelFactory = footerActionsViewModelFactory;
         mListeningAndVisibilityLifecycleOwner = new ListeningAndVisibilityLifecycleOwner();
@@ -716,8 +703,10 @@
     private void setAlphaAnimationProgress(float progress) {
         final View view = getView();
         if (progress == 0 && view.getVisibility() != View.INVISIBLE) {
+            mLogger.logVisibility("QS fragment", View.INVISIBLE);
             view.setVisibility(View.INVISIBLE);
         } else if (progress > 0 && view.getVisibility() != View.VISIBLE) {
+            mLogger.logVisibility("QS fragment", View.VISIBLE);
             view.setVisibility((View.VISIBLE));
         }
         view.setAlpha(interpolateAlphaAnimationProgress(progress));
@@ -914,7 +903,6 @@
             getView().getViewTreeObserver().removeOnPreDrawListener(this);
             getView().animate()
                     .translationY(0f)
-                    .setStartDelay(mDelay)
                     .setDuration(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE)
                     .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                     .setListener(mAnimateHeaderSlidingInListener)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 7067c220..a0be151 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -25,8 +25,6 @@
 import android.content.res.Resources;
 import android.graphics.Rect;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
 import android.util.ArrayMap;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -108,6 +106,11 @@
     private boolean mShouldMoveMediaOnExpansion = true;
     private boolean mUsingCombinedHeaders = false;
     private QSLogger mQsLogger;
+    /**
+     * Specifies if we can collapse to QQS in current state. In split shade that should be always
+     * false. It influences available accessibility actions.
+     */
+    private boolean mCanCollapse = true;
 
     public QSPanel(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -650,7 +653,9 @@
     @Override
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(info);
-        info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE);
+        if (mCanCollapse) {
+            info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE);
+        }
     }
 
     @Override
@@ -669,15 +674,11 @@
         mCollapseExpandAction = action;
     }
 
-    private class H extends Handler {
-        private static final int ANNOUNCE_FOR_ACCESSIBILITY = 1;
-
-        @Override
-        public void handleMessage(Message msg) {
-            if (msg.what == ANNOUNCE_FOR_ACCESSIBILITY) {
-                announceForAccessibility((CharSequence) msg.obj);
-            }
-        }
+    /**
+     * Specifies if these expanded QS can collapse to QQS.
+     */
+    public void setCanCollapse(boolean canCollapse) {
+        mCanCollapse = canCollapse;
     }
 
     public interface QSTileLayout {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index cabe1da..01dbb18 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -148,9 +148,10 @@
     }
 
     @Override
-    protected void onSplitShadeChanged() {
+    protected void onSplitShadeChanged(boolean shouldUseSplitNotificationShade) {
         ((PagedTileLayout) mView.getOrCreateTileLayout())
                 .forceTilesRedistribution("Split shade state changed");
+        mView.setCanCollapse(!shouldUseSplitNotificationShade);
     }
 
     /** */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index e85d0a3..bbdf6cc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -104,14 +104,14 @@
                     switchTileLayoutIfNeeded();
                     onConfigurationChanged();
                     if (previousSplitShadeState != mShouldUseSplitNotificationShade) {
-                        onSplitShadeChanged();
+                        onSplitShadeChanged(mShouldUseSplitNotificationShade);
                     }
                 }
             };
 
     protected void onConfigurationChanged() { }
 
-    protected void onSplitShadeChanged() { }
+    protected void onSplitShadeChanged(boolean shouldUseSplitNotificationShade) { }
 
     private final Function1<Boolean, Unit> mMediaHostVisibilityListener = (visible) -> {
         if (mMediaVisibilityChangedListener != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 100853c..98af9df 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -314,7 +314,6 @@
         if (!TILES_SETTING.equals(key)) {
             return;
         }
-        Log.d(TAG, "Recreating tiles");
         if (newValue == null && UserManager.isDeviceInDemoMode(mContext)) {
             newValue = mContext.getResources().getString(R.string.quick_settings_tiles_retail_mode);
         }
@@ -327,6 +326,7 @@
             }
         }
         if (tileSpecs.equals(mTileSpecs) && currentUser == mCurrentUser) return;
+        Log.d(TAG, "Recreating tiles: " + tileSpecs);
         mTiles.entrySet().stream().filter(tile -> !tileSpecs.contains(tile.getKey())).forEach(
                 tile -> {
                     Log.d(TAG, "Destroying tile: " + tile.getKey());
@@ -372,6 +372,8 @@
                             Log.d(TAG, "Destroying not available tile: " + tileSpec);
                             mQSLogger.logTileDestroyed(tileSpec, "Tile not available");
                         }
+                    } else {
+                        Log.d(TAG, "No factory for a spec: " + tileSpec);
                     }
                 } catch (Throwable t) {
                     Log.w(TAG, "Error creating tile for spec: " + tileSpec, t);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 946fe54..e696d13 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -152,9 +152,7 @@
         Configuration config = mContext.getResources().getConfiguration();
         setDatePrivacyContainersWidth(config.orientation == Configuration.ORIENTATION_LANDSCAPE);
 
-        // QS will always show the estimate, and BatteryMeterView handles the case where
-        // it's unavailable or charging
-        mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
+        updateBatteryMode();
 
         mIconsAlphaAnimatorFixed = new TouchAnimator.Builder()
                 .addFloat(mIconContainer, "alpha", 0, 1)
@@ -460,24 +458,24 @@
                 (LinearLayout.LayoutParams) mDatePrivacySeparator.getLayoutParams();
         LinearLayout.LayoutParams mClockIconsSeparatorLayoutParams =
                 (LinearLayout.LayoutParams) mClockIconsSeparator.getLayoutParams();
-        if (cutout != null) {
-            Rect topCutout = cutout.getBoundingRectTop();
-            if (topCutout.isEmpty() || hasCornerCutout) {
-                datePrivacySeparatorLayoutParams.width = 0;
-                mDatePrivacySeparator.setVisibility(View.GONE);
-                mClockIconsSeparatorLayoutParams.width = 0;
-                setSeparatorVisibility(false);
-                mShowClockIconsSeparator = false;
-                mHasCenterCutout = false;
-            } else {
-                datePrivacySeparatorLayoutParams.width = topCutout.width();
-                mDatePrivacySeparator.setVisibility(View.VISIBLE);
-                mClockIconsSeparatorLayoutParams.width = topCutout.width();
-                mShowClockIconsSeparator = true;
-                setSeparatorVisibility(mKeyguardExpansionFraction == 0f);
-                mHasCenterCutout = true;
-            }
+
+        Rect topCutout = cutout == null ? null : cutout.getBoundingRectTop();
+        if (topCutout == null || topCutout.isEmpty() || hasCornerCutout) {
+            datePrivacySeparatorLayoutParams.width = 0;
+            mDatePrivacySeparator.setVisibility(View.GONE);
+            mClockIconsSeparatorLayoutParams.width = 0;
+            setSeparatorVisibility(false);
+            mShowClockIconsSeparator = false;
+            mHasCenterCutout = false;
+        } else {
+            datePrivacySeparatorLayoutParams.width = topCutout.width();
+            mDatePrivacySeparator.setVisibility(View.VISIBLE);
+            mClockIconsSeparatorLayoutParams.width = topCutout.width();
+            mShowClockIconsSeparator = true;
+            setSeparatorVisibility(mKeyguardExpansionFraction == 0f);
+            mHasCenterCutout = true;
         }
+
         mDatePrivacySeparator.setLayoutParams(datePrivacySeparatorLayoutParams);
         mClockIconsSeparator.setLayoutParams(mClockIconsSeparatorLayoutParams);
         mCutOutPaddingLeft = sbInsets.first;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
index 628964a..27ae171 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -22,6 +22,7 @@
 import android.hardware.display.NightDisplayListener;
 import android.os.Handler;
 
+import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.media.dagger.MediaModule;
 import com.android.systemui.qs.AutoAddTracker;
@@ -29,6 +30,7 @@
 import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.qs.ReduceBrightColorsController;
 import com.android.systemui.qs.external.QSExternalModule;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.statusbar.phone.AutoTileManager;
 import com.android.systemui.statusbar.phone.ManagedProfileController;
 import com.android.systemui.statusbar.policy.CastController;
@@ -39,11 +41,14 @@
 import com.android.systemui.statusbar.policy.WalletController;
 import com.android.systemui.util.settings.SecureSettings;
 
+import java.util.Map;
+
 import javax.inject.Named;
 
 import dagger.Binds;
 import dagger.Module;
 import dagger.Provides;
+import dagger.multibindings.Multibinds;
 
 /**
  * Module for QS dependencies
@@ -52,7 +57,13 @@
         includes = {MediaModule.class, QSExternalModule.class, QSFlagsModule.class})
 public interface QSModule {
 
+    /** A map of internal QS tiles. Ensures that this can be injected even if
+     * it is empty */
+    @Multibinds
+    Map<String, QSTileImpl<?>> tileMap();
+
     @Provides
+    @SysUISingleton
     static AutoTileManager provideAutoTileManager(
             Context context,
             AutoAddTracker.Builder autoAddTrackerBuilder,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index cfda9fd..7c2536d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -15,7 +15,6 @@
  */
 package com.android.systemui.qs.external;
 
-import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
 
 import android.app.PendingIntent;
@@ -63,6 +62,7 @@
 import com.android.systemui.qs.external.TileLifecycleManager.TileChangeListener;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.settings.DisplayTracker;
 
 import java.util.Objects;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -90,6 +90,7 @@
     private final TileServiceManager mServiceManager;
     private final int mUser;
     private final CustomTileStatePersister mCustomTileStatePersister;
+    private final DisplayTracker mDisplayTracker;
     @Nullable
     private android.graphics.drawable.Icon mDefaultIcon;
     @Nullable
@@ -120,7 +121,8 @@
             String action,
             Context userContext,
             CustomTileStatePersister customTileStatePersister,
-            TileServices tileServices
+            TileServices tileServices,
+            DisplayTracker displayTracker
     ) {
         super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
                 statusBarStateController, activityStarter, qsLogger);
@@ -135,6 +137,7 @@
         mServiceManager = tileServices.getTileWrapper(this);
         mService = mServiceManager.getTileService();
         mCustomTileStatePersister = customTileStatePersister;
+        mDisplayTracker = displayTracker;
     }
 
     @Override
@@ -310,7 +313,7 @@
         mIsShowingDialog = false;
         try {
             if (DEBUG) Log.d(TAG, "Removing token");
-            mWindowManager.removeWindowToken(mToken, DEFAULT_DISPLAY);
+            mWindowManager.removeWindowToken(mToken, mDisplayTracker.getDefaultDisplayId());
         } catch (RemoteException e) {
         }
     }
@@ -335,7 +338,8 @@
                 if (mIsTokenGranted && !mIsShowingDialog) {
                     try {
                         if (DEBUG) Log.d(TAG, "Removing token");
-                        mWindowManager.removeWindowToken(mToken, DEFAULT_DISPLAY);
+                        mWindowManager.removeWindowToken(mToken,
+                                mDisplayTracker.getDefaultDisplayId());
                     } catch (RemoteException e) {
                     }
                     mIsTokenGranted = false;
@@ -354,7 +358,7 @@
         if (mIsTokenGranted) {
             try {
                 if (DEBUG) Log.d(TAG, "Removing token");
-                mWindowManager.removeWindowToken(mToken, DEFAULT_DISPLAY);
+                mWindowManager.removeWindowToken(mToken, mDisplayTracker.getDefaultDisplayId());
             } catch (RemoteException e) {
             }
         }
@@ -398,8 +402,8 @@
         mViewClicked = view;
         try {
             if (DEBUG) Log.d(TAG, "Adding token");
-            mWindowManager.addWindowToken(mToken, TYPE_QS_DIALOG, DEFAULT_DISPLAY,
-                    null /* options */);
+            mWindowManager.addWindowToken(mToken, TYPE_QS_DIALOG,
+                    mDisplayTracker.getDefaultDisplayId(), null /* options */);
             mIsTokenGranted = true;
         } catch (RemoteException e) {
         }
@@ -566,6 +570,7 @@
         final QSLogger mQSLogger;
         final CustomTileStatePersister mCustomTileStatePersister;
         private TileServices mTileServices;
+        final DisplayTracker mDisplayTracker;
 
         Context mUserContext;
         String mSpec = "";
@@ -581,7 +586,8 @@
                 ActivityStarter activityStarter,
                 QSLogger qsLogger,
                 CustomTileStatePersister customTileStatePersister,
-                TileServices tileServices
+                TileServices tileServices,
+                DisplayTracker displayTracker
         ) {
             mQSHostLazy = hostLazy;
             mBackgroundLooper = backgroundLooper;
@@ -593,6 +599,7 @@
             mQSLogger = qsLogger;
             mCustomTileStatePersister = customTileStatePersister;
             mTileServices = tileServices;
+            mDisplayTracker = displayTracker;
         }
 
         Builder setSpec(@NonNull String spec) {
@@ -623,7 +630,8 @@
                     action,
                     mUserContext,
                     mCustomTileStatePersister,
-                    mTileServices
+                    mTileServices,
+                    mDisplayTracker
             );
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
index d32ef32..23c41db 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
@@ -17,15 +17,14 @@
 package com.android.systemui.qs.logging
 
 import android.service.quicksettings.Tile
+import android.view.View
 import com.android.systemui.log.dagger.QSLog
 import com.android.systemui.plugins.log.ConstantStringsLogger
 import com.android.systemui.plugins.log.ConstantStringsLoggerImpl
 import com.android.systemui.plugins.log.LogBuffer
-import com.android.systemui.plugins.log.LogLevel
 import com.android.systemui.plugins.log.LogLevel.DEBUG
 import com.android.systemui.plugins.log.LogLevel.ERROR
 import com.android.systemui.plugins.log.LogLevel.VERBOSE
-import com.android.systemui.plugins.log.LogMessage
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.statusbar.StatusBarState
 import com.google.errorprone.annotations.CompileTimeConstant
@@ -332,4 +331,25 @@
             else -> "wrong state"
         }
     }
+
+    fun logVisibility(viewName: String, @View.Visibility visibility: Int) {
+        buffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = viewName
+                str2 = toVisibilityString(visibility)
+            },
+            { "$str1 visibility: $str2" }
+        )
+    }
+
+    private fun toVisibilityString(visibility: Int): String {
+        return when (visibility) {
+            View.VISIBLE -> "VISIBLE"
+            View.INVISIBLE -> "INVISIBLE"
+            View.GONE -> "GONE"
+            else -> "undefined"
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 24a4f60b..6b23f5d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -27,74 +27,32 @@
 import com.android.systemui.plugins.qs.QSTileView;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.external.CustomTile;
-import com.android.systemui.qs.tiles.AirplaneModeTile;
-import com.android.systemui.qs.tiles.AlarmTile;
-import com.android.systemui.qs.tiles.BatterySaverTile;
-import com.android.systemui.qs.tiles.BluetoothTile;
-import com.android.systemui.qs.tiles.CameraToggleTile;
-import com.android.systemui.qs.tiles.CastTile;
-import com.android.systemui.qs.tiles.ColorCorrectionTile;
-import com.android.systemui.qs.tiles.ColorInversionTile;
-import com.android.systemui.qs.tiles.DataSaverTile;
-import com.android.systemui.qs.tiles.DeviceControlsTile;
-import com.android.systemui.qs.tiles.DndTile;
-import com.android.systemui.qs.tiles.DreamTile;
-import com.android.systemui.qs.tiles.FlashlightTile;
-import com.android.systemui.qs.tiles.HotspotTile;
-import com.android.systemui.qs.tiles.InternetTile;
-import com.android.systemui.qs.tiles.LocationTile;
-import com.android.systemui.qs.tiles.MicrophoneToggleTile;
-import com.android.systemui.qs.tiles.NfcTile;
-import com.android.systemui.qs.tiles.NightDisplayTile;
-import com.android.systemui.qs.tiles.OneHandedModeTile;
-import com.android.systemui.qs.tiles.QRCodeScannerTile;
-import com.android.systemui.qs.tiles.QuickAccessWalletTile;
-import com.android.systemui.qs.tiles.ReduceBrightColorsTile;
-import com.android.systemui.qs.tiles.RotationLockTile;
-import com.android.systemui.qs.tiles.ScreenRecordTile;
-import com.android.systemui.qs.tiles.UiModeNightTile;
-import com.android.systemui.qs.tiles.WorkModeTile;
 import com.android.systemui.util.leak.GarbageMonitor;
 
+import java.util.Map;
+
 import javax.inject.Inject;
 import javax.inject.Provider;
 
 import dagger.Lazy;
 
+/**
+ * A factory that creates Quick Settings tiles based on a tileSpec
+ *
+ * To create a new tile within SystemUI, the tile class should extend {@link QSTileImpl} and have
+ * a public static final TILE_SPEC field which serves as a unique key for this tile. (e.g. {@link
+ * com.android.systemui.qs.tiles.DreamTile#TILE_SPEC})
+ *
+ * After, create or find an existing Module class to house the tile's binding method (e.g. {@link
+ * com.android.systemui.accessibility.AccessibilityModule}). If creating a new module, add your
+ * module to the SystemUI dagger graph by including it in an appropriate module.
+ */
 @SysUISingleton
 public class QSFactoryImpl implements QSFactory {
 
     private static final String TAG = "QSFactory";
 
-    private final Provider<InternetTile> mInternetTileProvider;
-    private final Provider<BluetoothTile> mBluetoothTileProvider;
-    private final Provider<DndTile> mDndTileProvider;
-    private final Provider<ColorCorrectionTile> mColorCorrectionTileProvider;
-    private final Provider<ColorInversionTile> mColorInversionTileProvider;
-    private final Provider<AirplaneModeTile> mAirplaneModeTileProvider;
-    private final Provider<WorkModeTile> mWorkModeTileProvider;
-    private final Provider<RotationLockTile> mRotationLockTileProvider;
-    private final Provider<FlashlightTile> mFlashlightTileProvider;
-    private final Provider<LocationTile> mLocationTileProvider;
-    private final Provider<CastTile> mCastTileProvider;
-    private final Provider<HotspotTile> mHotspotTileProvider;
-    private final Provider<BatterySaverTile> mBatterySaverTileProvider;
-    private final Provider<DataSaverTile> mDataSaverTileProvider;
-    private final Provider<NightDisplayTile> mNightDisplayTileProvider;
-    private final Provider<NfcTile> mNfcTileProvider;
-    private final Provider<GarbageMonitor.MemoryTile> mMemoryTileProvider;
-    private final Provider<UiModeNightTile> mUiModeNightTileProvider;
-    private final Provider<ScreenRecordTile> mScreenRecordTileProvider;
-    private final Provider<ReduceBrightColorsTile> mReduceBrightColorsTileProvider;
-    private final Provider<CameraToggleTile> mCameraToggleTileProvider;
-    private final Provider<MicrophoneToggleTile> mMicrophoneToggleTileProvider;
-    private final Provider<DeviceControlsTile> mDeviceControlsTileProvider;
-    private final Provider<AlarmTile> mAlarmTileProvider;
-    private final Provider<QuickAccessWalletTile> mQuickAccessWalletTileProvider;
-    private final Provider<QRCodeScannerTile> mQRCodeScannerTileProvider;
-    private final Provider<OneHandedModeTile> mOneHandedModeTileProvider;
-    private final Provider<DreamTile> mDreamTileProvider;
-
+    protected final Map<String, Provider<QSTileImpl<?>>> mTileMap;
     private final Lazy<QSHost> mQsHostLazy;
     private final Provider<CustomTile.Builder> mCustomTileBuilderProvider;
 
@@ -102,65 +60,10 @@
     public QSFactoryImpl(
             Lazy<QSHost> qsHostLazy,
             Provider<CustomTile.Builder> customTileBuilderProvider,
-            Provider<InternetTile> internetTileProvider,
-            Provider<BluetoothTile> bluetoothTileProvider,
-            Provider<DndTile> dndTileProvider,
-            Provider<ColorInversionTile> colorInversionTileProvider,
-            Provider<AirplaneModeTile> airplaneModeTileProvider,
-            Provider<WorkModeTile> workModeTileProvider,
-            Provider<RotationLockTile> rotationLockTileProvider,
-            Provider<FlashlightTile> flashlightTileProvider,
-            Provider<LocationTile> locationTileProvider,
-            Provider<CastTile> castTileProvider,
-            Provider<HotspotTile> hotspotTileProvider,
-            Provider<BatterySaverTile> batterySaverTileProvider,
-            Provider<DataSaverTile> dataSaverTileProvider,
-            Provider<NightDisplayTile> nightDisplayTileProvider,
-            Provider<NfcTile> nfcTileProvider,
-            Provider<GarbageMonitor.MemoryTile> memoryTileProvider,
-            Provider<UiModeNightTile> uiModeNightTileProvider,
-            Provider<ScreenRecordTile> screenRecordTileProvider,
-            Provider<ReduceBrightColorsTile> reduceBrightColorsTileProvider,
-            Provider<CameraToggleTile> cameraToggleTileProvider,
-            Provider<MicrophoneToggleTile> microphoneToggleTileProvider,
-            Provider<DeviceControlsTile> deviceControlsTileProvider,
-            Provider<AlarmTile> alarmTileProvider,
-            Provider<QuickAccessWalletTile> quickAccessWalletTileProvider,
-            Provider<QRCodeScannerTile> qrCodeScannerTileProvider,
-            Provider<OneHandedModeTile> oneHandedModeTileProvider,
-            Provider<ColorCorrectionTile> colorCorrectionTileProvider,
-            Provider<DreamTile> dreamTileProvider) {
+            Map<String, Provider<QSTileImpl<?>>> tileMap) {
         mQsHostLazy = qsHostLazy;
         mCustomTileBuilderProvider = customTileBuilderProvider;
-
-        mInternetTileProvider = internetTileProvider;
-        mBluetoothTileProvider = bluetoothTileProvider;
-        mDndTileProvider = dndTileProvider;
-        mColorInversionTileProvider = colorInversionTileProvider;
-        mAirplaneModeTileProvider = airplaneModeTileProvider;
-        mWorkModeTileProvider = workModeTileProvider;
-        mRotationLockTileProvider = rotationLockTileProvider;
-        mFlashlightTileProvider = flashlightTileProvider;
-        mLocationTileProvider = locationTileProvider;
-        mCastTileProvider = castTileProvider;
-        mHotspotTileProvider = hotspotTileProvider;
-        mBatterySaverTileProvider = batterySaverTileProvider;
-        mDataSaverTileProvider = dataSaverTileProvider;
-        mNightDisplayTileProvider = nightDisplayTileProvider;
-        mNfcTileProvider = nfcTileProvider;
-        mMemoryTileProvider = memoryTileProvider;
-        mUiModeNightTileProvider = uiModeNightTileProvider;
-        mScreenRecordTileProvider = screenRecordTileProvider;
-        mReduceBrightColorsTileProvider = reduceBrightColorsTileProvider;
-        mCameraToggleTileProvider = cameraToggleTileProvider;
-        mMicrophoneToggleTileProvider = microphoneToggleTileProvider;
-        mDeviceControlsTileProvider = deviceControlsTileProvider;
-        mAlarmTileProvider = alarmTileProvider;
-        mQuickAccessWalletTileProvider = quickAccessWalletTileProvider;
-        mQRCodeScannerTileProvider = qrCodeScannerTileProvider;
-        mOneHandedModeTileProvider = oneHandedModeTileProvider;
-        mColorCorrectionTileProvider = colorCorrectionTileProvider;
-        mDreamTileProvider = dreamTileProvider;
+        mTileMap = tileMap;
     }
 
     /** Creates a tile with a type based on {@code tileSpec} */
@@ -177,61 +80,10 @@
     @Nullable
     protected QSTileImpl createTileInternal(String tileSpec) {
         // Stock tiles.
-        switch (tileSpec) {
-            case "internet":
-                return mInternetTileProvider.get();
-            case "bt":
-                return mBluetoothTileProvider.get();
-            case "dnd":
-                return mDndTileProvider.get();
-            case "inversion":
-                return mColorInversionTileProvider.get();
-            case "airplane":
-                return mAirplaneModeTileProvider.get();
-            case "work":
-                return mWorkModeTileProvider.get();
-            case "rotation":
-                return mRotationLockTileProvider.get();
-            case "flashlight":
-                return mFlashlightTileProvider.get();
-            case "location":
-                return mLocationTileProvider.get();
-            case "cast":
-                return mCastTileProvider.get();
-            case "hotspot":
-                return mHotspotTileProvider.get();
-            case "battery":
-                return mBatterySaverTileProvider.get();
-            case "saver":
-                return mDataSaverTileProvider.get();
-            case "night":
-                return mNightDisplayTileProvider.get();
-            case "nfc":
-                return mNfcTileProvider.get();
-            case "dark":
-                return mUiModeNightTileProvider.get();
-            case "screenrecord":
-                return mScreenRecordTileProvider.get();
-            case "reduce_brightness":
-                return mReduceBrightColorsTileProvider.get();
-            case "cameratoggle":
-                return mCameraToggleTileProvider.get();
-            case "mictoggle":
-                return mMicrophoneToggleTileProvider.get();
-            case "controls":
-                return mDeviceControlsTileProvider.get();
-            case "alarm":
-                return mAlarmTileProvider.get();
-            case "wallet":
-                return mQuickAccessWalletTileProvider.get();
-            case "qr_code_scanner":
-                return mQRCodeScannerTileProvider.get();
-            case "onehanded":
-                return mOneHandedModeTileProvider.get();
-            case "color_correction":
-                return mColorCorrectionTileProvider.get();
-            case "dream":
-                return mDreamTileProvider.get();
+        if (mTileMap.containsKey(tileSpec)
+                // We should not return a Garbage Monitory Tile if the build is not Debuggable
+                && (!tileSpec.equals(GarbageMonitor.MemoryTile.TILE_SPEC) || Build.IS_DEBUGGABLE)) {
+            return mTileMap.get(tileSpec).get();
         }
 
         // Custom tiles
@@ -240,13 +92,6 @@
                     mCustomTileBuilderProvider.get(), tileSpec, mQsHostLazy.get().getUserContext());
         }
 
-        // Debug tiles.
-        if (Build.IS_DEBUGGABLE) {
-            if (tileSpec.equals(GarbageMonitor.MemoryTile.TILE_SPEC)) {
-                return mMemoryTileProvider.get();
-            }
-        }
-
         // Broken tiles.
         Log.w(TAG, "No stock tile spec: " + tileSpec);
         return null;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 2cffe89..49ba508 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -712,6 +712,10 @@
             return context.getDrawable(mResId);
         }
 
+        public int getResId() {
+            return mResId;
+        }
+
         @Override
         public boolean equals(Object o) {
             return o instanceof ResourceIcon && ((ResourceIcon) o).mResId == mResId;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 29d7fb0..de1137e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -440,12 +440,11 @@
 
         // State handling and description
         val stateDescription = StringBuilder()
-        val stateText = getStateText(state)
+        val arrayResId = SubtitleArrayMapping.getSubtitleId(state.spec)
+        val stateText = state.getStateText(arrayResId, resources)
+        state.secondaryLabel = state.getSecondaryLabel(stateText)
         if (!TextUtils.isEmpty(stateText)) {
             stateDescription.append(stateText)
-            if (TextUtils.isEmpty(state.secondaryLabel)) {
-                state.secondaryLabel = stateText
-            }
         }
         if (state.disabledByPolicy && state.state != Tile.STATE_UNAVAILABLE) {
             stateDescription.append(", ")
@@ -591,16 +590,6 @@
         return resources.getStringArray(arrayResId)[Tile.STATE_UNAVAILABLE]
     }
 
-    private fun getStateText(state: QSTile.State): String {
-        return if (state.state == Tile.STATE_UNAVAILABLE || state is BooleanState) {
-            val arrayResId = SubtitleArrayMapping.getSubtitleId(state.spec)
-            val array = resources.getStringArray(arrayResId)
-            array[state.state]
-        } else {
-            ""
-        }
-    }
-
     /*
      * The view should not be animated if it's not on screen and no part of it is visible.
      */
@@ -663,45 +652,6 @@
     )
 }
 
-@VisibleForTesting
-internal object SubtitleArrayMapping {
-    private val subtitleIdsMap = mapOf<String?, Int>(
-        "internet" to R.array.tile_states_internet,
-        "wifi" to R.array.tile_states_wifi,
-        "cell" to R.array.tile_states_cell,
-        "battery" to R.array.tile_states_battery,
-        "dnd" to R.array.tile_states_dnd,
-        "flashlight" to R.array.tile_states_flashlight,
-        "rotation" to R.array.tile_states_rotation,
-        "bt" to R.array.tile_states_bt,
-        "airplane" to R.array.tile_states_airplane,
-        "location" to R.array.tile_states_location,
-        "hotspot" to R.array.tile_states_hotspot,
-        "inversion" to R.array.tile_states_inversion,
-        "saver" to R.array.tile_states_saver,
-        "dark" to R.array.tile_states_dark,
-        "work" to R.array.tile_states_work,
-        "cast" to R.array.tile_states_cast,
-        "night" to R.array.tile_states_night,
-        "screenrecord" to R.array.tile_states_screenrecord,
-        "reverse" to R.array.tile_states_reverse,
-        "reduce_brightness" to R.array.tile_states_reduce_brightness,
-        "cameratoggle" to R.array.tile_states_cameratoggle,
-        "mictoggle" to R.array.tile_states_mictoggle,
-        "controls" to R.array.tile_states_controls,
-        "wallet" to R.array.tile_states_wallet,
-        "qr_code_scanner" to R.array.tile_states_qr_code_scanner,
-        "alarm" to R.array.tile_states_alarm,
-        "onehanded" to R.array.tile_states_onehanded,
-        "color_correction" to R.array.tile_states_color_correction,
-        "dream" to R.array.tile_states_dream
-    )
-
-    fun getSubtitleId(spec: String?): Int {
-        return subtitleIdsMap.getOrDefault(spec, R.array.tile_states_default)
-    }
-}
-
 fun constrainSquishiness(squish: Float): Float {
     return 0.1f + squish * 0.9f
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SubtitleArrayMapping.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SubtitleArrayMapping.kt
new file mode 100644
index 0000000..f672e51
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SubtitleArrayMapping.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.tileimpl
+
+import com.android.systemui.R
+
+/** Return the subtitle resource Id of the given tile. */
+object SubtitleArrayMapping {
+    private val subtitleIdsMap: HashMap<String, Int> = HashMap()
+    init {
+        subtitleIdsMap["internet"] = R.array.tile_states_internet
+        subtitleIdsMap["wifi"] = R.array.tile_states_wifi
+        subtitleIdsMap["cell"] = R.array.tile_states_cell
+        subtitleIdsMap["battery"] = R.array.tile_states_battery
+        subtitleIdsMap["dnd"] = R.array.tile_states_dnd
+        subtitleIdsMap["flashlight"] = R.array.tile_states_flashlight
+        subtitleIdsMap["rotation"] = R.array.tile_states_rotation
+        subtitleIdsMap["bt"] = R.array.tile_states_bt
+        subtitleIdsMap["airplane"] = R.array.tile_states_airplane
+        subtitleIdsMap["location"] = R.array.tile_states_location
+        subtitleIdsMap["hotspot"] = R.array.tile_states_hotspot
+        subtitleIdsMap["inversion"] = R.array.tile_states_inversion
+        subtitleIdsMap["saver"] = R.array.tile_states_saver
+        subtitleIdsMap["dark"] = R.array.tile_states_dark
+        subtitleIdsMap["work"] = R.array.tile_states_work
+        subtitleIdsMap["cast"] = R.array.tile_states_cast
+        subtitleIdsMap["night"] = R.array.tile_states_night
+        subtitleIdsMap["screenrecord"] = R.array.tile_states_screenrecord
+        subtitleIdsMap["reverse"] = R.array.tile_states_reverse
+        subtitleIdsMap["reduce_brightness"] = R.array.tile_states_reduce_brightness
+        subtitleIdsMap["cameratoggle"] = R.array.tile_states_cameratoggle
+        subtitleIdsMap["mictoggle"] = R.array.tile_states_mictoggle
+        subtitleIdsMap["controls"] = R.array.tile_states_controls
+        subtitleIdsMap["wallet"] = R.array.tile_states_wallet
+        subtitleIdsMap["qr_code_scanner"] = R.array.tile_states_qr_code_scanner
+        subtitleIdsMap["alarm"] = R.array.tile_states_alarm
+        subtitleIdsMap["onehanded"] = R.array.tile_states_onehanded
+        subtitleIdsMap["color_correction"] = R.array.tile_states_color_correction
+        subtitleIdsMap["dream"] = R.array.tile_states_dream
+        subtitleIdsMap["font_scaling"] = R.array.tile_states_font_scaling
+    }
+
+    /** Get the subtitle resource id of the given tile */
+    fun getSubtitleId(spec: String?): Int {
+        return if (spec == null) {
+            R.array.tile_states_default
+        } else subtitleIdsMap[spec] ?: R.array.tile_states_default
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index 033dbe0..92a83bb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -57,6 +57,9 @@
 
 /** Quick settings tile: Airplane mode **/
 public class AirplaneModeTile extends QSTileImpl<BooleanState> {
+
+    public static final String TILE_SPEC = "airplane";
+
     private final SettingObserver mSetting;
     private final BroadcastDispatcher mBroadcastDispatcher;
     private final Lazy<ConnectivityManager> mLazyConnectivityManager;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
index 14e0f70..2ca452e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
@@ -118,4 +118,8 @@
     override fun getLongClickIntent(): Intent? {
         return null
     }
-}
\ No newline at end of file
+
+    companion object {
+        const val TILE_SPEC = "alarm"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
index ee49b29..027a464 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
@@ -48,6 +48,8 @@
 public class BatterySaverTile extends QSTileImpl<BooleanState> implements
         BatteryController.BatteryStateChangeCallback {
 
+    public static final String TILE_SPEC = "battery";
+
     private final BatteryController mBatteryController;
     @VisibleForTesting
     protected final SettingObserver mSetting;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 9a0d0d9..df1c8df 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -55,6 +55,9 @@
 
 /** Quick settings tile: Bluetooth **/
 public class BluetoothTile extends QSTileImpl<BooleanState> {
+
+    public static final String TILE_SPEC = "bt";
+
     private static final Intent BLUETOOTH_SETTINGS = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
 
     private final BluetoothController mController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
index ee41f1d..93e5f1e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
@@ -45,6 +45,8 @@
 
 public class CameraToggleTile extends SensorPrivacyToggleTile {
 
+    public static final String TILE_SPEC = "cameratoggle";
+
     @Inject
     protected CameraToggleTile(QSHost host,
             @Background Looper backgroundLooper,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index dce137f..8d98481 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -66,7 +66,9 @@
 /** Quick settings tile: Cast **/
 public class CastTile extends QSTileImpl<BooleanState> {
 
-    private static final String INTERACTION_JANK_TAG = "cast";
+    public static final String TILE_SPEC = "cast";
+
+    private static final String INTERACTION_JANK_TAG = TILE_SPEC;
 
     private static final Intent CAST_SETTINGS =
             new Intent(Settings.ACTION_CAST_SETTINGS);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java
index 6dfcf5c..b6205d5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java
@@ -48,6 +48,8 @@
 /** Quick settings tile: Color correction **/
 public class ColorCorrectionTile extends QSTileImpl<BooleanState> {
 
+    public static final String TILE_SPEC = "color_correction";
+
     private final Icon mIcon = ResourceIcon.get(drawable.ic_qs_color_correction);
     private final SettingObserver mSetting;
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index a31500c..9a44e83 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -49,6 +49,7 @@
 /** Quick settings tile: Invert colors **/
 public class ColorInversionTile extends QSTileImpl<BooleanState> {
 
+    public static final String TILE_SPEC = "inversion";
     private final SettingObserver mSetting;
 
     @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index 2fc99f3..add517e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -48,6 +48,8 @@
 public class DataSaverTile extends QSTileImpl<BooleanState> implements
         DataSaverController.Listener{
 
+    public static final String TILE_SPEC = "saver";
+
     private static final String INTERACTION_JANK_TAG = "start_data_saver";
 
     private final DataSaverController mDataSaverController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
index 41d8549..01164fb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
@@ -159,4 +159,8 @@
     override fun getTileLabel(): CharSequence {
         return mContext.getText(controlsComponent.getTileTitleId())
     }
+
+    companion object {
+        const val TILE_SPEC = "controls"
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 8b7f53f..434fe45 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -67,6 +67,8 @@
 /** Quick settings tile: Do not disturb **/
 public class DndTile extends QSTileImpl<BooleanState> {
 
+    public static final String TILE_SPEC = "dnd";
+
     private static final Intent ZEN_SETTINGS =
             new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java
index 5bc209a..53774e8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java
@@ -60,6 +60,8 @@
 /** Quick settings tile: Screensaver (dream) **/
 public class DreamTile extends QSTileImpl<QSTile.BooleanState> {
 
+    public static final String TILE_SPEC = "dream";
+
     private static final String LOG_TAG = "QSDream";
     // TODO: consider 1 animated icon instead
     private final Icon mIconDocked = ResourceIcon.get(R.drawable.ic_qs_screen_saver);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index a747926..e091a75 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -49,6 +49,7 @@
 public class FlashlightTile extends QSTileImpl<BooleanState> implements
         FlashlightController.FlashlightListener {
 
+    public static final String TILE_SPEC = "flashlight";
     private final FlashlightController mFlashlightController;
 
     @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt
new file mode 100644
index 0000000..9b4ac1b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt
@@ -0,0 +1,112 @@
+/*
+ * 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
+
+import android.content.Intent
+import android.os.Handler
+import android.os.Looper
+import android.view.View
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.internal.logging.MetricsLogger
+import com.android.systemui.R
+import com.android.systemui.accessibility.fontscaling.FontScalingDialog
+import com.android.systemui.animation.DialogCuj
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.util.settings.SystemSettings
+import javax.inject.Inject
+
+class FontScalingTile
+@Inject
+constructor(
+    host: QSHost,
+    @Background backgroundLooper: Looper,
+    @Main mainHandler: Handler,
+    falsingManager: FalsingManager,
+    metricsLogger: MetricsLogger,
+    statusBarStateController: StatusBarStateController,
+    activityStarter: ActivityStarter,
+    qsLogger: QSLogger,
+    private val dialogLaunchAnimator: DialogLaunchAnimator,
+    private val systemSettings: SystemSettings,
+    private val featureFlags: FeatureFlags
+) :
+    QSTileImpl<QSTile.State?>(
+        host,
+        backgroundLooper,
+        mainHandler,
+        falsingManager,
+        metricsLogger,
+        statusBarStateController,
+        activityStarter,
+        qsLogger
+    ) {
+    private val icon = ResourceIcon.get(R.drawable.ic_qs_font_scaling)
+
+    override fun isAvailable(): Boolean {
+        return featureFlags.isEnabled(Flags.ENABLE_FONT_SCALING_TILE)
+    }
+
+    override fun newTileState(): QSTile.State {
+        val state = QSTile.State()
+        state.handlesLongClick = false
+        return state
+    }
+
+    override fun handleClick(view: View?) {
+        mUiHandler.post {
+            val dialog: SystemUIDialog = FontScalingDialog(mContext, systemSettings)
+            if (view != null) {
+                dialogLaunchAnimator.showFromView(
+                    dialog,
+                    view,
+                    DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, INTERACTION_JANK_TAG)
+                )
+            } else {
+                dialog.show()
+            }
+        }
+    }
+
+    override fun handleUpdateState(state: QSTile.State?, arg: Any?) {
+        state?.label = mContext.getString(R.string.quick_settings_font_scaling_label)
+        state?.icon = icon
+    }
+
+    override fun getLongClickIntent(): Intent? {
+        return null
+    }
+
+    override fun getTileLabel(): CharSequence {
+        return mContext.getString(R.string.quick_settings_font_scaling_label)
+    }
+
+    companion object {
+        const val TILE_SPEC = "font_scaling"
+        private const val INTERACTION_JANK_TAG = "font_scaling"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 624def6..6bf8b76 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -51,6 +51,7 @@
 /** Quick settings tile: Hotspot **/
 public class HotspotTile extends QSTileImpl<BooleanState> {
 
+    public static final String TILE_SPEC = "hotspot";
     private final HotspotController mHotspotController;
     private final DataSaverController mDataSaverController;
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index 51de522..12e9aeb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -67,6 +67,9 @@
 
 /** Quick settings tile: Internet **/
 public class InternetTile extends QSTileImpl<SignalState> {
+
+    public static final String TILE_SPEC = "internet";
+
     private static final Intent WIFI_SETTINGS = new Intent(Settings.ACTION_WIFI_SETTINGS);
 
     protected final NetworkController mController;
@@ -255,17 +258,19 @@
                 Log.d(TAG, "setWifiIndicators: " + indicators);
             }
             mWifiInfo.mEnabled = indicators.enabled;
-            if (indicators.qsIcon == null) {
-                return;
-            }
-            mWifiInfo.mConnected = indicators.qsIcon.visible;
-            mWifiInfo.mWifiSignalIconId = indicators.qsIcon.icon;
-            mWifiInfo.mWifiSignalContentDescription = indicators.qsIcon.contentDescription;
-            mWifiInfo.mEnabled = indicators.enabled;
             mWifiInfo.mSsid = indicators.description;
             mWifiInfo.mIsTransient = indicators.isTransient;
             mWifiInfo.mStatusLabel = indicators.statusLabel;
-            refreshState(mWifiInfo);
+            if (indicators.qsIcon != null) {
+                mWifiInfo.mConnected = indicators.qsIcon.visible;
+                mWifiInfo.mWifiSignalIconId = indicators.qsIcon.icon;
+                mWifiInfo.mWifiSignalContentDescription = indicators.qsIcon.contentDescription;
+                refreshState(mWifiInfo);
+            } else {
+                mWifiInfo.mConnected = false;
+                mWifiInfo.mWifiSignalIconId = 0;
+                mWifiInfo.mWifiSignalContentDescription = null;
+            }
         }
 
         @Override
@@ -529,6 +534,9 @@
         if (DEBUG) {
             Log.d(TAG, "handleUpdateEthernetState: " + "EthernetCallbackInfo = " + cb.toString());
         }
+        if (!cb.mConnected) {
+            return;
+        }
         final Resources r = mContext.getResources();
         state.label = r.getString(R.string.quick_settings_internet_label);
         state.state = Tile.STATE_ACTIVE;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index 9466a69..89d402a3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -48,6 +48,8 @@
 /** Quick settings tile: Location **/
 public class LocationTile extends QSTileImpl<BooleanState> {
 
+    public static final String TILE_SPEC = "location";
+
     private final LocationController mController;
     private final KeyguardStateController mKeyguard;
     private final Callback mCallback = new Callback();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
index e547095..2e475d4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
@@ -45,6 +45,8 @@
 
 public class MicrophoneToggleTile extends SensorPrivacyToggleTile {
 
+    public static final String TILE_SPEC = "mictoggle";
+
     @Inject
     protected MicrophoneToggleTile(QSHost host,
             @Background Looper backgroundLooper,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
index a61f0ce..e189f80 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
@@ -51,7 +51,9 @@
 /** Quick settings tile: Enable/Disable NFC **/
 public class NfcTile extends QSTileImpl<BooleanState> {
 
-    private static final String NFC = "nfc";
+    public static final String TILE_SPEC = "nfc";
+
+    private static final String NFC = TILE_SPEC;
     private final Icon mIcon = ResourceIcon.get(R.drawable.ic_qs_nfc);
 
     @Nullable
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
index 0e9f659..aacd53b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -61,6 +61,8 @@
 public class NightDisplayTile extends QSTileImpl<BooleanState> implements
         NightDisplayListener.Callback {
 
+    public static final String TILE_SPEC = "night";
+
     /**
      * Pattern for {@link java.time.format.DateTimeFormatter} used to approximate the time to the
      * nearest hour and add on the AM/PM indicator.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java
index 7e17124..ae67d99 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java
@@ -47,6 +47,9 @@
 
 /** Quick settings tile: One-handed mode **/
 public class OneHandedModeTile extends QSTileImpl<BooleanState> {
+
+    public static final String TILE_SPEC = "onehanded";
+
     private final Icon mIcon = ResourceIcon.get(
             com.android.internal.R.drawable.ic_qs_one_handed_mode);
     private final SettingObserver mSetting;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
index 6d50b56..92f5272 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
@@ -44,6 +44,9 @@
 
 /** Quick settings tile: QR Code Scanner **/
 public class QRCodeScannerTile extends QSTileImpl<QSTile.State> {
+
+    public static final String TILE_SPEC = "qr_code_scanner";
+
     private static final String TAG = "QRCodeScanner";
 
     private final CharSequence mLabel = mContext.getString(R.string.qr_code_scanner_title);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index 248c78e..4a3c563 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -62,6 +62,8 @@
 /** Quick settings tile: Quick access wallet **/
 public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
 
+    public static final String TILE_SPEC = "wallet";
+
     private static final String TAG = "QuickAccessWalletTile";
     private static final String FEATURE_CHROME_OS = "org.chromium.arc";
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
index 1dac339..10f1ce4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
@@ -49,6 +49,7 @@
 public class ReduceBrightColorsTile extends QSTileImpl<QSTile.BooleanState>
         implements ReduceBrightColorsController.Listener{
 
+    public static final String TILE_SPEC = "reduce_brightness";
     private final boolean mIsAvailable;
     private final ReduceBrightColorsController mReduceBrightColorsController;
     private boolean mIsListening;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index 600874f..8888c73 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -57,6 +57,9 @@
 /** Quick settings tile: Rotation **/
 public class RotationLockTile extends QSTileImpl<BooleanState> implements
         BatteryController.BatteryStateChangeCallback {
+
+    public static final String TILE_SPEC = "rotation";
+
     private static final String EMPTY_SECONDARY_STRING = "";
 
     private final Icon mIcon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_auto_rotate);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 64a8a14..07b50c9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -54,6 +54,9 @@
  */
 public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
         implements RecordingController.RecordingStateChangeCallback {
+
+    public static final String TILE_SPEC = "screenrecord";
+
     private static final String TAG = "ScreenRecordTile";
     private static final String INTERACTION_JANK_TAG = "screen_record";
 
@@ -171,8 +174,9 @@
             getHost().collapsePanels();
         };
 
-        Dialog dialog = mController.createScreenRecordDialog(mContext, mFlags,
+        final Dialog dialog = mController.createScreenRecordDialog(mContext, mFlags,
                 mDialogLaunchAnimator, mActivityStarter, onStartRecordingClicked);
+
         ActivityStarter.OnDismissAction dismissAction = () -> {
             if (shouldAnimateFromView) {
                 mDialogLaunchAnimator.showFromView(dialog, view, new DialogCuj(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
index 92f6690a..809689c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -17,7 +17,6 @@
 package com.android.systemui.qs.tiles;
 
 import android.app.UiModeManager;
-import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.os.Handler;
@@ -60,6 +59,9 @@
 public class UiModeNightTile extends QSTileImpl<QSTile.BooleanState> implements
         ConfigurationController.ConfigurationListener,
         BatteryController.BatteryStateChangeCallback {
+
+    public static final String TILE_SPEC = "dark";
+
     public static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("hh:mm a");
     private final UiModeManager mUiModeManager;
     private final BatteryController mBatteryController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index 72c6bfe..6a5c990 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -49,6 +49,9 @@
 /** Quick settings tile: Work profile on/off */
 public class WorkModeTile extends QSTileImpl<BooleanState> implements
         ManagedProfileController.Callback {
+
+    public static final String TILE_SPEC = "work";
+
     private final Icon mIcon = ResourceIcon.get(R.drawable.stat_sys_managed_profile_status);
 
     private final ManagedProfileController mProfileController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index 1ed18c3..c0e4995 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -805,6 +805,11 @@
     }
 
     @Override
+    public void onCarrierNetworkChange(boolean active) {
+        mHandler.post(() -> updateDialog(true /* shouldUpdateMobileNetwork */));
+    }
+
+    @Override
     @WorkerThread
     public void onAccessPointsChanged(@Nullable List<WifiEntry> wifiEntries,
             @Nullable WifiEntry connectedEntry, boolean hasMoreWifiEntries) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 2e6ea0e..557b718 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -206,6 +206,8 @@
     protected boolean mHasEthernet = false;
     @VisibleForTesting
     protected ConnectedWifiInternetMonitor mConnectedWifiInternetMonitor;
+    @VisibleForTesting
+    protected boolean mCarrierNetworkChangeMode;
 
     private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
             new KeyguardUpdateMonitorCallback() {
@@ -507,10 +509,13 @@
     Drawable getSignalStrengthIcon(int subId, Context context, int level, int numLevels,
             int iconType, boolean cutOut) {
         boolean isForDds = subId == mDefaultDataSubId;
+        int levelDrawable =
+                mCarrierNetworkChangeMode ? SignalDrawable.getCarrierChangeState(numLevels)
+                        : SignalDrawable.getState(level, numLevels, cutOut);
         if (isForDds) {
-            mSignalDrawable.setLevel(SignalDrawable.getState(level, numLevels, cutOut));
+            mSignalDrawable.setLevel(levelDrawable);
         } else {
-            mSecondarySignalDrawable.setLevel(SignalDrawable.getState(level, numLevels, cutOut));
+            mSecondarySignalDrawable.setLevel(levelDrawable);
         }
 
         // Make the network type drawable
@@ -672,10 +677,13 @@
         }
 
         int resId = Objects.requireNonNull(mapIconSets(config).get(iconKey)).dataContentDescription;
+        SignalIcon.MobileIconGroup iconGroup;
         if (isCarrierNetworkActive()) {
-            SignalIcon.MobileIconGroup carrierMergedWifiIconGroup =
-                    TelephonyIcons.CARRIER_MERGED_WIFI;
-            resId = carrierMergedWifiIconGroup.dataContentDescription;
+            iconGroup = TelephonyIcons.CARRIER_MERGED_WIFI;
+            resId = iconGroup.dataContentDescription;
+        } else if (mCarrierNetworkChangeMode) {
+            iconGroup = TelephonyIcons.CARRIER_NETWORK_CHANGE;
+            resId = iconGroup.dataContentDescription;
         }
 
         return resId != 0
@@ -1066,7 +1074,8 @@
             TelephonyCallback.DisplayInfoListener,
             TelephonyCallback.ServiceStateListener,
             TelephonyCallback.SignalStrengthsListener,
-            TelephonyCallback.UserMobileDataStateListener {
+            TelephonyCallback.UserMobileDataStateListener,
+            TelephonyCallback.CarrierNetworkListener{
 
         private final int mSubId;
         private InternetTelephonyCallback(int subId) {
@@ -1098,6 +1107,12 @@
         public void onUserMobileDataStateChanged(boolean enabled) {
             mCallback.onUserMobileDataStateChanged(enabled);
         }
+
+        @Override
+        public void onCarrierNetworkChange(boolean active) {
+            mCarrierNetworkChangeMode = active;
+            mCallback.onCarrierNetworkChange(active);
+        }
     }
 
     private class InternetOnSubscriptionChangedListener
@@ -1267,6 +1282,8 @@
 
         void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo);
 
+        void onCarrierNetworkChange(boolean active);
+
         void dismissDialog();
 
         void onAccessPointsChanged(@Nullable List<WifiEntry> wifiEntries,
diff --git a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java
index 802db7e..dc3c820 100644
--- a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java
@@ -27,7 +27,6 @@
 import com.android.systemui.CoreStartable;
 import com.android.systemui.R;
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 1151475..25ff308b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -17,7 +17,6 @@
 package com.android.systemui.recents;
 
 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
-import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.MotionEvent.ACTION_CANCEL;
 import static android.view.MotionEvent.ACTION_DOWN;
 import static android.view.MotionEvent.ACTION_UP;
@@ -26,10 +25,12 @@
 import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DREAMING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
@@ -88,6 +89,7 @@
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.navigationbar.buttons.KeyButtonView;
 import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
+import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.shared.recents.IOverviewProxy;
@@ -98,6 +100,7 @@
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
 import com.android.systemui.statusbar.policy.CallbackController;
+import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder;
 import com.android.wm.shell.sysui.ShellInterface;
 
 import java.io.PrintWriter;
@@ -143,7 +146,9 @@
     private final CommandQueue mCommandQueue;
     private final UserTracker mUserTracker;
     private final KeyguardUnlockAnimationController mSysuiUnlockAnimationController;
+    private final Optional<UnfoldTransitionProgressForwarder> mUnfoldTransitionProgressForwarder;
     private final UiEventLogger mUiEventLogger;
+    private final DisplayTracker mDisplayTracker;
 
     private Region mActiveNavBarRegion;
     private SurfaceControl mNavigationBarSurface;
@@ -225,11 +230,11 @@
 
         @Override
         public void onImeSwitcherPressed() {
-            // TODO(b/204901476) We're intentionally using DEFAULT_DISPLAY for now since
+            // TODO(b/204901476) We're intentionally using the default display for now since
             // Launcher/Taskbar isn't display aware.
             mContext.getSystemService(InputMethodManager.class)
                     .showInputMethodPickerFromSystem(true /* showAuxiliarySubtypes */,
-                            DEFAULT_DISPLAY);
+                            mDisplayTracker.getDefaultDisplayId());
             mUiEventLogger.log(KeyButtonView.NavBarButtonEvent.NAVBAR_IME_SWITCHER_BUTTON_TAP);
         }
 
@@ -413,6 +418,10 @@
             params.putBoolean(KEY_EXTRA_SUPPORTS_WINDOW_CORNERS, mSupportsRoundedCornersOnWindows);
             params.putBinder(KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER,
                     mSysuiUnlockAnimationController.asBinder());
+            mUnfoldTransitionProgressForwarder.ifPresent(
+                    unfoldProgressForwarder -> params.putBinder(
+                            KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER,
+                            unfoldProgressForwarder.asBinder()));
             // Add all the interfaces exposed by the shell
             mShellInterface.createExternalInterfaces(params);
 
@@ -507,9 +516,12 @@
             UserTracker userTracker,
             ScreenLifecycle screenLifecycle,
             UiEventLogger uiEventLogger,
+            DisplayTracker displayTracker,
             KeyguardUnlockAnimationController sysuiUnlockAnimationController,
             AssistUtils assistUtils,
-            DumpManager dumpManager) {
+            DumpManager dumpManager,
+            Optional<UnfoldTransitionProgressForwarder> unfoldTransitionProgressForwarder
+    ) {
         // b/241601880: This component shouldn't be running for a non-primary user
         if (!Process.myUserHandle().equals(UserHandle.SYSTEM)) {
             Log.e(TAG_OPS, "Unexpected initialization for non-primary user", new Throwable());
@@ -534,6 +546,8 @@
         mSysUiState = sysUiState;
         mSysUiState.addCallback(this::notifySystemUiStateFlags);
         mUiEventLogger = uiEventLogger;
+        mDisplayTracker = displayTracker;
+        mUnfoldTransitionProgressForwarder = unfoldTransitionProgressForwarder;
 
         dumpManager.registerDumpable(getClass().getSimpleName(), this);
 
@@ -652,13 +666,14 @@
     }
 
     private void onStatusBarStateChanged(boolean keyguardShowing, boolean keyguardOccluded,
-            boolean bouncerShowing, boolean isDozing, boolean panelExpanded) {
+            boolean bouncerShowing, boolean isDozing, boolean panelExpanded, boolean isDreaming) {
         mSysUiState.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING,
                         keyguardShowing && !keyguardOccluded)
                 .setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED,
                         keyguardShowing && keyguardOccluded)
                 .setFlag(SYSUI_STATE_BOUNCER_SHOWING, bouncerShowing)
                 .setFlag(SYSUI_STATE_DEVICE_DOZING, isDozing)
+                .setFlag(SYSUI_STATE_DEVICE_DREAMING, isDreaming)
                 .commitUpdate(mContext.getDisplayId());
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockModule.kt b/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockModule.kt
new file mode 100644
index 0000000..9abe90f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockModule.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.rotationlock
+
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.RotationLockTile
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
+
+@Module
+interface RotationLockModule {
+
+    /** Inject RotationLockTile into tileMap in QSModule */
+    @Binds
+    @IntoMap
+    @StringKey(RotationLockTile.TILE_SPEC)
+    fun bindRotationLockTile(rotationLockTile: RotationLockTile): QSTileImpl<*>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index b8684ee..db2e62b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -36,6 +36,8 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
+import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
+import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.settings.UserTracker;
@@ -46,6 +48,8 @@
 
 import javax.inject.Inject;
 
+import dagger.Lazy;
+
 /**
  * Helper class to initiate a screen recording
  */
@@ -60,6 +64,8 @@
     private CountDownTimer mCountDownTimer = null;
     private final Executor mMainExecutor;
     private final BroadcastDispatcher mBroadcastDispatcher;
+    private final Context mContext;
+    private final FeatureFlags mFlags;
     private final UserContextProvider mUserContextProvider;
     private final UserTracker mUserTracker;
 
@@ -70,6 +76,8 @@
     private CopyOnWriteArrayList<RecordingStateChangeCallback> mListeners =
             new CopyOnWriteArrayList<>();
 
+    private final Lazy<ScreenCaptureDevicePolicyResolver> mDevicePolicyResolver;
+
     @VisibleForTesting
     final UserTracker.Callback mUserChangedCallback =
             new UserTracker.Callback() {
@@ -100,22 +108,44 @@
     @Inject
     public RecordingController(@Main Executor mainExecutor,
             BroadcastDispatcher broadcastDispatcher,
+            Context context,
+            FeatureFlags flags,
             UserContextProvider userContextProvider,
+            Lazy<ScreenCaptureDevicePolicyResolver> devicePolicyResolver,
             UserTracker userTracker) {
         mMainExecutor = mainExecutor;
+        mContext = context;
+        mFlags = flags;
+        mDevicePolicyResolver = devicePolicyResolver;
         mBroadcastDispatcher = broadcastDispatcher;
         mUserContextProvider = userContextProvider;
         mUserTracker = userTracker;
     }
 
-    /** Create a dialog to show screen recording options to the user. */
+    /**
+     * MediaProjection host is SystemUI for the screen recorder, so return 'my user handle'
+     */
+    private UserHandle getHostUserHandle() {
+        return UserHandle.of(UserHandle.myUserId());
+    }
+
+    /** Create a dialog to show screen recording options to the user.
+     *  If screen capturing is currently not allowed it will return a dialog
+     *  that warns users about it. */
     public Dialog createScreenRecordDialog(Context context, FeatureFlags flags,
                                            DialogLaunchAnimator dialogLaunchAnimator,
                                            ActivityStarter activityStarter,
                                            @Nullable Runnable onStartRecordingClicked) {
+        if (mFlags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES)
+                && mDevicePolicyResolver.get()
+                        .isScreenCaptureCompletelyDisabled(getHostUserHandle())) {
+            return new ScreenCaptureDisabledDialog(mContext);
+        }
+
         return flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)
-                ? new ScreenRecordPermissionDialog(context, this, activityStarter,
-                        dialogLaunchAnimator, mUserContextProvider, onStartRecordingClicked)
+                ? new ScreenRecordPermissionDialog(context,  getHostUserHandle(), this,
+                    activityStarter, dialogLaunchAnimator, mUserContextProvider,
+                    onStartRecordingClicked)
                 : new ScreenRecordDialog(context, this, activityStarter,
                 mUserContextProvider, flags, dialogLaunchAnimator, onStartRecordingClicked);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt
new file mode 100644
index 0000000..7467805
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.screenrecord
+
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.ScreenRecordTile
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
+
+@Module
+interface ScreenRecordModule {
+    /** Inject ScreenRecordTile into tileMap in QSModule */
+    @Binds
+    @IntoMap
+    @StringKey(ScreenRecordTile.TILE_SPEC)
+    fun bindScreenRecordTile(screenRecordTile: ScreenRecordTile): QSTileImpl<*>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
index 68e3dcd..dd21be9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
@@ -42,6 +42,7 @@
 /** Dialog to select screen recording options */
 class ScreenRecordPermissionDialog(
     context: Context?,
+    private val hostUserHandle: UserHandle,
     private val controller: RecordingController,
     private val activityStarter: ActivityStarter,
     private val dialogLaunchAnimator: DialogLaunchAnimator,
@@ -79,11 +80,9 @@
                     CaptureTargetResultReceiver()
                 )
 
-                // Send SystemUI's user handle as the host app user handle because SystemUI
-                // is the 'host app' (the app that receives screen capture data)
                 intent.putExtra(
                     MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_USER_HANDLE,
-                    UserHandle.of(UserHandle.myUserId())
+                    hostUserHandle
                 )
 
                 val animationController = dialogLaunchAnimator.createActivityLaunchController(v!!)
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt
index 017e57f..310baaf 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt
@@ -25,8 +25,22 @@
 import com.android.systemui.R
 
 object ActionIntentCreator {
+    /** @return a chooser intent to share the given URI. */
+    fun createShareIntent(uri: Uri) = createShareIntent(uri, null, null)
+
     /** @return a chooser intent to share the given URI with the optional provided subject. */
-    fun createShareIntent(uri: Uri, subject: String?): Intent {
+    fun createShareIntentWithSubject(uri: Uri, subject: String?) =
+        createShareIntent(uri, subject = subject)
+
+    /** @return a chooser intent to share the given URI with the optional provided extra text. */
+    fun createShareIntentWithExtraText(uri: Uri, extraText: String?) =
+        createShareIntent(uri, extraText = extraText)
+
+    private fun createShareIntent(
+        uri: Uri,
+        subject: String? = null,
+        extraText: String? = null
+    ): Intent {
         // Create a share intent, this will always go through the chooser activity first
         // which should not trigger auto-enter PiP
         val sharingIntent =
@@ -43,6 +57,7 @@
                     )
 
                 putExtra(Intent.EXTRA_SUBJECT, subject)
+                putExtra(Intent.EXTRA_TEXT, extraText)
                 addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                 addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
             }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
index 01e32b7a..aa8e2c0 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
@@ -22,7 +22,6 @@
 import android.os.RemoteException
 import android.os.UserHandle
 import android.util.Log
-import android.view.Display
 import android.view.IRemoteAnimationFinishedCallback
 import android.view.IRemoteAnimationRunner
 import android.view.RemoteAnimationAdapter
@@ -33,6 +32,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.settings.DisplayTracker
 import javax.inject.Inject
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.CoroutineDispatcher
@@ -47,6 +47,7 @@
     @Application private val applicationScope: CoroutineScope,
     @Main private val mainDispatcher: CoroutineDispatcher,
     private val context: Context,
+    private val displayTracker: DisplayTracker
 ) {
     /**
      * Execute the given intent with startActivity while performing operations for screenshot action
@@ -82,7 +83,7 @@
             val runner = RemoteAnimationAdapter(SCREENSHOT_REMOTE_RUNNER, 0, 0)
             try {
                 WindowManagerGlobal.getWindowManagerService()
-                    .overridePendingAppTransitionRemote(runner, Display.DEFAULT_DISPLAY)
+                    .overridePendingAppTransitionRemote(runner, displayTracker.defaultDisplayId)
             } catch (e: Exception) {
                 Log.e(TAG, "Error overriding screenshot app transition", e)
             }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
index 814b8e9..4f5cb72 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.screenshot;
 
-import static android.view.Display.DEFAULT_DISPLAY;
-
 import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_EDIT;
 import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_SHARE;
 import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_INTENT;
@@ -35,6 +33,7 @@
 import android.view.RemoteAnimationAdapter;
 import android.view.WindowManagerGlobal;
 
+import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 
@@ -52,14 +51,17 @@
     private final CentralSurfaces mCentralSurfaces;
     private final ActivityManagerWrapper mActivityManagerWrapper;
     private final ScreenshotSmartActions mScreenshotSmartActions;
+    private final DisplayTracker mDisplayTracker;
 
     @Inject
     public ActionProxyReceiver(Optional<CentralSurfaces> centralSurfacesOptional,
             ActivityManagerWrapper activityManagerWrapper,
-            ScreenshotSmartActions screenshotSmartActions) {
+            ScreenshotSmartActions screenshotSmartActions,
+            DisplayTracker displayTracker) {
         mCentralSurfaces = centralSurfacesOptional.orElse(null);
         mActivityManagerWrapper = activityManagerWrapper;
         mScreenshotSmartActions = screenshotSmartActions;
+        mDisplayTracker = displayTracker;
     }
 
     @Override
@@ -78,7 +80,8 @@
                             ScreenshotController.SCREENSHOT_REMOTE_RUNNER, 0, 0);
                     try {
                         WindowManagerGlobal.getWindowManagerService()
-                                .overridePendingAppTransitionRemote(runner, DEFAULT_DISPLAY);
+                                .overridePendingAppTransitionRemote(runner,
+                                        mDisplayTracker.getDefaultDisplayId());
                     } catch (Exception e) {
                         Log.e(TAG, "Error overriding screenshot app transition", e);
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/AssistContentRequester.java b/packages/SystemUI/src/com/android/systemui/screenshot/AssistContentRequester.java
new file mode 100644
index 0000000..146e576
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/AssistContentRequester.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2022 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.screenshot;
+import android.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
+import android.app.IAssistDataReceiver;
+import android.app.assist.AssistContent;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+
+import java.lang.ref.WeakReference;
+import java.util.Collections;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+
+/**
+ * Can be used to request the AssistContent from a provided task id, useful for getting the web uri
+ * if provided from the task.
+ *
+ * Forked from
+ * packages/apps/Launcher3/quickstep/src/com/android/quickstep/util/AssistContentRequester.java
+ */
+@SysUISingleton
+public class AssistContentRequester {
+    private static final String TAG = "AssistContentRequester";
+    private static final String ASSIST_KEY_CONTENT = "content";
+
+    /** For receiving content, called on the main thread. */
+    public interface Callback {
+        /**
+         * Called when the {@link android.app.assist.AssistContent} of the requested task is
+         * available.
+         **/
+        void onAssistContentAvailable(AssistContent assistContent);
+    }
+
+    private final IActivityTaskManager mActivityTaskManager;
+    private final String mPackageName;
+    private final Executor mCallbackExecutor;
+    private final Executor mSystemInteractionExecutor;
+
+    // If system loses the callback, our internal cache of original callback will also get cleared.
+    private final Map<Object, Callback> mPendingCallbacks =
+            Collections.synchronizedMap(new WeakHashMap<>());
+
+    @Inject
+    public AssistContentRequester(Context context, @Main Executor mainExecutor,
+            @Background Executor bgExecutor) {
+        mActivityTaskManager = ActivityTaskManager.getService();
+        mPackageName = context.getApplicationContext().getPackageName();
+        mCallbackExecutor = mainExecutor;
+        mSystemInteractionExecutor = bgExecutor;
+    }
+
+    /**
+     * Request the {@link AssistContent} from the task with the provided id.
+     *
+     * @param taskId to query for the content.
+     * @param callback to call when the content is available, called on the main thread.
+     */
+    public void requestAssistContent(final int taskId, final Callback callback) {
+        // ActivityTaskManager interaction here is synchronous, so call off the main thread.
+        mSystemInteractionExecutor.execute(() -> {
+            try {
+                mActivityTaskManager.requestAssistDataForTask(
+                        new AssistDataReceiver(callback, this), taskId, mPackageName);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Requesting assist content failed for task: " + taskId, e);
+            }
+        });
+    }
+
+    private void executeOnMainExecutor(Runnable callback) {
+        mCallbackExecutor.execute(callback);
+    }
+
+    private static final class AssistDataReceiver extends IAssistDataReceiver.Stub {
+
+        // The AssistDataReceiver binder callback object is passed to a system server, that may
+        // keep hold of it for longer than the lifetime of the AssistContentRequester object,
+        // potentially causing a memory leak. In the callback passed to the system server, only
+        // keep a weak reference to the parent object and lookup its callback if it still exists.
+        private final WeakReference<AssistContentRequester> mParentRef;
+        private final Object mCallbackKey = new Object();
+
+        AssistDataReceiver(Callback callback, AssistContentRequester parent) {
+            parent.mPendingCallbacks.put(mCallbackKey, callback);
+            mParentRef = new WeakReference<>(parent);
+        }
+
+        @Override
+        public void onHandleAssistData(Bundle data) {
+            if (data == null) {
+                return;
+            }
+
+            final AssistContent content = data.getParcelable(ASSIST_KEY_CONTENT);
+            if (content == null) {
+                Log.e(TAG, "Received AssistData, but no AssistContent found");
+                return;
+            }
+
+            AssistContentRequester requester = mParentRef.get();
+            if (requester != null) {
+                Callback callback = requester.mPendingCallbacks.get(mCallbackKey);
+                if (callback != null) {
+                    requester.executeOnMainExecutor(
+                            () -> callback.onAssistContentAvailable(content));
+                } else {
+                    Log.d(TAG, "Callback received after calling UI was disposed of");
+                }
+            } else {
+                Log.d(TAG, "Callback received after Requester was collected");
+            }
+        }
+
+        @Override
+        public void onHandleAssistScreenshot(Bitmap screenshot) {}
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
index d64b33b..ca8e101 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
@@ -366,7 +366,7 @@
 
     private void doShare(Uri uri) {
         if (mFeatureFlags.isEnabled(Flags.SCREENSHOT_WORK_PROFILE_POLICY)) {
-            Intent shareIntent = ActionIntentCreator.INSTANCE.createShareIntent(uri, null);
+            Intent shareIntent = ActionIntentCreator.INSTANCE.createShareIntent(uri);
             mActionExecutor.launchIntentAsync(shareIntent, null,
                     mScreenshotUserHandle.getIdentifier(), false);
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt
new file mode 100644
index 0000000..ad66514
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt
@@ -0,0 +1,145 @@
+package com.android.systemui.screenshot
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
+import android.os.UserHandle
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewGroup.MarginLayoutParams
+import android.view.ViewTreeObserver
+import android.view.animation.AccelerateDecelerateInterpolator
+import androidx.constraintlayout.widget.Guideline
+import com.android.systemui.R
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import javax.inject.Inject
+
+/**
+ * MessageContainerController controls the display of content in the screenshot message container.
+ */
+class MessageContainerController
+@Inject
+constructor(
+    private val workProfileMessageController: WorkProfileMessageController,
+    private val screenshotDetectionController: ScreenshotDetectionController,
+    private val featureFlags: FeatureFlags,
+) {
+    private lateinit var container: ViewGroup
+    private lateinit var guideline: Guideline
+    private lateinit var workProfileFirstRunView: ViewGroup
+    private lateinit var detectionNoticeView: ViewGroup
+    private var animateOut: Animator? = null
+
+    fun setView(screenshotView: ViewGroup) {
+        container = screenshotView.requireViewById(R.id.screenshot_message_container)
+        guideline = screenshotView.requireViewById(R.id.guideline)
+
+        workProfileFirstRunView = container.requireViewById(R.id.work_profile_first_run)
+        detectionNoticeView = container.requireViewById(R.id.screenshot_detection_notice)
+
+        // Restore to starting state.
+        container.visibility = View.GONE
+        guideline.setGuidelineEnd(0)
+        workProfileFirstRunView.visibility = View.GONE
+        detectionNoticeView.visibility = View.GONE
+    }
+
+    // Minimal implementation for use when Flags.SCREENSHOT_METADATA isn't turned on.
+    fun onScreenshotTaken(userHandle: UserHandle) {
+        if (featureFlags.isEnabled(Flags.SCREENSHOT_WORK_PROFILE_POLICY)) {
+            val workProfileData = workProfileMessageController.onScreenshotTaken(userHandle)
+            if (workProfileData != null) {
+                workProfileFirstRunView.visibility = View.VISIBLE
+                detectionNoticeView.visibility = View.GONE
+
+                workProfileMessageController.populateView(
+                    workProfileFirstRunView,
+                    workProfileData,
+                    this::animateOutMessageContainer
+                )
+                animateInMessageContainer()
+            }
+        }
+    }
+
+    fun onScreenshotTaken(screenshot: ScreenshotData) {
+        if (featureFlags.isEnabled(Flags.SCREENSHOT_WORK_PROFILE_POLICY)) {
+            val workProfileData =
+                workProfileMessageController.onScreenshotTaken(screenshot.userHandle)
+            var notifiedApps: List<CharSequence> = listOf()
+            if (featureFlags.isEnabled(Flags.SCREENSHOT_DETECTION)) {
+                notifiedApps = screenshotDetectionController.maybeNotifyOfScreenshot(screenshot)
+            }
+
+            // If work profile first run needs to show, bias towards that, otherwise show screenshot
+            // detection notification if needed.
+            if (workProfileData != null) {
+                workProfileFirstRunView.visibility = View.VISIBLE
+                detectionNoticeView.visibility = View.GONE
+                workProfileMessageController.populateView(
+                    workProfileFirstRunView,
+                    workProfileData,
+                    this::animateOutMessageContainer
+                )
+                animateInMessageContainer()
+            } else if (notifiedApps.isNotEmpty()) {
+                detectionNoticeView.visibility = View.VISIBLE
+                workProfileFirstRunView.visibility = View.GONE
+                screenshotDetectionController.populateView(detectionNoticeView, notifiedApps)
+                animateInMessageContainer()
+            }
+        }
+    }
+
+    private fun animateInMessageContainer() {
+        if (container.visibility == View.VISIBLE) return
+
+        // Need the container to be fully measured before animating in (to know animation offset
+        // destination)
+        container.visibility = View.VISIBLE
+        container.viewTreeObserver.addOnPreDrawListener(
+            object : ViewTreeObserver.OnPreDrawListener {
+                override fun onPreDraw(): Boolean {
+                    container.viewTreeObserver.removeOnPreDrawListener(this)
+                    getAnimator(true).start()
+                    return false
+                }
+            }
+        )
+    }
+
+    private fun animateOutMessageContainer() {
+        if (animateOut != null) return
+
+        animateOut =
+            getAnimator(false).apply {
+                addListener(
+                    object : AnimatorListenerAdapter() {
+                        override fun onAnimationEnd(animation: Animator) {
+                            super.onAnimationEnd(animation)
+                            container.visibility = View.GONE
+                            animateOut = null
+                        }
+                    }
+                )
+                start()
+            }
+    }
+
+    private fun getAnimator(animateIn: Boolean): Animator {
+        val params = container.layoutParams as MarginLayoutParams
+        val offset = container.height + params.topMargin + params.bottomMargin
+        val anim = if (animateIn) ValueAnimator.ofFloat(0f, 1f) else ValueAnimator.ofFloat(1f, 0f)
+        with(anim) {
+            duration = ScreenshotView.SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS
+            interpolator = AccelerateDecelerateInterpolator()
+            addUpdateListener { valueAnimator: ValueAnimator ->
+                val interpolation = valueAnimator.animatedValue as Float
+                guideline.setGuidelineEnd((interpolation * offset).toInt())
+                container.alpha = interpolation
+            }
+        }
+        return anim
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
index f011aab..4db48ac 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
@@ -45,6 +45,7 @@
      *
      * @param request the request to process
      */
+    // TODO: Delete once SCREENSHOT_METADATA flag is launched
     suspend fun process(request: ScreenshotRequest): ScreenshotRequest {
         var result = request
 
@@ -93,12 +94,67 @@
      * @param request the request to process
      * @param callback the callback to provide the processed request, invoked from the main thread
      */
+    // TODO: Delete once SCREENSHOT_METADATA flag is launched
     fun processAsync(request: ScreenshotRequest, callback: Consumer<ScreenshotRequest>) {
         mainScope.launch {
             val result = process(request)
             callback.accept(result)
         }
     }
+
+    /**
+     * Inspects the incoming ScreenshotData, potentially modifying it based upon policy.
+     *
+     * @param screenshot the screenshot to process
+     */
+    suspend fun process(screenshot: ScreenshotData): ScreenshotData {
+        var result = screenshot
+
+        // Apply work profile screenshots policy:
+        //
+        // If the focused app belongs to a work profile, transforms a full screen
+        // (or partial) screenshot request to a task snapshot (provided image) screenshot.
+
+        // Whenever displayContentInfo is fetched, the topComponent is also populated
+        // regardless of the managed profile status.
+
+        if (screenshot.type != TAKE_SCREENSHOT_PROVIDED_IMAGE &&
+            flags.isEnabled(SCREENSHOT_WORK_PROFILE_POLICY)
+        ) {
+            val info = policy.findPrimaryContent(policy.getDefaultDisplayId())
+            Log.d(TAG, "findPrimaryContent: $info")
+            result.taskId = info.taskId
+            result.topComponent = info.component
+            result.userHandle = info.user
+
+            if (policy.isManagedProfile(info.user.identifier)) {
+                val image = capture.captureTask(info.taskId)
+                    ?: error("Task snapshot returned a null Bitmap!")
+
+                // Provide the task snapshot as the screenshot
+                result.type = TAKE_SCREENSHOT_PROVIDED_IMAGE
+                result.bitmap = image
+                result.screenBounds = info.bounds
+            }
+        }
+
+        return result
+    }
+
+    /**
+     * Note: This is for compatibility with existing Java. Prefer the suspending function when
+     * calling from a Coroutine context.
+     *
+     * @param screenshot the screenshot to process
+     * @param callback the callback to provide the processed screenshot, invoked from the main
+     *                 thread
+     */
+    fun processAsync(screenshot: ScreenshotData, callback: Consumer<ScreenshotData>) {
+        mainScope.launch {
+            val result = process(screenshot)
+            callback.accept(result)
+        }
+    }
 }
 
 private const val TAG = "RequestProcessor"
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 6d87922..8721d71 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -17,7 +17,6 @@
 package com.android.systemui.screenshot;
 
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
 
 import static com.android.systemui.flags.Flags.SCREENSHOT_WORK_PROFILE_POLICY;
@@ -44,6 +43,7 @@
 import android.app.ExitTransitionCoordinator.ExitTransitionCallbacks;
 import android.app.ICompatCameraControlCallback;
 import android.app.Notification;
+import android.app.assist.AssistContent;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -99,8 +99,10 @@
 import com.android.systemui.clipboardoverlay.ClipboardOverlayController;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
 import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition;
 import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback;
+import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.util.Assert;
 
 import com.google.common.util.concurrent.ListenableFuture;
@@ -272,6 +274,7 @@
     private final ScrollCaptureClient mScrollCaptureClient;
     private final PhoneWindow mWindow;
     private final DisplayManager mDisplayManager;
+    private final DisplayTracker mDisplayTracker;
     private final ScrollCaptureController mScrollCaptureController;
     private final LongScreenshotData mLongScreenshotHolder;
     private final boolean mIsLowRamDevice;
@@ -280,7 +283,7 @@
     private final TimeoutHandler mScreenshotHandler;
     private final ActionIntentExecutor mActionExecutor;
     private final UserManager mUserManager;
-    private final WorkProfileMessageController mWorkProfileMessageController;
+    private final AssistContentRequester mAssistContentRequester;
 
     private final OnBackInvokedCallback mOnBackInvokedCallback = () -> {
         if (DEBUG_INPUT) {
@@ -290,6 +293,7 @@
     };
 
     private ScreenshotView mScreenshotView;
+    private final MessageContainerController mMessageContainerController;
     private Bitmap mScreenBitmap;
     private SaveImageInBackgroundTask mSaveInBgTask;
     private boolean mScreenshotTakenInPortrait;
@@ -328,7 +332,9 @@
             ScreenshotNotificationSmartActionsProvider screenshotNotificationSmartActionsProvider,
             ActionIntentExecutor actionExecutor,
             UserManager userManager,
-            WorkProfileMessageController workProfileMessageController
+            AssistContentRequester assistContentRequester,
+            MessageContainerController messageContainerController,
+            DisplayTracker displayTracker
     ) {
         mScreenshotSmartActions = screenshotSmartActions;
         mNotificationsController = screenshotNotificationsController;
@@ -354,13 +360,15 @@
         });
 
         mDisplayManager = requireNonNull(context.getSystemService(DisplayManager.class));
+        mDisplayTracker = displayTracker;
         final Context displayContext = context.createDisplayContext(getDefaultDisplay());
         mContext = (WindowContext) displayContext.createWindowContext(TYPE_SCREENSHOT, null);
         mWindowManager = mContext.getSystemService(WindowManager.class);
         mFlags = flags;
         mActionExecutor = actionExecutor;
         mUserManager = userManager;
-        mWorkProfileMessageController = workProfileMessageController;
+        mMessageContainerController = messageContainerController;
+        mAssistContentRequester = assistContentRequester;
 
         mAccessibilityManager = AccessibilityManager.getInstance(mContext);
 
@@ -390,16 +398,147 @@
                 ClipboardOverlayController.SELF_PERMISSION, null, Context.RECEIVER_NOT_EXPORTED);
     }
 
+    void handleScreenshot(ScreenshotData screenshot, Consumer<Uri> finisher,
+            RequestCallback requestCallback) {
+        Assert.isMainThread();
+        mCurrentRequestCallback = requestCallback;
+        if (screenshot.getType() == WindowManager.TAKE_SCREENSHOT_FULLSCREEN) {
+            Rect bounds = getFullScreenRect();
+            screenshot.setBitmap(
+                    mImageCapture.captureDisplay(mDisplayTracker.getDefaultDisplayId(), bounds));
+            screenshot.setScreenBounds(bounds);
+        }
+
+        if (screenshot.getBitmap() == null) {
+            Log.e(TAG, "handleScreenshot: Screenshot bitmap was null");
+            mNotificationsController.notifyScreenshotError(
+                    R.string.screenshot_failed_to_capture_text);
+            if (mCurrentRequestCallback != null) {
+                mCurrentRequestCallback.reportError();
+            }
+            return;
+        }
+
+        if (!isUserSetupComplete(Process.myUserHandle())) {
+            Log.w(TAG, "User setup not complete, displaying toast only");
+            // User setup isn't complete, so we don't want to show any UI beyond a toast, as editing
+            // and sharing shouldn't be exposed to the user.
+            saveScreenshotAndToast(screenshot.getUserHandle(), finisher);
+            return;
+        }
+
+        mBroadcastSender.sendBroadcast(new Intent(ClipboardOverlayController.SCREENSHOT_ACTION),
+                ClipboardOverlayController.SELF_PERMISSION);
+
+        mScreenshotTakenInPortrait =
+                mContext.getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT;
+
+        String oldPackageName = mPackageName;
+        mPackageName = screenshot.getPackageNameString();
+
+        mScreenBitmap = screenshot.getBitmap();
+        // Optimizations
+        mScreenBitmap.setHasAlpha(false);
+        mScreenBitmap.prepareToDraw();
+
+        prepareViewForNewScreenshot(screenshot, oldPackageName);
+
+        saveScreenshotInWorkerThread(screenshot.getUserHandle(), finisher,
+                this::showUiOnActionsReady, this::showUiOnQuickShareActionReady);
+
+        // The window is focusable by default
+        setWindowFocusable(true);
+        mScreenshotView.requestFocus();
+
+        enqueueScrollCaptureRequest(screenshot.getUserHandle());
+
+        attachWindow();
+
+        boolean showFlash = true;
+        if (screenshot.getType() == WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE) {
+            if (screenshot.getScreenBounds() != null
+                    && aspectRatiosMatch(screenshot.getBitmap(), screenshot.getInsets(),
+                            screenshot.getScreenBounds())) {
+                showFlash = false;
+            } else {
+                showFlash = true;
+                screenshot.setInsets(Insets.NONE);
+                screenshot.setScreenBounds(new Rect(0, 0, screenshot.getBitmap().getWidth(),
+                        screenshot.getBitmap().getHeight()));
+            }
+        }
+
+        prepareAnimation(screenshot.getScreenBounds(), showFlash, () -> {
+            if (mFlags.isEnabled(SCREENSHOT_WORK_PROFILE_POLICY)) {
+                mMessageContainerController.onScreenshotTaken(screenshot);
+            }
+        });
+
+        if (mFlags.isEnabled(SCREENSHOT_WORK_PROFILE_POLICY)) {
+            mScreenshotView.badgeScreenshot(mContext.getPackageManager().getUserBadgedIcon(
+                    mContext.getDrawable(R.drawable.overlay_badge_background),
+                    screenshot.getUserHandle()));
+        }
+        mScreenshotView.setScreenshot(screenshot);
+
+        if (mFlags.isEnabled(Flags.SCREENSHOT_METADATA) && screenshot.getTaskId() >= 0) {
+            mAssistContentRequester.requestAssistContent(screenshot.getTaskId(),
+                    new AssistContentRequester.Callback() {
+                        @Override
+                        public void onAssistContentAvailable(AssistContent assistContent) {
+                            screenshot.setContextUrl(assistContent.getWebUri());
+                        }
+                    });
+        }
+
+        if (DEBUG_WINDOW) {
+            Log.d(TAG, "setContentView: " + mScreenshotView);
+        }
+        setContentView(mScreenshotView);
+        // ignore system bar insets for the purpose of window layout
+        mWindow.getDecorView().setOnApplyWindowInsetsListener(
+                (v, insets) -> WindowInsets.CONSUMED);
+        mScreenshotHandler.cancelTimeout(); // restarted after animation
+    }
+
+    void prepareViewForNewScreenshot(ScreenshotData screenshot, String oldPackageName) {
+        withWindowAttached(() -> {
+            if (mFlags.isEnabled(SCREENSHOT_WORK_PROFILE_POLICY)
+                    && mUserManager.isManagedProfile(screenshot.getUserHandle().getIdentifier())) {
+                mScreenshotView.announceForAccessibility(mContext.getResources().getString(
+                        R.string.screenshot_saving_work_profile_title));
+            } else {
+                mScreenshotView.announceForAccessibility(
+                        mContext.getResources().getString(R.string.screenshot_saving_title));
+            }
+        });
+
+        mScreenshotView.reset();
+
+        if (mScreenshotView.isAttachedToWindow()) {
+            // if we didn't already dismiss for another reason
+            if (!mScreenshotView.isDismissing()) {
+                mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED, 0,
+                        oldPackageName);
+            }
+            if (DEBUG_WINDOW) {
+                Log.d(TAG, "saveScreenshot: screenshotView is already attached, resetting. "
+                        + "(dismissing=" + mScreenshotView.isDismissing() + ")");
+            }
+        }
+
+        mScreenshotView.setPackageName(mPackageName);
+
+        mScreenshotView.updateOrientation(
+                mWindowManager.getCurrentWindowMetrics().getWindowInsets());
+    }
+
     @MainThread
     void takeScreenshotFullscreen(ComponentName topComponent, Consumer<Uri> finisher,
             RequestCallback requestCallback) {
         Assert.isMainThread();
         mCurrentRequestCallback = requestCallback;
-        DisplayMetrics displayMetrics = new DisplayMetrics();
-        getDefaultDisplay().getRealMetrics(displayMetrics);
-        takeScreenshotInternal(
-                topComponent, finisher,
-                new Rect(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels));
+        takeScreenshotInternal(topComponent, finisher, getFullScreenRect());
     }
 
     @MainThread
@@ -497,6 +636,9 @@
         // Inflate the screenshot layout
         mScreenshotView = (ScreenshotView)
                 LayoutInflater.from(mContext).inflate(R.layout.screenshot, null);
+        if (mFlags.isEnabled(SCREENSHOT_WORK_PROFILE_POLICY)) {
+            mMessageContainerController.setView(mScreenshotView);
+        }
         mScreenshotView.addOnAttachStateChangeListener(
                 new View.OnAttachStateChangeListener() {
                     @Override
@@ -537,6 +679,7 @@
                 setWindowFocusable(false);
             }
         }, mActionExecutor, mFlags);
+        mScreenshotView.setDefaultDisplay(mDisplayTracker.getDefaultDisplayId());
         mScreenshotView.setDefaultTimeoutMillis(mScreenshotHandler.getDefaultTimeoutMillis());
 
         mScreenshotView.setOnKeyListener((v, keyCode, event) -> {
@@ -570,7 +713,8 @@
 
         // copy the input Rect, since SurfaceControl.screenshot can mutate it
         Rect screenRect = new Rect(crop);
-        Bitmap screenshot = mImageCapture.captureDisplay(DEFAULT_DISPLAY, crop);
+        Bitmap screenshot = mImageCapture.captureDisplay(mDisplayTracker.getDefaultDisplayId(),
+                crop);
 
         if (screenshot == null) {
             Log.e(TAG, "takeScreenshotInternal: Screenshot bitmap was null");
@@ -641,6 +785,47 @@
         setWindowFocusable(true);
         mScreenshotView.requestFocus();
 
+        enqueueScrollCaptureRequest(owner);
+
+        attachWindow();
+        prepareAnimation(screenRect, showFlash, () -> {
+            if (mFlags.isEnabled(SCREENSHOT_WORK_PROFILE_POLICY)) {
+                mMessageContainerController.onScreenshotTaken(owner);
+            }
+        });
+
+        if (mFlags.isEnabled(SCREENSHOT_WORK_PROFILE_POLICY)) {
+            mScreenshotView.badgeScreenshot(mContext.getPackageManager().getUserBadgedIcon(
+                    mContext.getDrawable(R.drawable.overlay_badge_background), owner));
+        }
+        mScreenshotView.setScreenshot(mScreenBitmap, screenInsets);
+        if (DEBUG_WINDOW) {
+            Log.d(TAG, "setContentView: " + mScreenshotView);
+        }
+        setContentView(mScreenshotView);
+        // ignore system bar insets for the purpose of window layout
+        mWindow.getDecorView().setOnApplyWindowInsetsListener(
+                (v, insets) -> WindowInsets.CONSUMED);
+        mScreenshotHandler.cancelTimeout(); // restarted after animation
+    }
+
+    private void prepareAnimation(Rect screenRect, boolean showFlash,
+            Runnable onAnimationComplete) {
+        mScreenshotView.getViewTreeObserver().addOnPreDrawListener(
+                new ViewTreeObserver.OnPreDrawListener() {
+                    @Override
+                    public boolean onPreDraw() {
+                        if (DEBUG_WINDOW) {
+                            Log.d(TAG, "onPreDraw: startAnimation");
+                        }
+                        mScreenshotView.getViewTreeObserver().removeOnPreDrawListener(this);
+                        startAnimation(screenRect, showFlash, onAnimationComplete);
+                        return true;
+                    }
+                });
+    }
+
+    private void enqueueScrollCaptureRequest(UserHandle owner) {
         // Wait until this window is attached to request because it is
         // the reference used to locate the target window (below).
         withWindowAttached(() -> {
@@ -678,30 +863,6 @@
                         }
                     });
         });
-
-        attachWindow();
-        mScreenshotView.getViewTreeObserver().addOnPreDrawListener(
-                new ViewTreeObserver.OnPreDrawListener() {
-                    @Override
-                    public boolean onPreDraw() {
-                        if (DEBUG_WINDOW) {
-                            Log.d(TAG, "onPreDraw: startAnimation");
-                        }
-                        mScreenshotView.getViewTreeObserver().removeOnPreDrawListener(this);
-                        startAnimation(screenRect, showFlash);
-                        return true;
-                    }
-                });
-        if (mFlags.isEnabled(SCREENSHOT_WORK_PROFILE_POLICY)) {
-            mScreenshotView.badgeScreenshot(mContext.getPackageManager().getUserBadgedIcon(
-                    mContext.getDrawable(R.drawable.overlay_badge_background), owner));
-        }
-        mScreenshotView.setScreenshot(mScreenBitmap, screenInsets);
-
-        // ignore system bar insets for the purpose of window layout
-        mWindow.getDecorView().setOnApplyWindowInsetsListener(
-                (v, insets) -> WindowInsets.CONSUMED);
-        mScreenshotHandler.cancelTimeout(); // restarted after animation
     }
 
     private void requestScrollCapture(UserHandle owner) {
@@ -714,7 +875,7 @@
             mLastScrollCaptureRequest.cancel(true);
         }
         final ListenableFuture<ScrollCaptureResponse> future =
-                mScrollCaptureClient.request(DEFAULT_DISPLAY);
+                mScrollCaptureClient.request(mDisplayTracker.getDefaultDisplayId());
         mLastScrollCaptureRequest = future;
         mLastScrollCaptureRequest.addListener(() ->
                 onScrollCaptureResponseReady(future, owner), mMainExecutor);
@@ -745,7 +906,8 @@
             mScreenshotView.showScrollChip(response.getPackageName(), /* onClick */ () -> {
                 DisplayMetrics displayMetrics = new DisplayMetrics();
                 getDefaultDisplay().getRealMetrics(displayMetrics);
-                Bitmap newScreenshot = mImageCapture.captureDisplay(DEFAULT_DISPLAY,
+                Bitmap newScreenshot = mImageCapture.captureDisplay(
+                        mDisplayTracker.getDefaultDisplayId(),
                         new Rect(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels));
 
                 mScreenshotView.prepareScrollingTransition(response, mScreenBitmap, newScreenshot,
@@ -809,7 +971,8 @@
                     SCREENSHOT_REMOTE_RUNNER, 0, 0);
             try {
                 WindowManagerGlobal.getWindowManagerService()
-                        .overridePendingAppTransitionRemote(runner, DEFAULT_DISPLAY);
+                        .overridePendingAppTransitionRemote(runner,
+                                mDisplayTracker.getDefaultDisplayId());
             } catch (Exception e) {
                 Log.e(TAG, "Error overriding screenshot app transition", e);
             }
@@ -937,13 +1100,22 @@
     /**
      * Starts the animation after taking the screenshot
      */
-    private void startAnimation(Rect screenRect, boolean showFlash) {
+    private void startAnimation(Rect screenRect, boolean showFlash, Runnable onAnimationComplete) {
         if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) {
             mScreenshotAnimation.cancel();
         }
 
         mScreenshotAnimation =
                 mScreenshotView.createScreenshotDropInAnimation(screenRect, showFlash);
+        if (onAnimationComplete != null) {
+            mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    super.onAnimationEnd(animation);
+                    onAnimationComplete.run();
+                }
+            });
+        }
 
         // Play the shutter sound to notify that we've taken a screenshot
         playCameraSound();
@@ -1042,9 +1214,6 @@
 
     private void doPostAnimation(ScreenshotController.SavedImageData imageData) {
         mScreenshotView.setChipIntents(imageData);
-        if (mFlags.isEnabled(SCREENSHOT_WORK_PROFILE_POLICY)) {
-            mWorkProfileMessageController.onScreenshotTaken(imageData.owner, mScreenshotView);
-        }
     }
 
     /**
@@ -1147,13 +1316,19 @@
     }
 
     private Display getDefaultDisplay() {
-        return mDisplayManager.getDisplay(DEFAULT_DISPLAY);
+        return mDisplayManager.getDisplay(mDisplayTracker.getDefaultDisplayId());
     }
 
     private boolean allowLongScreenshots() {
         return !mIsLowRamDevice;
     }
 
+    private Rect getFullScreenRect() {
+        DisplayMetrics displayMetrics = new DisplayMetrics();
+        getDefaultDisplay().getRealMetrics(displayMetrics);
+        return new Rect(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels);
+    }
+
     /** Does the aspect ratio of the bitmap with insets removed match the bounds. */
     private static boolean aspectRatiosMatch(Bitmap bitmap, Insets bitmapInsets,
             Rect screenBounds) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotData.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotData.kt
new file mode 100644
index 0000000..e9be88a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotData.kt
@@ -0,0 +1,52 @@
+package com.android.systemui.screenshot
+
+import android.content.ComponentName
+import android.graphics.Bitmap
+import android.graphics.Insets
+import android.graphics.Rect
+import android.net.Uri
+import android.os.UserHandle
+import android.view.WindowManager.ScreenshotSource
+import android.view.WindowManager.ScreenshotType
+import androidx.annotation.VisibleForTesting
+import com.android.internal.util.ScreenshotRequest
+
+/** ScreenshotData represents the current state of a single screenshot being acquired. */
+data class ScreenshotData(
+    @ScreenshotType var type: Int,
+    @ScreenshotSource var source: Int,
+    /** UserHandle for the owner of the app being screenshotted, if known. */
+    var userHandle: UserHandle?,
+    /** ComponentName of the top-most app in the screenshot. */
+    var topComponent: ComponentName?,
+    var screenBounds: Rect?,
+    var taskId: Int,
+    var insets: Insets,
+    var bitmap: Bitmap?,
+    /** App-provided URL representing the content the user was looking at in the screenshot. */
+    var contextUrl: Uri? = null,
+) {
+    val packageNameString: String
+        get() = if (topComponent == null) "" else topComponent!!.packageName
+
+    companion object {
+        @JvmStatic
+        fun fromRequest(request: ScreenshotRequest): ScreenshotData {
+            return ScreenshotData(
+                request.type,
+                request.source,
+                if (request.userId >= 0) UserHandle.of(request.userId) else null,
+                request.topComponent,
+                request.boundsInScreen,
+                request.taskId,
+                request.insets,
+                request.bitmap,
+            )
+        }
+
+        @VisibleForTesting
+        fun forTesting(): ScreenshotData {
+            return ScreenshotData(0, 0, null, null, null, 0, Insets.NONE, null)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotDetectionController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotDetectionController.kt
new file mode 100644
index 0000000..70ea2b5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotDetectionController.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.screenshot
+
+import android.content.pm.PackageManager
+import android.view.IWindowManager
+import android.view.ViewGroup
+import android.widget.TextView
+import com.android.systemui.R
+import javax.inject.Inject
+
+class ScreenshotDetectionController
+@Inject
+constructor(
+    private val windowManager: IWindowManager,
+    private val packageManager: PackageManager,
+) {
+    /**
+     * Notify potentially listening apps of the screenshot. Return a list of the names of the apps
+     * notified.
+     */
+    fun maybeNotifyOfScreenshot(data: ScreenshotData): List<CharSequence> {
+        // TODO: actually ask the window manager once API is available.
+        return listOf()
+    }
+
+    fun populateView(view: ViewGroup, appNames: List<CharSequence>) {
+        assert(appNames.isNotEmpty())
+
+        val textView: TextView = view.requireViewById(R.id.screenshot_detection_notice_text)
+        if (appNames.size == 1) {
+            textView.text =
+                view.resources.getString(R.string.screenshot_detected_template, appNames[0])
+        } else {
+            textView.text =
+                view.resources.getString(
+                    R.string.screenshot_detected_multiple_template,
+                    appNames[0]
+                )
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
index c891686..fc94aed 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
@@ -46,6 +46,8 @@
     SCREENSHOT_SAVED(306),
     @UiEvent(doc = "screenshot failed to save")
     SCREENSHOT_NOT_SAVED(336),
+    @UiEvent(doc = "failed to capture screenshot")
+    SCREENSHOT_CAPTURE_FAILED(1281),
     @UiEvent(doc = "screenshot preview tapped")
     SCREENSHOT_PREVIEW_TAPPED(307),
     @UiEvent(doc = "screenshot edit button tapped")
@@ -91,7 +93,13 @@
     @UiEvent(doc = "User has discarded the result of a long screenshot")
     SCREENSHOT_LONG_SCREENSHOT_EXIT(911),
     @UiEvent(doc = "A screenshot has been taken and saved to work profile")
-    SCREENSHOT_SAVED_TO_WORK_PROFILE(1240);
+    SCREENSHOT_SAVED_TO_WORK_PROFILE(1240),
+    @UiEvent(doc = "Notes application triggered the screenshot for notes")
+    SCREENSHOT_FOR_NOTE_TRIGGERED(1308),
+    @UiEvent(doc = "User accepted the screenshot to be sent to the notes app")
+    SCREENSHOT_FOR_NOTE_ACCEPTED(1309),
+    @UiEvent(doc = "User cancelled the screenshot for notes app flow")
+    SCREENSHOT_FOR_NOTE_CANCELLED(1310);
 
     private final int mId;
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicyImpl.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicyImpl.kt
index 3a35286..21a7310 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicyImpl.kt
@@ -32,12 +32,12 @@
 import android.os.UserHandle
 import android.os.UserManager
 import android.util.Log
-import android.view.Display.DEFAULT_DISPLAY
 import com.android.internal.annotations.VisibleForTesting
 import com.android.internal.infra.ServiceConnector
 import com.android.systemui.SystemUIService
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.settings.DisplayTracker
 import com.android.systemui.screenshot.ScreenshotPolicy.DisplayContentInfo
 import java.util.Arrays
 import javax.inject.Inject
@@ -52,6 +52,7 @@
     private val userMgr: UserManager,
     private val atmService: IActivityTaskManager,
     @Background val bgDispatcher: CoroutineDispatcher,
+    private val displayTracker: DisplayTracker
 ) : ScreenshotPolicy {
 
     private val proxyConnector: ServiceConnector<IScreenshotProxy> =
@@ -64,7 +65,7 @@
         )
 
     override fun getDefaultDisplayId(): Int {
-        return DEFAULT_DISPLAY
+        return displayTracker.defaultDisplayId
     }
 
     override suspend fun isManagedProfile(@UserIdInt userId: Int): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 200a7dc..afba7ad 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -33,11 +33,11 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
-import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.Context;
+import android.content.Intent;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -81,7 +81,6 @@
 import android.widget.HorizontalScrollView;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
-import android.widget.TextView;
 
 import androidx.constraintlayout.widget.ConstraintLayout;
 
@@ -101,8 +100,7 @@
  * Handles the visual elements and animations for the screenshot flow.
  */
 public class ScreenshotView extends FrameLayout implements
-        ViewTreeObserver.OnComputeInternalInsetsListener,
-        WorkProfileMessageController.WorkProfileMessageDisplay {
+        ViewTreeObserver.OnComputeInternalInsetsListener {
 
     interface ScreenshotViewCallback {
         void onUserInteraction();
@@ -122,7 +120,7 @@
     private static final long SCREENSHOT_TO_CORNER_X_DURATION_MS = 234;
     private static final long SCREENSHOT_TO_CORNER_Y_DURATION_MS = 500;
     private static final long SCREENSHOT_TO_CORNER_SCALE_DURATION_MS = 234;
-    private static final long SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS = 400;
+    public static final long SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS = 400;
     private static final long SCREENSHOT_ACTIONS_ALPHA_DURATION_MS = 100;
     private static final float SCREENSHOT_ACTIONS_START_SCALE_X = .7f;
     private static final int SWIPE_PADDING_DP = 12; // extra padding around views to allow swipe
@@ -134,14 +132,13 @@
     private final AccessibilityManager mAccessibilityManager;
     private final GestureDetector mSwipeDetector;
 
+    private int mDefaultDisplay = Display.DEFAULT_DISPLAY;
     private int mNavMode;
     private boolean mOrientationPortrait;
     private boolean mDirectionLTR;
 
     private ImageView mScrollingScrim;
     private DraggableConstraintLayout mScreenshotStatic;
-    private ViewGroup mMessageContainer;
-    private TextView mMessageContent;
     private ImageView mScreenshotPreview;
     private ImageView mScreenshotBadge;
     private View mScreenshotPreviewBorder;
@@ -166,6 +163,8 @@
 
     private final ArrayList<OverlayActionChip> mSmartChips = new ArrayList<>();
     private PendingInteraction mPendingInteraction;
+    // Should only be set/used if the SCREENSHOT_METADATA flag is set.
+    private ScreenshotData mScreenshotData;
 
     private final InteractionJankMonitor mInteractionJankMonitor;
     private long mDefaultTimeoutOfTimeoutHandler;
@@ -291,8 +290,11 @@
         mDismissButton.getBoundsOnScreen(tmpRect);
         swipeRegion.op(tmpRect, Region.Op.UNION);
 
-        mMessageContainer.findViewById(R.id.message_dismiss_button).getBoundsOnScreen(tmpRect);
-        swipeRegion.op(tmpRect, Region.Op.UNION);
+        View messageDismiss = findViewById(R.id.message_dismiss_button);
+        if (messageDismiss != null) {
+            messageDismiss.getBoundsOnScreen(tmpRect);
+            swipeRegion.op(tmpRect, Region.Op.UNION);
+        }
 
         return swipeRegion;
     }
@@ -323,7 +325,7 @@
 
     private void startInputListening() {
         stopInputListening();
-        mInputMonitor = new InputMonitorCompat("Screenshot", Display.DEFAULT_DISPLAY);
+        mInputMonitor = new InputMonitorCompat("Screenshot", mDefaultDisplay);
         mInputEventReceiver = mInputMonitor.getInputReceiver(
                 Looper.getMainLooper(), Choreographer.getInstance(), ev -> {
                     if (ev instanceof MotionEvent) {
@@ -348,39 +350,11 @@
         }
     }
 
-    /**
-     * Show a notification under the screenshot view indicating that a work profile screenshot has
-     * been taken and which app can be used to view it.
-     *
-     * @param appName The name of the app to use to view screenshots
-     * @param appIcon Optional icon for the relevant files app
-     * @param onDismiss Runnable to be run when the user dismisses this message
-     */
-    @Override
-    public void showWorkProfileMessage(CharSequence appName, @Nullable Drawable appIcon,
-            Runnable onDismiss) {
-        if (appIcon != null) {
-            // Replace the default icon if one is provided.
-            ImageView imageView = mMessageContainer.findViewById(R.id.screenshot_message_icon);
-            imageView.setImageDrawable(appIcon);
-        }
-        mMessageContent.setText(
-                mContext.getString(R.string.screenshot_work_profile_notification, appName));
-        mMessageContainer.setVisibility(VISIBLE);
-        mMessageContainer.findViewById(R.id.message_dismiss_button).setOnClickListener((v) -> {
-            mMessageContainer.setVisibility(View.GONE);
-            onDismiss.run();
-        });
-    }
-
     @Override // View
     protected void onFinishInflate() {
+        super.onFinishInflate();
         mScrollingScrim = requireNonNull(findViewById(R.id.screenshot_scrolling_scrim));
         mScreenshotStatic = requireNonNull(findViewById(R.id.screenshot_static));
-        mMessageContainer =
-                requireNonNull(mScreenshotStatic.findViewById(R.id.screenshot_message_container));
-        mMessageContent =
-                requireNonNull(mMessageContainer.findViewById(R.id.screenshot_message_content));
         mScreenshotPreview = requireNonNull(findViewById(R.id.screenshot_preview));
 
         mScreenshotPreviewBorder = requireNonNull(
@@ -470,10 +444,21 @@
         mScreenshotPreview.setImageDrawable(createScreenDrawable(mResources, bitmap, screenInsets));
     }
 
+    void setScreenshot(ScreenshotData screenshot) {
+        mScreenshotData = screenshot;
+        setScreenshot(screenshot.getBitmap(), screenshot.getInsets());
+        mScreenshotPreview.setImageDrawable(createScreenDrawable(mResources, screenshot.getBitmap(),
+                screenshot.getInsets()));
+    }
+
     void setPackageName(String packageName) {
         mPackageName = packageName;
     }
 
+    void setDefaultDisplay(int displayId) {
+        mDefaultDisplay = displayId;
+    }
+
     void updateInsets(WindowInsets insets) {
         int orientation = mContext.getResources().getConfiguration().orientation;
         mOrientationPortrait = (orientation == ORIENTATION_PORTRAIT);
@@ -808,9 +793,17 @@
             mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SHARE_TAPPED, 0, mPackageName);
             if (mFlags.isEnabled(Flags.SCREENSHOT_WORK_PROFILE_POLICY)) {
                 prepareSharedTransition();
-                mActionExecutor.launchIntentAsync(
-                        ActionIntentCreator.INSTANCE.createShareIntent(
-                                imageData.uri, imageData.subject),
+
+                Intent shareIntent;
+                if (mFlags.isEnabled(Flags.SCREENSHOT_METADATA) && mScreenshotData != null
+                        && mScreenshotData.getContextUrl() != null) {
+                    shareIntent = ActionIntentCreator.INSTANCE.createShareIntentWithExtraText(
+                            imageData.uri, mScreenshotData.getContextUrl().toString());
+                } else {
+                    shareIntent = ActionIntentCreator.INSTANCE.createShareIntentWithSubject(
+                            imageData.uri, imageData.subject);
+                }
+                mActionExecutor.launchIntentAsync(shareIntent,
                         imageData.shareTransition.get().bundle,
                         imageData.owner.getIdentifier(), false);
             } else {
@@ -1112,6 +1105,7 @@
         mQuickShareChip = null;
         setAlpha(1);
         mScreenshotStatic.setAlpha(1);
+        mScreenshotData = null;
     }
 
     private void startSharedTransition(ActionTransition transition) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 7b271a8..111278a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -26,6 +26,7 @@
 import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS;
 import static com.android.systemui.screenshot.LogConfig.DEBUG_SERVICE;
 import static com.android.systemui.screenshot.LogConfig.logTag;
+import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_CAPTURE_FAILED;
 import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_DISMISSED_OTHER;
 
 import android.annotation.MainThread;
@@ -59,6 +60,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.FlagListenable.FlagEvent;
+import com.android.systemui.flags.Flags;
 
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
@@ -201,6 +203,7 @@
         // animation and error notification.
         if (!mUserManager.isUserUnlocked()) {
             Log.w(TAG, "Skipping screenshot because storage is locked!");
+            logFailedRequest(request);
             mNotificationsController.notifyScreenshotError(
                     R.string.screenshot_failed_to_save_user_locked_text);
             callback.reportError();
@@ -211,6 +214,7 @@
             mBgExecutor.execute(() -> {
                 Log.w(TAG, "Skipping screenshot because an IT admin has disabled "
                         + "screenshots on the device");
+                logFailedRequest(request);
                 String blockedByAdminText = mDevicePolicyManager.getResources().getString(
                         SCREENSHOT_BLOCKED_BY_ADMIN,
                         () -> mContext.getString(R.string.screenshot_blocked_by_admin));
@@ -221,16 +225,46 @@
             return;
         }
 
-        mProcessor.processAsync(request,
-                (r) -> dispatchToController(r, onSaved, callback));
+        if (mFeatureFlags.isEnabled(Flags.SCREENSHOT_METADATA_REFACTOR)) {
+            Log.d(TAG, "Processing screenshot data");
+            ScreenshotData screenshotData = ScreenshotData.fromRequest(request);
+            try {
+                mProcessor.processAsync(screenshotData,
+                        (data) -> dispatchToController(data, onSaved, callback));
+            } catch (IllegalStateException e) {
+                Log.e(TAG, "Failed to process screenshot request!", e);
+                logFailedRequest(request);
+                mNotificationsController.notifyScreenshotError(
+                        R.string.screenshot_failed_to_capture_text);
+                callback.reportError();
+            }
+        } else {
+            try {
+                mProcessor.processAsync(request,
+                        (r) -> dispatchToController(r, onSaved, callback));
+            } catch (IllegalStateException e) {
+                Log.e(TAG, "Failed to process screenshot request!", e);
+                logFailedRequest(request);
+                mNotificationsController.notifyScreenshotError(
+                        R.string.screenshot_failed_to_capture_text);
+                callback.reportError();
+            }
+        }
+    }
+
+    private void dispatchToController(ScreenshotData screenshot,
+            Consumer<Uri> uriConsumer, RequestCallback callback) {
+        mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(screenshot.getSource()), 0,
+                screenshot.getPackageNameString());
+        mScreenshot.handleScreenshot(screenshot, uriConsumer, callback);
     }
 
     private void dispatchToController(ScreenshotRequest request,
             Consumer<Uri> uriConsumer, RequestCallback callback) {
-
         ComponentName topComponent = request.getTopComponent();
-        mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(request.getSource()), 0,
-                topComponent == null ? "" : topComponent.getPackageName());
+        String packageName = topComponent == null ? "" : topComponent.getPackageName();
+        mUiEventLogger.log(
+                ScreenshotEvent.getScreenshotSource(request.getSource()), 0, packageName);
 
         switch (request.getType()) {
             case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
@@ -249,21 +283,22 @@
                 int taskId = request.getTaskId();
                 int userId = request.getUserId();
 
-                if (screenshot == null) {
-                    Log.e(TAG, "Got null bitmap from screenshot message");
-                    mNotificationsController.notifyScreenshotError(
-                            R.string.screenshot_failed_to_capture_text);
-                    callback.reportError();
-                } else {
-                    mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets,
-                            taskId, userId, topComponent, uriConsumer, callback);
-                }
+                mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets,
+                        taskId, userId, topComponent, uriConsumer, callback);
                 break;
             default:
-                Log.w(TAG, "Invalid screenshot option: " + request.getType());
+                Log.wtf(TAG, "Invalid screenshot option: " + request.getType());
         }
     }
 
+    private void logFailedRequest(ScreenshotRequest request) {
+        ComponentName topComponent = request.getTopComponent();
+        String packageName = topComponent == null ? "" : topComponent.getPackageName();
+        mUiEventLogger.log(
+                ScreenshotEvent.getScreenshotSource(request.getSource()), 0, packageName);
+        mUiEventLogger.log(SCREENSHOT_CAPTURE_FAILED, 0, packageName);
+    }
+
     private static void sendComplete(Messenger target) {
         try {
             if (DEBUG_CALLBACK) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/WorkProfileMessageController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/WorkProfileMessageController.kt
index 5d7e56f..1b728b8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/WorkProfileMessageController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/WorkProfileMessageController.kt
@@ -23,13 +23,16 @@
 import android.os.UserHandle
 import android.os.UserManager
 import android.util.Log
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
 import com.android.systemui.R
 import javax.inject.Inject
 
 /**
- * Handles all the non-UI portions of the work profile first run:
- * - Track whether the user has already dismissed it.
- * - Load the proper icon and app name.
+ * Handles work profile first run, determining whether a first run UI should be shown and populating
+ * that UI if needed.
  */
 class WorkProfileMessageController
 @Inject
@@ -40,10 +43,12 @@
 ) {
 
     /**
-     * Determine if a message should be shown to the user, send message details to messageDisplay if
-     * appropriate.
+     * @return a populated WorkProfileFirstRunData object if a work profile first run message should
+     * be shown
      */
-    fun onScreenshotTaken(userHandle: UserHandle, messageDisplay: WorkProfileMessageDisplay) {
+    fun onScreenshotTaken(userHandle: UserHandle?): WorkProfileFirstRunData? {
+        if (userHandle == null) return null
+
         if (userManager.isManagedProfile(userHandle.identifier) && !messageAlreadyDismissed()) {
             var badgedIcon: Drawable? = null
             var label: CharSequence? = null
@@ -62,10 +67,27 @@
             }
 
             // If label wasn't loaded, use a default
-            val badgedLabel =
-                packageManager.getUserBadgedLabel(label ?: defaultFileAppName(), userHandle)
+            return WorkProfileFirstRunData(label ?: defaultFileAppName(), badgedIcon)
+        }
+        return null
+    }
 
-            messageDisplay.showWorkProfileMessage(badgedLabel, badgedIcon) { onMessageDismissed() }
+    /**
+     * Use the provided WorkProfileFirstRunData to populate the work profile first run UI in the
+     * given view.
+     */
+    fun populateView(view: ViewGroup, data: WorkProfileFirstRunData, animateOut: () -> Unit) {
+        if (data.icon != null) {
+            // Replace the default icon if one is provided.
+            val imageView: ImageView = view.requireViewById<ImageView>(R.id.screenshot_message_icon)
+            imageView.setImageDrawable(data.icon)
+        }
+        val messageContent = view.requireViewById<TextView>(R.id.screenshot_message_content)
+        messageContent.text =
+            view.context.getString(R.string.screenshot_work_profile_notification, data.appName)
+        view.requireViewById<View>(R.id.message_dismiss_button).setOnClickListener {
+            animateOut()
+            onMessageDismissed()
         }
     }
 
@@ -89,14 +111,7 @@
 
     private fun defaultFileAppName() = context.getString(R.string.screenshot_default_files_app_name)
 
-    /** UI that can show work profile messages (ScreenshotView in practice) */
-    interface WorkProfileMessageDisplay {
-        /**
-         * Show the given message and icon, calling onDismiss if the user explicitly dismisses the
-         * message.
-         */
-        fun showWorkProfileMessage(text: CharSequence, icon: Drawable?, onDismiss: Runnable)
-    }
+    data class WorkProfileFirstRunData constructor(val appName: CharSequence, val icon: Drawable?)
 
     companion object {
         const val TAG = "WorkProfileMessageCtrl"
diff --git a/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt
new file mode 100644
index 0000000..bb7f721
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2022 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.settings
+
+import android.view.Display
+import java.util.concurrent.Executor
+
+/**
+ * Display tracker for SystemUI.
+ *
+ * This tracker provides async access to display information, as well as callbacks for display
+ * changes.
+ */
+interface DisplayTracker {
+
+    /** The id for the default display for the current SystemUI instance. */
+    val defaultDisplayId: Int
+
+    /** All displays that should be associated with the current SystemUI instance. */
+    val allDisplays: Array<Display>
+
+    /**
+     * Add a [Callback] to be notified of display changes, including additions, removals, and
+     * configuration changes, on a particular [Executor].
+     */
+    fun addDisplayChangeCallback(callback: Callback, executor: Executor)
+
+    /**
+     * Add a [Callback] to be notified of display brightness changes, on a particular [Executor].
+     * This callback will trigger Callback#onDisplayChanged for a display brightness change.
+     */
+    fun addBrightnessChangeCallback(callback: Callback, executor: Executor)
+
+    /** Remove a [Callback] previously added. */
+    fun removeCallback(callback: Callback)
+
+    /** Ćallback for notifying of changes. */
+    interface Callback {
+
+        /** Notifies that a display has been added. */
+        @JvmDefault fun onDisplayAdded(displayId: Int) {}
+
+        /** Notifies that a display has been removed. */
+        @JvmDefault fun onDisplayRemoved(displayId: Int) {}
+
+        /** Notifies a display has been changed */
+        @JvmDefault fun onDisplayChanged(displayId: Int) {}
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/DisplayTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/DisplayTrackerImpl.kt
new file mode 100644
index 0000000..5169f88
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/DisplayTrackerImpl.kt
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2022 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.settings
+
+import android.hardware.display.DisplayManager
+import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
+import android.os.Handler
+import android.view.Display
+import androidx.annotation.GuardedBy
+import androidx.annotation.VisibleForTesting
+import androidx.annotation.WorkerThread
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.util.Assert
+import java.lang.ref.WeakReference
+import java.util.concurrent.Executor
+
+class DisplayTrackerImpl
+internal constructor(
+    val displayManager: DisplayManager,
+    @Background val backgroundHandler: Handler
+) : DisplayTracker {
+    override val defaultDisplayId: Int = Display.DEFAULT_DISPLAY
+    override val allDisplays: Array<Display>
+        get() = displayManager.displays
+
+    @GuardedBy("displayCallbacks")
+    private val displayCallbacks: MutableList<DisplayTrackerDataItem> = ArrayList()
+    @GuardedBy("brightnessCallbacks")
+    private val brightnessCallbacks: MutableList<DisplayTrackerDataItem> = ArrayList()
+
+    @VisibleForTesting
+    val displayChangedListener: DisplayManager.DisplayListener =
+        object : DisplayManager.DisplayListener {
+            override fun onDisplayAdded(displayId: Int) {
+                val list = synchronized(displayCallbacks) { displayCallbacks.toList() }
+                onDisplayAdded(displayId, list)
+            }
+
+            override fun onDisplayRemoved(displayId: Int) {
+                val list = synchronized(displayCallbacks) { displayCallbacks.toList() }
+                onDisplayRemoved(displayId, list)
+            }
+
+            override fun onDisplayChanged(displayId: Int) {
+                val list = synchronized(displayCallbacks) { displayCallbacks.toList() }
+                onDisplayChanged(displayId, list)
+            }
+        }
+
+    @VisibleForTesting
+    val displayBrightnessChangedListener: DisplayManager.DisplayListener =
+        object : DisplayManager.DisplayListener {
+            override fun onDisplayAdded(displayId: Int) {}
+
+            override fun onDisplayRemoved(displayId: Int) {}
+
+            override fun onDisplayChanged(displayId: Int) {
+                val list = synchronized(brightnessCallbacks) { brightnessCallbacks.toList() }
+                onDisplayChanged(displayId, list)
+            }
+        }
+
+    override fun addDisplayChangeCallback(callback: DisplayTracker.Callback, executor: Executor) {
+        synchronized(displayCallbacks) {
+            if (displayCallbacks.isEmpty()) {
+                displayManager.registerDisplayListener(displayChangedListener, backgroundHandler)
+            }
+            displayCallbacks.add(DisplayTrackerDataItem(WeakReference(callback), executor))
+        }
+    }
+
+    override fun addBrightnessChangeCallback(
+        callback: DisplayTracker.Callback,
+        executor: Executor
+    ) {
+        synchronized(brightnessCallbacks) {
+            if (brightnessCallbacks.isEmpty()) {
+                displayManager.registerDisplayListener(
+                    displayBrightnessChangedListener,
+                    backgroundHandler,
+                    EVENT_FLAG_DISPLAY_BRIGHTNESS
+                )
+            }
+            brightnessCallbacks.add(DisplayTrackerDataItem(WeakReference(callback), executor))
+        }
+    }
+
+    override fun removeCallback(callback: DisplayTracker.Callback) {
+        synchronized(displayCallbacks) {
+            val changed = displayCallbacks.removeIf { it.sameOrEmpty(callback) }
+            if (changed && displayCallbacks.isEmpty()) {
+                displayManager.unregisterDisplayListener(displayChangedListener)
+            }
+        }
+
+        synchronized(brightnessCallbacks) {
+            val changed = brightnessCallbacks.removeIf { it.sameOrEmpty(callback) }
+            if (changed && brightnessCallbacks.isEmpty()) {
+                displayManager.unregisterDisplayListener(displayBrightnessChangedListener)
+            }
+        }
+    }
+
+    @WorkerThread
+    private fun onDisplayAdded(displayId: Int, list: List<DisplayTrackerDataItem>) {
+        Assert.isNotMainThread()
+
+        notifySubscribers({ onDisplayAdded(displayId) }, list)
+    }
+
+    @WorkerThread
+    private fun onDisplayRemoved(displayId: Int, list: List<DisplayTrackerDataItem>) {
+        Assert.isNotMainThread()
+
+        notifySubscribers({ onDisplayRemoved(displayId) }, list)
+    }
+
+    @WorkerThread
+    private fun onDisplayChanged(displayId: Int, list: List<DisplayTrackerDataItem>) {
+        Assert.isNotMainThread()
+
+        notifySubscribers({ onDisplayChanged(displayId) }, list)
+    }
+
+    private inline fun notifySubscribers(
+        crossinline action: DisplayTracker.Callback.() -> Unit,
+        list: List<DisplayTrackerDataItem>
+    ) {
+        list.forEach {
+            if (it.callback.get() != null) {
+                it.executor.execute { it.callback.get()?.action() }
+            }
+        }
+    }
+
+    private data class DisplayTrackerDataItem(
+        val callback: WeakReference<DisplayTracker.Callback>,
+        val executor: Executor
+    ) {
+        fun sameOrEmpty(other: DisplayTracker.Callback): Boolean {
+            return callback.get()?.equals(other) ?: true
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt
index bfba6df..bb637dc 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt
@@ -32,74 +32,62 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.util.concurrency.DelayableExecutor
 import java.io.File
+import java.io.FilenameFilter
 import javax.inject.Inject
 
 /**
- * Implementation for retrieving file paths for file storage of system and secondary users. Files
- * lie in {File Directory}/UserFileManager/{User Id} for secondary user. For system user, we use the
- * conventional {File Directory}
+ * Implementation for retrieving file paths for file storage of system and secondary users. For
+ * non-system users, files will be prepended by a special prefix containing the user id.
  */
 @SysUISingleton
 class UserFileManagerImpl
 @Inject
 constructor(
-    // Context of system process and system user.
     private val context: Context,
     val userManager: UserManager,
     val broadcastDispatcher: BroadcastDispatcher,
     @Background val backgroundExecutor: DelayableExecutor
 ) : UserFileManager, CoreStartable {
     companion object {
-        private const val FILES = "files"
+        private const val PREFIX = "__USER_"
+        private const val TAG = "UserFileManagerImpl"
+        const val ROOT_DIR = "UserFileManager"
+        const val FILES = "files"
         const val SHARED_PREFS = "shared_prefs"
-        @VisibleForTesting internal const val ID = "UserFileManager"
-
-        /** Returns `true` if the given user ID is that for the primary/system user. */
-        fun isPrimaryUser(userId: Int): Boolean {
-            return UserHandle(userId).isSystem
-        }
 
         /**
-         * Returns a [File] pointing to the correct path for a secondary user ID.
-         *
-         * Note that there is no check for the type of user. This should only be called for
-         * secondary users, never for the system user. For that, make sure to call [isPrimaryUser].
-         *
-         * Note also that there is no guarantee that the parent directory structure for the file
-         * exists on disk. For that, call [ensureParentDirExists].
-         *
-         * @param context The context
-         * @param fileName The name of the file
-         * @param directoryName The name of the directory that would contain the file
-         * @param userId The ID of the user to build a file path for
+         * Returns a File object with a relative path, built from the userId for non-system users
          */
-        fun secondaryUserFile(
-            context: Context,
-            fileName: String,
-            directoryName: String,
-            userId: Int,
-        ): File {
-            return Environment.buildPath(
-                context.filesDir,
-                ID,
-                userId.toString(),
-                directoryName,
-                fileName,
-            )
-        }
-
-        /**
-         * Checks to see if parent dir of the file exists. If it does not, we create the parent dirs
-         * recursively.
-         */
-        fun ensureParentDirExists(file: File) {
-            val parent = file.parentFile
-            if (!parent.exists()) {
-                if (!parent.mkdirs()) {
-                    Log.e(ID, "Could not create parent directory for file: ${file.absolutePath}")
-                }
+        fun createFile(fileName: String, userId: Int): File {
+            return if (isSystemUser(userId)) {
+                File(fileName)
+            } else {
+                File(getFilePrefix(userId) + fileName)
             }
         }
+
+        fun createLegacyFile(context: Context, dir: String, fileName: String, userId: Int): File? {
+            return if (isSystemUser(userId)) {
+                null
+            } else {
+                return Environment.buildPath(
+                    context.filesDir,
+                    ROOT_DIR,
+                    userId.toString(),
+                    dir,
+                    fileName
+                )
+            }
+        }
+
+        fun getFilePrefix(userId: Int): String {
+            return PREFIX + userId.toString() + "_"
+        }
+
+        /** Returns `true` if the given user ID is that for the system user. */
+        private fun isSystemUser(userId: Int): Boolean {
+            return UserHandle(userId).isSystem
+        }
     }
 
     private val broadcastReceiver =
@@ -119,64 +107,87 @@
         broadcastDispatcher.registerReceiver(broadcastReceiver, filter, backgroundExecutor)
     }
 
-    /** Return the file based on current user. */
+    /**
+     * Return the file based on current user. Files for all users will exist in [context.filesDir],
+     * but non system user files will be prepended with [getFilePrefix].
+     */
     override fun getFile(fileName: String, userId: Int): File {
-        return if (isPrimaryUser(userId)) {
-            Environment.buildPath(context.filesDir, fileName)
-        } else {
-            val secondaryFile =
-                secondaryUserFile(
-                    context = context,
-                    userId = userId,
-                    directoryName = FILES,
-                    fileName = fileName,
-                )
-            ensureParentDirExists(secondaryFile)
-            secondaryFile
-        }
+        val file = File(context.filesDir, createFile(fileName, userId).path)
+        createLegacyFile(context, FILES, fileName, userId)?.run { migrate(file, this) }
+        return file
     }
 
-    /** Get shared preferences from user. */
+    /**
+     * Get shared preferences from user. Files for all users will exist in the shared_prefs dir, but
+     * non system user files will be prepended with [getFilePrefix].
+     */
     override fun getSharedPreferences(
         fileName: String,
         @Context.PreferencesMode mode: Int,
         userId: Int
     ): SharedPreferences {
-        if (isPrimaryUser(userId)) {
-            return context.getSharedPreferences(fileName, mode)
+        val file = createFile(fileName, userId)
+        createLegacyFile(context, SHARED_PREFS, "$fileName.xml", userId)?.run {
+            val path = Environment.buildPath(context.dataDir, SHARED_PREFS, "${file.path}.xml")
+            migrate(path, this)
         }
-
-        val secondaryUserDir =
-            secondaryUserFile(
-                context = context,
-                fileName = fileName,
-                directoryName = SHARED_PREFS,
-                userId = userId,
-            )
-
-        ensureParentDirExists(secondaryUserDir)
-        return context.getSharedPreferences(secondaryUserDir, mode)
+        return context.getSharedPreferences(file.path, mode)
     }
 
-    /** Remove dirs for deleted users. */
+    /** Remove files for deleted users. */
     @VisibleForTesting
     internal fun clearDeletedUserData() {
         backgroundExecutor.execute {
-            val file = Environment.buildPath(context.filesDir, ID)
-            if (!file.exists()) return@execute
-            val aliveUsers = userManager.aliveUsers.map { it.id.toString() }
-            val dirsToDelete = file.list().filter { !aliveUsers.contains(it) }
+            deleteFiles(context.filesDir)
+            deleteFiles(File(context.dataDir, SHARED_PREFS))
+        }
+    }
 
-            dirsToDelete.forEach { dir ->
+    private fun migrate(dest: File, source: File) {
+        if (source.exists()) {
+            try {
+                val parent = source.getParentFile()
+                source.renameTo(dest)
+
+                deleteParentDirsIfEmpty(parent)
+            } catch (e: Exception) {
+                Log.e(TAG, "Failed to rename and delete ${source.path}", e)
+            }
+        }
+    }
+
+    private fun deleteParentDirsIfEmpty(dir: File?) {
+        if (dir != null && dir.listFiles().size == 0) {
+            val priorParent = dir.parentFile
+            val isRoot = dir.name == ROOT_DIR
+            dir.delete()
+
+            if (!isRoot) {
+                deleteParentDirsIfEmpty(priorParent)
+            }
+        }
+    }
+
+    private fun deleteFiles(parent: File) {
+        val aliveUserFilePrefix = userManager.aliveUsers.map { getFilePrefix(it.id) }
+        val filesToDelete =
+            parent.listFiles(
+                FilenameFilter { _, name ->
+                    name.startsWith(PREFIX) &&
+                        aliveUserFilePrefix.filter { name.startsWith(it) }.isEmpty()
+                }
+            )
+
+        // This can happen in test environments
+        if (filesToDelete == null) {
+            Log.i(TAG, "Empty directory: ${parent.path}")
+        } else {
+            filesToDelete.forEach { file ->
+                Log.i(TAG, "Deleting file: ${file.path}")
                 try {
-                    val dirToDelete =
-                        Environment.buildPath(
-                            file,
-                            dir,
-                        )
-                    dirToDelete.deleteRecursively()
+                    file.delete()
                 } catch (e: Exception) {
-                    Log.e(ID, "Deletion failed.", e)
+                    Log.e(TAG, "Deletion failed.", e)
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
index 1558ac5..287e810 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
@@ -62,12 +62,24 @@
     fun removeCallback(callback: Callback)
 
     /**
-     * Ćallback for notifying of changes.
+     * Callback for notifying of changes.
      */
     interface Callback {
 
         /**
+         * Notifies that the current user is being changed.
+         * Override this method to run things while the screen is frozen for the user switch.
+         * Please use {@link #onUserChanged} if the task doesn't need to push the unfreezing of the
+         * screen further. Please be aware that code executed in this callback will lengthen the
+         * user switch duration.
+         */
+        @JvmDefault
+        fun onUserChanging(newUser: Int, userContext: Context) {}
+
+        /**
          * Notifies that the current user has changed.
+         * Override this method to run things after the screen is unfrozen for the user switch.
+         * Please see {@link #onUserChanging} if you need to hide jank.
          */
         @JvmDefault
         fun onUserChanged(newUser: Int, userContext: Context) {}
@@ -78,4 +90,4 @@
         @JvmDefault
         fun onProfilesChanged(profiles: List<@JvmSuppressWildcards UserInfo>) {}
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index 61390c5..9f551c6 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.settings
 
+import android.app.IActivityManager
+import android.app.UserSwitchObserver
 import android.content.BroadcastReceiver
 import android.content.ContentResolver
 import android.content.Context
@@ -23,6 +25,7 @@
 import android.content.IntentFilter
 import android.content.pm.UserInfo
 import android.os.Handler
+import android.os.IRemoteCallback
 import android.os.UserHandle
 import android.os.UserManager
 import android.util.Log
@@ -34,6 +37,7 @@
 import java.io.PrintWriter
 import java.lang.IllegalStateException
 import java.lang.ref.WeakReference
+import java.util.concurrent.CountDownLatch
 import java.util.concurrent.Executor
 import kotlin.properties.ReadWriteProperty
 import kotlin.reflect.KProperty
@@ -56,6 +60,7 @@
 class UserTrackerImpl internal constructor(
     private val context: Context,
     private val userManager: UserManager,
+    private val iActivityManager: IActivityManager,
     private val dumpManager: DumpManager,
     private val backgroundHandler: Handler
 ) : UserTracker, Dumpable, BroadcastReceiver() {
@@ -107,7 +112,6 @@
         setUserIdInternal(startingUser)
 
         val filter = IntentFilter().apply {
-            addAction(Intent.ACTION_USER_SWITCHED)
             addAction(Intent.ACTION_USER_INFO_CHANGED)
             // These get called when a managed profile goes in or out of quiet mode.
             addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
@@ -118,14 +122,13 @@
         }
         context.registerReceiverForAllUsers(this, filter, null /* permission */, backgroundHandler)
 
+        registerUserSwitchObserver()
+
         dumpManager.registerDumpable(TAG, this)
     }
 
     override fun onReceive(context: Context, intent: Intent) {
         when (intent.action) {
-            Intent.ACTION_USER_SWITCHED -> {
-                handleSwitchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL))
-            }
             Intent.ACTION_USER_INFO_CHANGED,
             Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
             Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
@@ -157,22 +160,43 @@
         return ctx to profiles
     }
 
+    private fun registerUserSwitchObserver() {
+        iActivityManager.registerUserSwitchObserver(object : UserSwitchObserver() {
+            override fun onUserSwitching(newUserId: Int, reply: IRemoteCallback?) {
+                backgroundHandler.run {
+                    handleUserSwitching(newUserId)
+                    reply?.sendResult(null)
+                }
+            }
+
+            override fun onUserSwitchComplete(newUserId: Int) {
+                backgroundHandler.run {
+                    handleUserSwitchComplete(newUserId)
+                }
+            }
+        }, TAG)
+    }
+
     @WorkerThread
-    private fun handleSwitchUser(newUser: Int) {
+    private fun handleUserSwitching(newUserId: Int) {
         Assert.isNotMainThread()
-        if (newUser == UserHandle.USER_NULL) {
-            Log.w(TAG, "handleSwitchUser - Couldn't get new id from intent")
-            return
-        }
+        Log.i(TAG, "Switching to user $newUserId")
 
-        if (newUser == userId) return
-        Log.i(TAG, "Switching to user $newUser")
-
-        val (ctx, profiles) = setUserIdInternal(newUser)
-
+        setUserIdInternal(newUserId)
         notifySubscribers {
-            onUserChanged(newUser, ctx)
-            onProfilesChanged(profiles)
+            onUserChanging(newUserId, userContext)
+        }.await()
+    }
+
+    @WorkerThread
+    private fun handleUserSwitchComplete(newUserId: Int) {
+        Assert.isNotMainThread()
+        Log.i(TAG, "Switched to user $newUserId")
+
+        setUserIdInternal(newUserId)
+        notifySubscribers {
+            onUserChanged(newUserId, userContext)
+            onProfilesChanged(userProfiles)
         }
     }
 
@@ -201,17 +225,25 @@
         }
     }
 
-    private inline fun notifySubscribers(crossinline action: UserTracker.Callback.() -> Unit) {
+    private inline fun notifySubscribers(
+            crossinline action: UserTracker.Callback.() -> Unit
+    ): CountDownLatch {
         val list = synchronized(callbacks) {
             callbacks.toList()
         }
+        val latch = CountDownLatch(list.size)
+
         list.forEach {
             if (it.callback.get() != null) {
                 it.executor.execute {
                     it.callback.get()?.action()
+                    latch.countDown()
                 }
+            } else {
+                latch.countDown()
             }
         }
+        return latch
     }
 
     override fun dump(pw: PrintWriter, args: Array<out String>) {
@@ -258,4 +290,4 @@
     fun sameOrEmpty(other: UserTracker.Callback): Boolean {
         return callback.get()?.equals(other) ?: true
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
index 2f6081b..8089d01 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
@@ -27,10 +27,10 @@
 import android.database.ContentObserver;
 import android.hardware.display.BrightnessInfo;
 import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManager.DisplayListener;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.RemoteException;
@@ -49,6 +49,7 @@
 import com.android.settingslib.RestrictedLockUtilsInternal;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
 
@@ -78,19 +79,14 @@
     private final ToggleSlider mControl;
     private final DisplayManager mDisplayManager;
     private final UserTracker mUserTracker;
+    private final DisplayTracker mDisplayTracker;
     private final IVrManager mVrManager;
 
     private final Executor mMainExecutor;
     private final Handler mBackgroundHandler;
     private final BrightnessObserver mBrightnessObserver;
 
-    private final DisplayListener mDisplayListener = new DisplayListener() {
-        @Override
-        public void onDisplayAdded(int displayId) {}
-
-        @Override
-        public void onDisplayRemoved(int displayId) {}
-
+    private final DisplayTracker.Callback mBrightnessListener = new DisplayTracker.Callback() {
         @Override
         public void onDisplayChanged(int displayId) {
             mBackgroundHandler.post(mUpdateSliderRunnable);
@@ -143,14 +139,14 @@
             cr.registerContentObserver(
                     BRIGHTNESS_FOR_VR_FLOAT_URI,
                     false, this, UserHandle.USER_ALL);
-            mDisplayManager.registerDisplayListener(mDisplayListener, mHandler,
-                    DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS);
+            mDisplayTracker.addBrightnessChangeCallback(mBrightnessListener,
+                    new HandlerExecutor(mHandler));
         }
 
         public void stopObserving() {
             final ContentResolver cr = mContext.getContentResolver();
             cr.unregisterContentObserver(this);
-            mDisplayManager.unregisterDisplayListener(mDisplayListener);
+            mDisplayTracker.removeCallback(mBrightnessListener);
         }
 
     }
@@ -292,6 +288,7 @@
             Context context,
             ToggleSlider control,
             UserTracker userTracker,
+            DisplayTracker displayTracker,
             @Main Executor mainExecutor,
             @Background Handler bgHandler) {
         mContext = context;
@@ -300,6 +297,7 @@
         mMainExecutor = mainExecutor;
         mBackgroundHandler = bgHandler;
         mUserTracker = userTracker;
+        mDisplayTracker = displayTracker;
         mBrightnessObserver = new BrightnessObserver(mHandler);
 
         mDisplayId = mContext.getDisplayId();
@@ -450,6 +448,7 @@
     public static class Factory {
         private final Context mContext;
         private final UserTracker mUserTracker;
+        private final DisplayTracker mDisplayTracker;
         private final Executor mMainExecutor;
         private final Handler mBackgroundHandler;
 
@@ -457,10 +456,12 @@
         public Factory(
                 Context context,
                 UserTracker userTracker,
+                DisplayTracker displayTracker,
                 @Main Executor mainExecutor,
                 @Background Handler bgHandler) {
             mContext = context;
             mUserTracker = userTracker;
+            mDisplayTracker = displayTracker;
             mMainExecutor = mainExecutor;
             mBackgroundHandler = bgHandler;
         }
@@ -471,6 +472,7 @@
                     mContext,
                     toggleSlider,
                     mUserTracker,
+                    mDisplayTracker,
                     mMainExecutor,
                     mBackgroundHandler);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
index e208be9..8879501 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
@@ -36,6 +36,7 @@
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.settings.UserTracker;
 
 import java.util.List;
@@ -49,16 +50,19 @@
     private BrightnessController mBrightnessController;
     private final BrightnessSliderController.Factory mToggleSliderFactory;
     private final UserTracker mUserTracker;
+    private final DisplayTracker mDisplayTracker;
     private final Executor mMainExecutor;
     private final Handler mBackgroundHandler;
 
     @Inject
     public BrightnessDialog(
             UserTracker userTracker,
+            DisplayTracker displayTracker,
             BrightnessSliderController.Factory factory,
             @Main Executor mainExecutor,
             @Background Handler bgHandler) {
         mUserTracker = userTracker;
+        mDisplayTracker = displayTracker;
         mToggleSliderFactory = factory;
         mMainExecutor = mainExecutor;
         mBackgroundHandler = bgHandler;
@@ -106,7 +110,7 @@
         frame.addView(controller.getRootView(), MATCH_PARENT, WRAP_CONTENT);
 
         mBrightnessController = new BrightnessController(
-                this, controller, mUserTracker, mMainExecutor, mBackgroundHandler);
+                this, controller, mUserTracker, mDisplayTracker, mMainExecutor, mBackgroundHandler);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java b/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java
index 2f62e44..e9a1dd7 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java
@@ -17,7 +17,9 @@
 package com.android.systemui.settings.dagger;
 
 import android.app.ActivityManager;
+import android.app.IActivityManager;
 import android.content.Context;
+import android.hardware.display.DisplayManager;
 import android.os.Handler;
 import android.os.UserManager;
 
@@ -25,6 +27,8 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.settings.DisplayTracker;
+import com.android.systemui.settings.DisplayTrackerImpl;
 import com.android.systemui.settings.UserContentResolverProvider;
 import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.settings.UserFileManager;
@@ -57,15 +61,26 @@
     static UserTracker provideUserTracker(
             Context context,
             UserManager userManager,
+            IActivityManager iActivityManager,
             DumpManager dumpManager,
             @Background Handler handler
     ) {
         int startingUser = ActivityManager.getCurrentUser();
-        UserTrackerImpl tracker = new UserTrackerImpl(context, userManager, dumpManager, handler);
+        UserTrackerImpl tracker = new UserTrackerImpl(context, userManager, iActivityManager,
+                dumpManager, handler);
         tracker.initialize(startingUser);
         return tracker;
     }
 
+    @SysUISingleton
+    @Provides
+    static DisplayTracker provideDisplayTracker(
+            DisplayManager displayManager,
+            @Background Handler handler
+    ) {
+        return new DisplayTrackerImpl(displayManager, handler);
+    }
+
     @Binds
     @IntoMap
     @ClassKey(UserFileManagerImpl.class)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java b/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java
index ae303eb..fb2ddc1 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java
@@ -39,6 +39,7 @@
     private final NotificationPanelView mView;
     private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
     private final LockIconViewController mLockIconViewController;
+    private final QuickSettingsController mQsController;
     private final Set<Integer> mDebugTextUsedYPositions;
     private final Paint mDebugPaint;
 
@@ -46,12 +47,14 @@
             NotificationPanelViewController notificationPanelViewController,
             NotificationPanelView notificationPanelView,
             NotificationStackScrollLayoutController notificationStackScrollLayoutController,
-            LockIconViewController lockIconViewController
+            LockIconViewController lockIconViewController,
+            QuickSettingsController quickSettingsController
     ) {
         mNotificationPanelViewController = notificationPanelViewController;
         mView = notificationPanelView;
         mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
         mLockIconViewController = lockIconViewController;
+        mQsController = quickSettingsController;
         mDebugTextUsedYPositions = new HashSet<>();
         mDebugPaint = new Paint();
     }
@@ -71,12 +74,19 @@
                 Color.RED, "getMaxPanelHeight()");
         drawDebugInfo(canvas, (int) mNotificationPanelViewController.getExpandedHeight(),
                 Color.BLUE, "getExpandedHeight()");
-        drawDebugInfo(canvas, mNotificationPanelViewController.calculatePanelHeightQsExpanded(),
+        drawDebugInfo(canvas, mQsController.calculatePanelHeightExpanded(
+                        mNotificationPanelViewController.getClockPositionResult()
+                                .stackScrollerPadding),
                 Color.GREEN, "calculatePanelHeightQsExpanded()");
-        drawDebugInfo(canvas, mNotificationPanelViewController.calculatePanelHeightQsExpanded(),
+        drawDebugInfo(canvas, mQsController.calculatePanelHeightExpanded(
+                        mNotificationPanelViewController.getClockPositionResult()
+                                .stackScrollerPadding),
                 Color.YELLOW, "calculatePanelHeightShade()");
         drawDebugInfo(canvas,
-                (int) mNotificationPanelViewController.calculateNotificationsTopPadding(),
+                (int) mQsController.calculateNotificationsTopPadding(
+                        mNotificationPanelViewController.isExpanding(),
+                        mNotificationPanelViewController.getKeyguardNotificationStaticPadding(),
+                        mNotificationPanelViewController.getExpandedFraction()),
                 Color.MAGENTA, "calculateNotificationsTopPadding()");
         drawDebugInfo(canvas, mNotificationPanelViewController.getClockPositionResult().clockY,
                 Color.GRAY, "mClockPositionResult.clockY");
diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
index 8867637..9d8ed46 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.shade
 
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
 import android.annotation.IdRes
 import android.app.StatusBarManager
 import android.content.res.Configuration
@@ -23,6 +25,7 @@
 import android.os.Trace
 import android.os.Trace.TRACE_TAG_APP
 import android.util.Pair
+import android.view.DisplayCutout
 import android.view.View
 import android.view.WindowInsets
 import android.widget.TextView
@@ -45,7 +48,6 @@
 import com.android.systemui.qs.carrier.QSCarrierGroup
 import com.android.systemui.qs.carrier.QSCarrierGroupController
 import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.HEADER_TRANSITION_ID
-import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.LARGE_SCREEN_HEADER_CONSTRAINT
 import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.QQS_HEADER_CONSTRAINT
 import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.QS_HEADER_CONSTRAINT
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
@@ -91,7 +93,8 @@
     private val featureFlags: FeatureFlags,
     private val qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder,
     private val combinedShadeHeadersConstraintManager: CombinedShadeHeadersConstraintManager,
-    private val demoModeController: DemoModeController
+    private val demoModeController: DemoModeController,
+    private val qsBatteryModeController: QsBatteryModeController,
 ) : ViewController<View>(header), Dumpable {
 
     companion object {
@@ -129,9 +132,8 @@
     private val iconContainer: StatusIconContainer = header.findViewById(R.id.statusIcons)
     private val qsCarrierGroup: QSCarrierGroup = header.findViewById(R.id.carrier_group)
 
-    private var cutoutLeft = 0
-    private var cutoutRight = 0
     private var roundedCorners = 0
+    private var cutout: DisplayCutout? = null
     private var lastInsets: WindowInsets? = null
 
     private var qsDisabled = false
@@ -144,6 +146,14 @@
             updateListeners()
         }
 
+    private var customizing = false
+        set(value) {
+            if (field != value) {
+                field = value
+                updateVisibility()
+            }
+        }
+
     /**
      * Whether the QQS/QS part of the shade is visible. This is particularly important in
      * Lockscreen, as the shade is visible but QS is not.
@@ -175,10 +185,9 @@
      */
     var shadeExpandedFraction = -1f
         set(value) {
-            if (field != value) {
+            if (qsVisible && field != value) {
                 header.alpha = ShadeInterpolation.getContentAlpha(value)
                 field = value
-                updateVisibility()
             }
         }
 
@@ -269,7 +278,6 @@
 
         // battery settings same as in QS icons
         batteryMeterViewController.ignoreTunerUpdates()
-        batteryIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)
 
         iconManager = tintedIconManagerFactory.create(iconContainer, StatusBarLocation.QS)
         iconManager.setTint(
@@ -301,16 +309,20 @@
 
         if (header is MotionLayout) {
             header.setOnApplyWindowInsetsListener(insetListener)
+
             clock.addOnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
                 val newPivot = if (v.isLayoutRtl) v.width.toFloat() else 0f
                 v.pivotX = newPivot
                 v.pivotY = v.height.toFloat() / 2
+
+                qsCarrierGroup.setPaddingRelative((v.width * v.scaleX).toInt(), 0, 0, 0)
             }
         }
 
         dumpManager.registerDumpable(this)
         configurationController.addCallback(configurationControllerListener)
         demoModeController.addCallback(demoModeReceiver)
+        statusBarIconController.addIconGroup(iconManager)
     }
 
     override fun onViewDetached() {
@@ -318,6 +330,7 @@
         dumpManager.unregisterDumpable(this::class.java.simpleName)
         configurationController.removeCallback(configurationControllerListener)
         demoModeController.removeCallback(demoModeReceiver)
+        statusBarIconController.removeIconGroup(iconManager)
     }
 
     fun disable(state1: Int, state2: Int, animate: Boolean) {
@@ -332,9 +345,7 @@
                 .setDuration(duration)
                 .alpha(if (show) 0f else 1f)
                 .setInterpolator(if (show) Interpolators.ALPHA_OUT else Interpolators.ALPHA_IN)
-                .setUpdateListener {
-                    updateVisibility()
-                }
+                .setListener(CustomizerAnimationListener(show))
                 .start()
     }
 
@@ -351,11 +362,13 @@
     }
 
     private fun updateConstraintsForInsets(view: MotionLayout, insets: WindowInsets) {
-        val cutout = insets.displayCutout
+        val cutout = insets.displayCutout.also {
+            this.cutout = it
+        }
 
         val sbInsets: Pair<Int, Int> = insetsProvider.getStatusBarContentInsetsForCurrentRotation()
-        cutoutLeft = sbInsets.first
-        cutoutRight = sbInsets.second
+        val cutoutLeft = sbInsets.first
+        val cutoutRight = sbInsets.second
         val hasCornerCutout: Boolean = insetsProvider.currentRotationHasCornerCutout()
         updateQQSPaddings()
         // Set these guides as the left/right limits for content that lives in the top row, using
@@ -383,6 +396,13 @@
         }
 
         view.updateAllConstraints(changes)
+        updateBatteryMode()
+    }
+
+    private fun updateBatteryMode() {
+        qsBatteryModeController.getBatteryMode(cutout, qsExpandedFraction)?.let {
+            batteryIcon.setPercentShowMode(it)
+        }
     }
 
     private fun updateScrollY() {
@@ -418,7 +438,7 @@
     private fun updateVisibility() {
         val visibility = if (!largeScreenActive && !combinedHeaders || qsDisabled) {
             View.GONE
-        } else if (qsVisible && header.alpha > 0f) {
+        } else if (qsVisible && !customizing) {
             View.VISIBLE
         } else {
             View.INVISIBLE
@@ -450,6 +470,7 @@
         if (header is MotionLayout && !largeScreenActive && visible) {
             logInstantEvent("updatePosition: $qsExpandedFraction")
             header.progress = qsExpandedFraction
+            updateBatteryMode()
         }
     }
 
@@ -466,10 +487,8 @@
         if (visible) {
             updateSingleCarrier(qsCarrierGroupController.isSingleCarrier)
             qsCarrierGroupController.setOnSingleCarrierChangedListener { updateSingleCarrier(it) }
-            statusBarIconController.addIconGroup(iconManager)
         } else {
             qsCarrierGroupController.setOnSingleCarrierChangedListener(null)
-            statusBarIconController.removeIconGroup(iconManager)
         }
     }
 
@@ -486,6 +505,7 @@
         val padding = resources.getDimensionPixelSize(R.dimen.qs_panel_padding)
         header.setPadding(padding, header.paddingTop, padding, header.paddingBottom)
         updateQQSPaddings()
+        qsBatteryModeController.updateResources()
     }
 
     private fun updateQQSPaddings() {
@@ -541,4 +561,23 @@
 
     @VisibleForTesting
     internal fun simulateViewDetached() = this.onViewDetached()
+
+    inner class CustomizerAnimationListener(
+            private val enteringCustomizing: Boolean,
+    ) : AnimatorListenerAdapter() {
+        override fun onAnimationEnd(animation: Animator?) {
+            super.onAnimationEnd(animation)
+            header.animate().setListener(null)
+            if (enteringCustomizing) {
+                customizing = true
+            }
+        }
+
+        override fun onAnimationStart(animation: Animator?) {
+            super.onAnimationStart(animation)
+            if (!enteringCustomizing) {
+                customizing = false
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 392a851..2175a33 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -23,14 +23,12 @@
 import static androidx.constraintlayout.widget.ConstraintSet.END;
 import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID;
 
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
 import static com.android.keyguard.KeyguardClockSwitch.LARGE;
 import static com.android.keyguard.KeyguardClockSwitch.SMALL;
 import static com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE;
 import static com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE;
 import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
 import static com.android.systemui.classifier.Classifier.GENERIC;
-import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
 import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
 import static com.android.systemui.classifier.Classifier.UNLOCK;
 import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED;
@@ -53,7 +51,6 @@
 import android.animation.ValueAnimator;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.Fragment;
 import android.app.StatusBarManager;
 import android.content.ContentResolver;
 import android.content.res.Resources;
@@ -101,10 +98,8 @@
 import androidx.constraintlayout.widget.ConstraintSet;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.policy.ScreenDecorationsUtils;
 import com.android.internal.policy.SystemBarUtils;
 import com.android.internal.util.LatencyTracker;
 import com.android.keyguard.ActiveUnlockConfig;
@@ -135,17 +130,20 @@
 import com.android.systemui.dump.DumpsysTableLogger;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
-import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
 import com.android.systemui.fragments.FragmentService;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
+import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants;
 import com.android.systemui.keyguard.shared.model.TransitionState;
 import com.android.systemui.keyguard.shared.model.TransitionStep;
+import com.android.systemui.keyguard.ui.binder.KeyguardLongPressViewBinder;
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel;
@@ -173,7 +171,6 @@
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.NotificationShelfController;
 import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.QsFrameTranslateController;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -204,7 +201,6 @@
 import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
 import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
 import com.android.systemui.statusbar.phone.KeyguardBottomAreaViewController;
-import com.android.systemui.statusbar.phone.KeyguardBouncer;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.KeyguardClockPositionAlgorithm;
 import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
@@ -243,6 +239,7 @@
 import javax.inject.Inject;
 import javax.inject.Provider;
 
+import kotlin.Unit;
 import kotlinx.coroutines.CoroutineDispatcher;
 
 @CentralSurfacesComponent.CentralSurfacesScope
@@ -259,13 +256,13 @@
     private static final VibrationEffect ADDITIONAL_TAP_REQUIRED_VIBRATION_EFFECT =
             VibrationEffect.get(VibrationEffect.EFFECT_STRENGTH_MEDIUM, false);
     /** The parallax amount of the quick settings translation when dragging down the panel. */
-    private static final float QS_PARALLAX_AMOUNT = 0.175f;
+    public static final float QS_PARALLAX_AMOUNT = 0.175f;
     /** Fling expanding QS. */
     public static final int FLING_EXPAND = 0;
     /** Fling collapsing QS, potentially stopping when QS becomes QQS. */
-    private static final int FLING_COLLAPSE = 1;
+    public static final int FLING_COLLAPSE = 1;
     /** Fling until QS is completely hidden. */
-    private static final int FLING_HIDE = 2;
+    public static final int FLING_HIDE = 2;
     /** The delay to reset the hint text when the hint animation is finished running. */
     private static final int HINT_RESET_DELAY_MS = 1200;
     private static final long ANIMATION_DELAY_ICON_FADE_IN =
@@ -287,7 +284,7 @@
     private static final int MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER = 300;
     private static final int MAX_DOWN_EVENT_BUFFER_SIZE = 50;
     private static final String COUNTER_PANEL_OPEN = "panel_open";
-    private static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs";
+    public static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs";
     private static final String COUNTER_PANEL_OPEN_PEEK = "panel_open_peek";
     private static final Rect M_DUMMY_DIRTY_RECT = new Rect(0, 0, 1, 1);
     private static final Rect EMPTY_RECT = new Rect();
@@ -306,14 +303,10 @@
     private final SystemClock mSystemClock;
     private final ShadeLogger mShadeLog;
     private final DozeParameters mDozeParameters;
-    private final Runnable mCollapseExpandAction = this::collapseOrExpand;
-    private final NsslOverscrollTopChangedListener mOnOverscrollTopChangedListener =
-            new NsslOverscrollTopChangedListener();
     private final NotificationStackScrollLayout.OnEmptySpaceClickListener
             mOnEmptySpaceClickListener = (x, y) -> onEmptySpaceClick();
     private final ShadeHeadsUpChangedListener mOnHeadsUpChangedListener =
             new ShadeHeadsUpChangedListener();
-    private final QS.HeightListener mHeightListener = this::onQsHeightChanged;
     private final ConfigurationListener mConfigurationListener = new ConfigurationListener();
     private final SettingsChangeObserver mSettingsChangeObserver;
     private final StatusBarStateListener mStatusBarStateListener = new StatusBarStateListener();
@@ -323,7 +316,6 @@
     private final ConfigurationController mConfigurationController;
     private final Provider<FlingAnimationUtils.Builder> mFlingAnimationUtilsBuilder;
     private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
-    private final InteractionJankMonitor mInteractionJankMonitor;
     private final LayoutInflater mLayoutInflater;
     private final FeatureFlags mFeatureFlags;
     private final PowerManager mPowerManager;
@@ -342,12 +334,9 @@
     private final KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory;
     private final FragmentService mFragmentService;
     private final ScrimController mScrimController;
-    private final NotificationRemoteInputManager mRemoteInputManager;
     private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
-    private final ShadeTransitionController mShadeTransitionController;
     private final TapAgainViewController mTapAgainViewController;
     private final LargeScreenShadeHeaderController mLargeScreenShadeHeaderController;
-    private final RecordingController mRecordingController;
     private final boolean mVibrateOnOpening;
     private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
     private final FlingAnimationUtils mFlingAnimationUtilsClosing;
@@ -359,12 +348,11 @@
     private final Interpolator mBounceInterpolator;
     private final NotificationShadeWindowController mNotificationShadeWindowController;
     private final ShadeExpansionStateManager mShadeExpansionStateManager;
-    private final QS.ScrollListener mQsScrollListener = this::onQsPanelScrollChanged;
     private final FalsingTapListener mFalsingTapListener = this::falsingAdditionalTapRequired;
-    private final FragmentListener mQsFragmentListener = new QsFragmentListener();
     private final AccessibilityDelegate mAccessibilityDelegate = new ShadeAccessibilityDelegate();
     private final NotificationGutsManager mGutsManager;
     private final AlternateBouncerInteractor mAlternateBouncerInteractor;
+    private final QuickSettingsController mQsController;
 
     private long mDownTime;
     private boolean mTouchSlopExceededBeforeDown;
@@ -391,9 +379,6 @@
     private KeyguardUserSwitcherController mKeyguardUserSwitcherController;
     private KeyguardStatusBarView mKeyguardStatusBar;
     private KeyguardStatusBarViewController mKeyguardStatusBarViewController;
-    private QS mQs;
-    private FrameLayout mQsFrame;
-    private final QsFrameTranslateController mQsFrameTranslateController;
     private KeyguardStatusViewController mKeyguardStatusViewController;
     private final LockIconViewController mLockIconViewController;
     private NotificationsQuickSettingsContainer mNotificationContainerParent;
@@ -401,47 +386,19 @@
     private final Provider<KeyguardBottomAreaViewController>
             mKeyguardBottomAreaViewControllerProvider;
     private boolean mAnimateNextPositionUpdate;
-    private float mQuickQsHeaderHeight;
     private final ScreenOffAnimationController mScreenOffAnimationController;
     private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
-    private int mQsTrackingPointer;
-    private VelocityTracker mQsVelocityTracker;
     private TrackingStartedListener mTrackingStartedListener;
     private OpenCloseListener mOpenCloseListener;
     private GestureRecorder mGestureRecorder;
-    private boolean mQsTracking;
-    /** Whether the ongoing gesture might both trigger the expansion in both the view and QS. */
-    private boolean mConflictingQsExpansionGesture;
     private boolean mPanelExpanded;
 
-    /**
-     * Indicates that QS is in expanded state which can happen by:
-     * - single pane shade: expanding shade and then expanding QS
-     * - split shade: just expanding shade (QS are expanded automatically)
-     */
-    private boolean mQsExpanded;
-    private boolean mQsExpandedWhenExpandingStarted;
-    private boolean mQsFullyExpanded;
-    private boolean mKeyguardShowing;
     private boolean mKeyguardQsUserSwitchEnabled;
     private boolean mKeyguardUserSwitcherEnabled;
     private boolean mDozing;
     private boolean mDozingOnDown;
     private boolean mBouncerShowing;
     private int mBarState;
-    private float mInitialHeightOnTouch;
-    private float mInitialTouchX;
-    private float mInitialTouchY;
-    private float mQsExpansionHeight;
-    private int mQsMinExpansionHeight;
-    private int mQsMaxExpansionHeight;
-    private int mQsPeekHeight;
-    private boolean mStackScrollerOverscrolling;
-    private boolean mQsExpansionFromOverscroll;
-    private float mLastOverscroll;
-    private boolean mQsExpansionEnabledPolicy = true;
-    private boolean mQsExpansionEnabledAmbient = true;
-    private ValueAnimator mQsExpansionAnimator;
     private FlingAnimationUtils mFlingAnimationUtils;
     private int mStatusBarMinHeight;
     private int mStatusBarHeaderHeightKeyguard;
@@ -451,8 +408,6 @@
     private int mDisplayTopInset = 0; // in pixels
     private int mDisplayRightInset = 0; // in pixels
     private int mDisplayLeftInset = 0; // in pixels
-    private int mLargeScreenShadeHeaderHeight;
-    private int mSplitShadeNotificationsScrimMarginBottom;
 
     private final KeyguardClockPositionAlgorithm
             mClockPositionAlgorithm =
@@ -462,23 +417,7 @@
             new KeyguardClockPositionAlgorithm.Result();
     private boolean mIsExpanding;
 
-    /**
-     * Determines if QS should be already expanded when expanding shade.
-     * Used for split shade, two finger gesture as well as accessibility shortcut to QS.
-     * It needs to be set when movement starts as it resets at the end of expansion/collapse.
-     */
-    private boolean mQsExpandImmediate;
-    private boolean mTwoFingerQsExpandPossible;
     private String mHeaderDebugInfo;
-    /**
-     * If we are in a panel collapsing motion, we reset scrollY of our scroll view but still
-     * need to take this into account in our panel height calculation.
-     */
-    private boolean mQsAnimatorExpand;
-    private ValueAnimator mQsSizeChangeAnimator;
-    private boolean mQsScrimEnabled = true;
-    private boolean mQsTouchAboveFalsingThreshold;
-    private int mQsFalsingThreshold;
 
     /**
      * Indicates drag starting height when swiping down or up on heads-up notifications.
@@ -560,51 +499,12 @@
     private Runnable mExpandAfterLayoutRunnable;
     private Runnable mHideExpandedRunnable;
 
-    /**
-     * The padding between the start of notifications and the qs boundary on the lockscreen.
-     * On lockscreen, notifications aren't inset this extra amount, but we still want the
-     * qs boundary to be padded.
-     */
-    private int mLockscreenNotificationQSPadding;
-    /**
-     * The amount of progress we are currently in if we're transitioning to the full shade.
-     * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
-     * shade. This value can also go beyond 1.1 when we're overshooting!
-     */
-    private float mTransitioningToFullShadeProgress;
-    /**
-     * Position of the qs bottom during the full shade transition. This is needed as the toppadding
-     * can change during state changes, which makes it much harder to do animations
-     */
-    private int mTransitionToFullShadeQSPosition;
-    /** Distance a full shade transition takes in order for qs to fully transition to the shade. */
-    private int mDistanceForQSFullShadeTransition;
-    /** The translation amount for QS for the full shade transition. */
-    private float mQsTranslationForFullShadeTransition;
-
     /** The maximum overshoot allowed for the top padding for the full shade transition. */
     private int mMaxOverscrollAmountForPulse;
-    /** Should we animate the next bounds update. */
-    private boolean mAnimateNextNotificationBounds;
-    /** The delay for the next bounds animation. */
-    private long mNotificationBoundsAnimationDelay;
-    /** The duration of the notification bounds animation. */
-    private long mNotificationBoundsAnimationDuration;
 
     /** Whether a collapse that started on the panel should allow the panel to intercept. */
     private boolean mIsPanelCollapseOnQQS;
-    private boolean mAnimatingQS;
-    /** The end bounds of a clipping animation. */
-    private final Rect mQsClippingAnimationEndBounds = new Rect();
-    /** The animator for the qs clipping bounds. */
-    private ValueAnimator mQsClippingAnimation = null;
-    /** Whether the current animator is resetting the qs translation. */
-    private boolean mIsQsTranslationResetAnimator;
 
-    /** Whether the current animator is resetting the pulse expansion after a drag down. */
-    private boolean mIsPulseExpansionResetAnimator;
-    private final Rect mLastQsClipBounds = new Rect();
-    private final Region mQsInterceptRegion = new Region();
     /** Alpha of the views which only show on the keyguard but not in shade / shade locked. */
     private float mKeyguardOnlyContentAlpha = 1.0f;
     /** Y translation of the views that only show on the keyguard but in shade / shade locked. */
@@ -614,15 +514,6 @@
     private boolean mIsGestureNavigation;
     private int mOldLayoutDirection;
     private NotificationShelfController mNotificationShelfController;
-    private int mScrimCornerRadius;
-    private int mScreenCornerRadius;
-    private boolean mQSAnimatingHiddenFromCollapsed;
-    private boolean mUseLargeScreenShadeHeader;
-    private boolean mEnableQsClipping;
-
-    private int mQsClipTop;
-    private int mQsClipBottom;
-    private boolean mQsVisible;
 
     private final ContentResolver mContentResolver;
     private float mMinFraction;
@@ -698,6 +589,7 @@
     private LockscreenToOccludedTransitionViewModel mLockscreenToOccludedTransitionViewModel;
 
     private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+    private final KeyguardInteractor mKeyguardInteractor;
     private CoroutineDispatcher mMainDispatcher;
     private boolean mIsOcclusionTransitionRunning = false;
     private int mDreamingToLockscreenTransitionTranslationY;
@@ -797,6 +689,7 @@
             TapAgainViewController tapAgainViewController,
             NavigationModeController navigationModeController,
             NavigationBarController navigationBarController,
+            QuickSettingsController quickSettingsController,
             FragmentService fragmentService,
             ContentResolver contentResolver,
             RecordingController recordingController,
@@ -806,8 +699,6 @@
             ShadeExpansionStateManager shadeExpansionStateManager,
             NotificationRemoteInputManager remoteInputManager,
             Optional<SysUIUnfoldComponent> unfoldComponent,
-            InteractionJankMonitor interactionJankMonitor,
-            QsFrameTranslateController qsFrameTranslateController,
             SysUiState sysUiState,
             Provider<KeyguardBottomAreaViewController> keyguardBottomAreaViewControllerProvider,
             KeyguardUnlockAnimationController keyguardUnlockAnimationController,
@@ -827,7 +718,9 @@
             LockscreenToOccludedTransitionViewModel lockscreenToOccludedTransitionViewModel,
             @Main CoroutineDispatcher mainDispatcher,
             KeyguardTransitionInteractor keyguardTransitionInteractor,
-            DumpManager dumpManager) {
+            DumpManager dumpManager,
+            KeyguardLongPressViewModel keyguardLongPressViewModel,
+            KeyguardInteractor keyguardInteractor) {
         keyguardStateController.addCallback(new KeyguardStateController.Callback() {
             @Override
             public void onKeyguardFadingAwayChanged() {
@@ -848,6 +741,7 @@
         mGoneToDreamingTransitionViewModel = goneToDreamingTransitionViewModel;
         mLockscreenToOccludedTransitionViewModel = lockscreenToOccludedTransitionViewModel;
         mKeyguardTransitionInteractor = keyguardTransitionInteractor;
+        mKeyguardInteractor = keyguardInteractor;
         mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
             @Override
             public void onViewAttachedToWindow(View v) {
@@ -864,6 +758,7 @@
 
         mResources = mView.getResources();
         mKeyguardStateController = keyguardStateController;
+        mQsController = quickSettingsController;
         mKeyguardIndicationController = keyguardIndicationController;
         mStatusBarStateController = (SysuiStatusBarStateController) statusBarStateController;
         mNotificationShadeWindowController = notificationShadeWindowController;
@@ -894,7 +789,6 @@
         mVibratorHelper = vibratorHelper;
         mVibrateOnOpening = mResources.getBoolean(R.bool.config_vibrateOnIconAnimation);
         mStatusBarTouchableRegionManager = statusBarTouchableRegionManager;
-        mInteractionJankMonitor = interactionJankMonitor;
         mSystemClock = systemClock;
         mKeyguardMediaController = keyguardMediaController;
         mMetricsLogger = metricsLogger;
@@ -930,7 +824,6 @@
         mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
         setPanelAlpha(255, false /* animate */);
         mCommandQueue = commandQueue;
-        mRecordingController = recordingController;
         mDisplayId = displayId;
         mPulseExpansionHandler = pulseExpansionHandler;
         mDozeParameters = dozeParameters;
@@ -939,20 +832,19 @@
         mMediaDataManager = mediaDataManager;
         mTapAgainViewController = tapAgainViewController;
         mSysUiState = sysUiState;
-        pulseExpansionHandler.setPulseExpandAbortListener(() -> {
-            if (mQs != null) {
-                mQs.animateHeaderSlidingOut();
-            }
-        });
         statusBarWindowStateController.addListener(this::onStatusBarWindowStateChanged);
         mKeyguardBypassController = bypassController;
         mUpdateMonitor = keyguardUpdateMonitor;
         mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
-        mShadeTransitionController = shadeTransitionController;
         lockscreenShadeTransitionController.setNotificationPanelController(this);
         shadeTransitionController.setNotificationPanelViewController(this);
         dynamicPrivacyController.addListener(this::onDynamicPrivacyChanged);
-
+        quickSettingsController.setExpansionHeightListener(this::onQsSetExpansionHeightCalled);
+        quickSettingsController.setQsStateUpdateListener(this::onQsStateUpdated);
+        quickSettingsController.setApplyClippingImmediatelyListener(
+                this::onQsClippingImmediatelyApplied);
+        quickSettingsController.setFlingQsWithoutClickListener(this::onFlingQsWithoutClick);
+        quickSettingsController.setExpansionHeightSetToMaxListener(this::onExpansionHeightSetToMax);
         shadeExpansionStateManager.addStateListener(this::onPanelStateChanged);
 
         mBottomAreaShadeAlphaAnimator = ValueAnimator.ofFloat(1f, 0);
@@ -967,7 +859,6 @@
         mLockIconViewController = lockIconViewController;
         mScreenOffAnimationController = screenOffAnimationController;
         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
-        mRemoteInputManager = remoteInputManager;
         mLastDownEvents = new NPVCDownEventState.Buffer(MAX_DOWN_EVENT_BUFFER_SIZE);
 
         int currentMode = navigationModeController.addListener(
@@ -986,7 +877,8 @@
 
         if (DEBUG_DRAWABLE) {
             mView.getOverlay().add(new DebugDrawable(this, mView,
-                    mNotificationStackScrollLayoutController, mLockIconViewController));
+                    mNotificationStackScrollLayoutController, mLockIconViewController,
+                    mQsController));
         }
 
         mKeyguardUnfoldTransition = unfoldComponent.map(
@@ -995,11 +887,17 @@
                 SysUIUnfoldComponent::getNotificationPanelUnfoldAnimationController);
 
         mUnocclusionTransitionFlagEnabled = featureFlags.isEnabled(Flags.UNOCCLUSION_TRANSITION);
-
-        mQsFrameTranslateController = qsFrameTranslateController;
         updateUserSwitcherFlags();
         mKeyguardBottomAreaViewModel = keyguardBottomAreaViewModel;
         mKeyguardBottomAreaInteractor = keyguardBottomAreaInteractor;
+        KeyguardLongPressViewBinder.bind(
+                mView.requireViewById(R.id.keyguard_long_press),
+                keyguardLongPressViewModel,
+                () -> {
+                    onEmptySpaceClick();
+                    return Unit.INSTANCE;
+                },
+                mFalsingManager);
         onFinishInflate();
         keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(
                 new KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener() {
@@ -1104,19 +1002,16 @@
         mNotificationStackScrollLayoutController.attach(stackScrollLayout);
         mNotificationStackScrollLayoutController.setOnHeightChangedListener(
                 new NsslHeightChangedListener());
-        mNotificationStackScrollLayoutController.setOverscrollTopChangedListener(
-                mOnOverscrollTopChangedListener);
-        mNotificationStackScrollLayoutController.setOnScrollListener(this::onNotificationScrolled);
-        mNotificationStackScrollLayoutController.setOnStackYChanged(this::onStackYChanged);
         mNotificationStackScrollLayoutController.setOnEmptySpaceClickListener(
                 mOnEmptySpaceClickListener);
+        mQsController.initNotificationStackScrollLayoutController();
+        mShadeExpansionStateManager.addQsExpansionListener(this::onQsExpansionChanged);
         addTrackingHeadsUpListener(mNotificationStackScrollLayoutController::setTrackingHeadsUp);
         setKeyguardBottomArea(mView.findViewById(R.id.keyguard_bottom_area));
 
         initBottomArea();
 
         mWakeUpCoordinator.setStackScroller(mNotificationStackScrollLayoutController);
-        mQsFrame = mView.findViewById(R.id.qs_frame);
         mPulseExpansionHandler.setUp(mNotificationStackScrollLayoutController);
         mWakeUpCoordinator.addListener(new NotificationWakeUpCoordinator.WakeUpListener() {
             @Override
@@ -1219,24 +1114,14 @@
                 .setMaxLengthSeconds(0.4f).build();
         mStatusBarMinHeight = SystemBarUtils.getStatusBarHeight(mView.getContext());
         mStatusBarHeaderHeightKeyguard = Utils.getStatusBarHeaderHeightKeyguard(mView.getContext());
-        mQsPeekHeight = mResources.getDimensionPixelSize(R.dimen.qs_peek_height);
         mClockPositionAlgorithm.loadDimens(mResources);
-        mQsFalsingThreshold = mResources.getDimensionPixelSize(R.dimen.qs_falsing_threshold);
         mIndicationBottomPadding = mResources.getDimensionPixelSize(
                 R.dimen.keyguard_indication_bottom_padding);
         int statusbarHeight = SystemBarUtils.getStatusBarHeight(mView.getContext());
         mHeadsUpInset = statusbarHeight + mResources.getDimensionPixelSize(
                 R.dimen.heads_up_status_bar_padding);
-        mDistanceForQSFullShadeTransition = mResources.getDimensionPixelSize(
-                R.dimen.lockscreen_shade_qs_transition_distance);
         mMaxOverscrollAmountForPulse = mResources.getDimensionPixelSize(
                 R.dimen.pulse_expansion_max_top_overshoot);
-        mScrimCornerRadius = mResources.getDimensionPixelSize(
-                R.dimen.notification_scrim_corner_radius);
-        mScreenCornerRadius = (int) ScreenDecorationsUtils.getWindowCornerRadius(
-                mView.getContext());
-        mLockscreenNotificationQSPadding = mResources.getDimensionPixelSize(
-                R.dimen.notification_side_paddings);
         mUdfpsMaxYBurnInOffset = mResources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
         mSplitShadeScrimTransitionDistance = mResources.getDimensionPixelSize(
                 R.dimen.split_shade_scrim_transition_distance);
@@ -1250,6 +1135,8 @@
                 R.dimen.gone_to_dreaming_transition_lockscreen_translation_y);
         mLockscreenToOccludedTransitionTranslationY = mResources.getDimensionPixelSize(
                 R.dimen.lockscreen_to_occluded_transition_lockscreen_translation_y);
+        // TODO (b/265193930): remove this and make QsController listen to NotificationPanelViews
+        mQsController.loadDimens();
     }
 
     private void updateViewControllers(KeyguardStatusView keyguardStatusView,
@@ -1292,40 +1179,13 @@
     }
 
     public void updateResources() {
-        mSplitShadeNotificationsScrimMarginBottom =
-                mResources.getDimensionPixelSize(
-                        R.dimen.split_shade_notifications_scrim_margin_bottom);
         final boolean newSplitShadeEnabled =
                 LargeScreenUtils.shouldUseSplitNotificationShade(mResources);
         final boolean splitShadeChanged = mSplitShadeEnabled != newSplitShadeEnabled;
         mSplitShadeEnabled = newSplitShadeEnabled;
-
-        if (mQs != null) {
-            mQs.setInSplitShade(mSplitShadeEnabled);
-        }
-
-        mUseLargeScreenShadeHeader =
-                LargeScreenUtils.shouldUseLargeScreenShadeHeader(mView.getResources());
-
-        mLargeScreenShadeHeaderHeight =
-                mResources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height);
-        // TODO: When the flag is eventually removed, it means that we have a single view that is
-        // the same height in QQS and in Large Screen (large_screen_shade_header_height). Eventually
-        // the concept of largeScreenHeader or quickQsHeader will disappear outside of the class
-        // that controls the view as the offset needs to be the same regardless.
-        if (mUseLargeScreenShadeHeader || mFeatureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)) {
-            mQuickQsHeaderHeight = mLargeScreenShadeHeaderHeight;
-        } else {
-            mQuickQsHeaderHeight = SystemBarUtils.getQuickQsOffsetHeight(mView.getContext());
-        }
-        int topMargin = mUseLargeScreenShadeHeader ? mLargeScreenShadeHeaderHeight :
-                mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_top);
-        mLargeScreenShadeHeaderController.setLargeScreenActive(mUseLargeScreenShadeHeader);
-        mAmbientState.setStackTopMargin(topMargin);
+        mQsController.updateResources();
         mNotificationsQSContainerController.updateResources();
-
         updateKeyguardStatusViewAlignment(/* animate= */false);
-
         mKeyguardMediaController.refreshMediaPosition();
 
         if (splitShadeChanged) {
@@ -1334,17 +1194,15 @@
 
         mSplitShadeFullTransitionDistance =
                 mResources.getDimensionPixelSize(R.dimen.split_shade_full_transition_distance);
-
-        mEnableQsClipping = mResources.getBoolean(R.bool.qs_enable_clipping);
     }
 
     private void onSplitShadeEnabledChanged() {
         // when we switch between split shade and regular shade we want to enforce setting qs to
         // the default state: expanded for split shade and collapsed otherwise
         if (!isOnKeyguard() && mPanelExpanded) {
-            setQsExpanded(mSplitShadeEnabled);
+            mQsController.setExpanded(mSplitShadeEnabled);
         }
-        if (isOnKeyguard() && mQsExpanded && mSplitShadeEnabled) {
+        if (isOnKeyguard() && mQsController.getExpanded() && mSplitShadeEnabled) {
             // In single column keyguard - when you swipe from the top - QS is fully expanded and
             // StatusBarState is KEYGUARD. That state doesn't make sense for split shade,
             // where notifications are always visible and we effectively go to fully expanded
@@ -1354,7 +1212,7 @@
             mStatusBarStateController.setState(StatusBarState.SHADE_LOCKED, /* force= */false);
         }
         updateClockAppearance();
-        updateQsState();
+        mQsController.updateQsState();
         mNotificationStackScrollLayoutController.updateFooter();
     }
 
@@ -1462,11 +1320,6 @@
         mNotificationPanelUnfoldAnimationController.ifPresent(u -> u.setup(mView));
     }
 
-    @VisibleForTesting
-    void setQs(QS qs) {
-        mQs = qs;
-    }
-
     private void attachSplitShadeMediaPlayerContainer(FrameLayout container) {
         mKeyguardMediaController.attachSplitShadeContainer(container);
     }
@@ -1498,7 +1351,7 @@
             if (SPEW_LOGCAT) Log.d(TAG, "Skipping computeMaxKeyguardNotifications() by request");
         }
 
-        if (mKeyguardShowing && !mKeyguardBypassController.getBypassEnabled()) {
+        if (getKeyguardShowing() && !mKeyguardBypassController.getBypassEnabled()) {
             mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(
                     mMaxAllowedKeyguardNotifications);
             mNotificationStackScrollLayoutController.setKeyguardBottomPaddingForDebug(
@@ -1547,39 +1400,14 @@
         mIsFullWidth = isFullWidth;
         mScrimController.setClipsQsScrim(isFullWidth);
         mNotificationStackScrollLayoutController.setIsFullWidth(isFullWidth);
-        if (mQs != null) {
-            mQs.setIsNotificationPanelFullWidth(isFullWidth);
-        }
-    }
-
-    private void startQsSizeChangeAnimation(int oldHeight, final int newHeight) {
-        if (mQsSizeChangeAnimator != null) {
-            oldHeight = (int) mQsSizeChangeAnimator.getAnimatedValue();
-            mQsSizeChangeAnimator.cancel();
-        }
-        mQsSizeChangeAnimator = ValueAnimator.ofInt(oldHeight, newHeight);
-        mQsSizeChangeAnimator.setDuration(300);
-        mQsSizeChangeAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-        mQsSizeChangeAnimator.addUpdateListener(animation -> {
-            requestScrollerTopPaddingUpdate(false /* animate */);
-            updateExpandedHeightToMaxHeight();
-            int height = (int) mQsSizeChangeAnimator.getAnimatedValue();
-            mQs.setHeightOverride(height);
-        });
-        mQsSizeChangeAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mQsSizeChangeAnimator = null;
-            }
-        });
-        mQsSizeChangeAnimator.start();
+        mQsController.setNotificationPanelFullWidth(isFullWidth);
     }
 
     /**
      * Positions the clock and notifications dynamically depending on how many notifications are
      * showing.
      */
-    private void positionClockAndNotifications() {
+    void positionClockAndNotifications() {
         positionClockAndNotifications(false /* forceUpdate */);
     }
 
@@ -1604,7 +1432,7 @@
                 // so we should not add a padding for them
                 stackScrollerPadding = 0;
             } else {
-                stackScrollerPadding = getUnlockedStackScrollerPadding();
+                stackScrollerPadding = mQsController.getUnlockedStackScrollerPadding();
             }
         } else {
             stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded;
@@ -1652,8 +1480,9 @@
                 userSwitcherHeight,
                 userSwitcherPreferredY,
                 darkAmount, mOverStretchAmount,
-                bypassEnabled, getUnlockedStackScrollerPadding(),
-                computeQsExpansionFraction(),
+                bypassEnabled,
+                mQsController.getUnlockedStackScrollerPadding(),
+                mQsController.computeExpansionFraction(),
                 mDisplayTopInset,
                 mSplitShadeEnabled,
                 udfpsAodTopLocation,
@@ -1805,19 +1634,16 @@
         return mDozing && mDozeParameters.getAlwaysOn();
     }
 
+    boolean isDozing() {
+        return mDozing;
+    }
+
     private boolean hasVisibleNotifications() {
         return mNotificationStackScrollLayoutController
                 .getVisibleNotificationCount() != 0
                 || mMediaDataManager.hasActiveMediaOrRecommendation();
     }
 
-    /**
-     * @return the padding of the stackscroller when unlocked
-     */
-    private int getUnlockedStackScrollerPadding() {
-        return (mQs != null ? mQs.getHeader().getHeight() : 0) + mQsPeekHeight;
-    }
-
     /** Returns space between top of lock icon and bottom of NotificationStackScrollLayout. */
     private float getLockIconPadding() {
         float lockIconPadding = 0f;
@@ -1935,23 +1761,23 @@
         mAnimateNextPositionUpdate = true;
     }
 
-    private void setQsExpansionEnabled() {
-        if (mQs == null) return;
-        mQs.setHeaderClickable(isQsExpansionEnabled());
-    }
+    /** Animate QS closing. */
+    public void animateCloseQs(boolean animateAway) {
+        if (mSplitShadeEnabled) {
+            collapsePanel(true, false, 1.0f);
+        } else {
+            mQsController.animateCloseQs(animateAway);
+        }
 
-    public void setQsExpansionEnabledPolicy(boolean qsExpansionEnabledPolicy) {
-        mQsExpansionEnabledPolicy = qsExpansionEnabledPolicy;
-        setQsExpansionEnabled();
     }
 
     public void resetViews(boolean animate) {
         mGutsManager.closeAndSaveGuts(true /* leavebehind */, true /* force */,
                 true /* controls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
         if (animate && !isFullyCollapsed()) {
-            animateCloseQs(true /* animateAway */);
+            animateCloseQs(true);
         } else {
-            closeQs();
+            mQsController.closeQs();
         }
         mNotificationStackScrollLayoutController.setOverScrollAmount(0f, true /* onTop */, animate,
                 !animate /* cancelAnimators */);
@@ -1982,8 +1808,8 @@
             return;
         }
 
-        if (mQsExpanded) {
-            setQsExpandImmediate(true);
+        if (mQsController.getExpanded()) {
+            mQsController.setExpandImmediate(true);
             setShowShelfOnly(true);
         }
         debugLog("collapse: %s", this);
@@ -2002,33 +1828,11 @@
         }
     }
 
-    @VisibleForTesting
-    void setQsExpandImmediate(boolean expandImmediate) {
-        if (expandImmediate != mQsExpandImmediate) {
-            mQsExpandImmediate = expandImmediate;
-            mShadeExpansionStateManager.notifyExpandImmediateChange(expandImmediate);
-        }
-    }
-
-    @VisibleForTesting
-    boolean isQsExpandImmediate() {
-        return mQsExpandImmediate;
-    }
-
     private void setShowShelfOnly(boolean shelfOnly) {
         mNotificationStackScrollLayoutController.setShouldShowShelfOnly(
                 shelfOnly && !mSplitShadeEnabled);
     }
 
-    public void closeQs() {
-        cancelQsAnimation();
-        setQsExpansionHeight(mQsMinExpansionHeight);
-        // qsExpandImmediate is a safety latch in case we're calling closeQS while we're in the
-        // middle of animation - we need to make sure that value is always false when shade if
-        // fully collapsed or expanded
-        setQsExpandImmediate(false);
-    }
-
     @VisibleForTesting
     void cancelHeightAnimator() {
         if (mHeightAnimator != null) {
@@ -2044,39 +1848,9 @@
         mView.animate().cancel();
     }
 
-    /**
-     * Animate QS closing by flinging it.
-     * If QS is expanded, it will collapse into QQS and stop.
-     * If in split shade, it will collapse the whole shade.
-     *
-     * @param animateAway Do not stop when QS becomes QQS. Fling until QS isn't visible anymore.
-     */
-    public void animateCloseQs(boolean animateAway) {
-        if (mSplitShadeEnabled) {
-            collapsePanel(
-                    /* animate= */true, /* delayed= */false, /* speedUpFactor= */1.0f);
-            return;
-        }
-
-        if (mQsExpansionAnimator != null) {
-            if (!mQsAnimatorExpand) {
-                return;
-            }
-            float height = mQsExpansionHeight;
-            mQsExpansionAnimator.cancel();
-            setQsExpansionHeight(height);
-        }
-        flingSettings(0 /* vel */, animateAway ? FLING_HIDE : FLING_COLLAPSE);
-    }
-
-    private boolean isQsExpansionEnabled() {
-        return mQsExpansionEnabledPolicy && mQsExpansionEnabledAmbient
-                && !mRemoteInputManager.isRemoteInputActive();
-    }
-
     public void expandWithQs() {
-        if (isQsExpansionEnabled()) {
-            setQsExpandImmediate(true);
+        if (mQsController.isExpansionEnabled()) {
+            mQsController.setExpandImmediate(true);
             setShowShelfOnly(true);
         }
         if (mSplitShadeEnabled && isOnKeyguard()) {
@@ -2092,8 +1866,8 @@
         } else if (isFullyCollapsed()) {
             expand(true /* animate */);
         } else {
-            traceQsJank(true /* startTracing */, false /* wasCancelled */);
-            flingSettings(0 /* velocity */, FLING_EXPAND);
+            mQsController.traceQsJank(true /* startTracing */, false /* wasCancelled */);
+            mQsController.flingQs(0, FLING_EXPAND);
         }
     }
 
@@ -2108,8 +1882,8 @@
         if (mSplitShadeEnabled && (isShadeFullyOpen() || isExpanding())) {
             return;
         }
-        if (isQsExpanded()) {
-            flingSettings(0 /* velocity */, FLING_COLLAPSE);
+        if (mQsController.getExpanded()) {
+            mQsController.flingQs(0, FLING_COLLAPSE);
         } else {
             expand(true /* animate */);
         }
@@ -2126,6 +1900,7 @@
     @VisibleForTesting
     void flingToHeight(float vel, boolean expand, float target,
             float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) {
+        mQsController.setLastShadeFlingWasExpanding(expand);
         mHeadsUpTouchHelper.notifyFling(!expand);
         mKeyguardStateController.notifyPanelFlingStart(!expand /* flingingToDismiss */);
         setClosingWithAlphaFadeout(!expand && !isOnKeyguard() && getFadeoutAlpha() == 1.0f);
@@ -2157,6 +1932,7 @@
         }
         ValueAnimator animator = createHeightAnimator(target, overshootAmount);
         if (expand) {
+            maybeVibrateOnOpening(true /* openingWithTouch */);
             if (expandBecauseOfFalsing && vel < 0) {
                 vel = 0;
             }
@@ -2167,6 +1943,7 @@
                 animator.setDuration(SHADE_OPEN_SPRING_OUT_DURATION);
             }
         } else {
+            mHasVibratedOnOpen = false;
             if (shouldUseDismissingAnimation()) {
                 if (vel == 0) {
                     animator.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED);
@@ -2195,7 +1972,7 @@
             @Override
             public void onAnimationStart(Animator animation) {
                 if (!mStatusBarStateController.isDozing()) {
-                    beginJankMonitoring();
+                    mQsController.beginJankMonitoring(isFullyCollapsed());
                 }
             }
 
@@ -2227,117 +2004,15 @@
         setAnimator(null);
         mKeyguardStateController.notifyPanelFlingEnd();
         if (!cancelled) {
-            endJankMonitoring();
+            mQsController.endJankMonitoring();
             notifyExpandingFinished();
         } else {
-            cancelJankMonitoring();
+            mQsController.cancelJankMonitoring();
         }
         updatePanelExpansionAndVisibility();
         mNotificationStackScrollLayoutController.setPanelFlinging(false);
     }
 
-    private boolean onQsIntercept(MotionEvent event) {
-        debugLog("onQsIntercept");
-        int pointerIndex = event.findPointerIndex(mQsTrackingPointer);
-        if (pointerIndex < 0) {
-            pointerIndex = 0;
-            mQsTrackingPointer = event.getPointerId(pointerIndex);
-        }
-        final float x = event.getX(pointerIndex);
-        final float y = event.getY(pointerIndex);
-
-        switch (event.getActionMasked()) {
-            case MotionEvent.ACTION_DOWN:
-                mInitialTouchY = y;
-                mInitialTouchX = x;
-                initVelocityTracker();
-                trackMovement(event);
-                float qsExpansionFraction = computeQsExpansionFraction();
-                // Intercept the touch if QS is between fully collapsed and fully expanded state
-                if (!mSplitShadeEnabled
-                        && qsExpansionFraction > 0.0 && qsExpansionFraction < 1.0) {
-                    mShadeLog.logMotionEvent(event,
-                            "onQsIntercept: down action, QS partially expanded/collapsed");
-                    return true;
-                }
-                if (mKeyguardShowing
-                        && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) {
-                    // Dragging down on the lockscreen statusbar should prohibit other interactions
-                    // immediately, otherwise we'll wait on the touchslop. This is to allow
-                    // dragging down to expanded quick settings directly on the lockscreen.
-                    mView.getParent().requestDisallowInterceptTouchEvent(true);
-                }
-                if (mQsExpansionAnimator != null) {
-                    mInitialHeightOnTouch = mQsExpansionHeight;
-                    mShadeLog.logMotionEvent(event,
-                            "onQsIntercept: down action, QS tracking enabled");
-                    mQsTracking = true;
-                    traceQsJank(true /* startTracing */, false /* wasCancelled */);
-                    mNotificationStackScrollLayoutController.cancelLongPress();
-                }
-                break;
-            case MotionEvent.ACTION_POINTER_UP:
-                final int upPointer = event.getPointerId(event.getActionIndex());
-                if (mQsTrackingPointer == upPointer) {
-                    // gesture is ongoing, find a new pointer to track
-                    final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
-                    mQsTrackingPointer = event.getPointerId(newIndex);
-                    mInitialTouchX = event.getX(newIndex);
-                    mInitialTouchY = event.getY(newIndex);
-                }
-                break;
-
-            case MotionEvent.ACTION_MOVE:
-                final float h = y - mInitialTouchY;
-                trackMovement(event);
-                if (mQsTracking) {
-
-                    // Already tracking because onOverscrolled was called. We need to update here
-                    // so we don't stop for a frame until the next touch event gets handled in
-                    // onTouchEvent.
-                    setQsExpansionHeight(h + mInitialHeightOnTouch);
-                    trackMovement(event);
-                    return true;
-                } else {
-                    mShadeLog.logMotionEvent(event,
-                            "onQsIntercept: move ignored because qs tracking disabled");
-                }
-                float touchSlop = getTouchSlop(event);
-                if ((h > touchSlop || (h < -touchSlop && mQsExpanded))
-                        && Math.abs(h) > Math.abs(x - mInitialTouchX)
-                        && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) {
-                    mView.getParent().requestDisallowInterceptTouchEvent(true);
-                    mShadeLog.onQsInterceptMoveQsTrackingEnabled(h);
-                    mQsTracking = true;
-                    traceQsJank(true /* startTracing */, false /* wasCancelled */);
-                    onQsExpansionStarted();
-                    notifyExpandingFinished();
-                    mInitialHeightOnTouch = mQsExpansionHeight;
-                    mInitialTouchY = y;
-                    mInitialTouchX = x;
-                    mNotificationStackScrollLayoutController.cancelLongPress();
-                    return true;
-                } else {
-                    mShadeLog.logQsTrackingNotStarted(mInitialTouchY, y, h, touchSlop, mQsExpanded,
-                            mCollapsedOnDown, mKeyguardShowing, isQsExpansionEnabled());
-                }
-                break;
-
-            case MotionEvent.ACTION_CANCEL:
-            case MotionEvent.ACTION_UP:
-                trackMovement(event);
-                mShadeLog.logMotionEvent(event, "onQsIntercept: up action, QS tracking disabled");
-                mQsTracking = false;
-                break;
-        }
-        return false;
-    }
-
-    @VisibleForTesting
-    boolean isQsTracking() {
-        return mQsTracking;
-    }
-
     private boolean isInContentBounds(float x, float y) {
         float stackScrollerX = mNotificationStackScrollLayoutController.getX();
         return !mNotificationStackScrollLayoutController
@@ -2346,29 +2021,14 @@
                 && x < stackScrollerX + mNotificationStackScrollLayoutController.getWidth();
     }
 
-    private void traceQsJank(boolean startTracing, boolean wasCancelled) {
-        if (mInteractionJankMonitor == null) {
-            return;
-        }
-        if (startTracing) {
-            mInteractionJankMonitor.begin(mView, CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
-        } else {
-            if (wasCancelled) {
-                mInteractionJankMonitor.cancel(CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
-            } else {
-                mInteractionJankMonitor.end(CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
-            }
-        }
-    }
-
     private void initDownStates(MotionEvent event) {
         if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
-            mQsTouchAboveFalsingThreshold = mQsFullyExpanded;
             mDozingOnDown = mDozing;
             mDownX = event.getX();
             mDownY = event.getY();
             mCollapsedOnDown = isFullyCollapsed();
-            mIsPanelCollapseOnQQS = canPanelCollapseOnQQS(mDownX, mDownY);
+            mQsController.setCollapsedOnDown(mCollapsedOnDown);
+            mIsPanelCollapseOnQQS = mQsController.canPanelCollapseOnQQS(mDownX, mDownY);
             mListenForHeadsUp = mCollapsedOnDown && mHeadsUpManager.hasPinnedHeadsUp();
             mAllowExpandForSmallExpansion = mExpectingSynthesizedDown;
             mTouchSlopExceededBeforeDown = mExpectingSynthesizedDown;
@@ -2378,7 +2038,7 @@
                     event.getEventTime(),
                     mDownX,
                     mDownY,
-                    mQsTouchAboveFalsingThreshold,
+                    mQsController.updateAndGetTouchAboveFalsingThreshold(),
                     mDozingOnDown,
                     mCollapsedOnDown,
                     mIsPanelCollapseOnQQS,
@@ -2393,84 +2053,14 @@
         }
     }
 
-    /**
-     * Can the panel collapse in this motion because it was started on QQS?
-     *
-     * @param downX the x location where the touch started
-     * @param downY the y location where the touch started
-     * @return true if the panel could be collapsed because it stared on QQS
-     */
-    private boolean canPanelCollapseOnQQS(float downX, float downY) {
-        if (mCollapsedOnDown || mKeyguardShowing || mQsExpanded) {
-            return false;
-        }
-        View header = mQs == null ? mKeyguardStatusBar : mQs.getHeader();
-        return downX >= mQsFrame.getX() && downX <= mQsFrame.getX() + mQsFrame.getWidth()
-                && downY <= header.getBottom();
-
-    }
-
-    private void flingQsWithCurrentVelocity(float y, boolean isCancelMotionEvent) {
-        float vel = getCurrentQSVelocity();
-        boolean expandsQs = flingExpandsQs(vel);
-        if (expandsQs) {
-            if (mFalsingManager.isUnlockingDisabled() || isFalseTouch()) {
-                expandsQs = false;
-            } else {
-                logQsSwipeDown(y);
-            }
-        } else if (vel < 0) {
-            mFalsingManager.isFalseTouch(QS_COLLAPSE);
-        }
-
-        int flingType;
-        if (expandsQs && !isCancelMotionEvent) {
-            flingType = FLING_EXPAND;
-        } else if (mSplitShadeEnabled) {
-            flingType = FLING_HIDE;
-        } else {
-            flingType = FLING_COLLAPSE;
-        }
-        flingSettings(vel, flingType);
-    }
-
-    private void logQsSwipeDown(float y) {
-        float vel = getCurrentQSVelocity();
-        final int
-                gesture =
-                mBarState == KEYGUARD ? MetricsEvent.ACTION_LS_QS
-                        : MetricsEvent.ACTION_SHADE_QS_PULL;
-        mLockscreenGestureLogger.write(gesture,
-                (int) ((y - mInitialTouchY) / mCentralSurfaces.getDisplayDensity()),
-                (int) (vel / mCentralSurfaces.getDisplayDensity()));
-    }
-
-    private boolean flingExpandsQs(float vel) {
+    boolean flingExpandsQs(float vel) {
         if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
-            return computeQsExpansionFraction() > 0.5f;
+            return mQsController.computeExpansionFraction() > 0.5f;
         } else {
             return vel > 0;
         }
     }
 
-    private boolean isFalseTouch() {
-        if (mFalsingManager.isClassifierEnabled()) {
-            return mFalsingManager.isFalseTouch(Classifier.QUICK_SETTINGS);
-        }
-        return !mQsTouchAboveFalsingThreshold;
-    }
-
-    private float computeQsExpansionFraction() {
-        if (mQSAnimatingHiddenFromCollapsed) {
-            // When hiding QS from collapsed state, the expansion can sometimes temporarily
-            // be larger than 0 because of the timing, leading to flickers.
-            return 0.0f;
-        }
-        return Math.min(
-                1f, (mQsExpansionHeight - mQsMinExpansionHeight) / (mQsMaxExpansionHeight
-                        - mQsMinExpansionHeight));
-    }
-
     private boolean shouldExpandWhenNotFlinging() {
         if (getExpandedFraction() > 0.5f) {
             return true;
@@ -2488,121 +2078,13 @@
         return mNotificationStackScrollLayoutController.getOpeningHeight();
     }
 
-
-    private boolean handleQsTouch(MotionEvent event) {
-        if (isSplitShadeAndTouchXOutsideQs(event.getX())) {
-            return false;
-        }
-        final int action = event.getActionMasked();
-        boolean collapsedQs = !mQsExpanded && !mSplitShadeEnabled;
-        boolean expandedShadeCollapsedQs = getExpandedFraction() == 1f && mBarState != KEYGUARD
-                && collapsedQs && isQsExpansionEnabled();
-        if (action == MotionEvent.ACTION_DOWN && expandedShadeCollapsedQs) {
-            // Down in the empty area while fully expanded - go to QS.
-            mShadeLog.logMotionEvent(event, "handleQsTouch: down action, QS tracking enabled");
-            mQsTracking = true;
-            traceQsJank(true /* startTracing */, false /* wasCancelled */);
-            mConflictingQsExpansionGesture = true;
-            onQsExpansionStarted();
-            mInitialHeightOnTouch = mQsExpansionHeight;
-            mInitialTouchY = event.getY();
-            mInitialTouchX = event.getX();
-        }
-        if (!isFullyCollapsed() && !isShadeOrQsHeightAnimationRunning()) {
-            handleQsDown(event);
-        }
-        // defer touches on QQS to shade while shade is collapsing. Added margin for error
-        // as sometimes the qsExpansionFraction can be a tiny value instead of 0 when in QQS.
-        if (!mSplitShadeEnabled
-                && computeQsExpansionFraction() <= 0.01 && getExpandedFraction() < 1.0) {
-            mShadeLog.logMotionEvent(event,
-                    "handleQsTouch: QQS touched while shade collapsing, QS tracking disabled");
-            mQsTracking = false;
-        }
-        if (!mQsExpandImmediate && mQsTracking) {
-            onQsTouch(event);
-            if (!mConflictingQsExpansionGesture && !mSplitShadeEnabled) {
-                mShadeLog.logMotionEvent(event,
-                        "handleQsTouch: not immediate expand or conflicting gesture");
-                return true;
-            }
-        }
-        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
-            mConflictingQsExpansionGesture = false;
-        }
-        if (action == MotionEvent.ACTION_DOWN && isFullyCollapsed() && isQsExpansionEnabled()) {
-            mTwoFingerQsExpandPossible = true;
-        }
-        if (mTwoFingerQsExpandPossible && isOpenQsEvent(event) && event.getY(event.getActionIndex())
-                < mStatusBarMinHeight) {
-            mMetricsLogger.count(COUNTER_PANEL_OPEN_QS, 1);
-            setQsExpandImmediate(true);
-            setShowShelfOnly(true);
-            updateExpandedHeightToMaxHeight();
-
-            // Normally, we start listening when the panel is expanded, but here we need to start
-            // earlier so the state is already up to date when dragging down.
-            setListening(true);
-        }
-        return false;
+    float getDisplayDensity() {
+        return mCentralSurfaces.getDisplayDensity();
     }
 
-    /** Returns whether split shade is enabled and an x coordinate is outside of the QS frame. */
-    private boolean isSplitShadeAndTouchXOutsideQs(float touchX) {
-        return mSplitShadeEnabled && (touchX < mQsFrame.getX()
-                || touchX > mQsFrame.getX() + mQsFrame.getWidth());
-    }
-
-    private boolean isInQsArea(float x, float y) {
-        if (isSplitShadeAndTouchXOutsideQs(x)) {
-            return false;
-        }
-        // Let's reject anything at the very bottom around the home handle in gesture nav
-        if (mIsGestureNavigation && y > mView.getHeight() - mNavigationBarBottomHeight) {
-            return false;
-        }
-        return y <= mNotificationStackScrollLayoutController.getBottomMostNotificationBottom()
-                || y <= mQs.getView().getY() + mQs.getView().getHeight();
-    }
-
-    private boolean isOpenQsEvent(MotionEvent event) {
-        final int pointerCount = event.getPointerCount();
-        final int action = event.getActionMasked();
-
-        final boolean
-                twoFingerDrag =
-                action == MotionEvent.ACTION_POINTER_DOWN && pointerCount == 2;
-
-        final boolean
-                stylusButtonClickDrag =
-                action == MotionEvent.ACTION_DOWN && (event.isButtonPressed(
-                        MotionEvent.BUTTON_STYLUS_PRIMARY) || event.isButtonPressed(
-                        MotionEvent.BUTTON_STYLUS_SECONDARY));
-
-        final boolean
-                mouseButtonClickDrag =
-                action == MotionEvent.ACTION_DOWN && (event.isButtonPressed(
-                        MotionEvent.BUTTON_SECONDARY) || event.isButtonPressed(
-                        MotionEvent.BUTTON_TERTIARY));
-
-        return twoFingerDrag || stylusButtonClickDrag || mouseButtonClickDrag;
-    }
-
-    private void handleQsDown(MotionEvent event) {
-        if (event.getActionMasked() == MotionEvent.ACTION_DOWN && shouldQuickSettingsIntercept(
-                event.getX(), event.getY(), -1)) {
-            debugLog("handleQsDown");
-            mFalsingCollector.onQsDown();
-            mShadeLog.logMotionEvent(event, "handleQsDown: down action, QS tracking enabled");
-            mQsTracking = true;
-            onQsExpansionStarted();
-            mInitialHeightOnTouch = mQsExpansionHeight;
-            mInitialTouchY = event.getY();
-            mInitialTouchX = event.getX();
-
-            // If we interrupt an expansion gesture here, make sure to update the state correctly.
-            notifyExpandingFinished();
-        }
+    /** Return whether a touch is near the gesture handle at the bottom of screen */
+    public boolean isInGestureNavHomeHandleArea(float x, float y) {
+        return mIsGestureNavigation && y > mView.getHeight() - mNavigationBarBottomHeight;
     }
 
     /** Input focus transfer is about to happen. */
@@ -2659,7 +2141,7 @@
         }
 
         // If we are already running a QS expansion, make sure that we keep the panel open.
-        if (mQsExpansionAnimator != null) {
+        if (mQsController.isExpansionAnimating()) {
             expands = true;
         }
         return expands;
@@ -2673,124 +2155,9 @@
         return isFullyCollapsed() || mBarState != StatusBarState.SHADE;
     }
 
-    private void onQsTouch(MotionEvent event) {
-        int pointerIndex = event.findPointerIndex(mQsTrackingPointer);
-        if (pointerIndex < 0) {
-            pointerIndex = 0;
-            mQsTrackingPointer = event.getPointerId(pointerIndex);
-        }
-        final float y = event.getY(pointerIndex);
-        final float x = event.getX(pointerIndex);
-        final float h = y - mInitialTouchY;
-
-        switch (event.getActionMasked()) {
-            case MotionEvent.ACTION_DOWN:
-                mShadeLog.logMotionEvent(event, "onQsTouch: down action, QS tracking enabled");
-                mQsTracking = true;
-                traceQsJank(true /* startTracing */, false /* wasCancelled */);
-                mInitialTouchY = y;
-                mInitialTouchX = x;
-                onQsExpansionStarted();
-                mInitialHeightOnTouch = mQsExpansionHeight;
-                initVelocityTracker();
-                trackMovement(event);
-                break;
-
-            case MotionEvent.ACTION_POINTER_UP:
-                final int upPointer = event.getPointerId(event.getActionIndex());
-                if (mQsTrackingPointer == upPointer) {
-                    // gesture is ongoing, find a new pointer to track
-                    final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
-                    final float newY = event.getY(newIndex);
-                    final float newX = event.getX(newIndex);
-                    mQsTrackingPointer = event.getPointerId(newIndex);
-                    mInitialHeightOnTouch = mQsExpansionHeight;
-                    mInitialTouchY = newY;
-                    mInitialTouchX = newX;
-                }
-                break;
-
-            case MotionEvent.ACTION_MOVE:
-                debugLog("onQSTouch move");
-                mShadeLog.logMotionEvent(event, "onQsTouch: move action, setting QS expansion");
-                setQsExpansionHeight(h + mInitialHeightOnTouch);
-                if (h >= getFalsingThreshold()) {
-                    mQsTouchAboveFalsingThreshold = true;
-                }
-                trackMovement(event);
-                break;
-
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_CANCEL:
-                mShadeLog.logMotionEvent(event,
-                        "onQsTouch: up/cancel action, QS tracking disabled");
-                mQsTracking = false;
-                mQsTrackingPointer = -1;
-                trackMovement(event);
-                float fraction = computeQsExpansionFraction();
-                if (fraction != 0f || y >= mInitialTouchY) {
-                    flingQsWithCurrentVelocity(y,
-                            event.getActionMasked() == MotionEvent.ACTION_CANCEL);
-                } else {
-                    traceQsJank(false /* startTracing */,
-                            event.getActionMasked() == MotionEvent.ACTION_CANCEL);
-                }
-                if (mQsVelocityTracker != null) {
-                    mQsVelocityTracker.recycle();
-                    mQsVelocityTracker = null;
-                }
-                break;
-        }
-    }
-
-    private int getFalsingThreshold() {
+    int getFalsingThreshold() {
         float factor = mCentralSurfaces.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
-        return (int) (mQsFalsingThreshold * factor);
-    }
-
-    private void setOverScrolling(boolean overscrolling) {
-        mStackScrollerOverscrolling = overscrolling;
-        if (mQs == null) return;
-        mQs.setOverscrolling(overscrolling);
-    }
-
-    private void onQsExpansionStarted() {
-        cancelQsAnimation();
-        cancelHeightAnimator();
-
-        // Reset scroll position and apply that position to the expanded height.
-        float height = mQsExpansionHeight;
-        setQsExpansionHeight(height);
-        mNotificationStackScrollLayoutController.checkSnoozeLeavebehind();
-
-        // When expanding QS, let's authenticate the user if possible,
-        // this will speed up notification actions.
-        if (height == 0 && !mKeyguardStateController.canDismissLockScreen()) {
-            mUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.QS_EXPANDED);
-        }
-    }
-
-    @VisibleForTesting
-    void setQsExpanded(boolean expanded) {
-        boolean changed = mQsExpanded != expanded;
-        if (changed) {
-            mQsExpanded = expanded;
-            updateQsState();
-            updateExpandedHeightToMaxHeight();
-            setStatusAccessibilityImportance(expanded
-                    ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
-                    : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
-            updateSystemUiStateFlags();
-            NavigationBarView navigationBarView =
-                    mNavigationBarController.getNavigationBarView(mDisplayId);
-            if (navigationBarView != null) {
-                navigationBarView.onStatusBarPanelStateChanged();
-            }
-            mShadeExpansionStateManager.onQsExpansionChanged(expanded);
-            mShadeLog.logQsExpansionChanged("QS Expansion Changed.", expanded,
-                    mQsMinExpansionHeight, mQsMaxExpansionHeight, mStackScrollerOverscrolling,
-                    mDozing, mQsAnimatorExpand, mAnimatingQS);
-        }
+        return (int) (mQsController.getFalsingThreshold() * factor);
     }
 
     private void maybeAnimateBottomAreaAlpha() {
@@ -2822,402 +2189,29 @@
         }
     }
 
-    private void updateQsState() {
-        boolean qsFullScreen = mQsExpanded && !mSplitShadeEnabled;
-        mNotificationStackScrollLayoutController.setQsFullScreen(qsFullScreen);
-        mNotificationStackScrollLayoutController.setScrollingEnabled(
-                mBarState != KEYGUARD && (!qsFullScreen || mQsExpansionFromOverscroll));
-
-        if (mKeyguardUserSwitcherController != null && mQsExpanded
-                && !mStackScrollerOverscrolling) {
-            mKeyguardUserSwitcherController.closeSwitcherIfOpenAndNotSimple(true);
-        }
-        if (mQs == null) return;
-        mQs.setExpanded(mQsExpanded);
-    }
-
-    void setQsExpansionHeight(float height) {
-        height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight);
-        mQsFullyExpanded = height == mQsMaxExpansionHeight && mQsMaxExpansionHeight != 0;
-        boolean qsAnimatingAway = !mQsAnimatorExpand && mAnimatingQS;
-        if (height > mQsMinExpansionHeight && !mQsExpanded && !mStackScrollerOverscrolling
-                && !mDozing && !qsAnimatingAway) {
-            setQsExpanded(true);
-        } else if (height <= mQsMinExpansionHeight && mQsExpanded) {
-            setQsExpanded(false);
-        }
-        mQsExpansionHeight = height;
-        updateQsExpansion();
-        requestScrollerTopPaddingUpdate(false /* animate */);
-        mKeyguardStatusBarViewController.updateViewState();
-        if (mBarState == StatusBarState.SHADE_LOCKED || mBarState == KEYGUARD) {
-            updateKeyguardBottomAreaAlpha();
-            positionClockAndNotifications();
-        }
-
-        if (mAccessibilityManager.isEnabled()) {
-            mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
-        }
-
-        if (!mFalsingManager.isUnlockingDisabled() && mQsFullyExpanded
-                && mFalsingCollector.shouldEnforceBouncer()) {
-            mCentralSurfaces.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
-                    false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */);
-        }
-        if (DEBUG_DRAWABLE) {
-            mView.invalidate();
-        }
-    }
-
-    private void updateQsExpansion() {
-        if (mQs == null) return;
-        final float squishiness;
-        if ((mQsExpandImmediate || mQsExpanded) && !mSplitShadeEnabled) {
-            squishiness = 1;
-        } else if (mTransitioningToFullShadeProgress > 0.0f) {
-            squishiness = mLockscreenShadeTransitionController.getQsSquishTransitionFraction();
-        } else {
-            squishiness = mNotificationStackScrollLayoutController
-                    .getNotificationSquishinessFraction();
-        }
-        final float qsExpansionFraction = computeQsExpansionFraction();
-        final float adjustedExpansionFraction = mSplitShadeEnabled
-                ? 1f : computeQsExpansionFraction();
-        mQs.setQsExpansion(adjustedExpansionFraction, getExpandedFraction(), getHeaderTranslation(),
-                squishiness);
-        mMediaHierarchyManager.setQsExpansion(qsExpansionFraction);
-        int qsPanelBottomY = calculateQsBottomPosition(qsExpansionFraction);
-        mScrimController.setQsPosition(qsExpansionFraction, qsPanelBottomY);
-        setQSClippingBounds();
-
-        if (mSplitShadeEnabled) {
-            // In split shade we want to pretend that QS are always collapsed so their behaviour and
-            // interactions don't influence notifications as they do in portrait. But we want to set
-            // 0 explicitly in case we're rotating from non-split shade with QS expansion of 1.
-            mNotificationStackScrollLayoutController.setQsExpansionFraction(0);
-        } else {
-            mNotificationStackScrollLayoutController.setQsExpansionFraction(qsExpansionFraction);
-        }
-
-        mDepthController.setQsPanelExpansion(qsExpansionFraction);
-        mStatusBarKeyguardViewManager.setQsExpansion(qsExpansionFraction);
-
-        float shadeExpandedFraction = isOnKeyguard()
-                ? getLockscreenShadeDragProgress()
-                : getExpandedFraction();
-        mLargeScreenShadeHeaderController.setShadeExpandedFraction(shadeExpandedFraction);
-        mLargeScreenShadeHeaderController.setQsExpandedFraction(qsExpansionFraction);
-        mLargeScreenShadeHeaderController.setQsVisible(mQsVisible);
-    }
-
-    private float getLockscreenShadeDragProgress() {
+    /** */
+    public float getLockscreenShadeDragProgress() {
         // mTransitioningToFullShadeProgress > 0 means we're doing regular lockscreen to shade
         // transition. If that's not the case we should follow QS expansion fraction for when
         // user is pulling from the same top to go directly to expanded QS
-        return mTransitioningToFullShadeProgress > 0
+        return mQsController.getTransitioningToFullShadeProgress() > 0
                 ? mLockscreenShadeTransitionController.getQSDragProgress()
-                : computeQsExpansionFraction();
+                : mQsController.computeExpansionFraction();
     }
 
-    private void onStackYChanged(boolean shouldAnimate) {
-        if (mQs != null) {
-            if (shouldAnimate) {
-                animateNextNotificationBounds(StackStateAnimator.ANIMATION_DURATION_STANDARD,
-                        0 /* delay */);
-                mNotificationBoundsAnimationDelay = 0;
-            }
-            setQSClippingBounds();
-        }
-    }
-
-    private void onNotificationScrolled(int newScrollPosition) {
-        updateQSExpansionEnabledAmbient();
-    }
-
-    private void updateQSExpansionEnabledAmbient() {
-        final float scrollRangeToTop = mAmbientState.getTopPadding() - mQuickQsHeaderHeight;
-        mQsExpansionEnabledAmbient = mSplitShadeEnabled
-                || (mAmbientState.getScrollY() <= scrollRangeToTop);
-        setQsExpansionEnabled();
-    }
-
-    /**
-     * Updates scrim bounds, QS clipping, notifications clipping and keyguard status view clipping
-     * as well based on the bounds of the shade and QS state.
-     */
-    private void setQSClippingBounds() {
-        float qsExpansionFraction = computeQsExpansionFraction();
-        final int qsPanelBottomY = calculateQsBottomPosition(qsExpansionFraction);
-        final boolean qsVisible = (qsExpansionFraction > 0 || qsPanelBottomY > 0);
-        checkCorrectScrimVisibility(qsExpansionFraction);
-
-        int top = calculateTopQsClippingBound(qsPanelBottomY);
-        int bottom = calculateBottomQsClippingBound(top);
-        int left = calculateLeftQsClippingBound();
-        int right = calculateRightQsClippingBound();
-        // top should never be lower than bottom, otherwise it will be invisible.
-        top = Math.min(top, bottom);
-        applyQSClippingBounds(left, top, right, bottom, qsVisible);
-    }
-
-    private void checkCorrectScrimVisibility(float expansionFraction) {
-        // issues with scrims visible on keyguard occur only in split shade
-        if (mSplitShadeEnabled) {
-            boolean keyguardViewsVisible = mBarState == KEYGUARD && mKeyguardOnlyContentAlpha == 1;
-            // expansionFraction == 1 means scrims are fully visible as their size/visibility depend
-            // on QS expansion
-            if (expansionFraction == 1 && keyguardViewsVisible) {
-                Log.wtf(TAG,
-                        "Incorrect state, scrim is visible at the same time when clock is visible");
-            }
-        }
-    }
-
-    private int calculateTopQsClippingBound(int qsPanelBottomY) {
-        int top;
-        if (mSplitShadeEnabled) {
-            top = Math.min(qsPanelBottomY, mLargeScreenShadeHeaderHeight);
-        } else {
-            if (mTransitioningToFullShadeProgress > 0.0f) {
-                // If we're transitioning, let's use the actual value. The else case
-                // can be wrong during transitions when waiting for the keyguard to unlock
-                top = mTransitionToFullShadeQSPosition;
-            } else {
-                final float notificationTop = getQSEdgePosition();
-                if (isOnKeyguard()) {
-                    if (mKeyguardBypassController.getBypassEnabled()) {
-                        // When bypassing on the keyguard, let's use the panel bottom.
-                        // this should go away once we unify the stackY position and don't have
-                        // to do this min anymore below.
-                        top = qsPanelBottomY;
-                    } else {
-                        top = (int) Math.min(qsPanelBottomY, notificationTop);
-                    }
-                } else {
-                    top = (int) notificationTop;
-                }
-            }
-            top += mOverStretchAmount;
-            // Correction for instant expansion caused by HUN pull down/
-            if (mMinFraction > 0f && mMinFraction < 1f) {
-                float realFraction =
-                        (getExpandedFraction() - mMinFraction) / (1f - mMinFraction);
-                top *= MathUtils.saturate(realFraction / mMinFraction);
-            }
-        }
-        return top;
-    }
-
-    private int calculateBottomQsClippingBound(int top) {
-        if (mSplitShadeEnabled) {
-            return top + mNotificationStackScrollLayoutController.getHeight()
-                    + mSplitShadeNotificationsScrimMarginBottom;
-        } else {
-            return mView.getBottom();
-        }
-    }
-
-    private int calculateLeftQsClippingBound() {
-        if (mIsFullWidth) {
-            // left bounds can ignore insets, it should always reach the edge of the screen
-            return 0;
-        } else {
-            return mNotificationStackScrollLayoutController.getLeft() + mDisplayLeftInset;
-        }
-    }
-
-    private int calculateRightQsClippingBound() {
-        if (mIsFullWidth) {
-            return mView.getRight() + mDisplayRightInset;
-        } else {
-            return mNotificationStackScrollLayoutController.getRight() + mDisplayLeftInset;
-        }
-    }
-
-    /**
-     * Applies clipping to quick settings, notifications layout and
-     * updates bounds of the notifications background (notifications scrim).
-     *
-     * The parameters are bounds of the notifications area rectangle, this function
-     * calculates bounds for the QS clipping based on the notifications bounds.
-     */
-    private void applyQSClippingBounds(int left, int top, int right, int bottom,
-            boolean qsVisible) {
-        if (!mAnimateNextNotificationBounds || mLastQsClipBounds.isEmpty()) {
-            if (mQsClippingAnimation != null) {
-                // update the end position of the animator
-                mQsClippingAnimationEndBounds.set(left, top, right, bottom);
-            } else {
-                applyQSClippingImmediately(left, top, right, bottom, qsVisible);
-            }
-        } else {
-            mQsClippingAnimationEndBounds.set(left, top, right, bottom);
-            final int startLeft = mLastQsClipBounds.left;
-            final int startTop = mLastQsClipBounds.top;
-            final int startRight = mLastQsClipBounds.right;
-            final int startBottom = mLastQsClipBounds.bottom;
-            if (mQsClippingAnimation != null) {
-                mQsClippingAnimation.cancel();
-            }
-            mQsClippingAnimation = ValueAnimator.ofFloat(0.0f, 1.0f);
-            mQsClippingAnimation.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-            mQsClippingAnimation.setDuration(mNotificationBoundsAnimationDuration);
-            mQsClippingAnimation.setStartDelay(mNotificationBoundsAnimationDelay);
-            mQsClippingAnimation.addUpdateListener(animation -> {
-                float fraction = animation.getAnimatedFraction();
-                int animLeft = (int) MathUtils.lerp(startLeft,
-                        mQsClippingAnimationEndBounds.left, fraction);
-                int animTop = (int) MathUtils.lerp(startTop,
-                        mQsClippingAnimationEndBounds.top, fraction);
-                int animRight = (int) MathUtils.lerp(startRight,
-                        mQsClippingAnimationEndBounds.right, fraction);
-                int animBottom = (int) MathUtils.lerp(startBottom,
-                        mQsClippingAnimationEndBounds.bottom, fraction);
-                applyQSClippingImmediately(animLeft, animTop, animRight, animBottom,
-                        qsVisible /* qsVisible */);
-            });
-            mQsClippingAnimation.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mQsClippingAnimation = null;
-                    mIsQsTranslationResetAnimator = false;
-                    mIsPulseExpansionResetAnimator = false;
-                }
-            });
-            mQsClippingAnimation.start();
-        }
-        mAnimateNextNotificationBounds = false;
-        mNotificationBoundsAnimationDelay = 0;
-    }
-
-    private void applyQSClippingImmediately(int left, int top, int right, int bottom,
-            boolean qsVisible) {
-        int radius = mScrimCornerRadius;
-        boolean clipStatusView = false;
-        mLastQsClipBounds.set(left, top, right, bottom);
-        if (mIsFullWidth) {
-            clipStatusView = qsVisible;
-            float screenCornerRadius = mRecordingController.isRecording() ? 0 : mScreenCornerRadius;
-            radius = (int) MathUtils.lerp(screenCornerRadius, mScrimCornerRadius,
-                    Math.min(top / (float) mScrimCornerRadius, 1f));
-        }
-        if (mQs != null) {
-            float qsTranslation = 0;
-            boolean pulseExpanding = mPulseExpansionHandler.isExpanding();
-            if (mTransitioningToFullShadeProgress > 0.0f || pulseExpanding
-                    || (mQsClippingAnimation != null
-                    && (mIsQsTranslationResetAnimator || mIsPulseExpansionResetAnimator))) {
-                if (pulseExpanding || mIsPulseExpansionResetAnimator) {
-                    // qsTranslation should only be positive during pulse expansion because it's
-                    // already translating in from the top
-                    qsTranslation = Math.max(0, (top - mQs.getHeader().getHeight()) / 2.0f);
-                } else if (!mSplitShadeEnabled) {
-                    qsTranslation = (top - mQs.getHeader().getHeight()) * QS_PARALLAX_AMOUNT;
-                }
-            }
-            mQsTranslationForFullShadeTransition = qsTranslation;
-            updateQsFrameTranslation();
-            float currentTranslation = mQsFrame.getTranslationY();
-            mQsClipTop = mEnableQsClipping
-                    ? (int) (top - currentTranslation - mQsFrame.getTop()) : 0;
-            mQsClipBottom = mEnableQsClipping
-                    ? (int) (bottom - currentTranslation - mQsFrame.getTop()) : 0;
-            mQsVisible = qsVisible;
-            mQs.setQsVisible(mQsVisible);
-            mQs.setFancyClipping(
-                    mQsClipTop,
-                    mQsClipBottom,
-                    radius,
-                    qsVisible && !mSplitShadeEnabled);
-        }
-        // The padding on this area is large enough that we can use a cheaper clipping strategy
-        mKeyguardStatusViewController.setClipBounds(clipStatusView ? mLastQsClipBounds : null);
-        if (!qsVisible && mSplitShadeEnabled) {
-            // On the lockscreen when qs isn't visible, we don't want the bounds of the shade to
-            // be visible, otherwise you can see the bounds once swiping up to see bouncer
-            mScrimController.setNotificationsBounds(0, 0, 0, 0);
-        } else {
-            // Increase the height of the notifications scrim when not in split shade
-            // (e.g. portrait tablet) so the rounded corners are not visible at the bottom,
-            // in this case they are rendered off-screen
-            final int notificationsScrimBottom = mSplitShadeEnabled ? bottom : bottom + radius;
-            mScrimController.setNotificationsBounds(left, top, right, notificationsScrimBottom);
-        }
-
-        if (mSplitShadeEnabled) {
-            mKeyguardStatusBarViewController.setNoTopClipping();
-        } else {
-            mKeyguardStatusBarViewController.updateTopClipping(top);
-        }
-
-        mScrimController.setScrimCornerRadius(radius);
-
-        // Convert global clipping coordinates to local ones,
-        // relative to NotificationStackScrollLayout
-        int nsslLeft = calculateNsslLeft(left);
-        int nsslRight = calculateNsslRight(right);
-        int nsslTop = getNotificationsClippingTopBounds(top);
-        int nsslBottom = bottom - mNotificationStackScrollLayoutController.getTop();
-        int bottomRadius = mSplitShadeEnabled ? radius : 0;
-        int topRadius = mSplitShadeEnabled && mExpandingFromHeadsUp ? 0 : radius;
-        mNotificationStackScrollLayoutController.setRoundedClippingBounds(
-                nsslLeft, nsslTop, nsslRight, nsslBottom, topRadius, bottomRadius);
-    }
-
-    private int calculateNsslLeft(int nsslLeftAbsolute) {
-        int left = nsslLeftAbsolute - mNotificationStackScrollLayoutController.getLeft();
-        if (mIsFullWidth) {
-            return left;
-        }
-        return left - mDisplayLeftInset;
-    }
-
-    private int calculateNsslRight(int nsslRightAbsolute) {
-        int right = nsslRightAbsolute - mNotificationStackScrollLayoutController.getLeft();
-        if (mIsFullWidth) {
-            return right;
-        }
-        return right - mDisplayLeftInset;
-    }
-
-    private int getNotificationsClippingTopBounds(int qsTop) {
-        if (mSplitShadeEnabled && mExpandingFromHeadsUp) {
-            // in split shade nssl has extra top margin so clipping at top 0 is not enough, we need
-            // to set top clipping bound to negative value to allow HUN to go up to the top edge of
-            // the screen without clipping.
-            return -mAmbientState.getStackTopMargin();
-        } else {
-            return qsTop - mNotificationStackScrollLayoutController.getTop();
-        }
-    }
-
-    private float getQSEdgePosition() {
-        // TODO: replace StackY with unified calculation
-        return Math.max(mQuickQsHeaderHeight * mAmbientState.getExpansionFraction(),
-                mAmbientState.getStackY()
-                        // need to adjust for extra margin introduced by large screen shade header
-                        + mAmbientState.getStackTopMargin() * mAmbientState.getExpansionFraction()
-                        - mAmbientState.getScrollY());
-    }
-
-    private int calculateQsBottomPosition(float qsExpansionFraction) {
-        if (mTransitioningToFullShadeProgress > 0.0f) {
-            return mTransitionToFullShadeQSPosition;
-        } else {
-            int qsBottomYFrom = (int) getHeaderTranslation() + mQs.getQsMinExpansionHeight();
-            int expandedTopMargin = mUseLargeScreenShadeHeader ? mLargeScreenShadeHeaderHeight : 0;
-            int qsBottomYTo = mQs.getDesiredHeight() + expandedTopMargin;
-            return (int) MathUtils.lerp(qsBottomYFrom, qsBottomYTo, qsExpansionFraction);
-        }
-    }
-
-    private String determineAccessibilityPaneTitle() {
-        if (mQs != null && mQs.isCustomizing()) {
+    String determineAccessibilityPaneTitle() {
+        if (mQsController != null && mQsController.isCustomizing()) {
             return mResources.getString(R.string.accessibility_desc_quick_settings_edit);
-        } else if (mQsExpansionHeight != 0.0f && mQsFullyExpanded) {
+        } else if (mQsController != null && mQsController.getExpansionHeight() != 0.0f
+                && mQsController.getFullyExpanded()) {
             // Upon initialisation when we are not layouted yet we don't want to announce that we
             // are fully expanded, hence the != 0.0f check.
-            return mResources.getString(R.string.accessibility_desc_quick_settings);
+            if (mSplitShadeEnabled) {
+                // In split shade, QS is expanded but it also shows notifications
+                return mResources.getString(R.string.accessibility_desc_qs_notification_shade);
+            } else {
+                return mResources.getString(R.string.accessibility_desc_quick_settings);
+            }
         } else if (mBarState == KEYGUARD) {
             return mResources.getString(R.string.accessibility_desc_lock_screen);
         } else {
@@ -3225,42 +2219,27 @@
         }
     }
 
-    float calculateNotificationsTopPadding() {
-        if (mSplitShadeEnabled) {
-            return mKeyguardShowing ? getKeyguardNotificationStaticPadding() : 0;
+    /** Returns the topPadding of notifications when on keyguard not respecting QS expansion. */
+    public int getKeyguardNotificationStaticPadding() {
+        if (!getKeyguardShowing()) {
+            return 0;
         }
-        if (mKeyguardShowing && (mQsExpandImmediate
-                || mIsExpanding && mQsExpandedWhenExpandingStarted)) {
-
-            // Either QS pushes the notifications down when fully expanded, or QS is fully above the
-            // notifications (mostly on tablets). maxNotificationPadding denotes the normal top
-            // padding on Keyguard, maxQsPadding denotes the top padding from the quick settings
-            // panel. We need to take the maximum and linearly interpolate with the panel expansion
-            // for a nice motion.
-            int maxNotificationPadding = getKeyguardNotificationStaticPadding();
-            int maxQsPadding = mQsMaxExpansionHeight;
-            int max = mBarState == KEYGUARD ? Math.max(
-                    maxNotificationPadding, maxQsPadding) : maxQsPadding;
-            return (int) MathUtils.lerp((float) mQsMinExpansionHeight, (float) max,
-                    getExpandedFraction());
-        } else if (mQsSizeChangeAnimator != null) {
-            return Math.max(
-                    (int) mQsSizeChangeAnimator.getAnimatedValue(),
-                    getKeyguardNotificationStaticPadding());
-        } else if (mKeyguardShowing) {
-            // We can only do the smoother transition on Keyguard when we also are not collapsing
-            // from a scrolled quick settings.
-            return MathUtils.lerp((float) getKeyguardNotificationStaticPadding(),
-                    (float) (mQsMaxExpansionHeight),
-                    computeQsExpansionFraction());
+        if (!mKeyguardBypassController.getBypassEnabled()) {
+            return mClockPositionResult.stackScrollerPadding;
+        }
+        int collapsedPosition = mHeadsUpInset;
+        if (!mNotificationStackScrollLayoutController.isPulseExpanding()) {
+            return collapsedPosition;
         } else {
-            return mQsFrameTranslateController.getNotificationsTopPadding(mQsExpansionHeight,
-                    mNotificationStackScrollLayoutController);
+            int expandedPosition =
+                    mClockPositionResult.stackScrollerPadding;
+            return (int) MathUtils.lerp(collapsedPosition, expandedPosition,
+                    mNotificationStackScrollLayoutController.calculateAppearFractionBypass());
         }
     }
 
     public boolean getKeyguardShowing() {
-        return mKeyguardShowing;
+        return mBarState == KEYGUARD;
     }
 
     public float getKeyguardNotificationTopPadding() {
@@ -3271,94 +2250,18 @@
         return mKeyguardNotificationBottomPadding;
     }
 
-    /** Returns the topPadding of notifications when on keyguard not respecting QS expansion. */
-    private int getKeyguardNotificationStaticPadding() {
-        if (!mKeyguardShowing) {
-            return 0;
-        }
-        if (!mKeyguardBypassController.getBypassEnabled()) {
-            return mClockPositionResult.stackScrollerPadding;
-        }
-        int collapsedPosition = mHeadsUpInset;
-        if (!mNotificationStackScrollLayoutController.isPulseExpanding()) {
-            return collapsedPosition;
-        } else {
-            int expandedPosition = mClockPositionResult.stackScrollerPadding;
-            return (int) MathUtils.lerp(collapsedPosition, expandedPosition,
-                    mNotificationStackScrollLayoutController.calculateAppearFractionBypass());
-        }
-    }
-
-    private void requestScrollerTopPaddingUpdate(boolean animate) {
+    void requestScrollerTopPaddingUpdate(boolean animate) {
         mNotificationStackScrollLayoutController.updateTopPadding(
-                calculateNotificationsTopPadding(), animate);
-        if (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled()) {
+                mQsController.calculateNotificationsTopPadding(mIsExpanding,
+                        getKeyguardNotificationStaticPadding(), mExpandedFraction), animate);
+        if (getKeyguardShowing()
+                && mKeyguardBypassController.getBypassEnabled()) {
             // update the position of the header
-            updateQsExpansion();
+            mQsController.updateExpansion();
         }
     }
 
     /**
-     * Set the amount of pixels we have currently dragged down if we're transitioning to the full
-     * shade. 0.0f means we're not transitioning yet.
-     */
-    public void setTransitionToFullShadeAmount(float pxAmount, boolean animate, long delay) {
-        if (animate && mIsFullWidth) {
-            animateNextNotificationBounds(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE,
-                    delay);
-            mIsQsTranslationResetAnimator = mQsTranslationForFullShadeTransition > 0.0f;
-        }
-        float endPosition = 0;
-        if (pxAmount > 0.0f) {
-            if (mSplitShadeEnabled) {
-                float qsHeight = MathUtils.lerp(mQsMinExpansionHeight, mQsMaxExpansionHeight,
-                        mLockscreenShadeTransitionController.getQSDragProgress());
-                setQsExpansionHeight(qsHeight);
-            }
-            if (mNotificationStackScrollLayoutController.getVisibleNotificationCount() == 0
-                    && !mMediaDataManager.hasActiveMediaOrRecommendation()) {
-                // No notifications are visible, let's animate to the height of qs instead
-                if (mQs != null) {
-                    // Let's interpolate to the header height instead of the top padding,
-                    // because the toppadding is way too low because of the large clock.
-                    // we still want to take into account the edgePosition though as that nicely
-                    // overshoots in the stackscroller
-                    endPosition = getQSEdgePosition()
-                            - mNotificationStackScrollLayoutController.getTopPadding()
-                            + mQs.getHeader().getHeight();
-                }
-            } else {
-                // Interpolating to the new bottom edge position!
-                endPosition = getQSEdgePosition()
-                        + mNotificationStackScrollLayoutController.getFullShadeTransitionInset();
-                if (isOnKeyguard()) {
-                    endPosition -= mLockscreenNotificationQSPadding;
-                }
-            }
-        }
-
-        // Calculate the overshoot amount such that we're reaching the target after our desired
-        // distance, but only reach it fully once we drag a full shade length.
-        mTransitioningToFullShadeProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
-                MathUtils.saturate(pxAmount / mDistanceForQSFullShadeTransition));
-
-        int position = (int) MathUtils.lerp((float) 0, endPosition,
-                mTransitioningToFullShadeProgress);
-        if (mTransitioningToFullShadeProgress > 0.0f) {
-            // we want at least 1 pixel otherwise the panel won't be clipped
-            position = Math.max(1, position);
-        }
-        mTransitionToFullShadeQSPosition = position;
-        updateQsExpansion();
-    }
-
-    /** Called when pulse expansion has finished and this is going to the full shade. */
-    public void onPulseExpansionFinished() {
-        animateNextNotificationBounds(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE, 0);
-        mIsPulseExpansionResetAnimator = true;
-    }
-
-    /**
      * Set the alpha and translationY of the keyguard elements which only show on the lockscreen,
      * but not in shade locked / shade. This is used when dragging down to the full shade.
      */
@@ -3382,154 +2285,14 @@
         mKeyguardStatusBarViewController.setAlpha(alpha);
     }
 
-    private void trackMovement(MotionEvent event) {
-        if (mQsVelocityTracker != null) mQsVelocityTracker.addMovement(event);
-    }
-
-    private void initVelocityTracker() {
-        if (mQsVelocityTracker != null) {
-            mQsVelocityTracker.recycle();
-        }
-        mQsVelocityTracker = VelocityTracker.obtain();
-    }
-
-    private float getCurrentQSVelocity() {
-        if (mQsVelocityTracker == null) {
-            return 0;
-        }
-        mQsVelocityTracker.computeCurrentVelocity(1000);
-        return mQsVelocityTracker.getYVelocity();
-    }
-
-    private void cancelQsAnimation() {
-        if (mQsExpansionAnimator != null) {
-            mQsExpansionAnimator.cancel();
-        }
-    }
-
-    /** @see #flingSettings(float, int, Runnable, boolean) */
-    public void flingSettings(float vel, int type) {
-        flingSettings(vel, type, null /* onFinishRunnable */, false /* isClick */);
-    }
-
-    /**
-     * Animates QS or QQS as if the user had swiped up or down.
-     *
-     * @param vel              Finger velocity or 0 when not initiated by touch events.
-     * @param type             Either {@link #FLING_EXPAND}, {@link #FLING_COLLAPSE} or {@link
-     *                         #FLING_HIDE}.
-     * @param onFinishRunnable Runnable to be executed at the end of animation.
-     * @param isClick          If originated by click (different interpolator and duration.)
-     */
-    private void flingSettings(float vel, int type, final Runnable onFinishRunnable,
-            boolean isClick) {
-        float target;
-        switch (type) {
-            case FLING_EXPAND:
-                target = mQsMaxExpansionHeight;
-                break;
-            case FLING_COLLAPSE:
-                target = mQsMinExpansionHeight;
-                break;
-            case FLING_HIDE:
-            default:
-                if (mQs != null) {
-                    mQs.closeDetail();
-                }
-                target = 0;
-        }
-        if (target == mQsExpansionHeight) {
-            if (onFinishRunnable != null) {
-                onFinishRunnable.run();
-            }
-            traceQsJank(false /* startTracing */, type != FLING_EXPAND /* wasCancelled */);
-            return;
-        }
-
-        // If we move in the opposite direction, reset velocity and use a different duration.
-        boolean oppositeDirection = false;
-        boolean expanding = type == FLING_EXPAND;
-        if (vel > 0 && !expanding || vel < 0 && expanding) {
-            vel = 0;
-            oppositeDirection = true;
-        }
-        ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target);
-        if (isClick) {
-            animator.setInterpolator(Interpolators.TOUCH_RESPONSE);
-            animator.setDuration(368);
-        } else {
-            mFlingAnimationUtils.apply(animator, mQsExpansionHeight, target, vel);
-        }
-        if (oppositeDirection) {
-            animator.setDuration(350);
-        }
-        animator.addUpdateListener(
-                animation -> setQsExpansionHeight((Float) animation.getAnimatedValue()));
-        animator.addListener(new AnimatorListenerAdapter() {
-            private boolean mIsCanceled;
-
-            @Override
-            public void onAnimationStart(Animator animation) {
-                notifyExpandingStarted();
-            }
-
-            @Override
-            public void onAnimationCancel(Animator animation) {
-                mIsCanceled = true;
-            }
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mQSAnimatingHiddenFromCollapsed = false;
-                mAnimatingQS = false;
-                notifyExpandingFinished();
-                mNotificationStackScrollLayoutController.resetCheckSnoozeLeavebehind();
-                mQsExpansionAnimator = null;
-                if (onFinishRunnable != null) {
-                    onFinishRunnable.run();
-                }
-                traceQsJank(false /* startTracing */, mIsCanceled /* wasCancelled */);
-            }
-        });
-        // Let's note that we're animating QS. Moving the animator here will cancel it immediately,
-        // so we need a separate flag.
-        mAnimatingQS = true;
-        animator.start();
-        mQsExpansionAnimator = animator;
-        mQsAnimatorExpand = expanding;
-        mQSAnimatingHiddenFromCollapsed = computeQsExpansionFraction() == 0.0f && target == 0;
-    }
-
-    /**
-     * @return Whether we should intercept a gesture to open Quick Settings.
-     */
-    private boolean shouldQuickSettingsIntercept(float x, float y, float yDiff) {
-        if (!isQsExpansionEnabled() || mCollapsedOnDown
-                || (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled())
-                || mSplitShadeEnabled) {
-            return false;
-        }
-        View header = mKeyguardShowing || mQs == null ? mKeyguardStatusBar : mQs.getHeader();
-        int frameTop = mKeyguardShowing || mQs == null ? 0 : mQsFrame.getTop();
-        mQsInterceptRegion.set(
-                /* left= */ (int) mQsFrame.getX(),
-                /* top= */ header.getTop() + frameTop,
-                /* right= */ (int) mQsFrame.getX() + mQsFrame.getWidth(),
-                /* bottom= */ header.getBottom() + frameTop);
-        // Also allow QS to intercept if the touch is near the notch.
-        mStatusBarTouchableRegionManager.updateRegionForNotch(mQsInterceptRegion);
-        final boolean onHeader = mQsInterceptRegion.contains((int) x, (int) y);
-
-        if (mQsExpanded) {
-            return onHeader || (yDiff < 0 && isInQsArea(x, y));
-        } else {
-            return onHeader;
-        }
+    /** */
+    public float getKeyguardOnlyContentAlpha() {
+        return mKeyguardOnlyContentAlpha;
     }
 
     @VisibleForTesting
     boolean canCollapsePanelOnTouch() {
-        if (!isInSettings() && mBarState == KEYGUARD) {
+        if (!mQsController.getExpanded() && mBarState == KEYGUARD) {
             return true;
         }
 
@@ -3537,20 +2300,22 @@
             return true;
         }
 
-        return !mSplitShadeEnabled && (isInSettings() || mIsPanelCollapseOnQQS);
+        return !mSplitShadeEnabled && (mQsController.getExpanded() || mIsPanelCollapseOnQQS);
     }
 
     int getMaxPanelHeight() {
         int min = mStatusBarMinHeight;
         if (!(mBarState == KEYGUARD)
                 && mNotificationStackScrollLayoutController.getNotGoneChildCount() == 0) {
-            int minHeight = mQsMinExpansionHeight;
+            int minHeight = mQsController.getMinExpansionHeight();
             min = Math.max(min, minHeight);
         }
         int maxHeight;
-        if (mQsExpandImmediate || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted
+        if (mQsController.isExpandImmediate() || mQsController.getExpanded()
+                || mIsExpanding && mQsController.getExpandedWhenExpandingStarted()
                 || mPulsing || mSplitShadeEnabled) {
-            maxHeight = calculatePanelHeightQsExpanded();
+            maxHeight = mQsController.calculatePanelHeightExpanded(
+                    mClockPositionResult.stackScrollerPadding);
         } else {
             maxHeight = calculatePanelHeightShade();
         }
@@ -3558,17 +2323,15 @@
         if (maxHeight == 0) {
             Log.wtf(TAG, "maxPanelHeight is invalid. mOverExpansion: "
                     + mOverExpansion + ", calculatePanelHeightQsExpanded: "
-                    + calculatePanelHeightQsExpanded() + ", calculatePanelHeightShade: "
-                    + calculatePanelHeightShade() + ", mStatusBarMinHeight = "
-                    + mStatusBarMinHeight + ", mQsMinExpansionHeight = " + mQsMinExpansionHeight);
+                    + mQsController.calculatePanelHeightExpanded(
+                            mClockPositionResult.stackScrollerPadding)
+                    + ", calculatePanelHeightShade: " + calculatePanelHeightShade()
+                    + ", mStatusBarMinHeight = " + mStatusBarMinHeight
+                    + ", mQsMinExpansionHeight = " + mQsController.getMinExpansionHeight());
         }
         return maxHeight;
     }
 
-    public boolean isInSettings() {
-        return mQsExpanded;
-    }
-
     public boolean isExpanding() {
         return mIsExpanding;
     }
@@ -3581,7 +2344,8 @@
             mShadeLog.logExpansionChanged("onHeightUpdated: fully expanded.",
                     mExpandedFraction, isExpanded(), mTracking, mExpansionDragDownAmountPx);
         }
-        if (!mQsExpanded || mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted) {
+        if (!mQsController.getExpanded() || mQsController.isExpandImmediate()
+                || mIsExpanding && mQsController.getExpandedWhenExpandingStarted()) {
             // Updating the clock position will set the top padding which might
             // trigger a new panel height and re-position the clock.
             // This is a circular dependency and should be avoided, otherwise we'll have
@@ -3592,14 +2356,8 @@
                 positionClockAndNotifications();
             }
         }
-        // Below is true when QS are expanded and we swipe up from the same bottom of panel to
-        // close the whole shade with one motion. Also this will be always true when closing
-        // split shade as there QS are always expanded so every collapsing motion is motion from
-        // expanded QS to closed panel
-        boolean collapsingShadeFromExpandedQs = mQsExpanded && !mQsTracking
-                && mQsExpansionAnimator == null && !mQsExpansionFromOverscroll;
         boolean goingBetweenClosedShadeAndExpandedQs =
-                mQsExpandImmediate || collapsingShadeFromExpandedQs;
+                mQsController.isGoingBetweenClosedShadeAndExpandedQs();
         // in split shade we react when HUN is visible only if shade height is over HUN start
         // height - which means user is swiping down. Otherwise shade QS will either not show at all
         // with HUN movement or it will blink when touching HUN initially
@@ -3609,7 +2367,7 @@
             float qsExpansionFraction;
             if (mSplitShadeEnabled) {
                 qsExpansionFraction = 1;
-            } else if (mKeyguardShowing) {
+            } else if (getKeyguardShowing()) {
                 // On Keyguard, interpolate the QS expansion linearly to the panel expansion
                 qsExpansionFraction = expandedHeight / (getMaxPanelHeight());
             } else {
@@ -3618,13 +2376,15 @@
                 float panelHeightQsCollapsed =
                         mNotificationStackScrollLayoutController.getIntrinsicPadding()
                                 + mNotificationStackScrollLayoutController.getLayoutMinHeight();
-                float panelHeightQsExpanded = calculatePanelHeightQsExpanded();
+                float panelHeightQsExpanded = mQsController.calculatePanelHeightExpanded(
+                        mClockPositionResult.stackScrollerPadding);
                 qsExpansionFraction = (expandedHeight - panelHeightQsCollapsed)
                         / (panelHeightQsExpanded - panelHeightQsCollapsed);
             }
-            float targetHeight = mQsMinExpansionHeight
-                    + qsExpansionFraction * (mQsMaxExpansionHeight - mQsMinExpansionHeight);
-            setQsExpansionHeight(targetHeight);
+            float targetHeight = mQsController.getMinExpansionHeight() + qsExpansionFraction
+                    * (mQsController.getMaxExpansionHeight()
+                    - mQsController.getMinExpansionHeight());
+            mQsController.setExpansionHeight(targetHeight);
         }
         updateExpandedHeight(expandedHeight);
         updateHeader();
@@ -3641,8 +2401,8 @@
         if (mPanelExpanded != isExpanded) {
             mPanelExpanded = isExpanded;
             mShadeExpansionStateManager.onShadeExpansionFullyChanged(isExpanded);
-            if (!isExpanded && mQs != null && mQs.isCustomizing()) {
-                mQs.closeCustomizer();
+            if (!isExpanded) {
+                mQsController.closeQsCustomizer();
             }
         }
     }
@@ -3664,40 +2424,6 @@
         }
     }
 
-    int calculatePanelHeightQsExpanded() {
-        float
-                notificationHeight =
-                mNotificationStackScrollLayoutController.getHeight()
-                        - mNotificationStackScrollLayoutController.getEmptyBottomMargin()
-                        - mNotificationStackScrollLayoutController.getTopPadding();
-
-        // When only empty shade view is visible in QS collapsed state, simulate that we would have
-        // it in expanded QS state as well so we don't run into troubles when fading the view in/out
-        // and expanding/collapsing the whole panel from/to quick settings.
-        if (mNotificationStackScrollLayoutController.getNotGoneChildCount() == 0
-                && mNotificationStackScrollLayoutController.isShowingEmptyShadeView()) {
-            notificationHeight = mNotificationStackScrollLayoutController.getEmptyShadeViewHeight();
-        }
-        int maxQsHeight = mQsMaxExpansionHeight;
-
-        // If an animation is changing the size of the QS panel, take the animated value.
-        if (mQsSizeChangeAnimator != null) {
-            maxQsHeight = (int) mQsSizeChangeAnimator.getAnimatedValue();
-        }
-        float totalHeight = Math.max(maxQsHeight,
-                mBarState == KEYGUARD ? mClockPositionResult.stackScrollerPadding
-                        : 0) + notificationHeight
-                + mNotificationStackScrollLayoutController.getTopPaddingOverflow();
-        if (totalHeight > mNotificationStackScrollLayoutController.getHeight()) {
-            float
-                    fullyCollapsedHeight =
-                    maxQsHeight + mNotificationStackScrollLayoutController.getLayoutMinHeight();
-            totalHeight = Math.max(fullyCollapsedHeight,
-                    mNotificationStackScrollLayoutController.getHeight());
-        }
-        return (int) totalHeight;
-    }
-
     private void updateNotificationTranslucency() {
         if (mIsOcclusionTransitionRunning) {
             return;
@@ -3716,10 +2442,10 @@
 
     private float getFadeoutAlpha() {
         float alpha;
-        if (mQsMinExpansionHeight == 0) {
+        if (mQsController.getMinExpansionHeight() == 0) {
             return 1.0f;
         }
-        alpha = getExpandedHeight() / mQsMinExpansionHeight;
+        alpha = getExpandedHeight() / mQsController.getMinExpansionHeight();
         alpha = Math.max(0, Math.min(alpha, 1));
         alpha = (float) Math.pow(alpha, 0.75);
         return alpha;
@@ -3730,30 +2456,7 @@
         if (mBarState == KEYGUARD) {
             mKeyguardStatusBarViewController.updateViewState();
         }
-        updateQsExpansion();
-    }
-
-    private float getHeaderTranslation() {
-        if (mSplitShadeEnabled) {
-            // in split shade QS don't translate, just (un)squish and overshoot
-            return 0;
-        }
-        if (mBarState == KEYGUARD && !mKeyguardBypassController.getBypassEnabled()) {
-            return -mQs.getQsMinExpansionHeight();
-        }
-        float appearAmount = mNotificationStackScrollLayoutController
-                .calculateAppearFraction(mExpandedHeight);
-        float startHeight = -mQsExpansionHeight;
-        if (mBarState == StatusBarState.SHADE) {
-            // Small parallax as we pull down and clip QS
-            startHeight = -mQsExpansionHeight * QS_PARALLAX_AMOUNT;
-        }
-        if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()) {
-            appearAmount = mNotificationStackScrollLayoutController.calculateAppearFractionBypass();
-            startHeight = -mQs.getQsMinExpansionHeight();
-        }
-        float translation = MathUtils.lerp(startHeight, 0, Math.min(1.0f, appearAmount));
-        return Math.min(0, translation);
+        mQsController.updateExpansion();
     }
 
     private void updateKeyguardBottomAreaAlpha() {
@@ -3767,30 +2470,15 @@
         //   change due to "unlock hint animation." In this case, fading out the bottom area
         //   would also hide the message that says "swipe to unlock," we don't want to do that.
         float expansionAlpha = MathUtils.map(
-                isUnlockHintRunning() ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f,
+                isUnlockHintRunning() ? 0 : KeyguardBouncerConstants.ALPHA_EXPANSION_THRESHOLD, 1f,
+                0f, 1f,
                 getExpandedFraction());
-        float alpha = Math.min(expansionAlpha, 1 - computeQsExpansionFraction());
+        float alpha = Math.min(expansionAlpha, 1 - mQsController.computeExpansionFraction());
         alpha *= mBottomAreaShadeAlpha;
         mKeyguardBottomAreaInteractor.setAlpha(alpha);
         mLockIconViewController.setAlpha(alpha);
     }
 
-    private void onExpandingStarted() {
-        mNotificationStackScrollLayoutController.onExpansionStarted();
-        mIsExpanding = true;
-        mQsExpandedWhenExpandingStarted = mQsFullyExpanded;
-        mMediaHierarchyManager.setCollapsingShadeFromQS(mQsExpandedWhenExpandingStarted &&
-                /* We also start expanding when flinging closed Qs. Let's exclude that */
-                !mAnimatingQS);
-        if (mQsExpanded) {
-            onQsExpansionStarted();
-        }
-        // Since there are QS tiles in the header now, we need to make sure we start listening
-        // immediately so they can be up to date.
-        if (mQs == null) return;
-        mQs.setHeaderListening(true);
-    }
-
     private void onExpandingFinished() {
         if (!mUnocclusionTransitionFlagEnabled) {
             mScrimController.onExpandingFinished();
@@ -3800,7 +2488,7 @@
         mConversationNotificationManager.onNotificationPanelExpandStateChanged(isFullyCollapsed());
         mIsExpanding = false;
         mMediaHierarchyManager.setCollapsingShadeFromQS(false);
-        mMediaHierarchyManager.setQsExpanded(mQsExpanded);
+        mMediaHierarchyManager.setQsExpanded(mQsController.getExpanded());
         if (isFullyCollapsed()) {
             DejankUtils.postAfterTraversal(() -> setListening(false));
 
@@ -3815,10 +2503,10 @@
         if (mBarState != SHADE) {
             // updating qsExpandImmediate is done in onPanelStateChanged for unlocked shade but
             // on keyguard panel state is always OPEN so we need to have that extra update
-            setQsExpandImmediate(false);
+            mQsController.setExpandImmediate(false);
         }
         setShowShelfOnly(false);
-        mTwoFingerQsExpandPossible = false;
+        mQsController.setTwoFingerExpandPossible(false);
         updateTrackingHeadsUp(null);
         mExpandingFromHeadsUp = false;
         setPanelScrimMinFraction(0.0f);
@@ -3841,8 +2529,7 @@
 
     private void setListening(boolean listening) {
         mKeyguardStatusBarViewController.setBatteryListening(listening);
-        if (mQs == null) return;
-        mQs.setListening(listening);
+        mQsController.setListening(listening);
     }
 
     public void expand(boolean animate) {
@@ -3876,7 +2563,7 @@
                                         this);
                                 if (mAnimateAfterExpanding) {
                                     notifyExpandingStarted();
-                                    beginJankMonitoring();
+                                    mQsController.beginJankMonitoring(isFullyCollapsed());
                                     fling(0  /* expand */);
                                 } else {
                                     mShadeHeightLogger.logFunctionCall("expand");
@@ -3905,16 +2592,10 @@
         mOverExpansion = overExpansion;
         // Translating the quick settings by half the overexpansion to center it in the background
         // frame
-        updateQsFrameTranslation();
+        mQsController.updateQsFrameTranslation();
         mNotificationStackScrollLayoutController.setOverExpansion(overExpansion);
     }
 
-    private void updateQsFrameTranslation() {
-        mQsFrameTranslateController.translateQsFrame(mQsFrame, mQs,
-                mNavigationBarBottomHeight + mAmbientState.getStackTopMargin());
-
-    }
-
     private void falsingAdditionalTapRequired() {
         if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED) {
             mTapAgainViewController.show();
@@ -3941,8 +2622,8 @@
         notifyExpandingStarted();
         updatePanelExpansionAndVisibility();
         mScrimController.onTrackingStarted();
-        if (mQsFullyExpanded) {
-            setQsExpandImmediate(true);
+        if (mQsController.getFullyExpanded()) {
+            mQsController.setExpandImmediate(true);
             setShowShelfOnly(true);
         }
         mNotificationStackScrollLayoutController.onPanelTrackingStarted();
@@ -4019,8 +2700,8 @@
         // the required distance to be a specific and constant value, to make sure the expansion
         // motion has the expected speed. We also only want this on non-lockscreen for now.
         if (mSplitShadeEnabled && mBarState == SHADE) {
-            boolean transitionFromHeadsUp =
-                    mHeadsUpManager.isTrackingHeadsUp() || mExpandingFromHeadsUp;
+            boolean transitionFromHeadsUp = (mHeadsUpManager != null
+                    && mHeadsUpManager.isTrackingHeadsUp()) || mExpandingFromHeadsUp;
             // heads-up starting height is too close to mSplitShadeFullTransitionDistance and
             // when dragging HUN transition is already 90% complete. It makes shade become
             // immediately visible when starting to drag. We want to set distance so that
@@ -4038,25 +2719,6 @@
         }
     }
 
-    @VisibleForTesting
-    boolean isTrackingBlocked() {
-        return mConflictingQsExpansionGesture && mQsExpanded || mBlockingExpansionForCurrentTouch;
-    }
-
-    public boolean isQsExpanded() {
-        return mQsExpanded;
-    }
-
-    /** Returns whether the QS customizer is currently active. */
-    public boolean isQsCustomizing() {
-        return mQs.isCustomizing();
-    }
-
-    /** Close the QS customizer if it is open. */
-    public void closeQsCustomizer() {
-        mQs.closeCustomizer();
-    }
-
     public void setIsLaunchAnimationRunning(boolean running) {
         boolean wasRunning = mIsLaunchAnimationRunning;
         mIsLaunchAnimationRunning = running;
@@ -4081,14 +2743,6 @@
         }
     }
 
-    public void setQsScrimEnabled(boolean qsScrimEnabled) {
-        boolean changed = mQsScrimEnabled != qsScrimEnabled;
-        mQsScrimEnabled = qsScrimEnabled;
-        if (changed) {
-            updateQsState();
-        }
-    }
-
     public void onScreenTurningOn() {
         mKeyguardStatusViewController.dozeTimeTick();
     }
@@ -4105,7 +2759,7 @@
 
                     if (didFaceAuthRun) {
                         mUpdateMonitor.requestActiveUnlock(
-                                ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT,
+                                ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT,
                                 "lockScreenEmptySpaceTap");
                     } else {
                         mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_HINT,
@@ -4117,7 +2771,7 @@
                 }
                 break;
             case StatusBarState.SHADE_LOCKED:
-                if (!mQsExpanded) {
+                if (!mQsController.getExpanded()) {
                     mStatusBarStateController.setState(KEYGUARD);
                 }
                 break;
@@ -4266,66 +2920,6 @@
         return !mShowIconsWhenExpanded;
     }
 
-    private void onQsPanelScrollChanged(int scrollY) {
-        mLargeScreenShadeHeaderController.setQsScrollY(scrollY);
-        if (scrollY > 0 && !mQsFullyExpanded) {
-            debugLog("Scrolling while not expanded. Forcing expand");
-            // If we are scrolling QS, we should be fully expanded.
-            expandWithQs();
-        }
-    }
-
-    private final class QsFragmentListener implements FragmentListener {
-        @Override
-        public void onFragmentViewCreated(String tag, Fragment fragment) {
-            mQs = (QS) fragment;
-            mQs.setPanelView(mHeightListener);
-            mQs.setCollapseExpandAction(mCollapseExpandAction);
-            mQs.setHeaderClickable(isQsExpansionEnabled());
-            mQs.setOverscrolling(mStackScrollerOverscrolling);
-            mQs.setInSplitShade(mSplitShadeEnabled);
-            mQs.setIsNotificationPanelFullWidth(mIsFullWidth);
-
-            // recompute internal state when qspanel height changes
-            mQs.getView().addOnLayoutChangeListener(
-                    (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
-                        final int height = bottom - top;
-                        final int oldHeight = oldBottom - oldTop;
-                        if (height != oldHeight) {
-                            onQsHeightChanged();
-                        }
-                    });
-            mQs.setCollapsedMediaVisibilityChangedListener((visible) -> {
-                if (mQs.getHeader().isShown()) {
-                    animateNextNotificationBounds(StackStateAnimator.ANIMATION_DURATION_STANDARD,
-                            0 /* delay */);
-                    mNotificationStackScrollLayoutController.animateNextTopPaddingChange();
-                }
-            });
-            mLockscreenShadeTransitionController.setQS(mQs);
-            mShadeTransitionController.setQs(mQs);
-            mNotificationStackScrollLayoutController.setQsHeader((ViewGroup) mQs.getHeader());
-            mQs.setScrollListener(mQsScrollListener);
-            updateQsExpansion();
-        }
-
-        @Override
-        public void onFragmentViewDestroyed(String tag, Fragment fragment) {
-            // Manual handling of fragment lifecycle is only required because this bridges
-            // non-fragment and fragment code. Once we are using a fragment for the notification
-            // panel, mQs will not need to be null cause it will be tied to the same lifecycle.
-            if (fragment == mQs) {
-                mQs = null;
-            }
-        }
-    }
-
-    private void animateNextNotificationBounds(long duration, long delay) {
-        mAnimateNextNotificationBounds = true;
-        mNotificationBoundsAnimationDuration = duration;
-        mNotificationBoundsAnimationDelay = delay;
-    }
-
     public void setTouchAndAnimationDisabled(boolean disabled) {
         mTouchDisabled = disabled;
         if (mTouchDisabled) {
@@ -4348,9 +2942,11 @@
         if (dozing == mDozing) return;
         mView.setDozing(dozing);
         mDozing = dozing;
+        // TODO (b/) make listeners for this
         mNotificationStackScrollLayoutController.setDozing(mDozing, animate);
         mKeyguardBottomAreaInteractor.setAnimateDozingTransitions(animate);
         mKeyguardStatusBarViewController.setDozing(mDozing);
+        mQsController.setDozing(mDozing);
 
         if (dozing) {
             mBottomAreaShadeAlphaAnimator.cancel();
@@ -4541,33 +3137,13 @@
         ipw.print("mMaxAllowedKeyguardNotifications=");
         ipw.println(mMaxAllowedKeyguardNotifications);
         ipw.print("mAnimateNextPositionUpdate="); ipw.println(mAnimateNextPositionUpdate);
-        ipw.print("mQuickQsHeaderHeight="); ipw.println(mQuickQsHeaderHeight);
-        ipw.print("mQsTrackingPointer="); ipw.println(mQsTrackingPointer);
-        ipw.print("mQsTracking="); ipw.println(mQsTracking);
-        ipw.print("mConflictingQsExpansionGesture="); ipw.println(mConflictingQsExpansionGesture);
         ipw.print("mPanelExpanded="); ipw.println(mPanelExpanded);
-        ipw.print("mQsExpanded="); ipw.println(mQsExpanded);
-        ipw.print("mQsExpandedWhenExpandingStarted="); ipw.println(mQsExpandedWhenExpandingStarted);
-        ipw.print("mQsFullyExpanded="); ipw.println(mQsFullyExpanded);
-        ipw.print("mKeyguardShowing="); ipw.println(mKeyguardShowing);
         ipw.print("mKeyguardQsUserSwitchEnabled="); ipw.println(mKeyguardQsUserSwitchEnabled);
         ipw.print("mKeyguardUserSwitcherEnabled="); ipw.println(mKeyguardUserSwitcherEnabled);
         ipw.print("mDozing="); ipw.println(mDozing);
         ipw.print("mDozingOnDown="); ipw.println(mDozingOnDown);
         ipw.print("mBouncerShowing="); ipw.println(mBouncerShowing);
         ipw.print("mBarState="); ipw.println(mBarState);
-        ipw.print("mInitialHeightOnTouch="); ipw.println(mInitialHeightOnTouch);
-        ipw.print("mInitialTouchX="); ipw.println(mInitialTouchX);
-        ipw.print("mInitialTouchY="); ipw.println(mInitialTouchY);
-        ipw.print("mQsExpansionHeight="); ipw.println(mQsExpansionHeight);
-        ipw.print("mQsMinExpansionHeight="); ipw.println(mQsMinExpansionHeight);
-        ipw.print("mQsMaxExpansionHeight="); ipw.println(mQsMaxExpansionHeight);
-        ipw.print("mQsPeekHeight="); ipw.println(mQsPeekHeight);
-        ipw.print("mStackScrollerOverscrolling="); ipw.println(mStackScrollerOverscrolling);
-        ipw.print("mQsExpansionFromOverscroll="); ipw.println(mQsExpansionFromOverscroll);
-        ipw.print("mLastOverscroll="); ipw.println(mLastOverscroll);
-        ipw.print("mQsExpansionEnabledPolicy="); ipw.println(mQsExpansionEnabledPolicy);
-        ipw.print("mQsExpansionEnabledAmbient="); ipw.println(mQsExpansionEnabledAmbient);
         ipw.print("mStatusBarMinHeight="); ipw.println(mStatusBarMinHeight);
         ipw.print("mStatusBarHeaderHeightKeyguard="); ipw.println(mStatusBarHeaderHeightKeyguard);
         ipw.print("mOverStretchAmount="); ipw.println(mOverStretchAmount);
@@ -4576,17 +3152,8 @@
         ipw.print("mDisplayTopInset="); ipw.println(mDisplayTopInset);
         ipw.print("mDisplayRightInset="); ipw.println(mDisplayRightInset);
         ipw.print("mDisplayLeftInset="); ipw.println(mDisplayLeftInset);
-        ipw.print("mLargeScreenShadeHeaderHeight="); ipw.println(mLargeScreenShadeHeaderHeight);
-        ipw.print("mSplitShadeNotificationsScrimMarginBottom=");
-        ipw.println(mSplitShadeNotificationsScrimMarginBottom);
         ipw.print("mIsExpanding="); ipw.println(mIsExpanding);
-        ipw.print("mQsExpandImmediate="); ipw.println(mQsExpandImmediate);
-        ipw.print("mTwoFingerQsExpandPossible="); ipw.println(mTwoFingerQsExpandPossible);
         ipw.print("mHeaderDebugInfo="); ipw.println(mHeaderDebugInfo);
-        ipw.print("mQsAnimatorExpand="); ipw.println(mQsAnimatorExpand);
-        ipw.print("mQsScrimEnabled="); ipw.println(mQsScrimEnabled);
-        ipw.print("mQsTouchAboveFalsingThreshold="); ipw.println(mQsTouchAboveFalsingThreshold);
-        ipw.print("mQsFalsingThreshold="); ipw.println(mQsFalsingThreshold);
         ipw.print("mHeadsUpStartHeight="); ipw.println(mHeadsUpStartHeight);
         ipw.print("mListenForHeadsUp="); ipw.println(mListenForHeadsUp);
         ipw.print("mNavigationBarBottomHeight="); ipw.println(mNavigationBarBottomHeight);
@@ -4612,40 +3179,14 @@
         ipw.print("mHeadsUpInset="); ipw.println(mHeadsUpInset);
         ipw.print("mHeadsUpPinnedMode="); ipw.println(mHeadsUpPinnedMode);
         ipw.print("mAllowExpandForSmallExpansion="); ipw.println(mAllowExpandForSmallExpansion);
-        ipw.print("mLockscreenNotificationQSPadding=");
-        ipw.println(mLockscreenNotificationQSPadding);
-        ipw.print("mTransitioningToFullShadeProgress=");
-        ipw.println(mTransitioningToFullShadeProgress);
-        ipw.print("mTransitionToFullShadeQSPosition=");
-        ipw.println(mTransitionToFullShadeQSPosition);
-        ipw.print("mDistanceForQSFullShadeTransition=");
-        ipw.println(mDistanceForQSFullShadeTransition);
-        ipw.print("mQsTranslationForFullShadeTransition=");
-        ipw.println(mQsTranslationForFullShadeTransition);
         ipw.print("mMaxOverscrollAmountForPulse="); ipw.println(mMaxOverscrollAmountForPulse);
-        ipw.print("mAnimateNextNotificationBounds="); ipw.println(mAnimateNextNotificationBounds);
-        ipw.print("mNotificationBoundsAnimationDelay=");
-        ipw.println(mNotificationBoundsAnimationDelay);
-        ipw.print("mNotificationBoundsAnimationDuration=");
-        ipw.println(mNotificationBoundsAnimationDuration);
         ipw.print("mIsPanelCollapseOnQQS="); ipw.println(mIsPanelCollapseOnQQS);
-        ipw.print("mAnimatingQS="); ipw.println(mAnimatingQS);
-        ipw.print("mIsQsTranslationResetAnimator="); ipw.println(mIsQsTranslationResetAnimator);
-        ipw.print("mIsPulseExpansionResetAnimator="); ipw.println(mIsPulseExpansionResetAnimator);
         ipw.print("mKeyguardOnlyContentAlpha="); ipw.println(mKeyguardOnlyContentAlpha);
         ipw.print("mKeyguardOnlyTransitionTranslationY=");
         ipw.println(mKeyguardOnlyTransitionTranslationY);
         ipw.print("mUdfpsMaxYBurnInOffset="); ipw.println(mUdfpsMaxYBurnInOffset);
         ipw.print("mIsGestureNavigation="); ipw.println(mIsGestureNavigation);
         ipw.print("mOldLayoutDirection="); ipw.println(mOldLayoutDirection);
-        ipw.print("mScrimCornerRadius="); ipw.println(mScrimCornerRadius);
-        ipw.print("mScreenCornerRadius="); ipw.println(mScreenCornerRadius);
-        ipw.print("mQSAnimatingHiddenFromCollapsed="); ipw.println(mQSAnimatingHiddenFromCollapsed);
-        ipw.print("mUseLargeScreenShadeHeader="); ipw.println(mUseLargeScreenShadeHeader);
-        ipw.print("mEnableQsClipping="); ipw.println(mEnableQsClipping);
-        ipw.print("mQsClipTop="); ipw.println(mQsClipTop);
-        ipw.print("mQsClipBottom="); ipw.println(mQsClipBottom);
-        ipw.print("mQsVisible="); ipw.println(mQsVisible);
         ipw.print("mMinFraction="); ipw.println(mMinFraction);
         ipw.print("mStatusViewCentered="); ipw.println(mStatusViewCentered);
         ipw.print("mSplitShadeFullTransitionDistance=");
@@ -4830,12 +3371,12 @@
     public void updateSystemUiStateFlags() {
         if (SysUiState.DEBUG) {
             Log.d(TAG, "Updating panel sysui state flags: fullyExpanded="
-                    + isFullyExpanded() + " inQs=" + isInSettings());
+                    + isFullyExpanded() + " inQs=" + mQsController.getExpanded());
         }
         mSysUiState.setFlag(SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED,
-                        isFullyExpanded() && !isInSettings())
-                .setFlag(SYSUI_STATE_QUICK_SETTINGS_EXPANDED, isFullyExpanded() && isInSettings())
-                .commitUpdate(mDisplayId);
+                        isFullyExpanded() && !mQsController.getExpanded())
+                .setFlag(SYSUI_STATE_QUICK_SETTINGS_EXPANDED,
+                        isFullyExpanded() && mQsController.getExpanded()).commitUpdate(mDisplayId);
     }
 
     private void debugLog(String fmt, Object... args) {
@@ -4848,11 +3389,11 @@
     void notifyExpandingStarted() {
         if (!mExpanding) {
             mExpanding = true;
-            onExpandingStarted();
+            mIsExpanding = true;
+            mQsController.onExpandingStarted(mQsController.getFullyExpanded());
         }
     }
 
-    @VisibleForTesting
     void notifyExpandingFinished() {
         endClosing();
         if (mExpanding) {
@@ -4861,7 +3402,7 @@
         }
     }
 
-    private float getTouchSlop(MotionEvent event) {
+    float getTouchSlop(MotionEvent event) {
         // Adjust the touch slop if another gesture may be being performed.
         return event.getClassification() == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE
                 ? mTouchSlop * mSlopMultiplier
@@ -4935,11 +3476,15 @@
     public void startExpandMotion(float newX, float newY, boolean startTracking,
             float expandedHeight) {
         if (!mHandlingPointerUp && !mStatusBarStateController.isDozing()) {
-            beginJankMonitoring();
+            mQsController.beginJankMonitoring(isFullyCollapsed());
         }
         mInitialOffsetOnTouch = expandedHeight;
-        mInitialExpandY = newY;
-        mInitialExpandX = newX;
+        if (!mTracking || isFullyCollapsed()) {
+            mInitialExpandY = newY;
+            mInitialExpandX = newX;
+        } else {
+            mShadeLog.d("not setting mInitialExpandY in startExpandMotion");
+        }
         mInitialTouchFromKeyguard = mKeyguardStateController.isShowing();
         if (startTracking) {
             mTouchSlopExceeded = true;
@@ -5093,7 +3638,8 @@
         setExpandedHeightInternal(height);
     }
 
-    private void updateExpandedHeightToMaxHeight() {
+    /** Try to set expanded height to max. */
+    void updateExpandedHeightToMaxHeight() {
         float currentMaxPanelHeight = getMaxPanelHeight();
 
         if (isFullyCollapsed()) {
@@ -5104,7 +3650,8 @@
             return;
         }
 
-        if (mTracking && !isTrackingBlocked()) {
+        if (mTracking && !(mBlockingExpansionForCurrentTouch
+                || mQsController.isTrackingBlocked())) {
             return;
         }
 
@@ -5146,6 +3693,7 @@
                     mHeightAnimator.end();
                 }
             }
+            mQsController.setShadeExpandedHeight(mExpandedHeight);
             mExpansionDragDownAmountPx = h;
             mExpandedFraction = Math.min(1f,
                     maxPanelHeight == 0 ? 0 : mExpandedHeight / maxPanelHeight);
@@ -5185,7 +3733,7 @@
         return mExpandedHeight;
     }
 
-    private float getExpandedFraction() {
+    float getExpandedFraction() {
         return mExpandedFraction;
     }
 
@@ -5216,7 +3764,7 @@
             return true;
         } else {
             // case of two finger swipe from the top of keyguard
-            return computeQsExpansionFraction() == 1;
+            return mQsController.computeExpansionFraction() == 1;
         }
     }
 
@@ -5314,7 +3862,7 @@
     }
 
     /** Returns whether a shade or QS expansion animation is running */
-    private boolean isShadeOrQsHeightAnimationRunning() {
+    public boolean isShadeOrQsHeightAnimationRunning() {
         return mHeightAnimator != null && !mHintAnimationRunning && !mIsSpringBackAnimation;
     }
 
@@ -5449,44 +3997,127 @@
         return mView.isEnabled();
     }
 
-    private void beginJankMonitoring() {
-        if (mInteractionJankMonitor == null) {
-            return;
-        }
-        InteractionJankMonitor.Configuration.Builder builder =
-                InteractionJankMonitor.Configuration.Builder.withView(
-                                InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE,
-                                mView)
-                        .setTag(isFullyCollapsed() ? "Expand" : "Collapse");
-        mInteractionJankMonitor.begin(builder);
+    int getDisplayRightInset() {
+        return mDisplayRightInset;
     }
 
-    private void endJankMonitoring() {
-        if (mInteractionJankMonitor == null) {
-            return;
-        }
-        InteractionJankMonitor.getInstance().end(
-                InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+    int getDisplayLeftInset() {
+        return mDisplayLeftInset;
     }
 
-    private void cancelJankMonitoring() {
-        if (mInteractionJankMonitor == null) {
-            return;
-        }
-        InteractionJankMonitor.getInstance().cancel(
-                InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+    float getOverStretchAmount() {
+        return mOverStretchAmount;
+    }
+
+    float getMinFraction() {
+        return mMinFraction;
+    }
+
+    boolean getCollapsedOnDown() {
+        return mCollapsedOnDown;
+    }
+
+    int getNavigationBarBottomHeight() {
+        return mNavigationBarBottomHeight;
+    }
+
+    boolean isExpandingFromHeadsUp() {
+        return mExpandingFromHeadsUp;
+    }
+
+    /** TODO: remove need for this delegate (b/254870148) */
+    public void closeQs() {
+        mQsController.closeQs();
+    }
+
+    /** TODO: remove need for this delegate (b/254870148) */
+    public void setQsScrimEnabled(boolean qsScrimEnabled) {
+        mQsController.setScrimEnabled(qsScrimEnabled);
     }
 
     private ShadeExpansionStateManager getShadeExpansionStateManager() {
         return mShadeExpansionStateManager;
     }
 
+    private void onQsExpansionChanged(boolean expanded) {
+        updateExpandedHeightToMaxHeight();
+        setStatusAccessibilityImportance(expanded
+                ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+                : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+        updateSystemUiStateFlags();
+        NavigationBarView navigationBarView =
+                mNavigationBarController.getNavigationBarView(mDisplayId);
+        if (navigationBarView != null) {
+            navigationBarView.onStatusBarPanelStateChanged();
+        }
+    }
+
+    @VisibleForTesting
+    void onQsSetExpansionHeightCalled(boolean qsFullyExpanded) {
+        requestScrollerTopPaddingUpdate(false);
+        mKeyguardStatusBarViewController.updateViewState();
+        int barState = getBarState();
+        if (barState == SHADE_LOCKED || barState == KEYGUARD) {
+            updateKeyguardBottomAreaAlpha();
+            positionClockAndNotifications();
+        }
+
+        if (mAccessibilityManager.isEnabled()) {
+            mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
+        }
+
+        if (!mFalsingManager.isUnlockingDisabled() && qsFullyExpanded
+                && mFalsingCollector.shouldEnforceBouncer()) {
+            mCentralSurfaces.executeRunnableDismissingKeyguard(null, null,
+                    false, true, false);
+        }
+        if (DEBUG_DRAWABLE) {
+            mView.invalidate();
+        }
+    }
+
+    private void onQsStateUpdated(boolean qsExpanded, boolean isStackScrollerOverscrolling) {
+        if (mKeyguardUserSwitcherController != null && qsExpanded
+                && !isStackScrollerOverscrolling) {
+            mKeyguardUserSwitcherController.closeSwitcherIfOpenAndNotSimple(true);
+        }
+    }
+
+    private void onQsClippingImmediatelyApplied(boolean clipStatusView,
+            Rect lastQsClipBounds, int top, boolean qsFragmentCreated, boolean qsVisible) {
+        if (qsFragmentCreated) {
+            mKeyguardInteractor.setQuickSettingsVisible(qsVisible);
+        }
+
+        // The padding on this area is large enough that
+        // we can use a cheaper clipping strategy
+        mKeyguardStatusViewController.setClipBounds(
+                clipStatusView ? lastQsClipBounds : null);
+        if (mSplitShadeEnabled) {
+            mKeyguardStatusBarViewController.setNoTopClipping();
+        } else {
+            mKeyguardStatusBarViewController.updateTopClipping(top);
+        }
+    }
+
+    private void onFlingQsWithoutClick(ValueAnimator animator, float qsExpansionHeight,
+            float target, float vel) {
+        mFlingAnimationUtils.apply(animator, qsExpansionHeight, target, vel);
+    }
+
+    private void onExpansionHeightSetToMax(boolean requestPaddingUpdate) {
+        if (requestPaddingUpdate) {
+            requestScrollerTopPaddingUpdate(false /* animate */);
+        }
+        updateExpandedHeightToMaxHeight();
+    }
+
     private final class NsslHeightChangedListener implements
             ExpandableView.OnHeightChangedListener {
         @Override
         public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
             // Block update if we are in QS and just the top padding changed (i.e. view == null).
-            if (view == null && mQsExpanded) {
+            if (view == null && mQsController.getExpanded()) {
                 return;
             }
             if (needsAnimation && mInterpolatedDarkAmount == 0) {
@@ -5502,7 +4133,7 @@
                     == firstRow))) {
                 requestScrollerTopPaddingUpdate(false /* animate */);
             }
-            if (mKeyguardShowing) {
+            if (getKeyguardShowing()) {
                 updateMaxDisplayedNotifications(true);
             }
             updateExpandedHeightToMaxHeight();
@@ -5512,62 +4143,6 @@
         public void onReset(ExpandableView view) {}
     }
 
-    private void collapseOrExpand() {
-        onQsExpansionStarted();
-        if (mQsExpanded) {
-            flingSettings(0 /* vel */, FLING_COLLAPSE, null /* onFinishRunnable */,
-                    true /* isClick */);
-        } else if (isQsExpansionEnabled()) {
-            mLockscreenGestureLogger.write(MetricsEvent.ACTION_SHADE_QS_TAP, 0, 0);
-            flingSettings(0 /* vel */, FLING_EXPAND, null /* onFinishRunnable */,
-                    true /* isClick */);
-        }
-    }
-
-    private final class NsslOverscrollTopChangedListener implements
-            NotificationStackScrollLayout.OnOverscrollTopChangedListener {
-        @Override
-        public void onOverscrollTopChanged(float amount, boolean isRubberbanded) {
-            // When in split shade, overscroll shouldn't carry through to QS
-            if (mSplitShadeEnabled) {
-                return;
-            }
-            cancelQsAnimation();
-            if (!isQsExpansionEnabled()) {
-                amount = 0f;
-            }
-            float rounded = amount >= 1f ? amount : 0f;
-            setOverScrolling(rounded != 0f && isRubberbanded);
-            mQsExpansionFromOverscroll = rounded != 0f;
-            mLastOverscroll = rounded;
-            updateQsState();
-            setQsExpansionHeight(mQsMinExpansionHeight + rounded);
-        }
-
-        @Override
-        public void flingTopOverscroll(float velocity, boolean open) {
-            // in split shade touches affect QS only when touch happens within QS
-            if (isSplitShadeAndTouchXOutsideQs(mInitialTouchX)) {
-                return;
-            }
-            mLastOverscroll = 0f;
-            mQsExpansionFromOverscroll = false;
-            if (open) {
-                // During overscrolling, qsExpansion doesn't actually change that the qs is
-                // becoming expanded. Any layout could therefore reset the position again. Let's
-                // make sure we can expand
-                setOverScrolling(false);
-            }
-            setQsExpansionHeight(mQsExpansionHeight);
-            boolean canExpand = isQsExpansionEnabled();
-            flingSettings(!canExpand && open ? 0f : velocity,
-                    open && canExpand ? FLING_EXPAND : FLING_COLLAPSE, () -> {
-                        setOverScrolling(false);
-                        updateQsState();
-                    }, false /* isClick */);
-        }
-    }
-
     private void onDynamicPrivacyChanged() {
         // Do not request animation when pulsing or waking up, otherwise the clock will be out
         // of sync with the notification panel.
@@ -5617,19 +4192,6 @@
         }
     }
 
-    private void onQsHeightChanged() {
-        mQsMaxExpansionHeight = mQs != null ? mQs.getDesiredHeight() : 0;
-        if (mQsExpanded && mQsFullyExpanded) {
-            mQsExpansionHeight = mQsMaxExpansionHeight;
-            requestScrollerTopPaddingUpdate(false /* animate */);
-            updateExpandedHeightToMaxHeight();
-        }
-        if (mAccessibilityManager.isEnabled()) {
-            mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
-        }
-        mNotificationStackScrollLayoutController.setMaxTopPadding(mQsMaxExpansionHeight);
-    }
-
     private final class ConfigurationListener implements
             ConfigurationController.ConfigurationListener {
         @Override
@@ -5705,8 +4267,9 @@
 
             setKeyguardBottomAreaVisibility(statusBarState, goingToFullShade);
 
+            // TODO: maybe add a listener for barstate
             mBarState = statusBarState;
-            mKeyguardShowing = keyguardShowing;
+            mQsController.setBarState(statusBarState);
 
             boolean fromShadeToKeyguard = statusBarState == KEYGUARD
                     && (oldState == SHADE || oldState == SHADE_LOCKED);
@@ -5714,7 +4277,7 @@
                 // user can go to keyguard from different shade states and closing animation
                 // may not fully run - we always want to make sure we close QS when that happens
                 // as we never need QS open in fresh keyguard state
-                closeQs();
+                mQsController.closeQs();
             }
 
             if (oldState == KEYGUARD && (goingToFullShade
@@ -5730,7 +4293,7 @@
                     duration = StackStateAnimator.ANIMATION_DURATION_STANDARD;
                 }
                 mKeyguardStatusBarViewController.animateKeyguardStatusBarOut(startDelay, duration);
-                updateQSMinHeight();
+                mQsController.updateMinHeight();
             } else if (oldState == StatusBarState.SHADE_LOCKED
                     && statusBarState == KEYGUARD) {
                 mKeyguardStatusBarViewController.animateKeyguardStatusBarIn();
@@ -5758,9 +4321,7 @@
                             keyguardShowing ? View.VISIBLE : View.INVISIBLE);
                 }
                 if (keyguardShowing && oldState != mBarState) {
-                    if (mQs != null) {
-                        mQs.hideImmediately();
-                    }
+                    mQsController.hideQsImmediately();
                 }
             }
             mKeyguardStatusBarViewController.updateForHeadsUp();
@@ -5772,7 +4333,7 @@
             // The update needs to happen after the headerSlide in above, otherwise the translation
             // would reset
             maybeAnimateBottomAreaAlpha();
-            updateQsState();
+            mQsController.updateQsState();
         }
 
         @Override
@@ -5849,7 +4410,7 @@
         @Override
         public void onViewAttachedToWindow(View v) {
             mFragmentService.getFragmentHostManager(mView)
-                    .addTagListener(QS.TAG, mQsFragmentListener);
+                    .addTagListener(QS.TAG, mQsController.getQsFragmentListener());
             mStatusBarStateController.addCallback(mStatusBarStateListener);
             mStatusBarStateListener.onStateChanged(mStatusBarStateController.getState());
             mConfigurationController.addCallback(mConfigurationListener);
@@ -5866,7 +4427,7 @@
         public void onViewDetachedFromWindow(View v) {
             mContentResolver.unregisterContentObserver(mSettingsChangeObserver);
             mFragmentService.getFragmentHostManager(mView)
-                    .removeTagListener(QS.TAG, mQsFragmentListener);
+                    .removeTagListener(QS.TAG, mQsController.getQsFragmentListener());
             mStatusBarStateController.removeCallback(mStatusBarStateListener);
             mConfigurationController.removeCallback(mConfigurationListener);
             mFalsingManager.removeTapListener(mFalsingTapListener);
@@ -5891,28 +4452,9 @@
             // Update Clock Pivot (used by anti-burnin transformations)
             mKeyguardStatusViewController.updatePivot(mView.getWidth(), mView.getHeight());
 
-            // Calculate quick setting heights.
-            int oldMaxHeight = mQsMaxExpansionHeight;
-            if (mQs != null) {
-                updateQSMinHeight();
-                mQsMaxExpansionHeight = mQs.getDesiredHeight();
-                mNotificationStackScrollLayoutController.setMaxTopPadding(mQsMaxExpansionHeight);
-            }
+            int oldMaxHeight = mQsController.updateHeightsOnShadeLayoutChange();
             positionClockAndNotifications();
-            if (mQsExpanded && mQsFullyExpanded) {
-                mQsExpansionHeight = mQsMaxExpansionHeight;
-                requestScrollerTopPaddingUpdate(false /* animate */);
-                updateExpandedHeightToMaxHeight();
-
-                // Size has changed, start an animation.
-                if (mQsMaxExpansionHeight != oldMaxHeight) {
-                    startQsSizeChangeAnimation(oldMaxHeight, mQsMaxExpansionHeight);
-                }
-            } else if (!mQsExpanded && mQsExpansionAnimator == null) {
-                setQsExpansionHeight(mQsMinExpansionHeight + mLastOverscroll);
-            } else {
-                mShadeLog.v("onLayoutChange: qs expansion not set");
-            }
+            mQsController.handleShadeLayoutChanged(oldMaxHeight);
             updateExpandedHeight(getExpandedHeight());
             updateHeader();
 
@@ -5921,9 +4463,8 @@
             // container the desired height so when closing the QS detail, it stays smaller after
             // the size change animation is finished but the detail view is still being animated
             // away (this animation takes longer than the size change animation).
-            if (mQsSizeChangeAnimator == null && mQs != null) {
-                mQs.setHeightOverride(mQs.getDesiredHeight());
-            }
+            mQsController.setHeightOverrideToDesiredHeight();
+
             updateMaxHeadsUpTranslation();
             updateGestureExclusionRect();
             if (mExpandAfterLayoutRunnable != null) {
@@ -5934,18 +4475,6 @@
         }
     }
 
-    private void updateQSMinHeight() {
-        float previousMin = mQsMinExpansionHeight;
-        if (mKeyguardShowing || mSplitShadeEnabled) {
-            mQsMinExpansionHeight = 0;
-        } else {
-            mQsMinExpansionHeight = mQs.getQsMinExpansionHeight();
-        }
-        if (mQsExpansionHeight == previousMin) {
-            mQsExpansionHeight = mQsMinExpansionHeight;
-        }
-    }
-
     @NonNull
     private WindowInsets onApplyShadeWindowInsets(WindowInsets insets) {
         // the same types of insets that are handled in NotificationShadeWindowView
@@ -5954,6 +4483,7 @@
         mDisplayTopInset = combinedInsets.top;
         mDisplayRightInset = combinedInsets.right;
         mDisplayLeftInset = combinedInsets.left;
+        mQsController.setDisplayInsets(mDisplayRightInset, mDisplayLeftInset);
 
         mNavigationBarBottomHeight = insets.getStableInsetBottom();
         updateMaxHeadsUpTranslation();
@@ -5966,10 +4496,10 @@
     }
 
     private void onPanelStateChanged(@PanelState int state) {
-        updateQSExpansionEnabledAmbient();
+        mQsController.updateExpansionEnabledAmbient();
 
         if (state == STATE_OPEN && mCurrentPanelState != state) {
-            setQsExpandImmediate(false);
+            mQsController.setExpandImmediate(false);
             mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
         }
         if (state == STATE_OPENING) {
@@ -5977,12 +4507,12 @@
             // to locked will trigger this event and we're not actually in the process of opening
             // the shade, lockscreen is just always expanded
             if (mSplitShadeEnabled && !isOnKeyguard()) {
-                setQsExpandImmediate(true);
+                mQsController.setExpandImmediate(true);
             }
             mOpenCloseListener.onOpenStarted();
         }
         if (state == STATE_CLOSED) {
-            setQsExpandImmediate(false);
+            mQsController.setExpandImmediate(false);
             // Close the status bar in the next frame so we can show the end of the
             // animation.
             mView.post(mMaybeHideExpandedRunnable);
@@ -6048,7 +4578,7 @@
         /** @see ViewGroup#onInterceptTouchEvent(MotionEvent) */
         public boolean onInterceptTouchEvent(MotionEvent event) {
             mShadeLog.logMotionEvent(event, "NPVC onInterceptTouchEvent");
-            if (mQs.disallowPanelTouches()) {
+            if (mQsController.disallowTouches()) {
                 mShadeLog.logMotionEvent(event,
                         "NPVC not intercepting touch, panel touches disallowed");
                 return false;
@@ -6070,14 +4600,14 @@
                         + "HeadsUpTouchHelper");
                 return true;
             }
-            if (!shouldQuickSettingsIntercept(mDownX, mDownY, 0)
+            if (!mQsController.shouldQuickSettingsIntercept(mDownX, mDownY, 0)
                     && mPulseExpansionHandler.onInterceptTouchEvent(event)) {
                 mShadeLog.v("NotificationPanelViewController MotionEvent intercepted: "
                         + "PulseExpansionHandler");
                 return true;
             }
 
-            if (!isFullyCollapsed() && onQsIntercept(event)) {
+            if (!isFullyCollapsed() && mQsController.onIntercept(event)) {
                 debugLog("onQsIntercept true");
                 mShadeLog.v("NotificationPanelViewController MotionEvent intercepted: "
                         + "QsIntercept");
@@ -6123,8 +4653,12 @@
                                 + " false");
                         return true;
                     }
-                    mInitialExpandY = y;
-                    mInitialExpandX = x;
+                    if (!mTracking || isFullyCollapsed()) {
+                        mInitialExpandY = y;
+                        mInitialExpandX = x;
+                    } else {
+                        mShadeLog.d("not setting mInitialExpandY in onInterceptTouch");
+                    }
                     mTouchStartedInEmptyArea = !isInContentBounds(x, y);
                     mTouchSlopExceeded = mTouchSlopExceededBeforeDown;
                     mMotionAborted = false;
@@ -6197,8 +4731,7 @@
                 mLastTouchDownTime = event.getDownTime();
             }
 
-
-            if (mQsFullyExpanded && mQs != null && mQs.disallowPanelTouches()) {
+            if (mQsController.isFullyExpandedAndTouchesDisallowed()) {
                 mShadeLog.logMotionEvent(event,
                         "onTouch: ignore touch, panel touches disallowed and qs fully expanded");
                 return false;
@@ -6229,7 +4762,7 @@
             // If pulse is expanding already, let's give it the touch. There are situations
             // where the panel starts expanding even though we're also pulsing
             boolean pulseShouldGetTouch = (!mIsExpanding
-                    && !shouldQuickSettingsIntercept(mDownX, mDownY, 0))
+                    && !mQsController.shouldQuickSettingsIntercept(mDownX, mDownY, 0))
                     || mPulseExpansionHandler.isExpanding();
             if (pulseShouldGetTouch && mPulseExpansionHandler.onTouchEvent(event)) {
                 // We're expanding all the other ones shouldn't get this anymore
@@ -6247,7 +4780,8 @@
             }
             boolean handled = mHeadsUpTouchHelper.onTouchEvent(event);
 
-            if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && handleQsTouch(event)) {
+            if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && mQsController.handleTouch(
+                    event, isFullyCollapsed(), isShadeOrQsHeightAnimationRunning())) {
                 mShadeLog.logMotionEvent(event, "onTouch: handleQsTouch handled event");
                 return true;
             }
@@ -6313,7 +4847,8 @@
             final float x = event.getX(pointerIndex);
             final float y = event.getY(pointerIndex);
 
-            if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+            if (event.getActionMasked() == MotionEvent.ACTION_DOWN
+                    || event.getActionMasked() == MotionEvent.ACTION_MOVE) {
                 mGestureWaitForTouchSlop = shouldGestureWaitForTouchSlop();
                 mIgnoreXTouchSlop = true;
             }
@@ -6375,7 +4910,7 @@
                         mShadeLog.logHasVibrated(mHasVibratedOnOpen, mExpandedFraction);
                     }
                     addMovement(event);
-                    if (!isFullyCollapsed()) {
+                    if (!isFullyCollapsed() && !isOnKeyguard()) {
                         maybeVibrateOnOpening(true /* openingWithTouch */);
                     }
                     float h = y - mInitialExpandY;
@@ -6401,7 +4936,9 @@
                         mTouchAboveFalsingThreshold = true;
                         mUpwardsWhenThresholdReached = isDirectionUpwards(x, y);
                     }
-                    if ((!mGestureWaitForTouchSlop || mTracking) && !isTrackingBlocked()) {
+                    if ((!mGestureWaitForTouchSlop || mTracking)
+                            && !(mBlockingExpansionForCurrentTouch
+                            || mQsController.isTrackingBlocked())) {
                         // Count h==0 as part of swipe-up,
                         // otherwise {@link NotificationStackScrollLayout}
                         // wrongly enables stack height updates at the start of lockscreen swipe-up
@@ -6419,9 +4956,9 @@
                     // mHeightAnimator is null, there is no remaining frame, ends instrumenting.
                     if (mHeightAnimator == null) {
                         if (event.getActionMasked() == MotionEvent.ACTION_UP) {
-                            endJankMonitoring();
+                            mQsController.endJankMonitoring();
                         } else {
-                            cancelJankMonitoring();
+                            mQsController.cancelJankMonitoring();
                         }
                     }
                     break;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index ab2e692..156e4fd 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -563,7 +563,8 @@
                     mCurrentState.keyguardOccluded,
                     mCurrentState.bouncerShowing,
                     mCurrentState.dozing,
-                    mCurrentState.panelExpanded);
+                    mCurrentState.panelExpanded,
+                    mCurrentState.dreaming);
         }
     }
 
@@ -778,6 +779,12 @@
     }
 
     @Override
+    public void setDreaming(boolean dreaming) {
+        mCurrentState.dreaming = dreaming;
+        apply(mCurrentState);
+    }
+
+    @Override
     public void setForcePluginOpen(boolean forceOpen, Object token) {
         if (forceOpen) {
             mCurrentState.forceOpenTokens.add(token);
@@ -904,5 +911,10 @@
         public void onDozingChanged(boolean isDozing) {
             setDozing(isDozing);
         }
+
+        @Override
+        public void onDreamingChanged(boolean isDreaming) {
+            setDreaming(isDreaming);
+        }
     };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
index 736404aa..fed9b84 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
@@ -23,8 +23,8 @@
 import com.android.systemui.statusbar.StatusBarState
 
 /**
- * Represents state of shade window, used by [NotificationShadeWindowControllerImpl].
- * Contains nested class [Buffer] for pretty table logging in bug reports.
+ * Represents state of shade window, used by [NotificationShadeWindowControllerImpl]. Contains
+ * nested class [Buffer] for pretty table logging in bug reports.
  */
 class NotificationShadeWindowState(
     @JvmField var keyguardShowing: Boolean = false,
@@ -55,6 +55,7 @@
     @JvmField var remoteInputActive: Boolean = false,
     @JvmField var forcePluginOpen: Boolean = false,
     @JvmField var dozing: Boolean = false,
+    @JvmField var dreaming: Boolean = false,
     @JvmField var scrimsVisibility: Int = 0,
     @JvmField var backgroundBlurRadius: Int = 0,
 ) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
index 1f0cbf9..74a61a3 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.shade;
 
-import static android.os.Trace.TRACE_TAG_ALWAYS;
+import static android.os.Trace.TRACE_TAG_APP;
 import static android.view.WindowInsets.Type.systemBars;
 
 import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG;
@@ -328,7 +328,7 @@
 
     @Override
     public void requestLayout() {
-        Trace.instant(TRACE_TAG_ALWAYS, "NotificationShadeWindowView#requestLayout");
+        Trace.instant(TRACE_TAG_APP, "NotificationShadeWindowView#requestLayout");
         super.requestLayout();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 7ed6e3e..60fa865 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -160,12 +160,10 @@
 
         // This view is not part of the newly inflated expanded status bar.
         mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container);
-        if (featureFlags.isEnabled(Flags.MODERN_BOUNCER)) {
-            KeyguardBouncerViewBinder.bind(
-                    mView.findViewById(R.id.keyguard_bouncer_container),
-                    keyguardBouncerViewModel,
-                    keyguardBouncerComponentFactory);
-        }
+        KeyguardBouncerViewBinder.bind(
+                mView.findViewById(R.id.keyguard_bouncer_container),
+                keyguardBouncerViewModel,
+                keyguardBouncerComponentFactory);
 
         if (featureFlags.isEnabled(Flags.UNOCCLUSION_TRANSITION)) {
             collectFlow(mView, keyguardTransitionInteractor.getLockscreenToDreamingTransition(),
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
index 85b259e..de02115 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
@@ -31,6 +31,7 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.fragments.FragmentService
 import com.android.systemui.navigationbar.NavigationModeController
 import com.android.systemui.plugins.qs.QS
 import com.android.systemui.plugins.qs.QSContainerController
@@ -54,6 +55,7 @@
     private val largeScreenShadeHeaderController: LargeScreenShadeHeaderController,
     private val shadeExpansionStateManager: ShadeExpansionStateManager,
     private val featureFlags: FeatureFlags,
+    private val fragmentService: FragmentService,
     @Main private val delayableExecutor: DelayableExecutor
 ) : ViewController<NotificationsQuickSettingsContainer>(view), QSContainerController {
 
@@ -128,6 +130,7 @@
         mView.setInsetsChangedListener(delayedInsetSetter)
         mView.setQSFragmentAttachedListener { qs: QS -> qs.setContainerController(this) }
         mView.setConfigurationChangedListener { updateResources() }
+        fragmentService.getFragmentHostManager(mView).addTagListener(QS.TAG, mView)
     }
 
     override fun onViewDetached() {
@@ -136,6 +139,7 @@
         mView.removeOnInsetsChangedListener()
         mView.removeQSFragmentAttachedListener()
         mView.setConfigurationChangedListener(null)
+        fragmentService.getFragmentHostManager(mView).removeTagListener(QS.TAG, mView)
     }
 
     fun updateResources() {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
index 02316b7..f73dde6 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
@@ -29,7 +29,6 @@
 import androidx.constraintlayout.widget.ConstraintSet;
 
 import com.android.systemui.R;
-import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.statusbar.notification.AboveShelfObserver;
@@ -133,18 +132,6 @@
     }
 
     @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        FragmentHostManager.get(this).addTagListener(QS.TAG, this);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        FragmentHostManager.get(this).removeTagListener(QS.TAG, this);
-    }
-
-    @Override
     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
         mInsetsChangedListener.accept(insets);
         return insets;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/OWNERS b/packages/SystemUI/src/com/android/systemui/shade/OWNERS
index 49709a8..c8b6a2e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/shade/OWNERS
@@ -7,6 +7,7 @@
 per-file NotificationsQuickSettingsContainer.java = kozynski@google.com, asc@google.com
 per-file NotificationsQSContainerController.kt = kozynski@google.com, asc@google.com
 per-file *ShadeHeader* = kozynski@google.com, asc@google.com
+per-file *Shade* = justinweir@google.com
 
 per-file NotificationShadeWindowViewController.java = pixel@google.com, cinek@google.com, juliacr@google.com
 per-file NotificationShadeWindowView.java = pixel@google.com, cinek@google.com, juliacr@google.com
@@ -14,4 +15,4 @@
 per-file NotificationPanelUnfoldAnimationController.kt = alexflo@google.com, jeffdq@google.com, juliacr@google.com
 
 per-file NotificationPanelView.java = pixel@google.com, cinek@google.com, juliacr@google.com, justinweir@google.com
-per-file NotificationPanelViewController.java = pixel@google.com, cinek@google.com, juliacr@google.com, justinweir@google.com
\ No newline at end of file
+per-file NotificationPanelViewController.java = pixel@google.com, cinek@google.com, juliacr@google.com, justinweir@google.com
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt b/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt
new file mode 100644
index 0000000..3eec7fa0e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt
@@ -0,0 +1,70 @@
+package com.android.systemui.shade
+
+import android.content.Context
+import android.view.DisplayCutout
+import com.android.systemui.R
+import com.android.systemui.battery.BatteryMeterView
+import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
+import javax.inject.Inject
+
+/**
+ * Controls [BatteryMeterView.BatteryPercentMode]. It takes into account cutout and qs-qqs
+ * transition fraction when determining the mode.
+ */
+class QsBatteryModeController
+@Inject
+constructor(
+    private val context: Context,
+    private val insetsProvider: StatusBarContentInsetsProvider,
+) {
+
+    private companion object {
+        // MotionLayout frames are in [0, 100]. Where 0 and 100 are reserved for start and end
+        // frames.
+        const val MOTION_LAYOUT_MAX_FRAME = 100
+        // We add a single buffer frame to ensure that battery view faded out completely when we are
+        // about to change it's state
+        const val BUFFER_FRAME_COUNT = 1
+    }
+
+    private var fadeInStartFraction: Float = 0f
+    private var fadeOutCompleteFraction: Float = 0f
+
+    init {
+        updateResources()
+    }
+
+    /**
+     * Returns an appropriate [BatteryMeterView.BatteryPercentMode] for the [qsExpandedFraction] and
+     * [cutout]. We don't show battery estimation in qqs header on the devices with center cutout.
+     * The result might be null when the battery icon is invisible during the qs-qqs transition
+     * animation.
+     */
+    @BatteryMeterView.BatteryPercentMode
+    fun getBatteryMode(cutout: DisplayCutout?, qsExpandedFraction: Float): Int? =
+        when {
+            qsExpandedFraction > fadeInStartFraction -> BatteryMeterView.MODE_ESTIMATE
+            qsExpandedFraction < fadeOutCompleteFraction ->
+                if (hasCenterCutout(cutout)) {
+                    BatteryMeterView.MODE_ON
+                } else {
+                    BatteryMeterView.MODE_ESTIMATE
+                }
+            else -> null
+        }
+
+    fun updateResources() {
+        fadeInStartFraction =
+            (context.resources.getInteger(R.integer.fade_in_start_frame) - BUFFER_FRAME_COUNT) /
+                MOTION_LAYOUT_MAX_FRAME.toFloat()
+        fadeOutCompleteFraction =
+            (context.resources.getInteger(R.integer.fade_out_complete_frame) + BUFFER_FRAME_COUNT) /
+                MOTION_LAYOUT_MAX_FRAME.toFloat()
+    }
+
+    private fun hasCenterCutout(cutout: DisplayCutout?): Boolean =
+        cutout?.let {
+            !insetsProvider.currentRotationHasCornerCutout() && !it.boundingRectTop.isEmpty
+        }
+            ?: false
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
new file mode 100644
index 0000000..d041212
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -0,0 +1,2054 @@
+/*
+ * Copyright (C) 2022 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.shade;
+
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
+import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
+import static com.android.systemui.shade.NotificationPanelViewController.COUNTER_PANEL_OPEN_QS;
+import static com.android.systemui.shade.NotificationPanelViewController.FLING_COLLAPSE;
+import static com.android.systemui.shade.NotificationPanelViewController.FLING_EXPAND;
+import static com.android.systemui.shade.NotificationPanelViewController.FLING_HIDE;
+import static com.android.systemui.shade.NotificationPanelViewController.QS_PARALLAX_AMOUNT;
+import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.app.Fragment;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.util.Log;
+import android.util.MathUtils;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
+import android.widget.FrameLayout;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.policy.ScreenDecorationsUtils;
+import com.android.internal.policy.SystemBarUtils;
+import com.android.keyguard.FaceAuthApiRequestReason;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.R;
+import com.android.systemui.animation.Interpolators;
+import com.android.systemui.classifier.Classifier;
+import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
+import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.media.controls.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.ui.MediaHierarchyManager;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.screenrecord.RecordingController;
+import com.android.systemui.shade.transition.ShadeTransitionController;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationShadeDepthController;
+import com.android.systemui.statusbar.PulseExpansionHandler;
+import com.android.systemui.statusbar.QsFrameTranslateController;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.stack.AmbientState;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
+import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
+import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.LargeScreenUtils;
+
+import javax.inject.Inject;
+
+import dagger.Lazy;
+
+/** Handles QuickSettings touch handling, expansion and animation state
+ * TODO (b/264460656) make this dumpable
+ */
+@CentralSurfacesComponent.CentralSurfacesScope
+public class QuickSettingsController {
+    public static final String TAG = "QuickSettingsController";
+
+    private QS mQs;
+    private final Lazy<NotificationPanelViewController> mPanelViewControllerLazy;
+
+    private final NotificationPanelView mPanelView;
+    private final KeyguardStatusBarView mKeyguardStatusBar;
+    private final FrameLayout mQsFrame;
+
+    private final QsFrameTranslateController mQsFrameTranslateController;
+    private final ShadeTransitionController mShadeTransitionController;
+    private final PulseExpansionHandler mPulseExpansionHandler;
+    private final ShadeExpansionStateManager mShadeExpansionStateManager;
+    private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+    private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
+    private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+    private final NotificationShadeDepthController mDepthController;
+    private final LargeScreenShadeHeaderController mLargeScreenShadeHeaderController;
+    private final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
+    private final KeyguardStateController mKeyguardStateController;
+    private final KeyguardBypassController mKeyguardBypassController;
+    private final NotificationRemoteInputManager mRemoteInputManager;
+    private VelocityTracker mQsVelocityTracker;
+    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    private final ScrimController mScrimController;
+    private final MediaDataManager mMediaDataManager;
+    private final MediaHierarchyManager mMediaHierarchyManager;
+    private final AmbientState mAmbientState;
+    private final RecordingController mRecordingController;
+    private final FalsingCollector mFalsingCollector;
+    private final LockscreenGestureLogger mLockscreenGestureLogger;
+    private final ShadeLogger mShadeLog;
+    private final FeatureFlags mFeatureFlags;
+    private final InteractionJankMonitor mInteractionJankMonitor;
+    private final FalsingManager mFalsingManager;
+    private final AccessibilityManager mAccessibilityManager;
+    private final MetricsLogger mMetricsLogger;
+    private final Resources mResources;
+
+    /** Whether the notifications are displayed full width (no margins on the side). */
+    private boolean mIsFullWidth;
+    private int mTouchSlop;
+    private float mSlopMultiplier;
+    /** the current {@link StatusBarState} */
+    private int mBarState;
+    private int mStatusBarMinHeight;
+    private boolean mScrimEnabled = true;
+    private int mScrimCornerRadius;
+    private int mScreenCornerRadius;
+    private boolean mUseLargeScreenShadeHeader;
+    private int mLargeScreenShadeHeaderHeight;
+    private int mDisplayRightInset = 0; // in pixels
+    private int mDisplayLeftInset = 0; // in pixels
+    private boolean mSplitShadeEnabled;
+    /**
+     * The padding between the start of notifications and the qs boundary on the lockscreen.
+     * On lockscreen, notifications aren't inset this extra amount, but we still want the
+     * qs boundary to be padded.
+     */
+    private int mLockscreenNotificationPadding;
+    private int mSplitShadeNotificationsScrimMarginBottom;
+    private boolean mDozing;
+    private boolean mEnableClipping;
+    private int mFalsingThreshold;
+    /**
+     * Position of the qs bottom during the full shade transition. This is needed as the toppadding
+     * can change during state changes, which makes it much harder to do animations
+     */
+    private int mTransitionToFullShadePosition;
+    private boolean mCollapsedOnDown;
+    private float mShadeExpandedHeight = 0;
+    private boolean mLastShadeFlingWasExpanding;
+
+    private float mInitialHeightOnTouch;
+    private float mInitialTouchX;
+    private float mInitialTouchY;
+    /** whether current touch Y delta is above falsing threshold */
+    private boolean mTouchAboveFalsingThreshold;
+    /** whether we are tracking a touch on QS container */
+    private boolean mTracking;
+    /** pointerId of the pointer we're currently tracking */
+    private int mTrackingPointer;
+
+    /**
+     * Indicates that QS is in expanded state which can happen by:
+     * - single pane shade: expanding shade and then expanding QS
+     * - split shade: just expanding shade (QS are expanded automatically)
+     */
+    private boolean mExpanded;
+    /** Indicates QS is at its max height */
+    private boolean mFullyExpanded;
+    /**
+     * Determines if QS should be already expanded when expanding shade.
+     * Used for split shade, two finger gesture as well as accessibility shortcut to QS.
+     * It needs to be set when movement starts as it resets at the end of expansion/collapse.
+     */
+    private boolean mExpandImmediate;
+    private boolean mExpandedWhenExpandingStarted;
+    private boolean mAnimatingHiddenFromCollapsed;
+    private boolean mVisible;
+    private float mExpansionHeight;
+    private int mMinExpansionHeight;
+    private int mMaxExpansionHeight;
+    /** Expansion fraction of the notification shade */
+    private float mShadeExpandedFraction;
+    private int mPeekHeight;
+    private float mLastOverscroll;
+    private boolean mExpansionFromOverscroll;
+    private boolean mExpansionEnabledPolicy = true;
+    private boolean mExpansionEnabledAmbient = true;
+    private float mQuickQsHeaderHeight;
+    /**
+     * Determines if QS should be already expanded when expanding shade.
+     * Used for split shade, two finger gesture as well as accessibility shortcut to QS.
+     * It needs to be set when movement starts as it resets at the end of expansion/collapse.
+     */
+    private boolean mTwoFingerExpandPossible;
+    /** Whether the ongoing gesture might both trigger the expansion in both the view and QS. */
+    private boolean mConflictingExpansionGesture;
+    /**
+     * If we are in a panel collapsing motion, we reset scrollY of our scroll view but still
+     * need to take this into account in our panel height calculation.
+     */
+    private boolean mAnimatorExpand;
+
+    /**
+     * The amount of progress we are currently in if we're transitioning to the full shade.
+     * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
+     * shade. This value can also go beyond 1.1 when we're overshooting!
+     */
+    private float mTransitioningToFullShadeProgress;
+    /** Distance a full shade transition takes in order for qs to fully transition to the shade. */
+    private int mDistanceForFullShadeTransition;
+    private boolean mStackScrollerOverscrolling;
+    /** Indicates QS is animating - set by flingQs */
+    private boolean mAnimating;
+    /** Whether the current animator is resetting the qs translation. */
+    private boolean mIsTranslationResettingAnimator;
+    /** Whether the current animator is resetting the pulse expansion after a drag down. */
+    private boolean mIsPulseExpansionResettingAnimator;
+    /** The translation amount for QS for the full shade transition. */
+    private float mTranslationForFullShadeTransition;
+    /** Should we animate the next bounds update. */
+    private boolean mAnimateNextNotificationBounds;
+    /** The delay for the next bounds animation. */
+    private long mNotificationBoundsAnimationDelay;
+    /** The duration of the notification bounds animation. */
+    private long mNotificationBoundsAnimationDuration;
+
+    private final Region mInterceptRegion = new Region();
+    /** The end bounds of a clipping animation. */
+    private final Rect mClippingAnimationEndBounds = new Rect();
+    private final Rect mLastClipBounds = new Rect();
+
+    /** The animator for the qs clipping bounds. */
+    private ValueAnimator mClippingAnimator = null;
+    /** The main animator for QS expansion */
+    private ValueAnimator mExpansionAnimator;
+    /** The animator for QS size change */
+    private ValueAnimator mSizeChangeAnimator;
+
+    private ExpansionHeightListener mExpansionHeightListener;
+    private QsStateUpdateListener mQsStateUpdateListener;
+    private ApplyClippingImmediatelyListener mApplyClippingImmediatelyListener;
+    private FlingQsWithoutClickListener mFlingQsWithoutClickListener;
+    private ExpansionHeightSetToMaxListener mExpansionHeightSetToMaxListener;
+    private final QS.HeightListener mQsHeightListener = this::onHeightChanged;
+    private final Runnable mQsCollapseExpandAction = this::collapseOrExpandQs;
+    private final QS.ScrollListener mQsScrollListener = this::onScroll;
+
+    @Inject
+    public QuickSettingsController(
+            Lazy<NotificationPanelViewController> panelViewControllerLazy,
+            NotificationPanelView panelView,
+            QsFrameTranslateController qsFrameTranslateController,
+            ShadeTransitionController shadeTransitionController,
+            PulseExpansionHandler pulseExpansionHandler,
+            NotificationRemoteInputManager remoteInputManager,
+            ShadeExpansionStateManager shadeExpansionStateManager,
+            StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+            NotificationStackScrollLayoutController notificationStackScrollLayoutController,
+            LockscreenShadeTransitionController lockscreenShadeTransitionController,
+            NotificationShadeDepthController notificationShadeDepthController,
+            LargeScreenShadeHeaderController largeScreenShadeHeaderController,
+            StatusBarTouchableRegionManager statusBarTouchableRegionManager,
+            KeyguardStateController keyguardStateController,
+            KeyguardBypassController keyguardBypassController,
+            KeyguardUpdateMonitor keyguardUpdateMonitor,
+            ScrimController scrimController,
+            MediaDataManager mediaDataManager,
+            MediaHierarchyManager mediaHierarchyManager,
+            AmbientState ambientState,
+            RecordingController recordingController,
+            FalsingManager falsingManager,
+            FalsingCollector falsingCollector,
+            AccessibilityManager accessibilityManager,
+            LockscreenGestureLogger lockscreenGestureLogger,
+            MetricsLogger metricsLogger,
+            FeatureFlags featureFlags,
+            InteractionJankMonitor interactionJankMonitor,
+            ShadeLogger shadeLog
+    ) {
+        mPanelViewControllerLazy = panelViewControllerLazy;
+        mPanelView = panelView;
+        mQsFrame = mPanelView.findViewById(R.id.qs_frame);
+        mKeyguardStatusBar = mPanelView.findViewById(R.id.keyguard_header);
+        mResources = mPanelView.getResources();
+        mSplitShadeEnabled = LargeScreenUtils.shouldUseSplitNotificationShade(mResources);
+        mQsFrameTranslateController = qsFrameTranslateController;
+        mShadeTransitionController = shadeTransitionController;
+        mPulseExpansionHandler = pulseExpansionHandler;
+        pulseExpansionHandler.setPulseExpandAbortListener(() -> {
+            if (mQs != null) {
+                mQs.animateHeaderSlidingOut();
+            }
+        });
+        mRemoteInputManager = remoteInputManager;
+        mShadeExpansionStateManager = shadeExpansionStateManager;
+        mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+        mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
+        mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
+        mDepthController = notificationShadeDepthController;
+        mLargeScreenShadeHeaderController = largeScreenShadeHeaderController;
+        mStatusBarTouchableRegionManager = statusBarTouchableRegionManager;
+        mKeyguardStateController = keyguardStateController;
+        mKeyguardBypassController = keyguardBypassController;
+        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+        mScrimController = scrimController;
+        mMediaDataManager = mediaDataManager;
+        mMediaHierarchyManager = mediaHierarchyManager;
+        mAmbientState = ambientState;
+        mRecordingController = recordingController;
+        mFalsingManager = falsingManager;
+        mFalsingCollector = falsingCollector;
+        mAccessibilityManager = accessibilityManager;
+
+        mLockscreenGestureLogger = lockscreenGestureLogger;
+        mMetricsLogger = metricsLogger;
+        mShadeLog = shadeLog;
+        mFeatureFlags = featureFlags;
+        mInteractionJankMonitor = interactionJankMonitor;
+
+        mShadeExpansionStateManager.addExpansionListener(this::onPanelExpansionChanged);
+        mLockscreenShadeTransitionController.addCallback(new LockscreenShadeTransitionCallback());
+    }
+
+    @VisibleForTesting
+    void setQs(QS qs) {
+        mQs = qs;
+    }
+
+    public void setExpansionHeightListener(ExpansionHeightListener listener) {
+        mExpansionHeightListener = listener;
+    }
+
+    public void setQsStateUpdateListener(QsStateUpdateListener listener) {
+        mQsStateUpdateListener = listener;
+    }
+
+    public void setApplyClippingImmediatelyListener(ApplyClippingImmediatelyListener listener) {
+        mApplyClippingImmediatelyListener = listener;
+    }
+
+    public void setFlingQsWithoutClickListener(FlingQsWithoutClickListener listener) {
+        mFlingQsWithoutClickListener = listener;
+    }
+
+    public void setExpansionHeightSetToMaxListener(ExpansionHeightSetToMaxListener callback) {
+        mExpansionHeightSetToMaxListener = callback;
+    }
+
+    void loadDimens() {
+        final ViewConfiguration configuration = ViewConfiguration.get(this.mPanelView.getContext());
+        mTouchSlop = configuration.getScaledTouchSlop();
+        mSlopMultiplier = configuration.getScaledAmbiguousGestureMultiplier();
+        mPeekHeight = mResources.getDimensionPixelSize(R.dimen.qs_peek_height);
+        mStatusBarMinHeight = SystemBarUtils.getStatusBarHeight(mPanelView.getContext());
+        mScrimCornerRadius = mResources.getDimensionPixelSize(
+                R.dimen.notification_scrim_corner_radius);
+        mScreenCornerRadius = (int) ScreenDecorationsUtils.getWindowCornerRadius(
+                mPanelView.getContext());
+        mFalsingThreshold = mResources.getDimensionPixelSize(R.dimen.qs_falsing_threshold);
+        mLockscreenNotificationPadding = mResources.getDimensionPixelSize(
+                R.dimen.notification_side_paddings);
+        mDistanceForFullShadeTransition = mResources.getDimensionPixelSize(
+                R.dimen.lockscreen_shade_qs_transition_distance);
+    }
+
+    void updateResources() {
+        mSplitShadeEnabled = LargeScreenUtils.shouldUseSplitNotificationShade(mResources);
+        if (mQs != null) {
+            mQs.setInSplitShade(mSplitShadeEnabled);
+        }
+        mSplitShadeNotificationsScrimMarginBottom =
+                mResources.getDimensionPixelSize(
+                        R.dimen.split_shade_notifications_scrim_margin_bottom);
+
+        mUseLargeScreenShadeHeader =
+                LargeScreenUtils.shouldUseLargeScreenShadeHeader(mPanelView.getResources());
+        mLargeScreenShadeHeaderHeight =
+                mResources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height);
+        int topMargin = mUseLargeScreenShadeHeader ? mLargeScreenShadeHeaderHeight :
+                mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_top);
+        mLargeScreenShadeHeaderController.setLargeScreenActive(mUseLargeScreenShadeHeader);
+        mAmbientState.setStackTopMargin(topMargin);
+
+        // TODO: When the flag is eventually removed, it means that we have a single view that is
+        // the same height in QQS and in Large Screen (large_screen_shade_header_height). Eventually
+        // the concept of largeScreenHeader or quickQsHeader will disappear outside of the class
+        // that controls the view as the offset needs to be the same regardless.
+        if (mUseLargeScreenShadeHeader || mFeatureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)) {
+            mQuickQsHeaderHeight = mLargeScreenShadeHeaderHeight;
+        } else {
+            mQuickQsHeaderHeight = SystemBarUtils.getQuickQsOffsetHeight(mPanelView.getContext());
+        }
+
+        mEnableClipping = mResources.getBoolean(R.bool.qs_enable_clipping);
+    }
+
+    // TODO (b/265054088): move this and others to a CoreStartable
+    void initNotificationStackScrollLayoutController() {
+        mNotificationStackScrollLayoutController.setOverscrollTopChangedListener(
+                new NsslOverscrollTopChangedListener());
+        mNotificationStackScrollLayoutController.setOnStackYChanged(this::onStackYChanged);
+        mNotificationStackScrollLayoutController.setOnScrollListener(this::onNotificationScrolled);
+    }
+
+    private void onStackYChanged(boolean shouldAnimate) {
+        if (isQsFragmentCreated()) {
+            if (shouldAnimate) {
+                setAnimateNextNotificationBounds(StackStateAnimator.ANIMATION_DURATION_STANDARD,
+                        0 /* delay */);
+            }
+            setClippingBounds();
+        }
+    }
+
+    private void onNotificationScrolled(int newScrollPosition) {
+        updateExpansionEnabledAmbient();
+    }
+
+    int getHeaderHeight() {
+        return mQs.getHeader().getHeight();
+    }
+
+    /** Returns the padding of the stackscroller when unlocked */
+    int getUnlockedStackScrollerPadding() {
+        return (mQs != null ? mQs.getHeader().getHeight() : 0) + mPeekHeight;
+    }
+
+    public boolean isExpansionEnabled() {
+        return mExpansionEnabledPolicy && mExpansionEnabledAmbient
+                && !mRemoteInputManager.isRemoteInputActive();
+    }
+
+    public float getTransitioningToFullShadeProgress() {
+        return mTransitioningToFullShadeProgress;
+    }
+
+    /** */
+    @VisibleForTesting
+    boolean isExpandImmediate() {
+        return mExpandImmediate;
+    }
+
+    float getInitialTouchY() {
+        return mInitialTouchY;
+    }
+
+    /** Returns whether split shade is enabled and an x coordinate is outside of the QS frame. */
+    private boolean isSplitShadeAndTouchXOutsideQs(float touchX) {
+        return mSplitShadeEnabled && touchX < mQsFrame.getX()
+                || touchX > mQsFrame.getX() + mQsFrame.getWidth();
+    }
+
+    /** Returns whether touch is within QS area */
+    private boolean isTouchInQsArea(float x, float y) {
+        if (isSplitShadeAndTouchXOutsideQs(x)) {
+            return false;
+        }
+        // TODO (b/265193930): remove dependency on NPVC
+        // Let's reject anything at the very bottom around the home handle in gesture nav
+        if (mPanelViewControllerLazy.get().isInGestureNavHomeHandleArea(x, y)) {
+            return false;
+        }
+        return y <= mNotificationStackScrollLayoutController.getBottomMostNotificationBottom()
+                || y <= mQs.getView().getY() + mQs.getView().getHeight();
+    }
+
+    /** Returns whether or not event should open QS */
+    private boolean isOpenQsEvent(MotionEvent event) {
+        final int pointerCount = event.getPointerCount();
+        final int action = event.getActionMasked();
+
+        final boolean
+                twoFingerDrag =
+                action == MotionEvent.ACTION_POINTER_DOWN && pointerCount == 2;
+
+        final boolean
+                stylusButtonClickDrag =
+                action == MotionEvent.ACTION_DOWN && (event.isButtonPressed(
+                        MotionEvent.BUTTON_STYLUS_PRIMARY) || event.isButtonPressed(
+                        MotionEvent.BUTTON_STYLUS_SECONDARY));
+
+        final boolean
+                mouseButtonClickDrag =
+                action == MotionEvent.ACTION_DOWN && (event.isButtonPressed(
+                        MotionEvent.BUTTON_SECONDARY) || event.isButtonPressed(
+                        MotionEvent.BUTTON_TERTIARY));
+
+        return twoFingerDrag || stylusButtonClickDrag || mouseButtonClickDrag;
+    }
+
+
+    public boolean getExpanded() {
+        return mExpanded;
+    }
+
+    @VisibleForTesting
+    boolean isTracking() {
+        return mTracking;
+    }
+
+    public boolean getFullyExpanded() {
+        return mFullyExpanded;
+    }
+
+    boolean isGoingBetweenClosedShadeAndExpandedQs() {
+        // Below is true when QS are expanded and we swipe up from the same bottom of panel to
+        // close the whole shade with one motion. Also this will be always true when closing
+        // split shade as there QS are always expanded so every collapsing motion is motion from
+        // expanded QS to closed panel
+        return mExpandImmediate || (mExpanded
+                && !mTracking && !isExpansionAnimating()
+                && !mExpansionFromOverscroll);
+    }
+
+    private boolean isQsFragmentCreated() {
+        return mQs != null;
+    }
+
+    public boolean isCustomizing() {
+        return isQsFragmentCreated() && mQs.isCustomizing();
+    }
+
+    public float getExpansionHeight() {
+        return mExpansionHeight;
+    }
+
+    public boolean getExpandedWhenExpandingStarted() {
+        return mExpandedWhenExpandingStarted;
+    }
+
+    public int getMinExpansionHeight() {
+        return mMinExpansionHeight;
+    }
+
+    public boolean isFullyExpandedAndTouchesDisallowed() {
+        return isQsFragmentCreated() && getFullyExpanded() && disallowTouches();
+    }
+
+    public int getMaxExpansionHeight() {
+        return mMaxExpansionHeight;
+    }
+
+    private boolean isQsFalseTouch() {
+        if (mFalsingManager.isClassifierEnabled()) {
+            return mFalsingManager.isFalseTouch(Classifier.QUICK_SETTINGS);
+        }
+        return !mTouchAboveFalsingThreshold;
+    }
+
+    public int getFalsingThreshold() {
+        return mFalsingThreshold;
+    }
+
+    /**
+     * Returns Whether we should intercept a gesture to open Quick Settings.
+     */
+    public boolean shouldQuickSettingsIntercept(float x, float y, float yDiff) {
+        boolean keyguardShowing = mBarState == KEYGUARD;
+        if (!isExpansionEnabled() || mCollapsedOnDown || (keyguardShowing
+                && mKeyguardBypassController.getBypassEnabled()) || mSplitShadeEnabled) {
+            return false;
+        }
+        View header = keyguardShowing || mQs == null ? mKeyguardStatusBar : mQs.getHeader();
+        int frameTop = keyguardShowing
+                || mQs == null ? 0 : mQsFrame.getTop();
+        mInterceptRegion.set(
+                /* left= */ (int) mQsFrame.getX(),
+                /* top= */ header.getTop() + frameTop,
+                /* right= */ (int) mQsFrame.getX() + mQsFrame.getWidth(),
+                /* bottom= */ header.getBottom() + frameTop);
+        // Also allow QS to intercept if the touch is near the notch.
+        mStatusBarTouchableRegionManager.updateRegionForNotch(mInterceptRegion);
+        final boolean onHeader = mInterceptRegion.contains((int) x, (int) y);
+
+        if (getExpanded()) {
+            return onHeader || (yDiff < 0 && isTouchInQsArea(x, y));
+        } else {
+            return onHeader;
+        }
+    }
+
+    /** Returns amount header should be translated */
+    private float getHeaderTranslation() {
+        if (mSplitShadeEnabled) {
+            // in split shade QS don't translate, just (un)squish and overshoot
+            return 0;
+        }
+        if (mBarState == KEYGUARD && !mKeyguardBypassController.getBypassEnabled()) {
+            return -mQs.getQsMinExpansionHeight();
+        }
+        float appearAmount = mNotificationStackScrollLayoutController
+                .calculateAppearFraction(mShadeExpandedHeight);
+        float startHeight = -getExpansionHeight();
+        if (mBarState == StatusBarState.SHADE) {
+            // Small parallax as we pull down and clip QS
+            startHeight = -getExpansionHeight() * QS_PARALLAX_AMOUNT;
+        }
+        if (mKeyguardBypassController.getBypassEnabled() && mBarState == KEYGUARD) {
+            appearAmount = mNotificationStackScrollLayoutController.calculateAppearFractionBypass();
+            startHeight = -mQs.getQsMinExpansionHeight();
+        }
+        float translation = MathUtils.lerp(startHeight, 0, Math.min(1.0f, appearAmount));
+        return Math.min(0, translation);
+    }
+
+    /**
+     * Can the panel collapse in this motion because it was started on QQS?
+     *
+     * @param downX the x location where the touch started
+     * @param downY the y location where the touch started
+     * Returns true if the panel could be collapsed because it stared on QQS
+     */
+    public boolean canPanelCollapseOnQQS(float downX, float downY) {
+        if (mCollapsedOnDown || mBarState == KEYGUARD || getExpanded()) {
+            return false;
+        }
+        View header = mQs == null ? mKeyguardStatusBar : mQs.getHeader();
+        return downX >= mQsFrame.getX() && downX <= mQsFrame.getX() + mQsFrame.getWidth()
+                && downY <= header.getBottom();
+    }
+
+    /** Closes the Qs customizer. */
+    public void closeQsCustomizer() {
+        mQs.closeCustomizer();
+    }
+
+    /** Returns whether touches from the notification panel should be disallowed */
+    public boolean disallowTouches() {
+        return mQs.disallowPanelTouches();
+    }
+
+    void setListening(boolean listening) {
+        if (mQs != null) {
+            mQs.setListening(listening);
+        }
+    }
+
+    void hideQsImmediately() {
+        if (mQs != null) {
+            mQs.hideImmediately();
+        }
+    }
+
+    public void setDozing(boolean dozing) {
+        mDozing = dozing;
+    }
+
+    /** set QS state to closed */
+    public void closeQs() {
+        cancelExpansionAnimation();
+        setExpansionHeight(getMinExpansionHeight());
+        // qsExpandImmediate is a safety latch in case we're calling closeQS while we're in the
+        // middle of animation - we need to make sure that value is always false when shade if
+        // fully collapsed or expanded
+        setExpandImmediate(false);
+    }
+
+    @VisibleForTesting
+    void setExpanded(boolean expanded) {
+        boolean changed = mExpanded != expanded;
+        if (changed) {
+            mExpanded = expanded;
+            updateQsState();
+            mShadeExpansionStateManager.onQsExpansionChanged(expanded);
+            mShadeLog.logQsExpansionChanged("QS Expansion Changed.", expanded,
+                    getMinExpansionHeight(), getMaxExpansionHeight(),
+                    mStackScrollerOverscrolling, mAnimatorExpand, mAnimating);
+        }
+    }
+
+    void setLastShadeFlingWasExpanding(boolean expanding) {
+        mLastShadeFlingWasExpanding = expanding;
+        mShadeLog.logLastFlingWasExpanding(expanding);
+    }
+
+    /** update Qs height state */
+    public void setExpansionHeight(float height) {
+        int maxHeight = getMaxExpansionHeight();
+        height = Math.min(Math.max(
+                height, getMinExpansionHeight()), maxHeight);
+        mFullyExpanded = height == maxHeight && maxHeight != 0;
+        boolean qsAnimatingAway = !mAnimatorExpand && mAnimating;
+        if (height > getMinExpansionHeight() && !getExpanded()
+                && !mStackScrollerOverscrolling
+                && !mDozing && !qsAnimatingAway) {
+            setExpanded(true);
+        } else if (height <= getMinExpansionHeight()
+                && getExpanded()) {
+            setExpanded(false);
+        }
+        mExpansionHeight = height;
+        updateExpansion();
+
+        if (mExpansionHeightListener != null) {
+            mExpansionHeightListener.onQsSetExpansionHeightCalled(getFullyExpanded());
+        }
+    }
+
+    /** */
+    public void setHeightOverrideToDesiredHeight() {
+        if (isSizeChangeAnimationRunning() && isQsFragmentCreated()) {
+            mQs.setHeightOverride(mQs.getDesiredHeight());
+        }
+    }
+
+    /** Updates quick setting heights and returns old max height. */
+    int updateHeightsOnShadeLayoutChange() {
+        int oldMaxHeight = getMaxExpansionHeight();
+        if (isQsFragmentCreated()) {
+            updateMinHeight();
+            mMaxExpansionHeight = mQs.getDesiredHeight();
+            mNotificationStackScrollLayoutController.setMaxTopPadding(
+                    getMaxExpansionHeight());
+        }
+        return oldMaxHeight;
+    }
+
+    /** Called when Shade view layout changed. Updates QS expansion or
+     * starts size change animation if height has changed. */
+    void handleShadeLayoutChanged(int oldMaxHeight) {
+        if (mExpanded && mFullyExpanded) {
+            mExpansionHeight = mMaxExpansionHeight;
+            if (mExpansionHeightSetToMaxListener != null) {
+                mExpansionHeightSetToMaxListener.onExpansionHeightSetToMax(true);
+            }
+
+            // Size has changed, start an animation.
+            if (getMaxExpansionHeight() != oldMaxHeight) {
+                startSizeChangeAnimation(oldMaxHeight,
+                        getMaxExpansionHeight());
+            }
+        } else if (!getExpanded()
+                && !isExpansionAnimating()) {
+            setExpansionHeight(getMinExpansionHeight() + mLastOverscroll);
+        } else {
+            mShadeLog.v("onLayoutChange: qs expansion not set");
+        }
+    }
+
+    private boolean isSizeChangeAnimationRunning() {
+        return mSizeChangeAnimator != null;
+    }
+
+    private void startSizeChangeAnimation(int oldHeight, final int newHeight) {
+        if (mSizeChangeAnimator != null) {
+            oldHeight = (int) mSizeChangeAnimator.getAnimatedValue();
+            mSizeChangeAnimator.cancel();
+        }
+        mSizeChangeAnimator = ValueAnimator.ofInt(oldHeight, newHeight);
+        mSizeChangeAnimator.setDuration(300);
+        mSizeChangeAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+        mSizeChangeAnimator.addUpdateListener(animation -> {
+            if (mExpansionHeightSetToMaxListener != null) {
+                mExpansionHeightSetToMaxListener.onExpansionHeightSetToMax(true);
+            }
+
+            int height = (int) mSizeChangeAnimator.getAnimatedValue();
+            mQs.setHeightOverride(height);
+        });
+        mSizeChangeAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mSizeChangeAnimator = null;
+            }
+        });
+        mSizeChangeAnimator.start();
+    }
+
+    void setNotificationPanelFullWidth(boolean isFullWidth) {
+        mIsFullWidth = isFullWidth;
+        if (mQs != null) {
+            mQs.setIsNotificationPanelFullWidth(isFullWidth);
+        }
+    }
+
+    void setBarState(int barState) {
+        mBarState = barState;
+    }
+
+    /** */
+    public void setExpansionEnabledPolicy(boolean expansionEnabledPolicy) {
+        mExpansionEnabledPolicy = expansionEnabledPolicy;
+        if (mQs != null) {
+            mQs.setHeaderClickable(isExpansionEnabled());
+        }
+    }
+
+    private void setOverScrolling(boolean overscrolling) {
+        mStackScrollerOverscrolling = overscrolling;
+        if (mQs != null) {
+            mQs.setOverscrolling(overscrolling);
+        }
+    }
+
+    /** Sets Qs ScrimEnabled and updates QS state. */
+    public void setScrimEnabled(boolean scrimEnabled) {
+        boolean changed = mScrimEnabled != scrimEnabled;
+        mScrimEnabled = scrimEnabled;
+        if (changed) {
+            updateQsState();
+        }
+    }
+
+    void setCollapsedOnDown(boolean collapsedOnDown) {
+        mCollapsedOnDown = collapsedOnDown;
+    }
+
+    void setShadeExpandedHeight(float shadeExpandedHeight) {
+        mShadeExpandedHeight = shadeExpandedHeight;
+    }
+
+    @VisibleForTesting
+    float getShadeExpandedHeight() {
+        return mShadeExpandedHeight;
+    }
+
+    @VisibleForTesting
+    void setExpandImmediate(boolean expandImmediate) {
+        if (expandImmediate != mExpandImmediate) {
+            mShadeLog.logQsExpandImmediateChanged(expandImmediate);
+            mExpandImmediate = expandImmediate;
+            mShadeExpansionStateManager.notifyExpandImmediateChange(expandImmediate);
+        }
+    }
+
+    void setTwoFingerExpandPossible(boolean expandPossible) {
+        mTwoFingerExpandPossible = expandPossible;
+    }
+
+    /** Called when Qs starts expanding */
+    private void onExpansionStarted() {
+        cancelExpansionAnimation();
+        // TODO (b/265193930): remove dependency on NPVC
+        mPanelViewControllerLazy.get().cancelHeightAnimator();
+        // end
+
+        // Reset scroll position and apply that position to the expanded height.
+        float height = mExpansionHeight;
+        setExpansionHeight(height);
+        mNotificationStackScrollLayoutController.checkSnoozeLeavebehind();
+
+        // When expanding QS, let's authenticate the user if possible,
+        // this will speed up notification actions.
+        if (height == 0 && !mKeyguardStateController.canDismissLockScreen()) {
+            mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.QS_EXPANDED);
+        }
+    }
+
+    void updateQsState() {
+        boolean qsFullScreen = mExpanded && !mSplitShadeEnabled;
+        mNotificationStackScrollLayoutController.setQsFullScreen(qsFullScreen);
+        mNotificationStackScrollLayoutController.setScrollingEnabled(
+                mBarState != KEYGUARD && (!qsFullScreen || mExpansionFromOverscroll));
+
+        if (mQsStateUpdateListener != null) {
+            mQsStateUpdateListener.onQsStateUpdated(mExpanded, mStackScrollerOverscrolling);
+        }
+
+        if (mQs == null) return;
+        mQs.setExpanded(mExpanded);
+    }
+
+    /** update expanded state of QS */
+    public void updateExpansion() {
+        if (mQs == null) return;
+        final float squishiness;
+        if ((mExpandImmediate || mExpanded) && !mSplitShadeEnabled) {
+            squishiness = 1;
+        } else if (mTransitioningToFullShadeProgress > 0.0f) {
+            squishiness = mLockscreenShadeTransitionController.getQsSquishTransitionFraction();
+        } else {
+            squishiness = mNotificationStackScrollLayoutController
+                    .getNotificationSquishinessFraction();
+        }
+        final float qsExpansionFraction = computeExpansionFraction();
+        final float adjustedExpansionFraction = mSplitShadeEnabled
+                ? 1f : computeExpansionFraction();
+        mQs.setQsExpansion(
+                adjustedExpansionFraction,
+                mShadeExpandedFraction,
+                getHeaderTranslation(),
+                squishiness
+        );
+        mMediaHierarchyManager.setQsExpansion(qsExpansionFraction);
+        int qsPanelBottomY = calculateBottomPosition(qsExpansionFraction);
+        mScrimController.setQsPosition(qsExpansionFraction, qsPanelBottomY);
+        setClippingBounds();
+
+        if (mSplitShadeEnabled) {
+            // In split shade we want to pretend that QS are always collapsed so their behaviour and
+            // interactions don't influence notifications as they do in portrait. But we want to set
+            // 0 explicitly in case we're rotating from non-split shade with QS expansion of 1.
+            mNotificationStackScrollLayoutController.setQsExpansionFraction(0);
+        } else {
+            mNotificationStackScrollLayoutController.setQsExpansionFraction(qsExpansionFraction);
+        }
+
+        mDepthController.setQsPanelExpansion(qsExpansionFraction);
+        mStatusBarKeyguardViewManager.setQsExpansion(qsExpansionFraction);
+
+        // TODO (b/265193930): remove dependency on NPVC
+        float shadeExpandedFraction = mBarState == KEYGUARD
+                ? mPanelViewControllerLazy.get().getLockscreenShadeDragProgress()
+                : mShadeExpandedFraction;
+        mLargeScreenShadeHeaderController.setShadeExpandedFraction(shadeExpandedFraction);
+        mLargeScreenShadeHeaderController.setQsExpandedFraction(qsExpansionFraction);
+        mLargeScreenShadeHeaderController.setQsVisible(mVisible);
+    }
+
+    /** */
+    public void updateExpansionEnabledAmbient() {
+        final float scrollRangeToTop = mAmbientState.getTopPadding() - mQuickQsHeaderHeight;
+        mExpansionEnabledAmbient = mSplitShadeEnabled
+                || (mAmbientState.getScrollY() <= scrollRangeToTop);
+        if (mQs != null) {
+            mQs.setHeaderClickable(isExpansionEnabled());
+        }
+    }
+
+    /** Calculate y value of bottom of QS */
+    private int calculateBottomPosition(float qsExpansionFraction) {
+        if (mTransitioningToFullShadeProgress > 0.0f) {
+            return mTransitionToFullShadePosition;
+        } else {
+            int qsBottomYFrom = (int) getHeaderTranslation() + mQs.getQsMinExpansionHeight();
+            int expandedTopMargin = mUseLargeScreenShadeHeader ? mLargeScreenShadeHeaderHeight : 0;
+            int qsBottomYTo = mQs.getDesiredHeight() + expandedTopMargin;
+            return (int) MathUtils.lerp(qsBottomYFrom, qsBottomYTo, qsExpansionFraction);
+        }
+    }
+
+    /** Calculate fraction of current QS expansion state */
+    public float computeExpansionFraction() {
+        if (mAnimatingHiddenFromCollapsed) {
+            // When hiding QS from collapsed state, the expansion can sometimes temporarily
+            // be larger than 0 because of the timing, leading to flickers.
+            return 0.0f;
+        }
+        return Math.min(
+                1f, (mExpansionHeight - mMinExpansionHeight) / (mMaxExpansionHeight
+                        - mMinExpansionHeight));
+    }
+
+    void updateMinHeight() {
+        float previousMin = mMinExpansionHeight;
+        if (mBarState == KEYGUARD || mSplitShadeEnabled) {
+            mMinExpansionHeight = 0;
+        } else {
+            mMinExpansionHeight = mQs.getQsMinExpansionHeight();
+        }
+        if (mExpansionHeight == previousMin) {
+            mExpansionHeight = mMinExpansionHeight;
+        }
+    }
+
+    void updateQsFrameTranslation() {
+        // TODO (b/265193930): remove dependency on NPVC
+        mQsFrameTranslateController.translateQsFrame(mQsFrame, mQs,
+                mPanelViewControllerLazy.get().getNavigationBarBottomHeight()
+                        + mAmbientState.getStackTopMargin());
+    }
+
+    /** Called when shade starts expanding. */
+    public void onExpandingStarted(boolean qsFullyExpanded) {
+        mNotificationStackScrollLayoutController.onExpansionStarted();
+        mExpandedWhenExpandingStarted = qsFullyExpanded;
+        mMediaHierarchyManager.setCollapsingShadeFromQS(mExpandedWhenExpandingStarted
+                /* We also start expanding when flinging closed Qs. Let's exclude that */
+                && !mAnimating);
+        if (mExpanded) {
+            onExpansionStarted();
+        }
+        // Since there are QS tiles in the header now, we need to make sure we start listening
+        // immediately so they can be up to date.
+        if (mQs == null) return;
+        mQs.setHeaderListening(true);
+    }
+
+    /** Set animate next notification bounds. */
+    private void setAnimateNextNotificationBounds(long duration, long delay) {
+        mAnimateNextNotificationBounds = true;
+        mNotificationBoundsAnimationDuration = duration;
+        mNotificationBoundsAnimationDelay = delay;
+    }
+
+    /**
+     * Updates scrim bounds, QS clipping, notifications clipping and keyguard status view clipping
+     * as well based on the bounds of the shade and QS state.
+     */
+    private void setClippingBounds() {
+        float qsExpansionFraction = computeExpansionFraction();
+        final int qsPanelBottomY = calculateBottomPosition(qsExpansionFraction);
+        final boolean qsVisible = (qsExpansionFraction > 0 || qsPanelBottomY > 0);
+        checkCorrectScrimVisibility(qsExpansionFraction);
+
+        int top = calculateTopClippingBound(qsPanelBottomY);
+        int bottom = calculateBottomClippingBound(top);
+        int left = calculateLeftClippingBound();
+        int right = calculateRightClippingBound();
+        // top should never be lower than bottom, otherwise it will be invisible.
+        top = Math.min(top, bottom);
+        applyClippingBounds(left, top, right, bottom, qsVisible);
+    }
+
+    /**
+     * Applies clipping to quick settings, notifications layout and
+     * updates bounds of the notifications background (notifications scrim).
+     *
+     * The parameters are bounds of the notifications area rectangle, this function
+     * calculates bounds for the QS clipping based on the notifications bounds.
+     */
+    private void applyClippingBounds(int left, int top, int right, int bottom,
+            boolean qsVisible) {
+        if (!mAnimateNextNotificationBounds || mLastClipBounds.isEmpty()) {
+            if (mClippingAnimator != null) {
+                // update the end position of the animator
+                mClippingAnimationEndBounds.set(left, top, right, bottom);
+            } else {
+                applyClippingImmediately(left, top, right, bottom, qsVisible);
+            }
+        } else {
+            mClippingAnimationEndBounds.set(left, top, right, bottom);
+            final int startLeft = mLastClipBounds.left;
+            final int startTop = mLastClipBounds.top;
+            final int startRight = mLastClipBounds.right;
+            final int startBottom = mLastClipBounds.bottom;
+            if (mClippingAnimator != null) {
+                mClippingAnimator.cancel();
+            }
+            mClippingAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
+            mClippingAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+            mClippingAnimator.setDuration(mNotificationBoundsAnimationDuration);
+            mClippingAnimator.setStartDelay(mNotificationBoundsAnimationDelay);
+            mClippingAnimator.addUpdateListener(animation -> {
+                float fraction = animation.getAnimatedFraction();
+                int animLeft = (int) MathUtils.lerp(startLeft,
+                        mClippingAnimationEndBounds.left, fraction);
+                int animTop = (int) MathUtils.lerp(startTop,
+                        mClippingAnimationEndBounds.top, fraction);
+                int animRight = (int) MathUtils.lerp(startRight,
+                        mClippingAnimationEndBounds.right, fraction);
+                int animBottom = (int) MathUtils.lerp(startBottom,
+                        mClippingAnimationEndBounds.bottom, fraction);
+                applyClippingImmediately(animLeft, animTop, animRight, animBottom,
+                        qsVisible /* qsVisible */);
+            });
+            mClippingAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mClippingAnimator = null;
+                    mIsTranslationResettingAnimator = false;
+                    mIsPulseExpansionResettingAnimator = false;
+                }
+            });
+            mClippingAnimator.start();
+        }
+        mAnimateNextNotificationBounds = false;
+        mNotificationBoundsAnimationDelay = 0;
+    }
+
+    private void applyClippingImmediately(int left, int top, int right, int bottom,
+            boolean qsVisible) {
+        int radius = mScrimCornerRadius;
+        boolean clipStatusView = false;
+        mLastClipBounds.set(left, top, right, bottom);
+        if (mIsFullWidth) {
+            clipStatusView = qsVisible;
+            float screenCornerRadius = mRecordingController.isRecording() ? 0 : mScreenCornerRadius;
+            radius = (int) MathUtils.lerp(screenCornerRadius, mScrimCornerRadius,
+                    Math.min(top / (float) mScrimCornerRadius, 1f));
+        }
+        if (isQsFragmentCreated()) {
+            float qsTranslation = 0;
+            boolean pulseExpanding = mPulseExpansionHandler.isExpanding();
+            if (mTransitioningToFullShadeProgress > 0.0f
+                    || pulseExpanding || (mClippingAnimator != null
+                    && (mIsTranslationResettingAnimator || mIsPulseExpansionResettingAnimator))) {
+                if (pulseExpanding || mIsPulseExpansionResettingAnimator) {
+                    // qsTranslation should only be positive during pulse expansion because it's
+                    // already translating in from the top
+                    qsTranslation = Math.max(0, (top - getHeaderHeight()) / 2.0f);
+                } else if (!mSplitShadeEnabled) {
+                    qsTranslation = (top - getHeaderHeight()) * QS_PARALLAX_AMOUNT;
+                }
+            }
+            mTranslationForFullShadeTransition = qsTranslation;
+            updateQsFrameTranslation();
+            float currentTranslation = mQsFrame.getTranslationY();
+            int clipTop = mEnableClipping
+                    ? (int) (top - currentTranslation - mQsFrame.getTop()) : 0;
+            int clipBottom = mEnableClipping
+                    ? (int) (bottom - currentTranslation - mQsFrame.getTop()) : 0;
+            mVisible = qsVisible;
+            mQs.setQsVisible(qsVisible);
+            mQs.setFancyClipping(
+                    clipTop,
+                    clipBottom,
+                    radius,
+                    qsVisible && !mSplitShadeEnabled);
+
+        }
+
+        // Increase the height of the notifications scrim when not in split shade
+        // (e.g. portrait tablet) so the rounded corners are not visible at the bottom,
+        // in this case they are rendered off-screen
+        final int notificationsScrimBottom = mSplitShadeEnabled ? bottom : bottom + radius;
+        mScrimController.setNotificationsBounds(left, top, right, notificationsScrimBottom);
+
+        if (mApplyClippingImmediatelyListener != null) {
+            mApplyClippingImmediatelyListener.onQsClippingImmediatelyApplied(clipStatusView,
+                    mLastClipBounds, top, isQsFragmentCreated(), mVisible);
+        }
+
+        mScrimController.setScrimCornerRadius(radius);
+
+        // Convert global clipping coordinates to local ones,
+        // relative to NotificationStackScrollLayout
+        int nsslLeft = calculateNsslLeft(left);
+        int nsslRight = calculateNsslRight(right);
+        int nsslTop = getNotificationsClippingTopBounds(top);
+        int nsslBottom = bottom - mNotificationStackScrollLayoutController.getTop();
+        int bottomRadius = mSplitShadeEnabled ? radius : 0;
+        // TODO (b/265193930): remove dependency on NPVC
+        int topRadius = mSplitShadeEnabled
+                && mPanelViewControllerLazy.get().isExpandingFromHeadsUp() ? 0 : radius;
+        mNotificationStackScrollLayoutController.setRoundedClippingBounds(
+                nsslLeft, nsslTop, nsslRight, nsslBottom, topRadius, bottomRadius);
+    }
+
+    void setDisplayInsets(int leftInset, int rightInset) {
+        mDisplayLeftInset = leftInset;
+        mDisplayRightInset = rightInset;
+    }
+
+    private int calculateNsslLeft(int nsslLeftAbsolute) {
+        int left = nsslLeftAbsolute - mNotificationStackScrollLayoutController.getLeft();
+        if (mIsFullWidth) {
+            return left;
+        }
+        return left - mDisplayLeftInset;
+    }
+
+    private int calculateNsslRight(int nsslRightAbsolute) {
+        int right = nsslRightAbsolute - mNotificationStackScrollLayoutController.getLeft();
+        if (mIsFullWidth) {
+            return right;
+        }
+        return right - mDisplayLeftInset;
+    }
+
+    private int getNotificationsClippingTopBounds(int qsTop) {
+        // TODO (b/265193930): remove dependency on NPVC
+        if (mSplitShadeEnabled && mPanelViewControllerLazy.get().isExpandingFromHeadsUp()) {
+            // in split shade nssl has extra top margin so clipping at top 0 is not enough, we need
+            // to set top clipping bound to negative value to allow HUN to go up to the top edge of
+            // the screen without clipping.
+            return -mAmbientState.getStackTopMargin();
+        } else {
+            return qsTop - mNotificationStackScrollLayoutController.getTop();
+        }
+    }
+
+    private void checkCorrectScrimVisibility(float expansionFraction) {
+        // issues with scrims visible on keyguard occur only in split shade
+        if (mSplitShadeEnabled) {
+            // TODO (b/265193930): remove dependency on NPVC
+            boolean keyguardViewsVisible = mBarState == KEYGUARD
+                            && mPanelViewControllerLazy.get().getKeyguardOnlyContentAlpha() == 1;
+            // expansionFraction == 1 means scrims are fully visible as their size/visibility depend
+            // on QS expansion
+            if (expansionFraction == 1 && keyguardViewsVisible) {
+                Log.wtf(TAG,
+                        "Incorrect state, scrim is visible at the same time when clock is visible");
+            }
+        }
+    }
+
+    /** Calculate top padding for notifications */
+    public float calculateNotificationsTopPadding(boolean isShadeExpanding,
+            int keyguardNotificationStaticPadding, float expandedFraction) {
+        boolean keyguardShowing = mBarState == KEYGUARD;
+        if (mSplitShadeEnabled) {
+            return keyguardShowing
+                    ? keyguardNotificationStaticPadding : 0;
+        }
+        if (keyguardShowing && (isExpandImmediate()
+                || isShadeExpanding && getExpandedWhenExpandingStarted())) {
+
+            // Either QS pushes the notifications down when fully expanded, or QS is fully above the
+            // notifications (mostly on tablets). maxNotificationPadding denotes the normal top
+            // padding on Keyguard, maxQsPadding denotes the top padding from the quick settings
+            // panel. We need to take the maximum and linearly interpolate with the panel expansion
+            // for a nice motion.
+            int maxQsPadding = getMaxExpansionHeight();
+            int max = keyguardShowing ? Math.max(
+                    keyguardNotificationStaticPadding, maxQsPadding) : maxQsPadding;
+            return (int) MathUtils.lerp((float) getMinExpansionHeight(),
+                    (float) max, expandedFraction);
+        } else if (isSizeChangeAnimationRunning()) {
+            return Math.max((int) mSizeChangeAnimator.getAnimatedValue(),
+                    keyguardNotificationStaticPadding);
+        } else if (keyguardShowing) {
+            // We can only do the smoother transition on Keyguard when we also are not collapsing
+            // from a scrolled quick settings.
+            return MathUtils.lerp((float) keyguardNotificationStaticPadding,
+                    (float) (getMaxExpansionHeight()), computeExpansionFraction());
+        } else {
+            return mQsFrameTranslateController.getNotificationsTopPadding(
+                    mExpansionHeight, mNotificationStackScrollLayoutController);
+        }
+    }
+
+    /** Calculate height of QS panel */
+    public int calculatePanelHeightExpanded(int stackScrollerPadding) {
+        float
+                notificationHeight =
+                mNotificationStackScrollLayoutController.getHeight()
+                        - mNotificationStackScrollLayoutController.getEmptyBottomMargin()
+                        - mNotificationStackScrollLayoutController.getTopPadding();
+
+        // When only empty shade view is visible in QS collapsed state, simulate that we would have
+        // it in expanded QS state as well so we don't run into troubles when fading the view in/out
+        // and expanding/collapsing the whole panel from/to quick settings.
+        if (mNotificationStackScrollLayoutController.getNotGoneChildCount() == 0
+                && mNotificationStackScrollLayoutController.isShowingEmptyShadeView()) {
+            notificationHeight = mNotificationStackScrollLayoutController.getEmptyShadeViewHeight();
+        }
+        int maxQsHeight = mMaxExpansionHeight;
+
+        // If an animation is changing the size of the QS panel, take the animated value.
+        if (mSizeChangeAnimator != null) {
+            maxQsHeight = (int) mSizeChangeAnimator.getAnimatedValue();
+        }
+        float totalHeight = Math.max(maxQsHeight, mBarState == KEYGUARD ? stackScrollerPadding : 0)
+                + notificationHeight
+                + mNotificationStackScrollLayoutController.getTopPaddingOverflow();
+        if (totalHeight > mNotificationStackScrollLayoutController.getHeight()) {
+            float
+                    fullyCollapsedHeight =
+                    maxQsHeight + mNotificationStackScrollLayoutController.getLayoutMinHeight();
+            totalHeight = Math.max(fullyCollapsedHeight,
+                    mNotificationStackScrollLayoutController.getHeight());
+        }
+        return (int) totalHeight;
+    }
+
+    private float getEdgePosition() {
+        // TODO: replace StackY with unified calculation
+        return Math.max(mQuickQsHeaderHeight * mAmbientState.getExpansionFraction(),
+                mAmbientState.getStackY()
+                        // need to adjust for extra margin introduced by large screen shade header
+                        + mAmbientState.getStackTopMargin() * mAmbientState.getExpansionFraction()
+                        - mAmbientState.getScrollY());
+    }
+
+    private int calculateTopClippingBound(int qsPanelBottomY) {
+        int top;
+        if (mSplitShadeEnabled) {
+            top = Math.min(qsPanelBottomY, mLargeScreenShadeHeaderHeight);
+        } else {
+            if (mTransitioningToFullShadeProgress > 0.0f) {
+                // If we're transitioning, let's use the actual value. The else case
+                // can be wrong during transitions when waiting for the keyguard to unlock
+                top = mTransitionToFullShadePosition;
+            } else {
+                final float notificationTop = getEdgePosition();
+                if (mBarState == KEYGUARD) {
+                    if (mKeyguardBypassController.getBypassEnabled()) {
+                        // When bypassing on the keyguard, let's use the panel bottom.
+                        // this should go away once we unify the stackY position and don't have
+                        // to do this min anymore below.
+                        top = qsPanelBottomY;
+                    } else {
+                        top = (int) Math.min(qsPanelBottomY, notificationTop);
+                    }
+                } else {
+                    top = (int) notificationTop;
+                }
+            }
+            // TODO (b/265193930): remove dependency on NPVC
+            top += mPanelViewControllerLazy.get().getOverStretchAmount();
+            // Correction for instant expansion caused by HUN pull down/
+            float minFraction = mPanelViewControllerLazy.get().getMinFraction();
+            if (minFraction > 0f && minFraction < 1f) {
+                float realFraction = (mShadeExpandedFraction
+                        - minFraction) / (1f - minFraction);
+                top *= MathUtils.saturate(realFraction / minFraction);
+            }
+        }
+        return top;
+    }
+
+    private int calculateBottomClippingBound(int top) {
+        if (mSplitShadeEnabled) {
+            return top + mNotificationStackScrollLayoutController.getHeight()
+                    + mSplitShadeNotificationsScrimMarginBottom;
+        } else {
+            return mPanelView.getBottom();
+        }
+    }
+
+    private int calculateLeftClippingBound() {
+        if (mIsFullWidth) {
+            // left bounds can ignore insets, it should always reach the edge of the screen
+            return 0;
+        } else {
+            return mNotificationStackScrollLayoutController.getLeft()
+                    + mDisplayLeftInset;
+        }
+    }
+
+    private int calculateRightClippingBound() {
+        if (mIsFullWidth) {
+            return mPanelView.getRight()
+                    + mDisplayRightInset;
+        } else {
+            return mNotificationStackScrollLayoutController.getRight()
+                    + mDisplayLeftInset;
+        }
+    }
+
+    private void trackMovement(MotionEvent event) {
+        if (mQsVelocityTracker != null) mQsVelocityTracker.addMovement(event);
+    }
+
+    private void initVelocityTracker() {
+        if (mQsVelocityTracker != null) {
+            mQsVelocityTracker.recycle();
+        }
+        mQsVelocityTracker = VelocityTracker.obtain();
+    }
+
+    private float getCurrentVelocity() {
+        if (mQsVelocityTracker == null) {
+            return 0;
+        }
+        mQsVelocityTracker.computeCurrentVelocity(1000);
+        return mQsVelocityTracker.getYVelocity();
+    }
+
+    boolean updateAndGetTouchAboveFalsingThreshold() {
+        mTouchAboveFalsingThreshold = mFullyExpanded;
+        return mTouchAboveFalsingThreshold;
+    }
+
+    private void onHeightChanged() {
+        mMaxExpansionHeight = isQsFragmentCreated() ? mQs.getDesiredHeight() : 0;
+        if (mExpanded && mFullyExpanded) {
+            mExpansionHeight = mMaxExpansionHeight;
+            if (mExpansionHeightSetToMaxListener != null) {
+                mExpansionHeightSetToMaxListener.onExpansionHeightSetToMax(true);
+            }
+        }
+        if (mAccessibilityManager.isEnabled()) {
+            // TODO (b/265193930): remove dependency on NPVC
+            mPanelView.setAccessibilityPaneTitle(
+                    mPanelViewControllerLazy.get().determineAccessibilityPaneTitle());
+        }
+        mNotificationStackScrollLayoutController.setMaxTopPadding(mMaxExpansionHeight);
+    }
+
+    private void collapseOrExpandQs() {
+        if (mSplitShadeEnabled) {
+            return; // QS is always expanded in split shade
+        }
+        onExpansionStarted();
+        if (getExpanded()) {
+            flingQs(0, FLING_COLLAPSE, null, true);
+        } else if (isExpansionEnabled()) {
+            mLockscreenGestureLogger.write(MetricsProto.MetricsEvent.ACTION_SHADE_QS_TAP, 0, 0);
+            flingQs(0, FLING_EXPAND, null, true);
+        }
+    }
+
+    private void onScroll(int scrollY) {
+        mLargeScreenShadeHeaderController.setQsScrollY(scrollY);
+        if (scrollY > 0 && !mFullyExpanded) {
+            // TODO (b/265193930): remove dependency on NPVC
+            // If we are scrolling QS, we should be fully expanded.
+            mPanelViewControllerLazy.get().expandWithQs();
+        }
+    }
+
+    @VisibleForTesting
+    boolean isTrackingBlocked() {
+        return mConflictingExpansionGesture && getExpanded();
+    }
+
+    boolean isExpansionAnimating() {
+        return mExpansionAnimator != null;
+    }
+
+    @VisibleForTesting
+    boolean isConflictingExpansionGesture() {
+        return mConflictingExpansionGesture;
+    }
+
+    /** handles touches in Qs panel area */
+    public boolean handleTouch(MotionEvent event, boolean isFullyCollapsed,
+            boolean isShadeOrQsHeightAnimationRunning) {
+        if (isSplitShadeAndTouchXOutsideQs(event.getX())) {
+            return false;
+        }
+        final int action = event.getActionMasked();
+        boolean collapsedQs = !getExpanded() && !mSplitShadeEnabled;
+        boolean expandedShadeCollapsedQs = mShadeExpandedFraction == 1f
+                && mBarState != KEYGUARD && collapsedQs && isExpansionEnabled();
+        if (action == MotionEvent.ACTION_DOWN && expandedShadeCollapsedQs) {
+            // Down in the empty area while fully expanded - go to QS.
+            mShadeLog.logMotionEvent(event, "handleQsTouch: down action, QS tracking enabled");
+            mTracking = true;
+            traceQsJank(true, false);
+            mConflictingExpansionGesture = true;
+            onExpansionStarted();
+            mInitialHeightOnTouch = mExpansionHeight;
+            mInitialTouchY = event.getY();
+            mInitialTouchX = event.getX();
+        }
+        if (!isFullyCollapsed && !isShadeOrQsHeightAnimationRunning) {
+            handleDown(event);
+        }
+        // defer touches on QQS to shade while shade is collapsing. Added margin for error
+        // as sometimes the qsExpansionFraction can be a tiny value instead of 0 when in QQS.
+        if (!mSplitShadeEnabled && !mLastShadeFlingWasExpanding
+                && computeExpansionFraction() <= 0.01 && mShadeExpandedFraction < 1.0) {
+            mShadeLog.logMotionEvent(event,
+                    "handleQsTouch: shade touched while shade collapsing, QS tracking disabled");
+            mTracking = false;
+        }
+        if (!isExpandImmediate() && mTracking) {
+            onTouch(event);
+            if (!mConflictingExpansionGesture && !mSplitShadeEnabled) {
+                mShadeLog.logMotionEvent(event,
+                        "handleQsTouch: not immediate expand or conflicting gesture");
+                return true;
+            }
+        }
+        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
+            mConflictingExpansionGesture = false;
+        }
+        if (action == MotionEvent.ACTION_DOWN && isFullyCollapsed && isExpansionEnabled()) {
+            mTwoFingerExpandPossible = true;
+        }
+        if (mTwoFingerExpandPossible && isOpenQsEvent(event)
+                && event.getY(event.getActionIndex())
+                < mStatusBarMinHeight) {
+            mMetricsLogger.count(COUNTER_PANEL_OPEN_QS, 1);
+            setExpandImmediate(true);
+            mNotificationStackScrollLayoutController.setShouldShowShelfOnly(!mSplitShadeEnabled);
+            if (mExpansionHeightSetToMaxListener != null) {
+                mExpansionHeightSetToMaxListener.onExpansionHeightSetToMax(false);
+            }
+
+            // Normally, we start listening when the panel is expanded, but here we need to start
+            // earlier so the state is already up to date when dragging down.
+            setListening(true);
+        }
+        return false;
+    }
+
+    private void handleDown(MotionEvent event) {
+        if (event.getActionMasked() == MotionEvent.ACTION_DOWN
+                && shouldQuickSettingsIntercept(event.getX(), event.getY(), -1)) {
+            mFalsingCollector.onQsDown();
+            mShadeLog.logMotionEvent(event, "handleQsDown: down action, QS tracking enabled");
+            mTracking = true;
+            onExpansionStarted();
+            mInitialHeightOnTouch = mExpansionHeight;
+            mInitialTouchY = event.getY();
+            mInitialTouchX = event.getX();
+            // TODO (b/265193930): remove dependency on NPVC
+            // If we interrupt an expansion gesture here, make sure to update the state correctly.
+            mPanelViewControllerLazy.get().notifyExpandingFinished();
+        }
+    }
+
+    private void onTouch(MotionEvent event) {
+        int pointerIndex = event.findPointerIndex(mTrackingPointer);
+        if (pointerIndex < 0) {
+            pointerIndex = 0;
+            mTrackingPointer = event.getPointerId(pointerIndex);
+        }
+        final float y = event.getY(pointerIndex);
+        final float x = event.getX(pointerIndex);
+        final float h = y - mInitialTouchY;
+
+        switch (event.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN:
+                mShadeLog.logMotionEvent(event, "onQsTouch: down action, QS tracking enabled");
+                mTracking = true;
+                traceQsJank(true, false);
+                mInitialTouchY = y;
+                mInitialTouchX = x;
+                onExpansionStarted();
+                mInitialHeightOnTouch = mExpansionHeight;
+                initVelocityTracker();
+                trackMovement(event);
+                break;
+
+            case MotionEvent.ACTION_POINTER_UP:
+                final int upPointer = event.getPointerId(event.getActionIndex());
+                if (mTrackingPointer == upPointer) {
+                    // gesture is ongoing, find a new pointer to track
+                    final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+                    final float newY = event.getY(newIndex);
+                    final float newX = event.getX(newIndex);
+                    mTrackingPointer = event.getPointerId(newIndex);
+                    mInitialHeightOnTouch = mExpansionHeight;
+                    mInitialTouchY = newY;
+                    mInitialTouchX = newX;
+                }
+                break;
+
+            case MotionEvent.ACTION_MOVE:
+                mShadeLog.logMotionEvent(event, "onQsTouch: move action, setting QS expansion");
+                setExpansionHeight(h + mInitialHeightOnTouch);
+                // TODO (b/265193930): remove dependency on NPVC
+                if (h >= mPanelViewControllerLazy.get().getFalsingThreshold()) {
+                    mTouchAboveFalsingThreshold = true;
+                }
+                trackMovement(event);
+                break;
+
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                mShadeLog.logMotionEvent(event,
+                        "onQsTouch: up/cancel action, QS tracking disabled");
+                mTracking = false;
+                mTrackingPointer = -1;
+                trackMovement(event);
+                float fraction = computeExpansionFraction();
+                if (fraction != 0f || y >= mInitialTouchY) {
+                    flingQsWithCurrentVelocity(y,
+                            event.getActionMasked() == MotionEvent.ACTION_CANCEL);
+                } else {
+                    traceQsJank(false,
+                            event.getActionMasked() == MotionEvent.ACTION_CANCEL);
+                }
+                if (mQsVelocityTracker != null) {
+                    mQsVelocityTracker.recycle();
+                    mQsVelocityTracker = null;
+                }
+                break;
+        }
+    }
+
+    /** intercepts touches on Qs panel area. */
+    public boolean onIntercept(MotionEvent event) {
+        int pointerIndex = event.findPointerIndex(mTrackingPointer);
+        if (pointerIndex < 0) {
+            pointerIndex = 0;
+            mTrackingPointer = event.getPointerId(pointerIndex);
+        }
+        final float x = event.getX(pointerIndex);
+        final float y = event.getY(pointerIndex);
+
+        switch (event.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN:
+                mInitialTouchY = y;
+                mInitialTouchX = x;
+                initVelocityTracker();
+                trackMovement(event);
+                float qsExpansionFraction = computeExpansionFraction();
+                // Intercept the touch if QS is between fully collapsed and fully expanded state
+                if (!mSplitShadeEnabled
+                        && qsExpansionFraction > 0.0 && qsExpansionFraction < 1.0) {
+                    mShadeLog.logMotionEvent(event,
+                            "onQsIntercept: down action, QS partially expanded/collapsed");
+                    return true;
+                }
+                // TODO (b/265193930): remove dependency on NPVC
+                if (mPanelViewControllerLazy.get().getKeyguardShowing()
+                        && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) {
+                    // Dragging down on the lockscreen statusbar should prohibit other interactions
+                    // immediately, otherwise we'll wait on the touchslop. This is to allow
+                    // dragging down to expanded quick settings directly on the lockscreen.
+                    mPanelView.getParent().requestDisallowInterceptTouchEvent(true);
+                }
+                if (mExpansionAnimator != null) {
+                    mInitialHeightOnTouch = mExpansionHeight;
+                    mShadeLog.logMotionEvent(event,
+                            "onQsIntercept: down action, QS tracking enabled");
+                    mTracking = true;
+                    traceQsJank(true, false);
+                    mNotificationStackScrollLayoutController.cancelLongPress();
+                }
+                break;
+            case MotionEvent.ACTION_POINTER_UP:
+                final int upPointer = event.getPointerId(event.getActionIndex());
+                if (mTrackingPointer == upPointer) {
+                    // gesture is ongoing, find a new pointer to track
+                    final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+                    mTrackingPointer = event.getPointerId(newIndex);
+                    mInitialTouchX = event.getX(newIndex);
+                    mInitialTouchY = event.getY(newIndex);
+                }
+                break;
+
+            case MotionEvent.ACTION_MOVE:
+                final float h = y - mInitialTouchY;
+                trackMovement(event);
+                if (mTracking) {
+
+                    // Already tracking because onOverscrolled was called. We need to update here
+                    // so we don't stop for a frame until the next touch event gets handled in
+                    // onTouchEvent.
+                    setExpansionHeight(h + mInitialHeightOnTouch);
+                    trackMovement(event);
+                    return true;
+                } else {
+                    mShadeLog.logMotionEvent(event,
+                            "onQsIntercept: move ignored because qs tracking disabled");
+                }
+                // TODO (b/265193930): remove dependency on NPVC
+                float touchSlop = event.getClassification()
+                        == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE
+                        ? mTouchSlop * mSlopMultiplier
+                        : mTouchSlop;
+                if ((h > touchSlop || (h < -touchSlop && getExpanded()))
+                        && Math.abs(h) > Math.abs(x - mInitialTouchX)
+                        && shouldQuickSettingsIntercept(
+                        mInitialTouchX, mInitialTouchY, h)) {
+                    mPanelView.getParent().requestDisallowInterceptTouchEvent(true);
+                    mShadeLog.onQsInterceptMoveQsTrackingEnabled(h);
+                    mTracking = true;
+                    traceQsJank(true, false);
+                    onExpansionStarted();
+                    mPanelViewControllerLazy.get().notifyExpandingFinished();
+                    mInitialHeightOnTouch = mExpansionHeight;
+                    mInitialTouchY = y;
+                    mInitialTouchX = x;
+                    mNotificationStackScrollLayoutController.cancelLongPress();
+                    return true;
+                } else {
+                    mShadeLog.logQsTrackingNotStarted(mInitialTouchY, y, h, touchSlop,
+                            getExpanded(), mPanelViewControllerLazy.get().getKeyguardShowing(),
+                            isExpansionEnabled());
+                }
+                break;
+
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP:
+                trackMovement(event);
+                mShadeLog.logMotionEvent(event, "onQsIntercept: up action, QS tracking disabled");
+                mTracking = false;
+                break;
+        }
+        return false;
+    }
+
+    private void onPanelExpansionChanged(ShadeExpansionChangeEvent event) {
+        mShadeExpandedFraction = event.getFraction();
+    }
+
+    /**
+     * Animate QS closing by flinging it.
+     * If QS is expanded, it will collapse into QQS and stop.
+     * If in split shade, it will collapse the whole shade.
+     *
+     * @param animateAway Do not stop when QS becomes QQS. Fling until QS isn't visible anymore.
+     */
+    public void animateCloseQs(boolean animateAway) {
+        if (mExpansionAnimator != null) {
+            if (!mAnimatorExpand) {
+                return;
+            }
+            float height = mExpansionHeight;
+            mExpansionAnimator.cancel();
+            setExpansionHeight(height);
+        }
+        flingQs(0 /* vel */, animateAway ? FLING_HIDE : FLING_COLLAPSE);
+    }
+
+    private void cancelExpansionAnimation() {
+        if (mExpansionAnimator != null) {
+            mExpansionAnimator.cancel();
+        }
+    }
+
+    /** @see #flingQs(float, int, Runnable, boolean) */
+    public void flingQs(float vel, int type) {
+        flingQs(vel, type, null /* onFinishRunnable */, false /* isClick */);
+    }
+
+    /**
+     * Animates QS or QQS as if the user had swiped up or down.
+     *
+     * @param vel              Finger velocity or 0 when not initiated by touch events.
+     * @param type             Either FLING_EXPAND, FLING_COLLAPSE or FLING_HIDE.
+     * @param onFinishRunnable Runnable to be executed at the end of animation.
+     * @param isClick          If originated by click (different interpolator and duration.)
+     */
+    private void flingQs(float vel, int type, final Runnable onFinishRunnable,
+            boolean isClick) {
+        float target;
+        switch (type) {
+            case FLING_EXPAND:
+                target = getMaxExpansionHeight();
+                break;
+            case FLING_COLLAPSE:
+                target = getMinExpansionHeight();
+                break;
+            case FLING_HIDE:
+            default:
+                if (isQsFragmentCreated()) {
+                    mQs.closeDetail();
+                }
+                target = 0;
+        }
+        if (target == mExpansionHeight) {
+            if (onFinishRunnable != null) {
+                onFinishRunnable.run();
+            }
+            traceQsJank(false, type != FLING_EXPAND);
+            return;
+        }
+
+        // If we move in the opposite direction, reset velocity and use a different duration.
+        boolean oppositeDirection = false;
+        boolean expanding = type == FLING_EXPAND;
+        if (vel > 0 && !expanding || vel < 0 && expanding) {
+            vel = 0;
+            oppositeDirection = true;
+        }
+        ValueAnimator animator = ValueAnimator.ofFloat(
+                mExpansionHeight, target);
+        if (isClick) {
+            animator.setInterpolator(Interpolators.TOUCH_RESPONSE);
+            animator.setDuration(368);
+        } else {
+            if (mFlingQsWithoutClickListener != null) {
+                mFlingQsWithoutClickListener.onFlingQsWithoutClick(animator, mExpansionHeight,
+                        target, vel);
+            }
+        }
+        if (oppositeDirection) {
+            animator.setDuration(350);
+        }
+        animator.addUpdateListener(
+                animation -> setExpansionHeight((Float) animation.getAnimatedValue()));
+        animator.addListener(new AnimatorListenerAdapter() {
+            private boolean mIsCanceled;
+
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mPanelViewControllerLazy.get().notifyExpandingStarted();
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                mIsCanceled = true;
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mAnimatingHiddenFromCollapsed = false;
+                mAnimating = false;
+                mPanelViewControllerLazy.get().notifyExpandingFinished();
+                mNotificationStackScrollLayoutController.resetCheckSnoozeLeavebehind();
+                mExpansionAnimator = null;
+                if (onFinishRunnable != null) {
+                    onFinishRunnable.run();
+                }
+                traceQsJank(false, mIsCanceled);
+            }
+        });
+        // Let's note that we're animating QS. Moving the animator here will cancel it immediately,
+        // so we need a separate flag.
+        mAnimating = true;
+        animator.start();
+        mExpansionAnimator = animator;
+        mAnimatorExpand = expanding;
+        mAnimatingHiddenFromCollapsed =
+                computeExpansionFraction() == 0.0f && target == 0;
+    }
+
+    private void flingQsWithCurrentVelocity(float y, boolean isCancelMotionEvent) {
+        float vel = getCurrentVelocity();
+        // TODO (b/265193930): remove dependency on NPVC
+        boolean expandsQs = mPanelViewControllerLazy.get().flingExpandsQs(vel);
+        if (expandsQs) {
+            if (mFalsingManager.isUnlockingDisabled() || isQsFalseTouch()) {
+                expandsQs = false;
+            } else {
+                logQsSwipeDown(y);
+            }
+        } else if (vel < 0) {
+            mFalsingManager.isFalseTouch(QS_COLLAPSE);
+        }
+
+        int flingType;
+        if (expandsQs && !isCancelMotionEvent) {
+            flingType = FLING_EXPAND;
+        } else if (mSplitShadeEnabled) {
+            flingType = FLING_HIDE;
+        } else {
+            flingType = FLING_COLLAPSE;
+        }
+        flingQs(vel, flingType);
+    }
+
+    private void logQsSwipeDown(float y) {
+        float vel = getCurrentVelocity();
+        final int gesture = mBarState == KEYGUARD ? MetricsProto.MetricsEvent.ACTION_LS_QS
+                : MetricsProto.MetricsEvent.ACTION_SHADE_QS_PULL;
+        // TODO (b/265193930): remove dependency on NPVC
+        float displayDensity = mPanelViewControllerLazy.get().getDisplayDensity();
+        mLockscreenGestureLogger.write(gesture,
+                (int) ((y - getInitialTouchY()) / displayDensity), (int) (vel / displayDensity));
+    }
+
+    /** */
+    public FragmentHostManager.FragmentListener getQsFragmentListener() {
+        return new QsFragmentListener();
+    }
+
+    /** */
+    public final class QsFragmentListener implements FragmentHostManager.FragmentListener {
+        /** */
+        @Override
+        public void onFragmentViewCreated(String tag, Fragment fragment) {
+            mQs = (QS) fragment;
+            mQs.setPanelView(mQsHeightListener);
+            mQs.setCollapseExpandAction(mQsCollapseExpandAction);
+            mQs.setHeaderClickable(isExpansionEnabled());
+            mQs.setOverscrolling(mStackScrollerOverscrolling);
+            mQs.setInSplitShade(mSplitShadeEnabled);
+            mQs.setIsNotificationPanelFullWidth(mIsFullWidth);
+
+            // recompute internal state when qspanel height changes
+            mQs.getView().addOnLayoutChangeListener(
+                    (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+                        final int height = bottom - top;
+                        final int oldHeight = oldBottom - oldTop;
+                        if (height != oldHeight) {
+                            onHeightChanged();
+                        }
+                    });
+            mQs.setCollapsedMediaVisibilityChangedListener((visible) -> {
+                if (mQs.getHeader().isShown()) {
+                    setAnimateNextNotificationBounds(
+                            StackStateAnimator.ANIMATION_DURATION_STANDARD, 0);
+                    mNotificationStackScrollLayoutController.animateNextTopPaddingChange();
+                }
+            });
+            mLockscreenShadeTransitionController.setQS(mQs);
+            mShadeTransitionController.setQs(mQs);
+            mNotificationStackScrollLayoutController.setQsHeader((ViewGroup) mQs.getHeader());
+            mQs.setScrollListener(mQsScrollListener);
+            updateExpansion();
+        }
+
+        /** */
+        @Override
+        public void onFragmentViewDestroyed(String tag, Fragment fragment) {
+            // Manual handling of fragment lifecycle is only required because this bridges
+            // non-fragment and fragment code. Once we are using a fragment for the notification
+            // panel, mQs will not need to be null cause it will be tied to the same lifecycle.
+            if (fragment == mQs) {
+                mQs = null;
+            }
+        }
+    }
+
+    private final class LockscreenShadeTransitionCallback
+            implements LockscreenShadeTransitionController.Callback {
+        /** Called when pulse expansion has finished and this is going to the full shade. */
+        @Override
+        public void onPulseExpansionFinished() {
+            setAnimateNextNotificationBounds(
+                    StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE, 0);
+            mIsPulseExpansionResettingAnimator = true;
+        }
+
+        @Override
+        public void setTransitionToFullShadeAmount(float pxAmount, boolean animate, long delay) {
+            if (animate && mIsFullWidth) {
+                setAnimateNextNotificationBounds(
+                        StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE, delay);
+                mIsTranslationResettingAnimator = mTranslationForFullShadeTransition > 0.0f;
+            }
+            float endPosition = 0;
+            if (pxAmount > 0.0f) {
+                if (mSplitShadeEnabled) {
+                    float qsHeight = MathUtils.lerp(getMinExpansionHeight(),
+                            getMaxExpansionHeight(),
+                            mLockscreenShadeTransitionController.getQSDragProgress());
+                    setExpansionHeight(qsHeight);
+                }
+                if (mNotificationStackScrollLayoutController.getVisibleNotificationCount() == 0
+                        && !mMediaDataManager.hasActiveMediaOrRecommendation()) {
+                    // No notifications are visible, let's animate to the height of qs instead
+                    if (isQsFragmentCreated()) {
+                        // Let's interpolate to the header height instead of the top padding,
+                        // because the toppadding is way too low because of the large clock.
+                        // we still want to take into account the edgePosition though as that nicely
+                        // overshoots in the stackscroller
+                        endPosition = getEdgePosition()
+                                - mNotificationStackScrollLayoutController.getTopPadding()
+                                + getHeaderHeight();
+                    }
+                } else {
+                    // Interpolating to the new bottom edge position!
+                    endPosition = getEdgePosition() + mNotificationStackScrollLayoutController
+                            .getFullShadeTransitionInset();
+                    if (mBarState == KEYGUARD) {
+                        endPosition -= mLockscreenNotificationPadding;
+                    }
+                }
+            }
+
+            // Calculate the overshoot amount such that we're reaching the target after our desired
+            // distance, but only reach it fully once we drag a full shade length.
+            mTransitioningToFullShadeProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
+                    MathUtils.saturate(pxAmount / mDistanceForFullShadeTransition));
+
+            int position = (int) MathUtils.lerp((float) 0, endPosition,
+                    mTransitioningToFullShadeProgress);
+            if (mTransitioningToFullShadeProgress > 0.0f) {
+                // we want at least 1 pixel otherwise the panel won't be clipped
+                position = Math.max(1, position);
+            }
+            mTransitionToFullShadePosition = position;
+            updateExpansion();
+        }
+    }
+
+    private final class NsslOverscrollTopChangedListener implements
+            NotificationStackScrollLayout.OnOverscrollTopChangedListener {
+        @Override
+        public void onOverscrollTopChanged(float amount, boolean isRubberbanded) {
+            // When in split shade, overscroll shouldn't carry through to QS
+            if (mSplitShadeEnabled) {
+                return;
+            }
+            cancelExpansionAnimation();
+            if (!isExpansionEnabled()) {
+                amount = 0f;
+            }
+            float rounded = amount >= 1f ? amount : 0f;
+            setOverScrolling(rounded != 0f && isRubberbanded);
+            mExpansionFromOverscroll = rounded != 0f;
+            mLastOverscroll = rounded;
+            updateQsState();
+            setExpansionHeight(getMinExpansionHeight() + rounded);
+        }
+
+        @Override
+        public void flingTopOverscroll(float velocity, boolean open) {
+            // in split shade mode we want to expand/collapse QS only when touch happens within QS
+            if (isSplitShadeAndTouchXOutsideQs(mInitialTouchX)) {
+                return;
+            }
+            mLastOverscroll = 0f;
+            mExpansionFromOverscroll = false;
+            if (open) {
+                // During overscrolling, qsExpansion doesn't actually change that the qs is
+                // becoming expanded. Any layout could therefore reset the position again. Let's
+                // make sure we can expand
+                setOverScrolling(false);
+            }
+            setExpansionHeight(getExpansionHeight());
+            boolean canExpand = isExpansionEnabled();
+            flingQs(!canExpand && open ? 0f : velocity,
+                    open && canExpand ? FLING_EXPAND : FLING_COLLAPSE, () -> {
+                        setOverScrolling(false);
+                        updateQsState();
+                    }, false);
+        }
+    }
+
+    void beginJankMonitoring(boolean isFullyCollapsed) {
+        if (mInteractionJankMonitor == null) {
+            return;
+        }
+        // TODO (b/265193930): remove dependency on NPVC
+        InteractionJankMonitor.Configuration.Builder builder =
+                InteractionJankMonitor.Configuration.Builder.withView(
+                        InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE,
+                        mPanelView).setTag(isFullyCollapsed ? "Expand" : "Collapse");
+        mInteractionJankMonitor.begin(builder);
+    }
+
+    void endJankMonitoring() {
+        if (mInteractionJankMonitor == null) {
+            return;
+        }
+        InteractionJankMonitor.getInstance().end(
+                InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+    }
+
+    void cancelJankMonitoring() {
+        if (mInteractionJankMonitor == null) {
+            return;
+        }
+        InteractionJankMonitor.getInstance().cancel(
+                InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+    }
+
+    void traceQsJank(boolean startTracing, boolean wasCancelled) {
+        if (mInteractionJankMonitor == null) {
+            return;
+        }
+        if (startTracing) {
+            mInteractionJankMonitor.begin(mPanelView, CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
+        } else {
+            if (wasCancelled) {
+                mInteractionJankMonitor.cancel(CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
+            } else {
+                mInteractionJankMonitor.end(CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
+            }
+        }
+    }
+
+    interface ExpansionHeightSetToMaxListener {
+        void onExpansionHeightSetToMax(boolean requestPaddingUpdate);
+    }
+
+    interface ExpansionHeightListener {
+        void onQsSetExpansionHeightCalled(boolean qsFullyExpanded);
+    }
+
+    interface QsStateUpdateListener {
+        void onQsStateUpdated(boolean qsExpanded, boolean isStackScrollerOverscrolling);
+    }
+
+    interface ApplyClippingImmediatelyListener {
+        void onQsClippingImmediatelyApplied(boolean clipStatusView, Rect lastQsClipBounds,
+                int top, boolean qsFragmentCreated, boolean qsVisible);
+    }
+
+    interface FlingQsWithoutClickListener {
+        void onFlingQsWithoutClick(ValueAnimator animator, float qsExpansionHeight,
+                float target, float vel);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
index 11617be..aa8c5b6 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
@@ -20,7 +20,6 @@
 import com.android.systemui.log.dagger.ShadeLog
 import com.android.systemui.plugins.log.LogBuffer
 import com.android.systemui.plugins.log.LogLevel
-import com.android.systemui.plugins.log.LogMessage
 import com.google.errorprone.annotations.CompileTimeConstant
 import javax.inject.Inject
 
@@ -51,7 +50,6 @@
         h: Float,
         touchSlop: Float,
         qsExpanded: Boolean,
-        collapsedOnDown: Boolean,
         keyguardShowing: Boolean,
         qsExpansionEnabled: Boolean
     ) {
@@ -64,13 +62,12 @@
                 long1 = h.toLong()
                 double1 = touchSlop.toDouble()
                 bool1 = qsExpanded
-                bool2 = collapsedOnDown
-                bool3 = keyguardShowing
-                bool4 = qsExpansionEnabled
+                bool2 = keyguardShowing
+                bool3 = qsExpansionEnabled
             },
             {
                 "QsTrackingNotStarted: initTouchY=$int1,y=$int2,h=$long1,slop=$double1,qsExpanded" +
-                    "=$bool1,collapsedDown=$bool2,keyguardShowing=$bool3,qsExpansion=$bool4"
+                    "=$bool1,keyguardShowing=$bool2,qsExpansion=$bool3"
             }
         )
     }
@@ -153,13 +150,23 @@
         )
     }
 
+    fun logQsExpandImmediateChanged(newValue: Boolean) {
+        buffer.log(
+            TAG,
+            LogLevel.VERBOSE,
+            {
+                bool1 = newValue
+            },
+            { "qsExpandImmediate=$bool1" }
+        )
+    }
+
     fun logQsExpansionChanged(
             message: String,
             qsExpanded: Boolean,
             qsMinExpansionHeight: Int,
             qsMaxExpansionHeight: Int,
             stackScrollerOverscrolling: Boolean,
-            dozing: Boolean,
             qsAnimatorExpand: Boolean,
             animatingQs: Boolean
     ) {
@@ -172,14 +179,13 @@
                 int1 = qsMinExpansionHeight
                 int2 = qsMaxExpansionHeight
                 bool2 = stackScrollerOverscrolling
-                bool3 = dozing
-                bool4 = qsAnimatorExpand
+                bool3 = qsAnimatorExpand
                 // 0 = false, 1 = true
                 long1 = animatingQs.compareTo(false).toLong()
             },
             {
                 "$str1 qsExpanded=$bool1,qsMinExpansionHeight=$int1,qsMaxExpansionHeight=$int2," +
-                    "stackScrollerOverscrolling=$bool2,dozing=$bool3,qsAnimatorExpand=$bool4," +
+                    "stackScrollerOverscrolling=$bool2,qsAnimatorExpand=$bool3," +
                     "animatingQs=$long1"
             }
         )
@@ -234,4 +240,19 @@
             }
         )
     }
+
+    fun logLastFlingWasExpanding(
+            expand: Boolean
+    ) {
+        buffer.log(
+                TAG,
+                LogLevel.VERBOSE,
+                {
+                    bool1 = expand
+                },
+                {
+                    "NPVC mLastFlingWasExpanding set to: $bool1"
+                }
+        )
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
index b02a45a..641131e 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
@@ -18,7 +18,6 @@
 import com.android.systemui.plugins.BcSmartspaceDataPlugin
 import com.android.systemui.smartspace.SmartspacePrecondition
 import com.android.systemui.smartspace.SmartspaceTargetFilter
-import com.android.systemui.smartspace.filters.LockscreenAndDreamTargetFilter
 import com.android.systemui.smartspace.preconditions.LockscreenPrecondition
 import dagger.Binds
 import dagger.BindsOptionalOf
@@ -35,11 +34,6 @@
         const val DREAM_SMARTSPACE_DATA_PLUGIN = "dreams_smartspace_data_plugin"
 
         /**
-         * The lockscreen smartspace target filter.
-         */
-        const val LOCKSCREEN_SMARTSPACE_TARGET_FILTER = "lockscreen_smartspace_target_filter"
-
-        /**
          * The dream smartspace target filter.
          */
         const val DREAM_SMARTSPACE_TARGET_FILTER = "dream_smartspace_target_filter"
@@ -48,6 +42,16 @@
          * The precondition for dream smartspace
          */
         const val DREAM_SMARTSPACE_PRECONDITION = "dream_smartspace_precondition"
+
+        /**
+         * The BcSmartspaceDataPlugin for the standalone date (+alarm+dnd).
+         */
+        const val DATE_SMARTSPACE_DATA_PLUGIN = "date_smartspace_data_plugin"
+
+        /**
+         * The BcSmartspaceDataPlugin for the standalone weather.
+         */
+        const val WEATHER_SMARTSPACE_DATA_PLUGIN = "weather_smartspace_data_plugin"
     }
 
     @BindsOptionalOf
@@ -59,12 +63,6 @@
     abstract fun optionalDreamsBcSmartspaceDataPlugin(): BcSmartspaceDataPlugin?
 
     @Binds
-    @Named(LOCKSCREEN_SMARTSPACE_TARGET_FILTER)
-    abstract fun provideLockscreenSmartspaceTargetFilter(
-        filter: LockscreenAndDreamTargetFilter?
-    ): SmartspaceTargetFilter?
-
-    @Binds
     @Named(DREAM_SMARTSPACE_PRECONDITION)
     abstract fun bindSmartspacePrecondition(
         lockscreenPrecondition: LockscreenPrecondition?
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt
index 236ba1f..5736a5c 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt
@@ -21,6 +21,7 @@
 import android.view.ViewGroup
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.BcSmartspaceDataPlugin
+import com.android.systemui.plugins.BcSmartspaceDataPlugin.UI_SURFACE_DREAM
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.smartspace.dagger.SmartspaceViewComponent.SmartspaceViewModule.PLUGIN
 import dagger.BindsInstance
@@ -57,7 +58,7 @@
                 BcSmartspaceDataPlugin.SmartspaceView {
             val ssView = plugin.getView(parent)
             // Currently, this is only used to provide SmartspaceView on Dream surface.
-            ssView.setIsDreaming(true)
+            ssView.setUiSurface(UI_SURFACE_DREAM)
             ssView.registerDataProvider(plugin)
 
             ssView.setIntentStarter(object : BcSmartspaceDataPlugin.IntentStarter {
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/preconditions/LockscreenPrecondition.kt b/packages/SystemUI/src/com/android/systemui/smartspace/preconditions/LockscreenPrecondition.kt
index 1302ec9..88e8ad9 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/preconditions/LockscreenPrecondition.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/preconditions/LockscreenPrecondition.kt
@@ -15,8 +15,6 @@
  */
 package com.android.systemui.smartspace.preconditions
 
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.smartspace.SmartspacePrecondition
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
 import com.android.systemui.util.concurrency.Execution
@@ -24,11 +22,9 @@
 
 /**
  * {@link LockscreenPrecondition} covers the conditions that must be met before Smartspace can be
- * used over lockscreen. These conditions include the device being provisioned with a setup user
- * and the Smartspace feature flag enabled.
+ * used over lockscreen. These conditions include the device being provisioned with a setup user.
  */
 class LockscreenPrecondition @Inject constructor(
-    private val featureFlags: FeatureFlags,
     private val deviceProvisionedController: DeviceProvisionedController,
     private val execution: Execution
 ) : SmartspacePrecondition {
@@ -90,6 +86,6 @@
 
     override fun conditionsMet(): Boolean {
         execution.assertIsMainThread()
-        return featureFlags.isEnabled(Flags.SMARTSPACE) && deviceReady
+        return deviceReady
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedButton.java
index 87c12c2..6577cf6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedButton.java
@@ -20,10 +20,21 @@
 import android.util.AttributeSet;
 import android.widget.Button;
 
+import com.android.systemui.animation.LaunchableView;
+import com.android.systemui.animation.LaunchableViewDelegate;
+
+import kotlin.Unit;
+
 /**
  * A Button which doesn't have overlapping drawing commands
  */
-public class AlphaOptimizedButton extends Button {
+public class AlphaOptimizedButton extends Button implements LaunchableView {
+    private LaunchableViewDelegate mDelegate = new LaunchableViewDelegate(this,
+            (visibility) -> {
+                super.setVisibility(visibility);
+                return Unit.INSTANCE;
+            });
+
     public AlphaOptimizedButton(Context context) {
         super(context);
     }
@@ -45,4 +56,14 @@
     public boolean hasOverlappingRendering() {
         return false;
     }
+
+    @Override
+    public void setShouldBlockVisibilityChanges(boolean block) {
+        mDelegate.setShouldBlockVisibilityChanges(block);
+    }
+
+    @Override
+    public void setVisibility(int visibility) {
+        mDelegate.setVisibility(visibility);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 1758379..f2e729d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -20,7 +20,6 @@
 import static android.app.StatusBarManager.DISABLE_NONE;
 import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT;
 import static android.inputmethodservice.InputMethodService.IME_INVISIBLE;
-import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 
 import android.annotation.Nullable;
@@ -38,7 +37,6 @@
 import android.hardware.biometrics.IBiometricContextListener;
 import android.hardware.biometrics.IBiometricSysuiReceiver;
 import android.hardware.biometrics.PromptInfo;
-import android.hardware.display.DisplayManager;
 import android.hardware.fingerprint.IUdfpsHbmListener;
 import android.inputmethodservice.InputMethodService.BackDispositionMode;
 import android.media.INearbyMediaDevicesProvider;
@@ -46,6 +44,7 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
@@ -60,6 +59,7 @@
 import android.view.WindowInsetsController.Behavior;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.os.SomeArgs;
 import com.android.internal.statusbar.IAddTileResultCallback;
@@ -70,6 +70,7 @@
 import com.android.internal.util.GcUtils;
 import com.android.internal.view.AppearanceRegion;
 import com.android.systemui.dump.DumpHandler;
+import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.statusbar.CommandQueue.Callbacks;
 import com.android.systemui.statusbar.commandline.CommandRegistry;
 import com.android.systemui.statusbar.policy.CallbackController;
@@ -89,8 +90,7 @@
  * are coalesced, note that they are all idempotent.
  */
 public class CommandQueue extends IStatusBar.Stub implements
-        CallbackController<Callbacks>,
-        DisplayManager.DisplayListener {
+        CallbackController<Callbacks> {
     private static final String TAG = CommandQueue.class.getSimpleName();
 
     private static final int INDEX_MASK = 0xffff;
@@ -192,6 +192,7 @@
     private ProtoTracer mProtoTracer;
     private final @Nullable CommandRegistry mRegistry;
     private final @Nullable DumpHandler mDumpHandler;
+    private final @Nullable DisplayTracker mDisplayTracker;
 
     /**
      * These methods are called back on the main thread.
@@ -351,7 +352,7 @@
         }
 
         /**
-         * @see DisplayManager.DisplayListener#onDisplayRemoved(int)
+         * @see DisplayTracker.Callback#onDisplayRemoved(int)
          */
         default void onDisplayRemoved(int displayId) {
         }
@@ -500,12 +501,14 @@
         default void showMediaOutputSwitcher(String packageName) {}
     }
 
-    public CommandQueue(Context context) {
-        this(context, null, null, null);
+    @VisibleForTesting
+    public CommandQueue(Context context, DisplayTracker displayTracker) {
+        this(context, displayTracker, null, null, null);
     }
 
     public CommandQueue(
             Context context,
+            DisplayTracker displayTracker,
             ProtoTracer protoTracer,
             CommandRegistry registry,
             DumpHandler dumpHandler
@@ -513,33 +516,28 @@
         mProtoTracer = protoTracer;
         mRegistry = registry;
         mDumpHandler = dumpHandler;
-        context.getSystemService(DisplayManager.class).registerDisplayListener(this, mHandler);
+        mDisplayTracker = displayTracker;
+        mDisplayTracker.addDisplayChangeCallback(new DisplayTracker.Callback() {
+            @Override
+            public void onDisplayRemoved(int displayId) {
+                synchronized (mLock) {
+                    mDisplayDisabled.remove(displayId);
+                }
+                // This callback is registered with {@link #mHandler} that already posts to run
+                // on main thread, so it is safe to dispatch directly.
+                for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+                    mCallbacks.get(i).onDisplayRemoved(displayId);
+                }
+            }
+        }, new HandlerExecutor(mHandler));
         // We always have default display.
-        setDisabled(DEFAULT_DISPLAY, DISABLE_NONE, DISABLE2_NONE);
+        setDisabled(mDisplayTracker.getDefaultDisplayId(), DISABLE_NONE, DISABLE2_NONE);
     }
 
-    @Override
-    public void onDisplayAdded(int displayId) { }
-
-    @Override
-    public void onDisplayRemoved(int displayId) {
-        synchronized (mLock) {
-            mDisplayDisabled.remove(displayId);
-        }
-        // This callback is registered with {@link #mHandler} that already posts to run on main
-        // thread, so it is safe to dispatch directly.
-        for (int i = mCallbacks.size() - 1; i >= 0; i--) {
-            mCallbacks.get(i).onDisplayRemoved(displayId);
-        }
-    }
-
-    @Override
-    public void onDisplayChanged(int displayId) { }
-
     // TODO(b/118592525): add multi-display support if needed.
     public boolean panelsEnabled() {
-        final int disabled1 = getDisabled1(DEFAULT_DISPLAY);
-        final int disabled2 = getDisabled2(DEFAULT_DISPLAY);
+        final int disabled1 = getDisabled1(mDisplayTracker.getDefaultDisplayId());
+        final int disabled2 = getDisabled2(mDisplayTracker.getDefaultDisplayId());
         return (disabled1 & StatusBarManager.DISABLE_EXPAND) == 0
                 && (disabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
index 63179da..5adb58b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
@@ -77,6 +77,12 @@
      */
     public static void fadeOut(View view, float fadeOutAmount, boolean remap) {
         view.animate().cancel();
+
+        // Don't fade out if already not visible.
+        if (view.getAlpha() == 0.0f) {
+            return;
+        }
+
         if (fadeOutAmount == 1.0f && view.getVisibility() != View.GONE) {
             view.setVisibility(View.INVISIBLE);
         } else if (view.getVisibility() == View.INVISIBLE) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 006b552..f4782c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -25,7 +25,9 @@
 import static android.view.View.GONE;
 import static android.view.View.VISIBLE;
 
+import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_AVAILABLE;
 import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED;
+import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED;
 import static com.android.keyguard.KeyguardUpdateMonitor.getCurrentUser;
 import static com.android.systemui.DejankUtils.whitelistIpcs;
 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.IMPORTANT_MSG_MIN_DURATION;
@@ -43,6 +45,7 @@
 import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY;
 import static com.android.systemui.plugins.log.LogLevel.ERROR;
 
+import android.app.AlarmManager;
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -93,11 +96,13 @@
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
 import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.log.LogLevel;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.AlarmTimeout;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.wakelock.SettableWakeLock;
 import com.android.systemui.util.wakelock.WakeLock;
@@ -124,13 +129,11 @@
 @SysUISingleton
 public class KeyguardIndicationController {
 
-    private static final String TAG = "KeyguardIndication";
+    public static final String TAG = "KeyguardIndication";
     private static final boolean DEBUG_CHARGING_SPEED = false;
 
-    private static final int MSG_HIDE_TRANSIENT = 1;
-    private static final int MSG_SHOW_ACTION_TO_UNLOCK = 2;
-    private static final int MSG_HIDE_BIOMETRIC_MESSAGE = 3;
-    private static final int MSG_RESET_ERROR_MESSAGE_ON_SCREEN_ON = 4;
+    private static final int MSG_SHOW_ACTION_TO_UNLOCK = 1;
+    private static final int MSG_RESET_ERROR_MESSAGE_ON_SCREEN_ON = 2;
     private static final long TRANSIENT_BIOMETRIC_ERROR_TIMEOUT = 1300;
     public static final long DEFAULT_HIDE_DELAY_MS =
             3500 + KeyguardIndicationTextView.Y_IN_DURATION;
@@ -212,6 +215,11 @@
     };
     private boolean mFaceLockedOutThisAuthSession;
 
+    // Use AlarmTimeouts to guarantee that the events are handled even if scheduled and
+    // triggered while the device is asleep
+    private final AlarmTimeout mHideTransientMessageHandler;
+    private final AlarmTimeout mHideBiometricMessageHandler;
+
     /**
      * Creates a new KeyguardIndicationController and registers callbacks.
      */
@@ -238,7 +246,9 @@
             AccessibilityManager accessibilityManager,
             FaceHelpMessageDeferral faceHelpMessageDeferral,
             KeyguardLogger keyguardLogger,
-            AlternateBouncerInteractor alternateBouncerInteractor) {
+            AlternateBouncerInteractor alternateBouncerInteractor,
+            AlarmManager alarmManager
+    ) {
         mContext = context;
         mBroadcastDispatcher = broadcastDispatcher;
         mDevicePolicyManager = devicePolicyManager;
@@ -273,17 +283,26 @@
         mHandler = new Handler(mainLooper) {
             @Override
             public void handleMessage(Message msg) {
-                if (msg.what == MSG_HIDE_TRANSIENT) {
-                    hideTransientIndication();
-                } else if (msg.what == MSG_SHOW_ACTION_TO_UNLOCK) {
+                if (msg.what == MSG_SHOW_ACTION_TO_UNLOCK) {
                     showActionToUnlock();
-                } else if (msg.what == MSG_HIDE_BIOMETRIC_MESSAGE) {
-                    hideBiometricMessage();
                 } else if (msg.what == MSG_RESET_ERROR_MESSAGE_ON_SCREEN_ON) {
                     mBiometricErrorMessageToShowOnScreenOn = null;
                 }
             }
         };
+
+        mHideTransientMessageHandler = new AlarmTimeout(
+                alarmManager,
+                this::hideTransientIndication,
+                TAG,
+                mHandler
+        );
+        mHideBiometricMessageHandler = new AlarmTimeout(
+                alarmManager,
+                this::hideBiometricMessage,
+                TAG,
+                mHandler
+        );
     }
 
     /** Call this after construction to finish setting up the instance. */
@@ -310,9 +329,11 @@
         mInitialTextColorState = mTopIndicationView != null
                 ? mTopIndicationView.getTextColors() : ColorStateList.valueOf(Color.WHITE);
         mRotateTextViewController = new KeyguardIndicationRotateTextViewController(
-            mLockScreenIndicationView,
-            mExecutor,
-            mStatusBarStateController);
+                mLockScreenIndicationView,
+                mExecutor,
+                mStatusBarStateController,
+                mKeyguardLogger
+        );
         updateDeviceEntryIndication(false /* animate */);
         updateOrganizedOwnedDevice();
         if (mBroadcastReceiver == null) {
@@ -335,6 +356,8 @@
      */
     public void destroy() {
         mHandler.removeCallbacksAndMessages(null);
+        mHideBiometricMessageHandler.cancel();
+        mHideTransientMessageHandler.cancel();
         mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
     }
 
@@ -521,23 +544,23 @@
                             .build(),
                     true
             );
-            if (!TextUtils.isEmpty(mBiometricMessageFollowUp)) {
-                mRotateTextViewController.updateIndication(
-                        INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
-                        new KeyguardIndication.Builder()
-                                .setMessage(mBiometricMessageFollowUp)
-                                .setMinVisibilityMillis(IMPORTANT_MSG_MIN_DURATION)
-                                .setTextColor(mInitialTextColorState)
-                                .build(),
-                        true
-                );
-            } else {
-                mRotateTextViewController.hideIndication(
-                        INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP);
-            }
         } else {
-            mRotateTextViewController.hideIndication(INDICATION_TYPE_BIOMETRIC_MESSAGE);
-            mRotateTextViewController.hideIndication(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP);
+            mRotateTextViewController.hideIndication(
+                    INDICATION_TYPE_BIOMETRIC_MESSAGE);
+        }
+        if (!TextUtils.isEmpty(mBiometricMessageFollowUp)) {
+            mRotateTextViewController.updateIndication(
+                    INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
+                    new KeyguardIndication.Builder()
+                            .setMessage(mBiometricMessageFollowUp)
+                            .setMinVisibilityMillis(IMPORTANT_MSG_MIN_DURATION)
+                            .setTextColor(mInitialTextColorState)
+                            .build(),
+                    true
+            );
+        } else {
+            mRotateTextViewController.hideIndication(
+                    INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP);
         }
     }
 
@@ -679,7 +702,7 @@
         if (visible) {
             // If this is called after an error message was already shown, we should not clear it.
             // Otherwise the error message won't be shown
-            if (!mHandler.hasMessages(MSG_HIDE_TRANSIENT)) {
+            if (!mHideTransientMessageHandler.isScheduled()) {
                 hideTransientIndication();
             }
             updateDeviceEntryIndication(false);
@@ -727,16 +750,14 @@
      * Hides transient indication in {@param delayMs}.
      */
     public void hideTransientIndicationDelayed(long delayMs) {
-        mHandler.sendMessageDelayed(
-                mHandler.obtainMessage(MSG_HIDE_TRANSIENT), delayMs);
+        mHideTransientMessageHandler.schedule(delayMs, AlarmTimeout.MODE_RESCHEDULE_IF_SCHEDULED);
     }
 
     /**
      * Hides biometric indication in {@param delayMs}.
      */
     public void hideBiometricMessageDelayed(long delayMs) {
-        mHandler.sendMessageDelayed(
-                mHandler.obtainMessage(MSG_HIDE_BIOMETRIC_MESSAGE), delayMs);
+        mHideBiometricMessageHandler.schedule(delayMs, AlarmTimeout.MODE_RESCHEDULE_IF_SCHEDULED);
     }
 
     /**
@@ -751,7 +772,6 @@
      */
     private void showTransientIndication(CharSequence transientIndication) {
         mTransientIndication = transientIndication;
-        mHandler.removeMessages(MSG_HIDE_TRANSIENT);
         hideTransientIndicationDelayed(DEFAULT_HIDE_DELAY_MS);
 
         updateTransient();
@@ -769,7 +789,8 @@
      */
     private void showBiometricMessage(CharSequence biometricMessage,
             @Nullable CharSequence biometricMessageFollowUp) {
-        if (TextUtils.equals(biometricMessage, mBiometricMessage)) {
+        if (TextUtils.equals(biometricMessage, mBiometricMessage)
+                && TextUtils.equals(biometricMessageFollowUp, mBiometricMessageFollowUp)) {
             return;
         }
 
@@ -777,9 +798,9 @@
         mBiometricMessageFollowUp = biometricMessageFollowUp;
 
         mHandler.removeMessages(MSG_SHOW_ACTION_TO_UNLOCK);
-        mHandler.removeMessages(MSG_HIDE_BIOMETRIC_MESSAGE);
         hideBiometricMessageDelayed(
-                mBiometricMessageFollowUp != null
+                !TextUtils.isEmpty(mBiometricMessage)
+                        && !TextUtils.isEmpty(mBiometricMessageFollowUp)
                         ? IMPORTANT_MSG_MIN_DURATION * 2
                         : DEFAULT_HIDE_DELAY_MS
         );
@@ -791,7 +812,7 @@
         if (mBiometricMessage != null || mBiometricMessageFollowUp != null) {
             mBiometricMessage = null;
             mBiometricMessageFollowUp = null;
-            mHandler.removeMessages(MSG_HIDE_BIOMETRIC_MESSAGE);
+            mHideBiometricMessageHandler.cancel();
             updateBiometricMessage();
         }
     }
@@ -802,7 +823,7 @@
     public void hideTransientIndication() {
         if (mTransientIndication != null) {
             mTransientIndication = null;
-            mHandler.removeMessages(MSG_HIDE_TRANSIENT);
+            mHideTransientMessageHandler.cancel();
             updateTransient();
         }
     }
@@ -813,6 +834,7 @@
      * may continuously be cycled through.
      */
     protected final void updateDeviceEntryIndication(boolean animate) {
+        mKeyguardLogger.logUpdateDeviceEntryIndication(animate, mVisible, mDozing);
         if (!mVisible) {
             return;
         }
@@ -1063,20 +1085,27 @@
                 }
             }
 
+            final boolean faceAuthUnavailable = biometricSourceType == FACE
+                    && msgId == BIOMETRIC_HELP_FACE_NOT_AVAILABLE;
+
             // TODO(b/141025588): refactor to reduce repetition of code/comments
             // Only checking if unlocking with Biometric is allowed (no matter strong or non-strong
             // as long as primary auth, i.e. PIN/pattern/password, is not required), so it's ok to
             // pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the
             // check of whether non-strong biometric is allowed
             if (!mKeyguardUpdateMonitor
-                    .isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)) {
+                    .isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)
+                    && !faceAuthUnavailable) {
                 return;
             }
 
             final boolean faceAuthSoftError = biometricSourceType == FACE
-                    && msgId != BIOMETRIC_HELP_FACE_NOT_RECOGNIZED;
+                    && msgId != BIOMETRIC_HELP_FACE_NOT_RECOGNIZED
+                    && msgId != BIOMETRIC_HELP_FACE_NOT_AVAILABLE;
             final boolean faceAuthFailed = biometricSourceType == FACE
                     && msgId == BIOMETRIC_HELP_FACE_NOT_RECOGNIZED; // ran through matcher & failed
+            final boolean fpAuthFailed = biometricSourceType == FINGERPRINT
+                    && msgId == BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED; // ran matcher & failed
             final boolean isUnlockWithFingerprintPossible = canUnlockWithFingerprint();
             final boolean isCoExFaceAcquisitionMessage =
                     faceAuthSoftError && isUnlockWithFingerprintPossible;
@@ -1099,6 +1128,29 @@
                             mContext.getString(R.string.keyguard_face_failed),
                             mContext.getString(R.string.keyguard_suggest_fingerprint)
                     );
+                } else if (fpAuthFailed
+                        && mKeyguardUpdateMonitor.getUserUnlockedWithFace(getCurrentUser())) {
+                    // face had already previously unlocked the device, so instead of showing a
+                    // fingerprint error, tell them they have already unlocked with face auth
+                    // and how to enter their device
+                    showBiometricMessage(
+                            mContext.getString(R.string.keyguard_face_successful_unlock),
+                            mContext.getString(R.string.keyguard_unlock)
+                    );
+                } else if (fpAuthFailed
+                        && mKeyguardUpdateMonitor.getUserHasTrust(
+                                KeyguardUpdateMonitor.getCurrentUser())) {
+                    showBiometricMessage(
+                            getTrustGrantedIndication(),
+                            mContext.getString(R.string.keyguard_unlock)
+                    );
+                } else if (faceAuthUnavailable) {
+                    showBiometricMessage(
+                            helpString,
+                            isUnlockWithFingerprintPossible
+                                    ? mContext.getString(R.string.keyguard_suggest_fingerprint)
+                                    : mContext.getString(R.string.keyguard_unlock)
+                    );
                 } else {
                     showBiometricMessage(helpString);
                 }
@@ -1382,6 +1434,7 @@
         public void onKeyguardShowingChanged() {
             // All transient messages are gone the next time keyguard is shown
             if (!mKeyguardStateController.isShowing()) {
+                mKeyguardLogger.log(TAG, LogLevel.DEBUG, "clear messages");
                 mTopIndicationView.clearMessages();
                 mRotateTextViewController.clearMessages();
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index f565f3d..f0d064b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -187,6 +187,8 @@
 
     private val qsTransitionController = qsTransitionControllerFactory.create { qS }
 
+    private val callbacks = mutableListOf<Callback>()
+
     /** See [LockscreenShadeQsTransitionController.qsTransitionFraction].*/
     @get:FloatRange(from = 0.0, to = 1.0)
     val qSDragProgress: Float
@@ -262,7 +264,6 @@
 
     fun setStackScroller(nsslController: NotificationStackScrollLayoutController) {
         this.nsslController = nsslController
-        touchHelper.host = nsslController.view
         touchHelper.expandCallback = nsslController.expandHelperCallback
     }
 
@@ -320,8 +321,8 @@
                                     true /* drag down is always an open */)
                         }
                         notificationPanelController.animateToFullShade(delay)
-                        notificationPanelController.setTransitionToFullShadeAmount(0f,
-                                true /* animated */, delay)
+                        callbacks.forEach { it.setTransitionToFullShadeAmount(0f,
+                                true /* animated */, delay) }
 
                         // Let's reset ourselves, ready for the next animation
 
@@ -425,8 +426,8 @@
 
                     qsTransitionController.dragDownAmount = value
 
-                    notificationPanelController.setTransitionToFullShadeAmount(field,
-                            false /* animate */, 0 /* delay */)
+                    callbacks.forEach { it.setTransitionToFullShadeAmount(field,
+                            false /* animate */, 0 /* delay */) }
 
                     mediaHierarchyManager.setTransitionToFullShadeAmount(field)
                     scrimTransitionController.dragDownAmount = value
@@ -689,7 +690,7 @@
         if (cancelled) {
             setPulseHeight(0f, animate = true)
         } else {
-            notificationPanelController.onPulseExpansionFinished()
+            callbacks.forEach { it.onPulseExpansionFinished() }
             setPulseHeight(0f, animate = false)
         }
     }
@@ -721,6 +722,27 @@
                 "${animationHandlerOnKeyguardDismiss != null}")
         }
     }
+
+
+    fun addCallback(callback: Callback) {
+        if (!callbacks.contains(callback)) {
+            callbacks.add(callback)
+        }
+    }
+
+    /**
+     * Callback for authentication events.
+     */
+    interface Callback {
+        /** TODO: comment here  */
+        fun onPulseExpansionFinished() {}
+
+        /**
+         * Sets the amount of pixels we have currently dragged down if we're transitioning
+         * to the full shade. 0.0f means we're not transitioning yet.
+         */
+        fun setTransitionToFullShadeAmount(pxAmount: Float, animate: Boolean, delay: Long) {}
+    }
 }
 
 /**
@@ -736,14 +758,12 @@
 
     private var dragDownAmountOnStart = 0.0f
     lateinit var expandCallback: ExpandHelper.Callback
-    lateinit var host: View
 
     private var minDragDistance = 0
     private var initialTouchX = 0f
     private var initialTouchY = 0f
     private var touchSlop = 0f
     private var slopMultiplier = 0f
-    private val temp2 = IntArray(2)
     private var draggedFarEnough = false
     private var startingChild: ExpandableView? = null
     private var lastHeight = 0f
@@ -923,7 +943,6 @@
     }
 
     private fun findView(x: Float, y: Float): ExpandableView? {
-        host.getLocationOnScreen(temp2)
-        return expandCallback.getChildAtRawPosition(x + temp2[0], y + temp2[1])
+        return expandCallback.getChildAtRawPosition(x, y)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
index 0b1807d..2ca0b00 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
@@ -143,6 +143,9 @@
     /** Sets the state of whether sysui is dozing or not. */
     default void setDozing(boolean dozing) {}
 
+    /** Sets the state of whether sysui is dreaming or not. */
+    default void setDreaming(boolean dreaming) {}
+
     /** Sets the state of whether plugin open is forced or not. */
     default void setForcePluginOpen(boolean forcePluginOpen, Object token) {}
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 56c34a0..8f1e0a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -23,6 +23,7 @@
 import android.content.res.Resources;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.util.IndentingPrintWriter;
 import android.util.MathUtils;
 import android.view.View;
 import android.view.ViewGroup;
@@ -52,6 +53,9 @@
 import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
 import com.android.systemui.statusbar.notification.stack.ViewState;
 import com.android.systemui.statusbar.phone.NotificationIconContainer;
+import com.android.systemui.util.DumpUtilsKt;
+
+import java.io.PrintWriter;
 
 /**
  * A notification shelf view that is placed inside the notification scroller. It manages the
@@ -86,7 +90,6 @@
     private boolean mInteractive;
     private boolean mAnimationsEnabled = true;
     private boolean mShowNotificationShelf;
-    private float mFirstElementRoundness;
     private Rect mClipRect = new Rect();
     private int mIndexOfFirstViewInShelf = -1;
     private float mCornerAnimationDistance;
@@ -263,8 +266,7 @@
         final float actualWidth = mAmbientState.isOnKeyguard()
                 ? MathUtils.lerp(shortestWidth, getWidth(), fractionToShade)
                 : getWidth();
-        ActivatableNotificationView anv = (ActivatableNotificationView) this;
-        anv.setBackgroundWidth((int) actualWidth);
+        setBackgroundWidth((int) actualWidth);
         if (mShelfIcons != null) {
             mShelfIcons.setActualLayoutWidth((int) actualWidth);
         }
@@ -365,9 +367,7 @@
         boolean expandingAnimated = mAmbientState.isExpansionChanging()
                 && !mAmbientState.isPanelTracking();
         int baseZHeight = mAmbientState.getBaseZHeight();
-        int backgroundTop = 0;
         int clipTopAmount = 0;
-        float firstElementRoundness = 0.0f;
 
         for (int i = 0; i < mHostLayoutController.getChildCount(); i++) {
             ExpandableView child = mHostLayoutController.getChildAt(i);
@@ -420,18 +420,6 @@
                 if (notGoneIndex != 0 || !aboveShelf) {
                     expandableRow.setAboveShelf(false);
                 }
-                if (notGoneIndex == 0) {
-                    StatusBarIconView icon = expandableRow.getEntry().getIcons().getShelfIcon();
-                    NotificationIconContainer.IconState iconState = getIconState(icon);
-                    // The icon state might be null in rare cases where the notification is actually
-                    // added to the layout, but not to the shelf. An example are replied messages,
-                    // since they don't show up on AOD
-                    if (iconState != null && iconState.clampedAppearAmount == 1.0f) {
-                        // only if the first icon is fully in the shelf we want to clip to it!
-                        backgroundTop = (int) (child.getTranslationY() - getTranslationY());
-                        firstElementRoundness = expandableRow.getTopRoundness();
-                    }
-                }
 
                 previousColor = ownColorUntinted;
                 notGoneIndex++;
@@ -467,8 +455,6 @@
 
         // TODO(b/172289889) transition last icon in shelf to notification icon and vice versa.
         setVisibility(isHidden ? View.INVISIBLE : View.VISIBLE);
-        setBackgroundTop(backgroundTop);
-        setFirstElementRoundness(firstElementRoundness);
         mShelfIcons.setSpeedBumpIndex(mHostLayoutController.getSpeedBumpIndex());
         mShelfIcons.calculateIconXTranslations();
         mShelfIcons.applyIconStates();
@@ -570,12 +556,6 @@
         }
     }
 
-    private void setFirstElementRoundness(float firstElementRoundness) {
-        if (mFirstElementRoundness != firstElementRoundness) {
-            mFirstElementRoundness = firstElementRoundness;
-        }
-    }
-
     private void updateIconClipAmount(ExpandableNotificationRow row) {
         float maxTop = row.getTranslationY();
         if (getClipTopAmount() != 0) {
@@ -1011,6 +991,18 @@
         expandableView.requestRoundnessReset(LegacySourceType.OnScroll);
     }
 
+    @Override
+    public void dump(PrintWriter pwOriginal, String[] args) {
+        IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal);
+        super.dump(pw, args);
+        if (DUMP_VERBOSE) {
+            DumpUtilsKt.withIncreasedIndent(pw, () -> {
+                pw.println("mActualWidth: " + mActualWidth);
+                pw.println("mStatusBarHeight: " + mStatusBarHeight);
+            });
+        }
+    }
+
     public class ShelfState extends ExpandableViewState {
         private boolean hasItemsInStableShelf;
         private ExpandableView firstViewInShelf;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerExt.kt b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerExt.kt
new file mode 100644
index 0000000..6148b40
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerExt.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.statusbar
+
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+
+/** Returns a [Flow] that emits whenever [StatusBarStateController.isExpanded] changes value. */
+val StatusBarStateController.expansionChanges: Flow<Boolean>
+    get() = conflatedCallbackFlow {
+        val listener =
+            object : StatusBarStateController.StateListener {
+                override fun onExpandedChanged(isExpanded: Boolean) {
+                    trySend(isExpanded)
+                }
+            }
+        trySend(isExpanded)
+        addCallback(listener)
+        awaitClose { removeCallback(listener) }
+    }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 186e6dc..784e2d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -129,6 +129,11 @@
     private boolean mIsDozing;
 
     /**
+     * If the device is currently dreaming or not.
+     */
+    private boolean mIsDreaming;
+
+    /**
      * If the status bar is currently expanded or not.
      */
     private boolean mIsExpanded;
@@ -294,6 +299,29 @@
     }
 
     @Override
+    public boolean setIsDreaming(boolean isDreaming) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "setIsDreaming:" + isDreaming);
+        }
+        if (mIsDreaming == isDreaming) {
+            return false;
+        }
+
+        mIsDreaming = isDreaming;
+
+        synchronized (mListeners) {
+            String tag = getClass().getSimpleName() + "#setIsDreaming";
+            DejankUtils.startDetectingBlockingIpcs(tag);
+            for (RankedListener rl : new ArrayList<>(mListeners)) {
+                rl.mListener.onDreamingChanged(isDreaming);
+            }
+            DejankUtils.stopDetectingBlockingIpcs(tag);
+        }
+
+        return true;
+    }
+
+    @Override
     public void setAndInstrumentDozeAmount(View view, float dozeAmount, boolean animated) {
         if (mDarkAnimator != null && mDarkAnimator.isRunning()) {
             if (animated && mDozeAmountTarget == dozeAmount) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
index e0cf812..088c568 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
@@ -99,6 +99,13 @@
     boolean setIsDozing(boolean isDozing);
 
     /**
+     * Update the dreaming state from {@link CentralSurfaces}'s perspective
+     * @param isDreaming whether we are dreaming
+     * @return {@code true} if the state changed, else {@code false}
+     */
+    boolean setIsDreaming(boolean isDreaming);
+
+    /**
      * Changes the current doze amount, also starts the
      * {@link com.android.internal.jank.InteractionJankMonitor InteractionJankMonitor} as possible.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
new file mode 100644
index 0000000..1099810
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.statusbar.connectivity
+
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.AirplaneModeTile
+import com.android.systemui.qs.tiles.BluetoothTile
+import com.android.systemui.qs.tiles.CastTile
+import com.android.systemui.qs.tiles.DataSaverTile
+import com.android.systemui.qs.tiles.HotspotTile
+import com.android.systemui.qs.tiles.InternetTile
+import com.android.systemui.qs.tiles.NfcTile
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
+
+@Module
+interface ConnectivityModule {
+
+    /** Inject InternetTile into tileMap in QSModule */
+    @Binds
+    @IntoMap
+    @StringKey(InternetTile.TILE_SPEC)
+    fun bindInternetTile(internetTile: InternetTile): QSTileImpl<*>
+
+    /** Inject BluetoothTile into tileMap in QSModule */
+    @Binds
+    @IntoMap
+    @StringKey(BluetoothTile.TILE_SPEC)
+    fun bindBluetoothTile(bluetoothTile: BluetoothTile): QSTileImpl<*>
+
+    /** Inject CastTile into tileMap in QSModule */
+    @Binds
+    @IntoMap
+    @StringKey(CastTile.TILE_SPEC)
+    fun bindCastTile(castTile: CastTile): QSTileImpl<*>
+
+    /** Inject HotspotTile into tileMap in QSModule */
+    @Binds
+    @IntoMap
+    @StringKey(HotspotTile.TILE_SPEC)
+    fun bindHotspotTile(hotspotTile: HotspotTile): QSTileImpl<*>
+
+    /** Inject AirplaneModeTile into tileMap in QSModule */
+    @Binds
+    @IntoMap
+    @StringKey(AirplaneModeTile.TILE_SPEC)
+    fun bindAirplaneModeTile(airplaneModeTile: AirplaneModeTile): QSTileImpl<*>
+
+    /** Inject DataSaverTile into tileMap in QSModule */
+    @Binds
+    @IntoMap
+    @StringKey(DataSaverTile.TILE_SPEC)
+    fun bindDataSaverTile(dataSaverTile: DataSaverTile): QSTileImpl<*>
+
+    /** Inject NfcTile into tileMap in QSModule */
+    @Binds @IntoMap @StringKey(NfcTile.TILE_SPEC) fun bindNfcTile(nfcTile: NfcTile): QSTileImpl<*>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index 098c617..d7568a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -39,6 +39,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.carrier.QSCarrierGroupController;
+import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.statusbar.ActionClickLogger;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.MediaArtworkProcessor;
@@ -184,11 +185,12 @@
     @SysUISingleton
     static CommandQueue provideCommandQueue(
             Context context,
+            DisplayTracker displayTracker,
             ProtoTracer protoTracer,
             CommandRegistry registry,
             DumpHandler dumpHandler
     ) {
-        return new CommandQueue(context, protoTracer, registry, dumpHandler);
+        return new CommandQueue(context, displayTracker, protoTracer, registry, dumpHandler);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
index 737b481..f259284 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
@@ -62,7 +62,7 @@
  */
 
 @SysUISingleton
-class PrivacyDotViewController @Inject constructor(
+open class PrivacyDotViewController @Inject constructor(
     @Main private val mainExecutor: Executor,
     private val stateController: StatusBarStateController,
     private val configurationController: ConfigurationController,
@@ -176,7 +176,7 @@
     }
 
     @UiThread
-    private fun hideDotView(dot: View, animate: Boolean) {
+    open fun hideDotView(dot: View, animate: Boolean) {
         dot.clearAnimation()
         if (animate) {
             dot.animate()
@@ -195,7 +195,7 @@
     }
 
     @UiThread
-    private fun showDotView(dot: View, animate: Boolean) {
+    open fun showDotView(dot: View, animate: Boolean) {
         dot.clearAnimation()
         if (animate) {
             dot.visibility = View.VISIBLE
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt
index 3a4731a..92a8356 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt
@@ -20,9 +20,9 @@
 import android.annotation.CallSuper
 import android.os.Looper
 import android.view.Choreographer
-import android.view.Display
 import android.view.InputEvent
 import android.view.MotionEvent
+import com.android.systemui.settings.DisplayTracker
 import com.android.systemui.shared.system.InputChannelCompat
 import com.android.systemui.shared.system.InputMonitorCompat
 
@@ -38,7 +38,8 @@
  * gesture is detected, they should call [onGestureDetected] (which will notify the callbacks).
  */
 abstract class GenericGestureDetector(
-    private val tag: String
+    private val tag: String,
+    private val displayTracker: DisplayTracker
 ) {
     /**
      * Active callbacks, each associated with a tag. Gestures will only be monitored if
@@ -86,7 +87,7 @@
     internal open fun startGestureListening() {
         stopGestureListening()
 
-        inputMonitor = InputMonitorCompat(tag, Display.DEFAULT_DISPLAY).also {
+        inputMonitor = InputMonitorCompat(tag, displayTracker.defaultDisplayId).also {
             inputReceiver = it.getInputReceiver(
                 Looper.getMainLooper(),
                 Choreographer.getInstance(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
index 5ab3d7c..693ae66 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
@@ -19,6 +19,7 @@
 import android.content.Context
 import android.view.MotionEvent
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.settings.DisplayTracker
 import com.android.systemui.statusbar.window.StatusBarWindowController
 import javax.inject.Inject
 
@@ -28,9 +29,10 @@
 @Inject
 constructor(
     context: Context,
+    displayTracker: DisplayTracker,
     logger: SwipeUpGestureLogger,
     private val statusBarWindowController: StatusBarWindowController,
-) : SwipeUpGestureHandler(context, logger, loggerTag = LOGGER_TAG) {
+) : SwipeUpGestureHandler(context, displayTracker, logger, loggerTag = LOGGER_TAG) {
     override fun startOfGestureIsWithinBounds(ev: MotionEvent): Boolean {
         // Gesture starts just below the status bar
         return ev.y >= statusBarWindowController.statusBarHeight &&
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureHandler.kt
index 5ecc35c..452762d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureHandler.kt
@@ -24,6 +24,7 @@
 import android.view.MotionEvent.ACTION_MOVE
 import android.view.MotionEvent.ACTION_UP
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.settings.DisplayTracker
 
 /**
  * A class to detect a generic "swipe up" gesture. To be notified when the swipe up gesture is
@@ -32,9 +33,10 @@
 @SysUISingleton
 abstract class SwipeUpGestureHandler(
     context: Context,
+    displayTracker: DisplayTracker,
     private val logger: SwipeUpGestureLogger,
-    private val loggerTag: String,
-) : GenericGestureDetector(SwipeUpGestureHandler::class.simpleName!!) {
+    private val loggerTag: String
+) : GenericGestureDetector(SwipeUpGestureHandler::class.simpleName!!, displayTracker) {
 
     private var startY: Float = 0f
     private var startTime: Long = 0L
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/TapGestureDetector.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/TapGestureDetector.kt
index 7ffb07a..a901d597 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/TapGestureDetector.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/TapGestureDetector.kt
@@ -21,6 +21,7 @@
 import android.view.InputEvent
 import android.view.MotionEvent
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.settings.DisplayTracker
 import javax.inject.Inject
 
 /**
@@ -29,8 +30,9 @@
  */
 @SysUISingleton
 class TapGestureDetector @Inject constructor(
-    private val context: Context
-) : GenericGestureDetector(TapGestureDetector::class.simpleName!!) {
+    private val context: Context,
+    displayTracker: DisplayTracker
+) : GenericGestureDetector(TapGestureDetector::class.simpleName!!, displayTracker) {
 
     private val gestureListener = object : GestureDetector.SimpleOnGestureListener() {
         override fun onSingleTapUp(e: MotionEvent): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index 43e2e52..6ef6165 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -31,15 +31,19 @@
 import android.os.UserHandle
 import android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS
 import android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS
+import android.provider.Settings.Secure.LOCK_SCREEN_WEATHER_ENABLED
 import android.util.Log
 import android.view.ContextThemeWrapper
 import android.view.View
 import android.view.ViewGroup
+import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.settingslib.Utils
+import com.android.systemui.Dumpable
 import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.plugins.ActivityStarter
@@ -52,14 +56,20 @@
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.shared.regionsampling.RegionSampler
 import com.android.systemui.shared.regionsampling.UpdateColorCallback
+import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DATE_SMARTSPACE_DATA_PLUGIN
+import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.WEATHER_SMARTSPACE_DATA_PLUGIN
+import com.android.systemui.plugins.Weather
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
 import com.android.systemui.util.concurrency.Execution
 import com.android.systemui.util.settings.SecureSettings
+import java.io.PrintWriter
+import java.time.Instant
 import java.util.Optional
 import java.util.concurrent.Executor
 import javax.inject.Inject
+import javax.inject.Named
 
 /** Controller for managing the smartspace view on the lockscreen */
 @SysUISingleton
@@ -78,18 +88,26 @@
         private val statusBarStateController: StatusBarStateController,
         private val deviceProvisionedController: DeviceProvisionedController,
         private val bypassController: KeyguardBypassController,
+        private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+        private val dumpManager: DumpManager,
         private val execution: Execution,
         @Main private val uiExecutor: Executor,
         @Background private val bgExecutor: Executor,
         @Main private val handler: Handler,
+        @Named(DATE_SMARTSPACE_DATA_PLUGIN)
+        optionalDatePlugin: Optional<BcSmartspaceDataPlugin>,
+        @Named(WEATHER_SMARTSPACE_DATA_PLUGIN)
+        optionalWeatherPlugin: Optional<BcSmartspaceDataPlugin>,
         optionalPlugin: Optional<BcSmartspaceDataPlugin>,
         optionalConfigPlugin: Optional<BcSmartspaceConfigPlugin>,
-) {
+) : Dumpable {
     companion object {
         private const val TAG = "LockscreenSmartspaceController"
     }
 
     private var session: SmartspaceSession? = null
+    private val datePlugin: BcSmartspaceDataPlugin? = optionalDatePlugin.orElse(null)
+    private val weatherPlugin: BcSmartspaceDataPlugin? = optionalWeatherPlugin.orElse(null)
     private val plugin: BcSmartspaceDataPlugin? = optionalPlugin.orElse(null)
     private val configPlugin: BcSmartspaceConfigPlugin? = optionalConfigPlugin.orElse(null)
 
@@ -131,6 +149,10 @@
 
     private val sessionListener = SmartspaceSession.OnTargetsAvailableListener { targets ->
         execution.assertIsMainThread()
+
+        // The weather data plugin takes unfiltered targets and performs the filtering internally.
+        weatherPlugin?.onTargetsAvailable(targets)
+
         val filteredTargets = targets.filter(::filterSmartspaceTarget)
         plugin?.onTargetsAvailable(filteredTargets)
         if (!isContentUpdatedOnce) {
@@ -151,6 +173,17 @@
             }
             isContentUpdatedOnce = true
         }
+
+        val now = Instant.now()
+        val weatherTarget = targets.find { t ->
+            t.featureType == SmartspaceTarget.FEATURE_WEATHER &&
+                    now.isAfter(Instant.ofEpochMilli(t.creationTimeMillis)) &&
+                    now.isBefore(Instant.ofEpochMilli(t.expiryTimeMillis))
+        }
+        if (weatherTarget != null) {
+            val weatherData = Weather.fromBundle(weatherTarget.baseAction.extras)
+            keyguardUpdateMonitor.sendWeatherData(weatherData)
+        }
     }
 
     private val userTrackerCallback = object : UserTracker.Callback {
@@ -201,12 +234,31 @@
 
     init {
         deviceProvisionedController.addCallback(deviceProvisionedListener)
+        dumpManager.registerDumpable(this)
     }
 
     fun isEnabled(): Boolean {
         execution.assertIsMainThread()
 
-        return featureFlags.isEnabled(Flags.SMARTSPACE) && plugin != null
+        return plugin != null
+    }
+
+    fun isDateWeatherDecoupled(): Boolean {
+        execution.assertIsMainThread()
+
+        return featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED) &&
+                datePlugin != null && weatherPlugin != null
+    }
+
+    fun isWeatherEnabled(): Boolean {
+       execution.assertIsMainThread()
+       val defaultValue = context.getResources().getBoolean(
+               com.android.internal.R.bool.config_lockscreenWeatherEnabledByDefault)
+       val showWeather = secureSettings.getIntForUser(
+           LOCK_SCREEN_WEATHER_ENABLED,
+           if (defaultValue) 1 else 0,
+           userTracker.userId) == 1
+       return showWeather
     }
 
     private fun updateBypassEnabled() {
@@ -215,6 +267,44 @@
     }
 
     /**
+     * Constructs the date view and connects it to the smartspace service.
+     */
+    fun buildAndConnectDateView(parent: ViewGroup): View? {
+        execution.assertIsMainThread()
+
+        if (!isEnabled()) {
+            throw RuntimeException("Cannot build view when not enabled")
+        }
+        if (!isDateWeatherDecoupled()) {
+            throw RuntimeException("Cannot build date view when not decoupled")
+        }
+
+        val view = buildView(parent, datePlugin)
+        connectSession()
+
+        return view
+    }
+
+    /**
+     * Constructs the weather view and connects it to the smartspace service.
+     */
+    fun buildAndConnectWeatherView(parent: ViewGroup): View? {
+        execution.assertIsMainThread()
+
+        if (!isEnabled()) {
+            throw RuntimeException("Cannot build view when not enabled")
+        }
+        if (!isDateWeatherDecoupled()) {
+            throw RuntimeException("Cannot build weather view when not decoupled")
+        }
+
+        val view = buildView(parent, weatherPlugin)
+        connectSession()
+
+        return view
+    }
+
+    /**
      * Constructs the smartspace view and connects it to the smartspace service.
      */
     fun buildAndConnectView(parent: ViewGroup): View? {
@@ -224,17 +314,17 @@
             throw RuntimeException("Cannot build view when not enabled")
         }
 
-        val view = buildView(parent)
+        val view = buildView(parent, plugin, configPlugin)
         connectSession()
 
         return view
     }
 
-    fun requestSmartspaceUpdate() {
-        session?.requestSmartspaceUpdate()
-    }
-
-    private fun buildView(parent: ViewGroup): View? {
+    private fun buildView(
+            parent: ViewGroup,
+            plugin: BcSmartspaceDataPlugin?,
+            configPlugin: BcSmartspaceConfigPlugin? = null
+    ): View? {
         if (plugin == null) {
             return null
         }
@@ -242,7 +332,7 @@
         val ssView = plugin.getView(parent)
         ssView.setUiSurface(BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
         ssView.registerDataProvider(plugin)
-        ssView.registerConfigProvider(configPlugin)
+        configPlugin?.let { ssView.registerConfigProvider(it) }
 
         ssView.setIntentStarter(object : BcSmartspaceDataPlugin.IntentStarter {
             override fun startIntent(view: View, intent: Intent, showOnLockscreen: Boolean) {
@@ -273,7 +363,8 @@
     }
 
     private fun connectSession() {
-        if (plugin == null || session != null || smartspaceViews.isEmpty()) {
+        if (datePlugin == null && weatherPlugin == null && plugin == null) return
+        if (session != null || smartspaceViews.isEmpty()) {
             return
         }
 
@@ -310,15 +401,22 @@
         statusBarStateController.addCallback(statusBarStateListener)
         bypassController.registerOnBypassStateChangedListener(bypassStateChangedListener)
 
-        plugin.registerSmartspaceEventNotifier {
-                e -> session?.notifySmartspaceEvent(e)
-        }
+        datePlugin?.registerSmartspaceEventNotifier { e -> session?.notifySmartspaceEvent(e) }
+        weatherPlugin?.registerSmartspaceEventNotifier { e -> session?.notifySmartspaceEvent(e) }
+        plugin?.registerSmartspaceEventNotifier { e -> session?.notifySmartspaceEvent(e) }
 
         updateBypassEnabled()
         reloadSmartspace()
     }
 
     /**
+     * Requests the smartspace session for an update.
+     */
+    fun requestSmartspaceUpdate() {
+        session?.requestSmartspaceUpdate()
+    }
+
+    /**
      * Disconnects the smartspace view from the smartspace service and cleans up any resources.
      */
     fun disconnect() {
@@ -341,9 +439,15 @@
         bypassController.unregisterOnBypassStateChangedListener(bypassStateChangedListener)
         session = null
 
+        datePlugin?.registerSmartspaceEventNotifier(null)
+
+        weatherPlugin?.registerSmartspaceEventNotifier(null)
+        weatherPlugin?.onTargetsAvailable(emptyList())
+
         plugin?.registerSmartspaceEventNotifier(null)
         plugin?.onTargetsAvailable(emptyList())
-        Log.d(TAG, "Ending smartspace session for lockscreen")
+
+        Log.d(TAG, "Ended smartspace session for lockscreen")
     }
 
     fun addListener(listener: SmartspaceTargetListener) {
@@ -357,8 +461,11 @@
     }
 
     private fun filterSmartspaceTarget(t: SmartspaceTarget): Boolean {
+        if (isDateWeatherDecoupled()) {
+            return t.featureType != SmartspaceTarget.FEATURE_WEATHER
+        }
         if (!showNotifications) {
-            return t.getFeatureType() == SmartspaceTarget.FEATURE_WEATHER
+            return t.featureType == SmartspaceTarget.FEATURE_WEATHER
         }
         return when (t.userHandle) {
             userTracker.userHandle -> {
@@ -441,4 +548,11 @@
         }
         return null
     }
+
+    override fun dump(pw: PrintWriter, args: Array<out String>) {
+        pw.println("Region Samplers: ${regionSamplers.size}")
+        regionSamplers.map { (_, sampler) ->
+            sampler.dump(pw)
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
index 635ed7c..fc89be2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
@@ -17,13 +17,16 @@
 package com.android.systemui.statusbar.notification
 
 import android.content.Context
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.FlagResolver
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import javax.inject.Inject
 
 class NotifPipelineFlags @Inject constructor(
     val context: Context,
-    val featureFlags: FeatureFlags
+    val featureFlags: FeatureFlags,
+    val sysPropFlags: FlagResolver,
 ) {
     init {
         featureFlags.addListener(Flags.DISABLE_FSI) { event -> event.requestNoRestart() }
@@ -32,8 +35,6 @@
     fun isDevLoggingEnabled(): Boolean =
         featureFlags.isEnabled(Flags.NOTIFICATION_PIPELINE_DEVELOPER_LOGGING)
 
-    fun isSmartspaceDedupingEnabled(): Boolean = featureFlags.isEnabled(Flags.SMARTSPACE)
-
     fun fullScreenIntentRequiresKeyguard(): Boolean =
         featureFlags.isEnabled(Flags.FSI_REQUIRES_KEYGUARD)
 
@@ -41,11 +42,21 @@
 
     fun disableFsi(): Boolean = featureFlags.isEnabled(Flags.DISABLE_FSI)
 
-    val shouldFilterUnseenNotifsOnKeyguard: Boolean by lazy {
-        featureFlags.isEnabled(Flags.FILTER_UNSEEN_NOTIFS_ON_KEYGUARD)
-    }
+    fun forceDemoteFsi(): Boolean =
+            sysPropFlags.isEnabled(NotificationFlags.FSI_FORCE_DEMOTE)
 
-    val isNoHunForOldWhenEnabled: Boolean by lazy {
-        featureFlags.isEnabled(Flags.NO_HUN_FOR_OLD_WHEN)
-    }
+    fun showStickyHunForDeniedFsi(): Boolean =
+            sysPropFlags.isEnabled(NotificationFlags.SHOW_STICKY_HUN_FOR_DENIED_FSI)
+
+    fun allowDismissOngoing(): Boolean =
+            sysPropFlags.isEnabled(NotificationFlags.ALLOW_DISMISS_ONGOING)
+
+    fun isOtpRedactionEnabled(): Boolean =
+            sysPropFlags.isEnabled(NotificationFlags.OTP_REDACTION)
+
+    val shouldFilterUnseenNotifsOnKeyguard: Boolean
+        get() = featureFlags.isEnabled(Flags.FILTER_UNSEEN_NOTIFS_ON_KEYGUARD)
+
+    val isNoHunForOldWhenEnabled: Boolean
+        get() = featureFlags.isEnabled(Flags.NO_HUN_FOR_OLD_WHEN)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index aeae89c..7e53d54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -31,6 +31,7 @@
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator
 import com.android.systemui.statusbar.phone.DozeParameters
 import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.phone.KeyguardBypassController.OnBypassStateChangedListener
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController
 import com.android.systemui.statusbar.policy.HeadsUpManager
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
@@ -38,7 +39,6 @@
 import javax.inject.Inject
 import kotlin.math.min
 
-
 @SysUISingleton
 class NotificationWakeUpCoordinator @Inject constructor(
     dumpManager: DumpManager,
@@ -68,6 +68,7 @@
     private var mLinearDozeAmount: Float = 0.0f
     private var mDozeAmount: Float = 0.0f
     private var mDozeAmountSource: String = "init"
+    private var mNotifsHiddenByDozeAmountOverride: Boolean = false
     private var mNotificationVisibleAmount = 0.0f
     private var mNotificationsVisible = false
     private var mNotificationsVisibleForExpansion = false
@@ -130,6 +131,7 @@
                 }
             }
         }
+
     /**
      * True if we can show pulsing heads up notifications
      */
@@ -149,10 +151,19 @@
             return canShow
         }
 
+    private val bypassStateChangedListener = object : OnBypassStateChangedListener {
+        override fun onBypassStateChanged(isEnabled: Boolean) {
+            // When the bypass state changes, we have to check whether we should re-show the
+            // notifications by clearing the doze amount override which hides them.
+            maybeClearDozeAmountOverrideHidingNotifs()
+        }
+    }
+
     init {
         dumpManager.registerDumpable(this)
         mHeadsUpManager.addListener(this)
         statusBarStateController.addCallback(this)
+        bypassController.registerOnBypassStateChangedListener(bypassStateChangedListener)
         addListener(object : WakeUpListener {
             override fun onFullyHiddenChanged(isFullyHidden: Boolean) {
                 if (isFullyHidden && mNotificationsVisibleForExpansion) {
@@ -261,12 +272,18 @@
         setDozeAmount(linear, eased, source = "StatusBar")
     }
 
-    fun setDozeAmount(linear: Float, eased: Float, source: String) {
+    fun setDozeAmount(
+        linear: Float,
+        eased: Float,
+        source: String,
+        hidesNotifsByOverride: Boolean = false
+    ) {
         val changed = linear != mLinearDozeAmount
         logger.logSetDozeAmount(linear, eased, source, statusBarStateController.state, changed)
         mLinearDozeAmount = linear
         mDozeAmount = eased
         mDozeAmountSource = source
+        mNotifsHiddenByDozeAmountOverride = hidesNotifsByOverride
         mStackScrollerController.setDozeAmount(mDozeAmount)
         updateHideAmount()
         if (changed && linear == 0.0f) {
@@ -295,6 +312,8 @@
             return
         }
 
+        maybeClearDozeAmountOverrideHidingNotifs()
+
         if (bypassController.bypassEnabled &&
                 newState == StatusBarState.KEYGUARD && state == StatusBarState.SHADE_LOCKED &&
             (!statusBarStateController.isDozing || shouldAnimateVisibility())) {
@@ -325,7 +344,8 @@
     private fun overrideDozeAmountIfBypass(): Boolean {
         if (bypassController.bypassEnabled) {
             if (statusBarStateController.state == StatusBarState.KEYGUARD) {
-                setDozeAmount(1f, 1f, source = "Override: bypass (keyguard)")
+                setDozeAmount(1f, 1f, source = "Override: bypass (keyguard)",
+                        hidesNotifsByOverride = true)
             } else {
                 setDozeAmount(0f, 0f, source = "Override: bypass (shade)")
             }
@@ -335,6 +355,37 @@
     }
 
     /**
+     * If the last [setDozeAmount] call was an override to hide notifications, then this call will
+     * check for the set of states that may have caused that override, and if none of them still
+     * apply, and the device is awake or not on the keyguard, then dozeAmount will be reset to 0.
+     * This fixes bugs where the bypass state changing could result in stale overrides, hiding
+     * notifications either on the inside screen or even after unlock.
+     */
+    private fun maybeClearDozeAmountOverrideHidingNotifs() {
+        if (mNotifsHiddenByDozeAmountOverride) {
+            val onKeyguard = statusBarStateController.state == StatusBarState.KEYGUARD
+            val dozing = statusBarStateController.isDozing
+            val bypass = bypassController.bypassEnabled
+            val animating =
+                    screenOffAnimationController.overrideNotificationsFullyDozingOnKeyguard()
+            // Overrides are set by [overrideDozeAmountIfAnimatingScreenOff] and
+            // [overrideDozeAmountIfBypass] based on 'animating' and 'bypass' respectively, so only
+            // clear the override if both those conditions are cleared.  But also require either
+            // !dozing or !onKeyguard because those conditions should indicate that we intend
+            // notifications to be visible, and thus it is safe to unhide them.
+            val willRemove = (!onKeyguard || !dozing) && !bypass && !animating
+            logger.logMaybeClearDozeAmountOverrideHidingNotifs(
+                    willRemove = willRemove,
+                    onKeyguard = onKeyguard, dozing = dozing,
+                    bypass = bypass, animating = animating,
+            )
+            if (willRemove) {
+                setDozeAmount(0f, 0f, source = "Removed: $mDozeAmountSource")
+            }
+        }
+    }
+
+    /**
      * If we're playing the screen off animation, force the notification doze amount to be 1f (fully
      * dozing). This is needed so that the notifications aren't briefly visible as the screen turns
      * off and dozeAmount goes from 1f to 0f.
@@ -344,7 +395,8 @@
      */
     private fun overrideDozeAmountIfAnimatingScreenOff(linearDozeAmount: Float): Boolean {
         if (screenOffAnimationController.overrideNotificationsFullyDozingOnKeyguard()) {
-            setDozeAmount(1f, 1f, source = "Override: animating screen off")
+            setDozeAmount(1f, 1f, source = "Override: animating screen off",
+                    hidesNotifsByOverride = true)
             return true
         }
 
@@ -430,6 +482,7 @@
         pw.println("mLinearDozeAmount: $mLinearDozeAmount")
         pw.println("mDozeAmount: $mDozeAmount")
         pw.println("mDozeAmountSource: $mDozeAmountSource")
+        pw.println("mNotifsHiddenByDozeAmountOverride: $mNotifsHiddenByDozeAmountOverride")
         pw.println("mNotificationVisibleAmount: $mNotificationVisibleAmount")
         pw.println("mNotificationsVisible: $mNotificationsVisible")
         pw.println("mNotificationsVisibleForExpansion: $mNotificationsVisibleForExpansion")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt
index de18b0c..4464531 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt
@@ -46,6 +46,25 @@
         )
     }
 
+    fun logMaybeClearDozeAmountOverrideHidingNotifs(
+        willRemove: Boolean,
+        onKeyguard: Boolean,
+        dozing: Boolean,
+        bypass: Boolean,
+        animating: Boolean,
+    ) {
+        buffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 =
+                    "willRemove=$willRemove onKeyguard=$onKeyguard dozing=$dozing" +
+                        " bypass=$bypass animating=$animating"
+            },
+            { "maybeClearDozeAmountOverrideHidingNotifs() $str1" }
+        )
+    }
+
     fun logOnDozeAmountChanged(linear: Float, eased: Float) {
         buffer.log(
             TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index df35c9e..aa9a6c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -164,6 +164,11 @@
 
 
     private Queue<NotifEvent> mEventQueue = new ArrayDeque<>();
+    private final Runnable mRebuildListRunnable = () -> {
+        if (mBuildListener != null) {
+            mBuildListener.onBuildList(mReadOnlyNotificationSet, "asynchronousUpdate");
+        }
+    };
 
     private boolean mAttached = false;
     private boolean mAmDispatchingToOtherCode;
@@ -458,7 +463,7 @@
             int modificationType) {
         Assert.isMainThread();
         mEventQueue.add(new ChannelChangedEvent(pkgName, user, channel, modificationType));
-        dispatchEventsAndRebuildList("onNotificationChannelModified");
+        dispatchEventsAndAsynchronouslyRebuildList();
     }
 
     private void onNotificationsInitialized() {
@@ -613,15 +618,39 @@
 
     private void dispatchEventsAndRebuildList(String reason) {
         Trace.beginSection("NotifCollection.dispatchEventsAndRebuildList");
+        if (mMainHandler.hasCallbacks(mRebuildListRunnable)) {
+            mMainHandler.removeCallbacks(mRebuildListRunnable);
+        }
+
+        dispatchEvents();
+
+        if (mBuildListener != null) {
+            mBuildListener.onBuildList(mReadOnlyNotificationSet, reason);
+        }
+        Trace.endSection();
+    }
+
+    private void dispatchEventsAndAsynchronouslyRebuildList() {
+        Trace.beginSection("NotifCollection.dispatchEventsAndAsynchronouslyRebuildList");
+
+        dispatchEvents();
+
+        if (!mMainHandler.hasCallbacks(mRebuildListRunnable)) {
+            mMainHandler.postDelayed(mRebuildListRunnable, 1000L);
+        }
+
+        Trace.endSection();
+    }
+
+    private void dispatchEvents() {
+        Trace.beginSection("NotifCollection.dispatchEvents");
+
         mAmDispatchingToOtherCode = true;
         while (!mEventQueue.isEmpty()) {
             mEventQueue.remove().dispatchTo(mNotifCollectionListeners);
         }
         mAmDispatchingToOtherCode = false;
 
-        if (mBuildListener != null) {
-            mBuildListener.onBuildList(mReadOnlyNotificationSet, reason);
-        }
         Trace.endSection();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
index e996b78..82bd45c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
@@ -14,18 +14,19 @@
  * limitations under the License.
  */
 
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
 package com.android.systemui.statusbar.notification.collection.coordinator
 
-import android.database.ContentObserver
 import android.os.UserHandle
 import android.provider.Settings
 import androidx.annotation.VisibleForTesting
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.expansionChanges
 import com.android.systemui.statusbar.notification.NotifPipelineFlags
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -35,20 +36,25 @@
 import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
 import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderImpl
 import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider
+import com.android.systemui.statusbar.policy.HeadsUpManager
+import com.android.systemui.statusbar.policy.headsUpEvents
 import com.android.systemui.util.settings.SecureSettings
-import com.android.systemui.util.settings.SettingsProxy
+import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
 import javax.inject.Inject
+import kotlin.time.Duration
 import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.transformLatest
 import kotlinx.coroutines.launch
 
 /**
@@ -60,6 +66,7 @@
 @Inject
 constructor(
     @Background private val bgDispatcher: CoroutineDispatcher,
+    private val headsUpManager: HeadsUpManager,
     private val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider,
     private val keyguardRepository: KeyguardRepository,
     private val notifPipelineFlags: NotifPipelineFlags,
@@ -87,28 +94,78 @@
     private fun attachUnseenFilter(pipeline: NotifPipeline) {
         pipeline.addFinalizeFilter(unseenNotifFilter)
         pipeline.addCollectionListener(collectionListener)
-        scope.launch { clearUnseenWhenKeyguardIsDismissed() }
+        scope.launch { trackUnseenNotificationsWhileUnlocked() }
         scope.launch { invalidateWhenUnseenSettingChanges() }
     }
 
-    private suspend fun clearUnseenWhenKeyguardIsDismissed() {
-        // Use collectLatest so that the suspending block is cancelled if isKeyguardShowing changes
-        // during the timeout period
+    private suspend fun trackUnseenNotificationsWhileUnlocked() {
+        // Use collectLatest so that trackUnseenNotifications() is cancelled when the keyguard is
+        // showing again
+        var clearUnseenOnUnlock = false
         keyguardRepository.isKeyguardShowing.collectLatest { isKeyguardShowing ->
-            if (!isKeyguardShowing) {
+            if (isKeyguardShowing) {
+                // Wait for the user to spend enough time on the lock screen before clearing unseen
+                // set when unlocked
+                awaitTimeSpentNotDozing(SEEN_TIMEOUT)
+                clearUnseenOnUnlock = true
+            } else {
                 unseenNotifFilter.invalidateList("keyguard no longer showing")
-                delay(SEEN_TIMEOUT)
+                if (clearUnseenOnUnlock) {
+                    clearUnseenOnUnlock = false
+                    unseenNotifications.clear()
+                }
+                trackUnseenNotifications()
+            }
+        }
+    }
+
+    private suspend fun awaitTimeSpentNotDozing(duration: Duration) {
+        keyguardRepository.isDozing
+            // Use transformLatest so that the timeout delay is cancelled if the device enters doze,
+            // and is restarted when doze ends.
+            .transformLatest { isDozing ->
+                if (!isDozing) {
+                    delay(duration)
+                    // Signal timeout has completed
+                    emit(Unit)
+                }
+            }
+            // Suspend until the first emission
+            .first()
+    }
+
+    private suspend fun trackUnseenNotifications() {
+        coroutineScope {
+            launch { clearUnseenNotificationsWhenShadeIsExpanded() }
+            launch { markHeadsUpNotificationsAsSeen() }
+        }
+    }
+
+    private suspend fun clearUnseenNotificationsWhenShadeIsExpanded() {
+        statusBarStateController.expansionChanges.collect { isExpanded ->
+            if (isExpanded) {
                 unseenNotifications.clear()
             }
         }
     }
 
+    private suspend fun markHeadsUpNotificationsAsSeen() {
+        headsUpManager.allEntries
+            .filter { it.isRowPinned }
+            .forEach { unseenNotifications.remove(it) }
+        headsUpManager.headsUpEvents.collect { (entry, isHun) ->
+            if (isHun) {
+                unseenNotifications.remove(entry)
+            }
+        }
+    }
+
     private suspend fun invalidateWhenUnseenSettingChanges() {
         secureSettings
             // emit whenever the setting has changed
-            .settingChangesForUser(
-                Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
+            .observerFlow(
                 UserHandle.USER_ALL,
+                Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
             )
             // perform a query immediately
             .onStart { emit(Unit) }
@@ -136,13 +193,17 @@
     private val collectionListener =
         object : NotifCollectionListener {
             override fun onEntryAdded(entry: NotificationEntry) {
-                if (keyguardRepository.isKeyguardShowing()) {
+                if (
+                    keyguardRepository.isKeyguardShowing() || !statusBarStateController.isExpanded
+                ) {
                     unseenNotifications.add(entry)
                 }
             }
 
             override fun onEntryUpdated(entry: NotificationEntry) {
-                if (keyguardRepository.isKeyguardShowing()) {
+                if (
+                    keyguardRepository.isKeyguardShowing() || !statusBarStateController.isExpanded
+                ) {
                     unseenNotifications.add(entry)
                 }
             }
@@ -215,15 +276,3 @@
         private val SEEN_TIMEOUT = 5.seconds
     }
 }
-
-private fun SettingsProxy.settingChangesForUser(name: String, userHandle: Int): Flow<Unit> =
-    conflatedCallbackFlow {
-        val observer =
-            object : ContentObserver(null) {
-                override fun onChange(selfChange: Boolean) {
-                    trySend(Unit)
-                }
-            }
-        registerContentObserverForUser(name, observer, userHandle)
-        awaitClose { unregisterContentObserver(observer) }
-    }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
index 1399385..03a3ca5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
@@ -88,9 +88,7 @@
         mCoordinators.add(viewConfigCoordinator)
         mCoordinators.add(visualStabilityCoordinator)
         mCoordinators.add(sensitiveContentCoordinator)
-        if (notifPipelineFlags.isSmartspaceDedupingEnabled()) {
-            mCoordinators.add(smartspaceDedupingCoordinator)
-        }
+        mCoordinators.add(smartspaceDedupingCoordinator)
         mCoordinators.add(headsUpCoordinator)
         mCoordinators.add(gutsCoordinator)
         mCoordinators.add(preparationCoordinator)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 808638a..8436ff7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -16,27 +16,15 @@
 
 package com.android.systemui.statusbar.notification.dagger;
 
-import android.app.INotificationManager;
 import android.content.Context;
-import android.content.pm.LauncherApps;
-import android.content.pm.ShortcutManager;
-import android.os.Handler;
-import android.view.accessibility.AccessibilityManager;
 
-import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.R;
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dagger.qualifiers.UiBackground;
-import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.settings.UserContextProvider;
-import com.android.systemui.shade.ShadeController;
 import com.android.systemui.shade.ShadeEventsModule;
 import com.android.systemui.shade.ShadeExpansionStateManager;
 import com.android.systemui.statusbar.NotificationListener;
-import com.android.systemui.statusbar.notification.AssistantFeedbackController;
 import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
 import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
 import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
@@ -50,7 +38,6 @@
 import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
 import com.android.systemui.statusbar.notification.collection.inflation.OnUserInteractionCallbackImpl;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
-import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
 import com.android.systemui.statusbar.notification.collection.provider.NotificationVisibilityProviderImpl;
 import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderModule;
 import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator;
@@ -72,22 +59,17 @@
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger;
 import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerImpl;
-import com.android.systemui.statusbar.notification.row.ChannelEditorDialogController;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
 import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager;
 import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.wmshell.BubblesManager;
 
-import java.util.Optional;
 import java.util.concurrent.Executor;
 
 import javax.inject.Provider;
 
 import dagger.Binds;
-import dagger.Lazy;
 import dagger.Module;
 import dagger.Provides;
 
@@ -109,48 +91,6 @@
     @Binds
     StackScrollAlgorithm.BypassController bindBypassController(KeyguardBypassController impl);
 
-    /** Provides an instance of {@link NotificationGutsManager} */
-    @SysUISingleton
-    @Provides
-    static NotificationGutsManager provideNotificationGutsManager(
-            Context context,
-            Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
-            @Main Handler mainHandler,
-            @Background Handler bgHandler,
-            AccessibilityManager accessibilityManager,
-            HighPriorityProvider highPriorityProvider,
-            INotificationManager notificationManager,
-            PeopleSpaceWidgetManager peopleSpaceWidgetManager,
-            LauncherApps launcherApps,
-            ShortcutManager shortcutManager,
-            ChannelEditorDialogController channelEditorDialogController,
-            UserContextProvider contextTracker,
-            AssistantFeedbackController assistantFeedbackController,
-            Optional<BubblesManager> bubblesManagerOptional,
-            UiEventLogger uiEventLogger,
-            OnUserInteractionCallback onUserInteractionCallback,
-            ShadeController shadeController) {
-        return new NotificationGutsManager(
-                context,
-                centralSurfacesOptionalLazy,
-                mainHandler,
-                bgHandler,
-                accessibilityManager,
-                highPriorityProvider,
-                notificationManager,
-                peopleSpaceWidgetManager,
-                launcherApps,
-                shortcutManager,
-                channelEditorDialogController,
-                contextTracker,
-                assistantFeedbackController,
-                bubblesManagerOptional,
-                uiEventLogger,
-                onUserInteractionCallback,
-                shadeController
-        );
-    }
-
     /** Provides an instance of {@link NotifGutsViewManager} */
     @Binds
     NotifGutsViewManager bindNotifGutsViewManager(NotificationGutsManager notificationGutsManager);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
index 13b3aca..27fe747 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
@@ -70,7 +70,15 @@
         buffer.log(TAG, DEBUG, {
             str1 = entry.logKey
         }, {
-            "No alerting: snoozed package: $str1"
+            "No heads up: snoozed package: $str1"
+        })
+    }
+
+    fun logHeadsUpPackageSnoozeBypassedHasFsi(entry: NotificationEntry) {
+        buffer.log(TAG, DEBUG, {
+            str1 = entry.logKey
+        }, {
+            "Heads up: package snooze bypassed because notification has full-screen intent: $str1"
         })
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index 9bcf92d..afeb72f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -19,6 +19,7 @@
 import static com.android.systemui.statusbar.StatusBarState.SHADE;
 import static com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD;
 import static com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR;
+import static com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.HUN_SNOOZE_BYPASSED_POTENTIALLY_SUPPRESSED_FSI;
 
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -87,7 +88,10 @@
         FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD(1236),
 
         @UiEvent(doc = "HUN suppressed for old when")
-        HUN_SUPPRESSED_OLD_WHEN(1237);
+        HUN_SUPPRESSED_OLD_WHEN(1237),
+
+        @UiEvent(doc = "HUN snooze bypassed for potentially suppressed FSI")
+        HUN_SNOOZE_BYPASSED_POTENTIALLY_SUPPRESSED_FSI(1269);
 
         private final int mId;
 
@@ -409,7 +413,15 @@
             return false;
         }
 
-        if (isSnoozedPackage(sbn)) {
+        final boolean isSnoozedPackage = isSnoozedPackage(sbn);
+        final boolean fsiRequiresKeyguard = mFlags.fullScreenIntentRequiresKeyguard();
+        final boolean hasFsi = sbn.getNotification().fullScreenIntent != null;
+
+        // Assume any notification with an FSI is time-sensitive (like an alarm or incoming call)
+        // and ignore whether HUNs have been snoozed for the package.
+        final boolean shouldBypassSnooze = fsiRequiresKeyguard && hasFsi;
+
+        if (isSnoozedPackage && !shouldBypassSnooze) {
             if (log) mLogger.logNoHeadsUpPackageSnoozed(entry);
             return false;
         }
@@ -447,6 +459,19 @@
                 return false;
             }
         }
+
+        if (isSnoozedPackage) {
+            if (log) {
+                mLogger.logHeadsUpPackageSnoozeBypassedHasFsi(entry);
+                final int uid = entry.getSbn().getUid();
+                final String packageName = entry.getSbn().getPackageName();
+                mUiEventLogger.log(HUN_SNOOZE_BYPASSED_POTENTIALLY_SUPPRESSED_FSI, uid,
+                        packageName);
+            }
+
+            return true;
+        }
+
         if (log) mLogger.logHeadsUp(entry);
         return true;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 58f59be..a37bbbc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -62,7 +62,7 @@
  * are not.
  */
 public class NotificationLogger implements StateListener {
-    private static final String TAG = "NotificationLogger";
+    static final String TAG = "NotificationLogger";
     private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
 
     /** The minimum delay in ms between reports of notification visibility. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryLogger.kt
index ec8501a..abe0670 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryLogger.kt
@@ -18,13 +18,16 @@
 package com.android.systemui.statusbar.notification.logging
 
 import android.app.StatsManager
+import android.util.Log
 import android.util.StatsEvent
+import androidx.annotation.VisibleForTesting
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.shared.system.SysUiStatsLog
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
 import com.android.systemui.util.traceSection
+import java.lang.Exception
 import java.util.concurrent.Executor
 import javax.inject.Inject
 import kotlin.math.roundToInt
@@ -82,43 +85,56 @@
                 return StatsManager.PULL_SKIP
             }
 
-            // Notifications can only be retrieved on the main thread, so switch to that thread.
-            val notifications = getAllNotificationsOnMainThread()
-            val notificationMemoryUse =
-                NotificationMemoryMeter.notificationMemoryUse(notifications)
-                    .sortedWith(
-                        compareBy(
-                            { it.packageName },
-                            { it.objectUsage.style },
-                            { it.notificationKey }
+            try {
+                // Notifications can only be retrieved on the main thread, so switch to that thread.
+                val notifications = getAllNotificationsOnMainThread()
+                val notificationMemoryUse =
+                    NotificationMemoryMeter.notificationMemoryUse(notifications)
+                        .sortedWith(
+                            compareBy(
+                                { it.packageName },
+                                { it.objectUsage.style },
+                                { it.notificationKey }
+                            )
+                        )
+                val usageData = aggregateMemoryUsageData(notificationMemoryUse)
+                usageData.forEach { (_, use) ->
+                    data.add(
+                        SysUiStatsLog.buildStatsEvent(
+                            SysUiStatsLog.NOTIFICATION_MEMORY_USE,
+                            use.uid,
+                            use.style,
+                            use.count,
+                            use.countWithInflatedViews,
+                            toKb(use.smallIconObject),
+                            use.smallIconBitmapCount,
+                            toKb(use.largeIconObject),
+                            use.largeIconBitmapCount,
+                            toKb(use.bigPictureObject),
+                            use.bigPictureBitmapCount,
+                            toKb(use.extras),
+                            toKb(use.extenders),
+                            toKb(use.smallIconViews),
+                            toKb(use.largeIconViews),
+                            toKb(use.systemIconViews),
+                            toKb(use.styleViews),
+                            toKb(use.customViews),
+                            toKb(use.softwareBitmaps),
+                            use.seenCount
                         )
                     )
-            val usageData = aggregateMemoryUsageData(notificationMemoryUse)
-            usageData.forEach { (_, use) ->
-                data.add(
-                    SysUiStatsLog.buildStatsEvent(
-                        SysUiStatsLog.NOTIFICATION_MEMORY_USE,
-                        use.uid,
-                        use.style,
-                        use.count,
-                        use.countWithInflatedViews,
-                        toKb(use.smallIconObject),
-                        use.smallIconBitmapCount,
-                        toKb(use.largeIconObject),
-                        use.largeIconBitmapCount,
-                        toKb(use.bigPictureObject),
-                        use.bigPictureBitmapCount,
-                        toKb(use.extras),
-                        toKb(use.extenders),
-                        toKb(use.smallIconViews),
-                        toKb(use.largeIconViews),
-                        toKb(use.systemIconViews),
-                        toKb(use.styleViews),
-                        toKb(use.customViews),
-                        toKb(use.softwareBitmaps),
-                        use.seenCount
-                    )
-                )
+                }
+            } catch (e: InterruptedException) {
+                // This can happen if the device is sleeping or view walking takes too long.
+                // The statsd collector will interrupt the thread and we need to handle it
+                // gracefully.
+                Log.w(NotificationLogger.TAG, "Timed out when measuring notification memory.", e)
+                return@traceSection StatsManager.PULL_SKIP
+            } catch (e: Exception) {
+                // Error while collecting data, this should not crash prod SysUI. Just
+                // log WTF and move on.
+                Log.wtf(NotificationLogger.TAG, "Failed to measure notification memory.", e)
+                return@traceSection StatsManager.PULL_SKIP
             }
 
             return StatsManager.PULL_SUCCESS
@@ -128,67 +144,70 @@
         runBlocking(mainDispatcher) {
             traceSection("NML#getNotifications") { notificationPipeline.allNotifs }
         }
-
-    /** Aggregates memory usage data by package and style, returning sums. */
-    private fun aggregateMemoryUsageData(
-        notificationMemoryUse: List<NotificationMemoryUsage>
-    ): Map<Pair<String, Int>, NotificationMemoryUseAtomBuilder> {
-        return notificationMemoryUse
-            .groupingBy { Pair(it.packageName, it.objectUsage.style) }
-            .aggregate {
-                _,
-                accumulator: NotificationMemoryUseAtomBuilder?,
-                element: NotificationMemoryUsage,
-                first ->
-                val use =
-                    if (first) {
-                        NotificationMemoryUseAtomBuilder(element.uid, element.objectUsage.style)
-                    } else {
-                        accumulator!!
-                    }
-
-                use.count++
-                // If the views of the notification weren't inflated, the list of memory usage
-                // parameters will be empty.
-                if (element.viewUsage.isNotEmpty()) {
-                    use.countWithInflatedViews++
-                }
-
-                use.smallIconObject += element.objectUsage.smallIcon
-                if (element.objectUsage.smallIcon > 0) {
-                    use.smallIconBitmapCount++
-                }
-
-                use.largeIconObject += element.objectUsage.largeIcon
-                if (element.objectUsage.largeIcon > 0) {
-                    use.largeIconBitmapCount++
-                }
-
-                use.bigPictureObject += element.objectUsage.bigPicture
-                if (element.objectUsage.bigPicture > 0) {
-                    use.bigPictureBitmapCount++
-                }
-
-                use.extras += element.objectUsage.extras
-                use.extenders += element.objectUsage.extender
-
-                // Use totals count which are more accurate when aggregated
-                // in this manner.
-                element.viewUsage
-                    .firstOrNull { vu -> vu.viewType == ViewType.TOTAL }
-                    ?.let {
-                        use.smallIconViews += it.smallIcon
-                        use.largeIconViews += it.largeIcon
-                        use.systemIconViews += it.systemIcons
-                        use.styleViews += it.style
-                        use.customViews += it.style
-                        use.softwareBitmaps += it.softwareBitmapsPenalty
-                    }
-
-                return@aggregate use
-            }
-    }
-
-    /** Rounds the passed value to the nearest KB - e.g. 700B rounds to 1KB. */
-    private fun toKb(value: Int): Int = (value.toFloat() / 1024f).roundToInt()
 }
+
+/** Aggregates memory usage data by package and style, returning sums. */
+@VisibleForTesting
+internal fun aggregateMemoryUsageData(
+    notificationMemoryUse: List<NotificationMemoryUsage>
+): Map<Pair<String, Int>, NotificationMemoryLogger.NotificationMemoryUseAtomBuilder> {
+    return notificationMemoryUse
+        .groupingBy { Pair(it.packageName, it.objectUsage.style) }
+        .aggregate {
+            _,
+            accumulator: NotificationMemoryLogger.NotificationMemoryUseAtomBuilder?,
+            element: NotificationMemoryUsage,
+            first ->
+            val use =
+                if (first) {
+                    NotificationMemoryLogger.NotificationMemoryUseAtomBuilder(
+                        element.uid,
+                        element.objectUsage.style
+                    )
+                } else {
+                    accumulator!!
+                }
+
+            use.count++
+            // If the views of the notification weren't inflated, the list of memory usage
+            // parameters will be empty.
+            if (element.viewUsage.isNotEmpty()) {
+                use.countWithInflatedViews++
+            }
+
+            use.smallIconObject += element.objectUsage.smallIcon
+            if (element.objectUsage.smallIcon > 0) {
+                use.smallIconBitmapCount++
+            }
+
+            use.largeIconObject += element.objectUsage.largeIcon
+            if (element.objectUsage.largeIcon > 0) {
+                use.largeIconBitmapCount++
+            }
+
+            use.bigPictureObject += element.objectUsage.bigPicture
+            if (element.objectUsage.bigPicture > 0) {
+                use.bigPictureBitmapCount++
+            }
+
+            use.extras += element.objectUsage.extras
+            use.extenders += element.objectUsage.extender
+
+            // Use totals count which are more accurate when aggregated
+            // in this manner.
+            element.viewUsage
+                .firstOrNull { vu -> vu.viewType == ViewType.TOTAL }
+                ?.let {
+                    use.smallIconViews += it.smallIcon
+                    use.largeIconViews += it.largeIcon
+                    use.systemIconViews += it.systemIcons
+                    use.styleViews += it.style
+                    use.customViews += it.customViews
+                    use.softwareBitmaps += it.softwareBitmapsPenalty
+                }
+
+            return@aggregate use
+        }
+}
+/** Rounds the passed value to the nearest KB - e.g. 700B rounds to 1KB. */
+private fun toKb(value: Int): Int = (value.toFloat() / 1024f).roundToInt()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryViewWalker.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryViewWalker.kt
index 2d04211..6491223 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryViewWalker.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryViewWalker.kt
@@ -184,19 +184,21 @@
     private fun computeDrawableUse(drawable: Drawable, seenObjects: HashSet<Int>): Int =
         when (drawable) {
             is BitmapDrawable -> {
-                val ref = System.identityHashCode(drawable.bitmap)
-                if (seenObjects.contains(ref)) {
-                    0
-                } else {
-                    seenObjects.add(ref)
-                    drawable.bitmap.allocationByteCount
-                }
+                drawable.bitmap?.let {
+                    val ref = System.identityHashCode(it)
+                    if (seenObjects.contains(ref)) {
+                        0
+                    } else {
+                        seenObjects.add(ref)
+                        it.allocationByteCount
+                    }
+                } ?: 0
             }
             else -> 0
         }
 
     private fun isDrawableSoftwareBitmap(drawable: Drawable) =
-        drawable is BitmapDrawable && drawable.bitmap.config != Bitmap.Config.HARDWARE
+        drawable is BitmapDrawable && drawable.bitmap?.config != Bitmap.Config.HARDWARE
 
     private fun identifierForView(view: View) =
         if (view.id == View.NO_ID) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 7addc8f..68ad49be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -23,6 +23,7 @@
 import android.graphics.Canvas;
 import android.graphics.Point;
 import android.util.AttributeSet;
+import android.util.IndentingPrintWriter;
 import android.util.MathUtils;
 import android.view.Choreographer;
 import android.view.MotionEvent;
@@ -43,7 +44,9 @@
 import com.android.systemui.statusbar.notification.SourceType;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+import com.android.systemui.util.DumpUtilsKt;
 
+import java.io.PrintWriter;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -651,11 +654,6 @@
         mBackgroundNormal.setRadius(topRadius, bottomRadius);
     }
 
-    @Override
-    protected void setBackgroundTop(int backgroundTop) {
-        mBackgroundNormal.setBackgroundTop(backgroundTop);
-    }
-
     protected abstract View getContentView();
 
     public int calculateBgColor() {
@@ -819,6 +817,22 @@
         mOnDetachResetRoundness.add(sourceType);
     }
 
+    @Override
+    public void dump(PrintWriter pwOriginal, String[] args) {
+        IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal);
+        super.dump(pw, args);
+        if (DUMP_VERBOSE) {
+            DumpUtilsKt.withIncreasedIndent(pw, () -> {
+                pw.println("mBackgroundNormal: " + mBackgroundNormal);
+                if (mBackgroundNormal != null) {
+                    DumpUtilsKt.withIncreasedIndent(pw, () -> {
+                        mBackgroundNormal.dump(pw, args);
+                    });
+                }
+            });
+        }
+    }
+
     public interface OnActivatedListener {
         void onActivated(ActivatableNotificationView view);
         void onActivationReset(ActivatableNotificationView view);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 9275e2b..64c1a59 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -1196,6 +1196,22 @@
         return getShowingLayout().getVisibleWrapper();
     }
 
+    /**
+     * @return whether the notification row is long clickable or not.
+     */
+    public boolean isNotificationRowLongClickable() {
+        if (mLongPressListener == null) {
+            return false;
+        }
+
+        if (!areGutsExposed()) { // guts is not opened
+            return true;
+        }
+
+        // if it is leave behind, it shouldn't be long clickable.
+        return !isGutsLeaveBehind();
+    }
+
     public void setLongPressListener(LongPressListener longPressListener) {
         mLongPressListener = longPressListener;
     }
@@ -2947,6 +2963,10 @@
         return (mGuts != null && mGuts.isExposed());
     }
 
+    private boolean isGutsLeaveBehind() {
+        return (mGuts != null && mGuts.isLeavebehind());
+    }
+
     @Override
     public boolean isContentExpandable() {
         if (mIsSummaryWithChildren && !shouldShowPublic()) {
@@ -3394,7 +3414,12 @@
     @Override
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
+        final boolean isLongClickable = isNotificationRowLongClickable();
+        if (isLongClickable) {
+            info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
+        }
+        info.setLongClickable(isLongClickable);
+
         if (canViewBeDismissed() && !mIsSnoozed) {
             info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
index 2041245..197caa2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
@@ -24,12 +24,16 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.util.AttributeSet;
+import android.util.IndentingPrintWriter;
 import android.view.View;
 import android.view.ViewOutlineProvider;
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.RoundableState;
 import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
+import com.android.systemui.util.DumpUtilsKt;
+
+import java.io.PrintWriter;
 
 /**
  * Like {@link ExpandableView}, but setting an outline for the height and clipping.
@@ -43,7 +47,6 @@
     private float mOutlineAlpha = -1f;
     private boolean mAlwaysRoundBothCorners;
     private Path mTmpPath = new Path();
-    private int mBackgroundTop;
 
     /**
      * {@code false} if the children views of the {@link ExpandableOutlineView} are translated when
@@ -59,7 +62,7 @@
                 // Only when translating just the contents, does the outline need to be shifted.
                 int translation = !mDismissUsingRowTranslationX ? (int) getTranslation() : 0;
                 int left = Math.max(translation, 0);
-                int top = mClipTopAmount + mBackgroundTop;
+                int top = mClipTopAmount;
                 int right = getWidth() + Math.min(translation, 0);
                 int bottom = Math.max(getActualHeight() - mClipBottomAmount, top);
                 outline.setRect(left, top, right, bottom);
@@ -92,7 +95,7 @@
                     ? (int) getTranslation() : 0;
             int halfExtraWidth = (int) (mExtraWidthForClipping / 2.0f);
             left = Math.max(translation, 0) - halfExtraWidth;
-            top = mClipTopAmount + mBackgroundTop;
+            top = mClipTopAmount;
             right = getWidth() + halfExtraWidth + Math.min(translation, 0);
             // If the top is rounded we want the bottom to be at most at the top roundness, in order
             // to avoid the shadow changing when scrolling up.
@@ -228,13 +231,6 @@
         super.applyRoundnessAndInvalidate();
     }
 
-    protected void setBackgroundTop(int backgroundTop) {
-        if (mBackgroundTop != backgroundTop) {
-            mBackgroundTop = backgroundTop;
-            invalidateOutline();
-        }
-    }
-
     public void onDensityOrFontScaleChanged() {
         initDimens();
         applyRoundnessAndInvalidate();
@@ -350,4 +346,18 @@
     public Path getCustomClipPath(View child) {
         return null;
     }
+
+    @Override
+    public void dump(PrintWriter pwOriginal, String[] args) {
+        IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal);
+        super.dump(pw, args);
+        DumpUtilsKt.withIncreasedIndent(pw, () -> {
+            pw.println("Roundness: " + getRoundableState().debugString());
+            if (DUMP_VERBOSE) {
+                pw.println("mCustomOutline: " + mCustomOutline + " mOutlineRect: " + mOutlineRect);
+                pw.println("mOutlineAlpha: " + mOutlineAlpha);
+                pw.println("mAlwaysRoundBothCorners: " + mAlwaysRoundBothCorners);
+            }
+        });
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index 955d7c1..25c7264 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -51,6 +51,8 @@
  */
 public abstract class ExpandableView extends FrameLayout implements Dumpable, Roundable {
     private static final String TAG = "ExpandableView";
+    /** whether the dump() for this class should include verbose details */
+    protected static final boolean DUMP_VERBOSE = false;
 
     private RoundableState mRoundableState = null;
     protected OnHeightChangedListener mOnHeightChangedListener;
@@ -825,6 +827,14 @@
                 viewState.dump(pw, args);
                 pw.println();
             }
+            if (DUMP_VERBOSE) {
+                pw.println("mClipTopAmount: " + mClipTopAmount);
+                pw.println("mClipBottomAmount " + mClipBottomAmount);
+                pw.println("mClipToActualHeight: " + mClipToActualHeight);
+                pw.println("mExtraWidthForClipping: " + mExtraWidthForClipping);
+                pw.println("mMinimumHeightForClipping: " + mMinimumHeightForClipping);
+                pw.println("getClipBounds(): " + getClipBounds());
+            }
         });
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
index 21f4cb5..49f17b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
@@ -88,6 +88,7 @@
         mSeenNotifsFooterTextView = findViewById(R.id.unlock_prompt_footer);
         updateResources();
         updateText();
+        updateColors();
     }
 
     public void setFooterLabelTextAndIcon(@StringRes int text, @DrawableRes int icon) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index 5171569..da8d2d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -28,12 +28,16 @@
 import android.view.View;
 
 import com.android.internal.util.ArrayUtils;
+import com.android.systemui.Dumpable;
 import com.android.systemui.R;
 
+import java.io.PrintWriter;
+import java.util.Arrays;
+
 /**
  * A view that can be used for both the dimmed and normal background of an notification.
  */
-public class NotificationBackgroundView extends View {
+public class NotificationBackgroundView extends View implements Dumpable {
 
     private final boolean mDontModifyCorners;
     private Drawable mBackground;
@@ -42,7 +46,6 @@
     private int mTintColor;
     private final float[] mCornerRadii = new float[8];
     private boolean mBottomIsRounded;
-    private int mBackgroundTop;
     private boolean mBottomAmountClips = true;
     private int mActualHeight = -1;
     private int mActualWidth = -1;
@@ -60,8 +63,7 @@
 
     @Override
     protected void onDraw(Canvas canvas) {
-        if (mClipTopAmount + mClipBottomAmount < getActualHeight() - mBackgroundTop
-                || mExpandAnimationRunning) {
+        if (mClipTopAmount + mClipBottomAmount < getActualHeight() || mExpandAnimationRunning) {
             canvas.save();
             if (!mExpandAnimationRunning) {
                 canvas.clipRect(0, mClipTopAmount, getWidth(),
@@ -74,7 +76,7 @@
 
     private void draw(Canvas canvas, Drawable drawable) {
         if (drawable != null) {
-            int top = mBackgroundTop;
+            int top = 0;
             int bottom = getActualHeight();
             if (mBottomIsRounded
                     && mBottomAmountClips
@@ -261,11 +263,6 @@
         }
     }
 
-    public void setBackgroundTop(int backgroundTop) {
-        mBackgroundTop = backgroundTop;
-        invalidate();
-    }
-
     /** Set the current expand animation size. */
     public void setExpandAnimationSize(int width, int height) {
         mExpandAnimationHeight = height;
@@ -291,4 +288,16 @@
     public void setPressedAllowed(boolean allowed) {
         mIsPressedAllowed = allowed;
     }
+
+    @Override
+    public void dump(PrintWriter pw, String[] args) {
+        pw.println("mDontModifyCorners: " + mDontModifyCorners);
+        pw.println("mClipTopAmount: " + mClipTopAmount);
+        pw.println("mClipBottomAmount: " + mClipBottomAmount);
+        pw.println("mCornerRadii: " + Arrays.toString(mCornerRadii));
+        pw.println("mBottomIsRounded: " + mBottomIsRounded);
+        pw.println("mBottomAmountClips: " + mBottomAmountClips);
+        pw.println("mActualWidth: " + mActualWidth);
+        pw.println("mActualHeight: " + mActualHeight);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index c534860..39e4000 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -28,8 +28,11 @@
 import android.content.ContextWrapper;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.res.Resources;
 import android.os.AsyncTask;
+import android.os.Build;
 import android.os.CancellationSignal;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
 import android.util.Log;
@@ -38,6 +41,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.widget.ImageMessageConsumer;
+import com.android.systemui.R;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.media.controls.util.MediaFeatureFlag;
@@ -468,6 +472,7 @@
                             result.packageContext,
                             parentLayout,
                             remoteViewClickHandler);
+                    validateView(v, entry, row.getResources());
                     v.setIsRootNamespace(true);
                     applyCallback.setResultView(v);
                 } else {
@@ -475,6 +480,7 @@
                             result.packageContext,
                             existingView,
                             remoteViewClickHandler);
+                    validateView(existingView, entry, row.getResources());
                     existingWrapper.onReinflated();
                 }
             } catch (Exception e) {
@@ -496,6 +502,13 @@
 
             @Override
             public void onViewApplied(View v) {
+                String invalidReason = isValidView(v, entry, row.getResources());
+                if (invalidReason != null) {
+                    handleInflationError(runningInflations, new InflationException(invalidReason),
+                            row.getEntry(), callback);
+                    runningInflations.remove(inflationId);
+                    return;
+                }
                 if (isNewView) {
                     v.setIsRootNamespace(true);
                     applyCallback.setResultView(v);
@@ -553,6 +566,65 @@
         runningInflations.put(inflationId, cancellationSignal);
     }
 
+    /**
+     * Checks if the given View is a valid notification View.
+     *
+     * @return null == valid, non-null == invalid, String represents reason for rejection.
+     */
+    @VisibleForTesting
+    @Nullable
+    static String isValidView(View view,
+            NotificationEntry entry,
+            Resources resources) {
+        if (!satisfiesMinHeightRequirement(view, entry, resources)) {
+            return "inflated notification does not meet minimum height requirement";
+        }
+        return null;
+    }
+
+    private static boolean satisfiesMinHeightRequirement(View view,
+            NotificationEntry entry,
+            Resources resources) {
+        if (!requiresHeightCheck(entry)) {
+            return true;
+        }
+        Trace.beginSection("NotificationContentInflater#satisfiesMinHeightRequirement");
+        int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
+        int referenceWidth = resources.getDimensionPixelSize(
+                R.dimen.notification_validation_reference_width);
+        int widthSpec = View.MeasureSpec.makeMeasureSpec(referenceWidth, View.MeasureSpec.EXACTLY);
+        view.measure(widthSpec, heightSpec);
+        int minHeight = resources.getDimensionPixelSize(
+                R.dimen.notification_validation_minimum_allowed_height);
+        boolean result = view.getMeasuredHeight() >= minHeight;
+        Trace.endSection();
+        return result;
+    }
+
+    private static boolean requiresHeightCheck(NotificationEntry entry) {
+        // Undecorated custom views are disallowed from S onwards
+        if (entry.targetSdk >= Build.VERSION_CODES.S) {
+            return false;
+        }
+        // No need to check if the app isn't using any custom views
+        Notification notification = entry.getSbn().getNotification();
+        if (notification.contentView == null
+                && notification.bigContentView == null
+                && notification.headsUpContentView == null) {
+            return false;
+        }
+        return true;
+    }
+
+    private static void validateView(View view,
+            NotificationEntry entry,
+            Resources resources) throws InflationException {
+        String invalidReason = isValidView(view, entry, resources);
+        if (invalidReason != null) {
+            throw new InflationException(invalidReason);
+        }
+    }
+
     private static void handleInflationError(
             HashMap<Integer, CancellationSignal> runningInflations, Exception e,
             NotificationEntry notification, @Nullable InflationCallback callback) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index ea12b82..06d4080 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -44,12 +44,10 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.settingslib.notification.ConversationIconFactory;
-import com.android.systemui.Dependency;
-import com.android.systemui.Dumpable;
 import com.android.systemui.R;
+import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dump.DumpManager;
 import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -65,28 +63,29 @@
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
 import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewListener;
 import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager;
-import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.wmshell.BubblesManager;
 
-import java.io.PrintWriter;
 import java.util.Optional;
 
+import javax.inject.Inject;
+
 import dagger.Lazy;
 
 /**
  * Handles various NotificationGuts related tasks, such as binding guts to a row, opening and
  * closing guts, and keeping track of the currently exposed notification guts.
  */
+@SysUISingleton
 public class NotificationGutsManager implements NotifGutsViewManager {
     private static final String TAG = "NotificationGutsManager";
 
     // Must match constant in Settings. Used to highlight preferences when linking to Settings.
     private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
 
-    private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
+    private final MetricsLogger mMetricsLogger;
     private final Context mContext;
     private final AccessibilityManager mAccessibilityManager;
     private final HighPriorityProvider mHighPriorityProvider;
@@ -94,12 +93,9 @@
     private final OnUserInteractionCallback mOnUserInteractionCallback;
 
     // Dependencies:
-    private final NotificationLockscreenUserManager mLockscreenUserManager =
-            Dependency.get(NotificationLockscreenUserManager.class);
-    private final StatusBarStateController mStatusBarStateController =
-            Dependency.get(StatusBarStateController.class);
-    private final DeviceProvisionedController mDeviceProvisionedController =
-            Dependency.get(DeviceProvisionedController.class);
+    private final NotificationLockscreenUserManager mLockscreenUserManager;
+    private final StatusBarStateController mStatusBarStateController;
+    private final DeviceProvisionedController mDeviceProvisionedController;
     private final AssistantFeedbackController mAssistantFeedbackController;
 
     // which notification is currently being longpress-examined by the user
@@ -124,9 +120,7 @@
     private final ShadeController mShadeController;
     private NotifGutsViewListener mGutsListener;
 
-    /**
-     * Injected constructor. See {@link NotificationsModule}.
-     */
+    @Inject
     public NotificationGutsManager(Context context,
             Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
             @Main Handler mainHandler,
@@ -143,7 +137,11 @@
             Optional<BubblesManager> bubblesManagerOptional,
             UiEventLogger uiEventLogger,
             OnUserInteractionCallback onUserInteractionCallback,
-            ShadeController shadeController) {
+            ShadeController shadeController,
+            NotificationLockscreenUserManager notificationLockscreenUserManager,
+            StatusBarStateController statusBarStateController,
+            DeviceProvisionedController deviceProvisionedController,
+            MetricsLogger metricsLogger) {
         mContext = context;
         mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
         mMainHandler = mainHandler;
@@ -161,6 +159,10 @@
         mUiEventLogger = uiEventLogger;
         mOnUserInteractionCallback = onUserInteractionCallback;
         mShadeController = shadeController;
+        mLockscreenUserManager = notificationLockscreenUserManager;
+        mStatusBarStateController = statusBarStateController;
+        mDeviceProvisionedController = deviceProvisionedController;
+        mMetricsLogger = metricsLogger;
     }
 
     public void setUpWithPresenter(NotificationPresenter presenter,
@@ -372,7 +374,8 @@
                 mDeviceProvisionedController.isDeviceProvisioned(),
                 row.getIsNonblockable(),
                 mHighPriorityProvider.isHighPriority(row.getEntry()),
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
     }
 
     /**
@@ -583,7 +586,9 @@
         }
 
         final ExpandableNotificationRow row = (ExpandableNotificationRow) view;
-        view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+        if (row.isNotificationRowLongClickable()) {
+            view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+        }
         if (row.areGutsExposed()) {
             closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
                     true /* removeControls */, -1 /* x */, -1 /* y */,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index ea0060a..8a50f2f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -204,10 +204,11 @@
             boolean isDeviceProvisioned,
             boolean isNonblockable,
             boolean wasShownHighPriority,
-            AssistantFeedbackController assistantFeedbackController)
+            AssistantFeedbackController assistantFeedbackController,
+            MetricsLogger metricsLogger)
             throws RemoteException {
         mINotificationManager = iNotificationManager;
-        mMetricsLogger = Dependency.get(MetricsLogger.class);
+        mMetricsLogger = metricsLogger;
         mOnUserInteractionCallback = onUserInteractionCallback;
         mChannelEditorDialogController = channelEditorDialogController;
         mAssistantFeedbackController = assistantFeedbackController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index aab36da..d2087ba6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
-import static android.os.Trace.TRACE_TAG_ALWAYS;
+import static android.os.Trace.TRACE_TAG_APP;
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_SHADE_CLEAR_ALL;
@@ -147,7 +147,7 @@
     private boolean mShadeNeedsToClose = false;
 
     @VisibleForTesting
-    static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
+    static final float RUBBER_BAND_FACTOR_NORMAL = 0.1f;
     private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f;
     private static final float RUBBER_BAND_FACTOR_ON_PANEL_EXPAND = 0.21f;
     /**
@@ -1121,7 +1121,7 @@
 
     @Override
     public void requestLayout() {
-        Trace.instant(TRACE_TAG_ALWAYS, "NotificationStackScrollLayout#requestLayout");
+        Trace.instant(TRACE_TAG_APP, "NotificationStackScrollLayout#requestLayout");
         super.requestLayout();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 6e63960..a425792 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -870,7 +870,8 @@
         }
 
         for (int i = childCount - 1; i >= 0; i--) {
-            updateChildZValue(i, algorithmState, ambientState, i == topHunIndex);
+            childrenOnTop = updateChildZValue(i, childrenOnTop,
+                    algorithmState, ambientState, i == topHunIndex);
         }
     }
 
@@ -880,34 +881,42 @@
      *
      * @param isTopHun      Whether the child is a top HUN. A top HUN means a HUN that shows on the
      *                      vertically top of screen. Top HUNs should have drop shadows
+     * @param childrenOnTop It is greater than 0 when there's an existing HUN that is elevated
+     * @return childrenOnTop The decimal part represents the fraction of the elevated HUN's height
+     *                      that overlaps with QQS Panel. The integer part represents the count of
+     *                      previous HUNs whose Z positions are greater than 0.
      */
-    protected void updateChildZValue(int i,
-                                     StackScrollAlgorithmState algorithmState,
-                                     AmbientState ambientState,
-                                     boolean isTopHun) {
+    protected float updateChildZValue(int i, float childrenOnTop,
+                                      StackScrollAlgorithmState algorithmState,
+                                      AmbientState ambientState,
+                                      boolean isTopHun) {
         ExpandableView child = algorithmState.visibleChildren.get(i);
         ExpandableViewState childViewState = child.getViewState();
         float baseZ = ambientState.getBaseZHeight();
 
-        // Handles HUN shadow when Shade is opened
-
         if (child.mustStayOnScreen() && !childViewState.headsUpIsVisible
                 && !ambientState.isDozingAndNotPulsing(child)
                 && childViewState.getYTranslation() < ambientState.getTopPadding()
                 + ambientState.getStackTranslation()) {
-            // Handles HUN shadow when Shade is opened, and AmbientState.mScrollY > 0
-            // Calculate the HUN's z-value based on its overlapping fraction with QQS Panel.
-            // When scrolling down shade to make HUN back to in-position in Notification Panel,
-            // the overlapFraction goes to 0, and the pinned HUN's shadows hides gradually.
-            float overlap = ambientState.getTopPadding()
-                    + ambientState.getStackTranslation() - childViewState.getYTranslation();
 
-            if (childViewState.height > 0) { // To avoid 0/0 problems
-                // To prevent over-shadow
-                float overlapFraction = MathUtils.saturate(overlap / childViewState.height);
-                childViewState.setZTranslation(baseZ
-                        + overlapFraction * mPinnedZTranslationExtra);
+            if (childrenOnTop != 0.0f) {
+                // To elevate the later HUN over previous HUN when multiple HUNs exist
+                childrenOnTop++;
+            } else {
+                // Handles HUN shadow when Shade is opened, and AmbientState.mScrollY > 0
+                // Calculate the HUN's z-value based on its overlapping fraction with QQS Panel.
+                // When scrolling down shade to make HUN back to in-position in Notification Panel,
+                // The overlapping fraction goes to 0, and shadows hides gradually.
+                float overlap = ambientState.getTopPadding()
+                        + ambientState.getStackTranslation() - childViewState.getYTranslation();
+                // To prevent over-shadow during HUN entry
+                childrenOnTop += Math.min(
+                        1.0f,
+                        overlap / childViewState.height
+                );
             }
+            childViewState.setZTranslation(baseZ
+                    + childrenOnTop * mPinnedZTranslationExtra);
         } else if (isTopHun) {
             // In case this is a new view that has never been measured before, we don't want to
             // elevate if we are currently expanded more than the notification
@@ -934,15 +943,15 @@
             childViewState.setZTranslation(baseZ);
         }
 
-        // Handles HUN shadow when shade is closed.
-        // While shade is closed, and during HUN's entry: headerVisibleAmount stays 0, shadow stays.
-        // While shade is closed, and HUN is showing: headerVisibleAmount stays 0, shadow stays.
+        // While HUN is showing and Shade is closed: headerVisibleAmount stays 0, shadow stays.
         // During HUN-to-Shade (eg. dragging down HUN to open Shade): headerVisibleAmount goes
         // gradually from 0 to 1, shadow hides gradually.
         // Header visibility is a deprecated concept, we are using headerVisibleAmount only because
         // this value nicely goes from 0 to 1 during the HUN-to-Shade process.
+
         childViewState.setZTranslation(childViewState.getZTranslation()
                 + (1.0f - child.getHeaderVisibleAmount()) * mPinnedZTranslationExtra);
+        return childrenOnTop;
     }
 
     public void setIsExpanded(boolean isExpanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index 149ec54..576df7a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -29,6 +29,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.qs.AutoAddTracker;
 import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.qs.ReduceBrightColorsController;
@@ -47,6 +48,7 @@
 import com.android.systemui.util.settings.SecureSettings;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Objects;
 
 import javax.inject.Named;
@@ -165,9 +167,10 @@
         if (!mAutoTracker.isAdded(BRIGHTNESS) && mIsReduceBrightColorsAvailable) {
             mReduceBrightColorsController.addCallback(mReduceBrightColorsCallback);
         }
-        if (!mAutoTracker.isAdded(DEVICE_CONTROLS)) {
-            mDeviceControlsController.setCallback(mDeviceControlsCallback);
-        }
+        // We always want this callback, because if the feature stops being supported,
+        // we want to remove the tile from AutoAddTracker. That way it will be re-added when the
+        // feature is reenabled (similar to work tile).
+        mDeviceControlsController.setCallback(mDeviceControlsCallback);
         if (!mAutoTracker.isAdded(WALLET)) {
             initWalletController();
         }
@@ -279,7 +282,8 @@
                 public void onManagedProfileChanged() {
                     if (mManagedProfileController.hasActiveProfile()) {
                         if (mAutoTracker.isAdded(WORK)) return;
-                        mHost.addTile(WORK);
+                        final int position = mAutoTracker.getRestoredTilePosition(WORK);
+                        mHost.addTile(WORK, position);
                         mAutoTracker.setTileAdded(WORK);
                     } else {
                         if (!mAutoTracker.isAdded(WORK)) return;
@@ -322,14 +326,30 @@
         @Override
         public void onControlsUpdate(@Nullable Integer position) {
             if (mAutoTracker.isAdded(DEVICE_CONTROLS)) return;
-            if (position != null) {
+            if (position != null && !hasTile(DEVICE_CONTROLS)) {
                 mHost.addTile(DEVICE_CONTROLS, position);
+                mAutoTracker.setTileAdded(DEVICE_CONTROLS);
             }
-            mAutoTracker.setTileAdded(DEVICE_CONTROLS);
             mHandler.post(() -> mDeviceControlsController.removeCallback());
         }
+
+        @Override
+        public void removeControlsAutoTracker() {
+            mAutoTracker.setTileRemoved(DEVICE_CONTROLS);
+        }
     };
 
+    private boolean hasTile(String tileSpec) {
+        if (tileSpec == null) return false;
+        Collection<QSTile> tiles = mHost.getTiles();
+        for (QSTile tile : tiles) {
+            if (tileSpec.equals(tile.getTileSpec())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private void initWalletController() {
         if (mAutoTracker.isAdded(WALLET)) return;
         Integer position = mWalletController.getWalletPosition();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index ccde3c2..b8ab956 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -57,6 +57,7 @@
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shade.CameraLauncher;
 import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.QuickSettingsController;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.DisableFlagsLogger;
@@ -104,6 +105,7 @@
     private final VibrationEffect mCameraLaunchGestureVibrationEffect;
     private final SystemBarAttributesListener mSystemBarAttributesListener;
     private final Lazy<CameraLauncher> mCameraLauncherLazy;
+    private final QuickSettingsController mQsController;
 
     private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
             VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
@@ -111,6 +113,7 @@
     @Inject
     CentralSurfacesCommandQueueCallbacks(
             CentralSurfaces centralSurfaces,
+            QuickSettingsController quickSettingsController,
             Context context,
             @Main Resources resources,
             ShadeController shadeController,
@@ -137,6 +140,7 @@
             Lazy<CameraLauncher> cameraLauncherLazy,
             UserTracker userTracker) {
         mCentralSurfaces = centralSurfaces;
+        mQsController = quickSettingsController;
         mContext = context;
         mShadeController = shadeController;
         mCommandQueue = commandQueue;
@@ -334,9 +338,9 @@
                 mNotificationStackScrollLayoutController.setWillExpand(true);
                 mHeadsUpManager.unpinAll(true /* userUnpinned */);
                 mMetricsLogger.count("panel_open", 1);
-            } else if (!mNotificationPanelViewController.isInSettings()
+            } else if (!mQsController.getExpanded()
                     && !mNotificationPanelViewController.isExpanding()) {
-                mNotificationPanelViewController.flingSettings(0 /* velocity */,
+                mQsController.flingQs(0 /* velocity */,
                         NotificationPanelViewController.FLING_EXPAND);
                 mMetricsLogger.count("panel_open_qs", 1);
             }
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 dd75d35..378b74a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -185,6 +185,7 @@
 import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.shade.NotificationShadeWindowView;
 import com.android.systemui.shade.NotificationShadeWindowViewController;
+import com.android.systemui.shade.QuickSettingsController;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.shade.ShadeExpansionChangeEvent;
 import com.android.systemui.shade.ShadeExpansionStateManager;
@@ -488,6 +489,8 @@
 
     // settings
     private QSPanelController mQSPanelController;
+    @VisibleForTesting
+    QuickSettingsController mQsController;
 
     KeyguardIndicationController mKeyguardIndicationController;
 
@@ -1306,8 +1309,13 @@
         // Set up the quick settings tile panel
         final View container = mNotificationShadeWindowView.findViewById(R.id.qs_frame);
         if (container != null) {
-            FragmentHostManager fragmentHostManager = FragmentHostManager.get(container);
-            ExtensionFragmentListener.attachExtensonToFragment(container, QS.TAG, R.id.qs_frame,
+            FragmentHostManager fragmentHostManager =
+                    mFragmentService.getFragmentHostManager(container);
+            ExtensionFragmentListener.attachExtensonToFragment(
+                    mFragmentService,
+                    container,
+                    QS.TAG,
+                    R.id.qs_frame,
                     mExtensionController
                             .newExtension(QS.class)
                             .withPlugin(QS.class)
@@ -1408,12 +1416,14 @@
         //   to dismiss the lock screen until entering the SIM PIN.
         // - QS is expanded and we're swiping - swiping up now will hide QS, not dismiss the
         //   keyguard.
+        // - Shade is in QQS over keyguard - swiping up should take us back to keyguard
         if (!isKeyguardShowing()
                 || mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()
                 || isOccluded()
                 || !mKeyguardStateController.canDismissLockScreen()
                 || mKeyguardViewMediator.isAnySimPinSecure()
-                || (mNotificationPanelViewController.isQsExpanded() && trackingTouch)) {
+                || (mQsController.getExpanded() && trackingTouch)
+                || mNotificationPanelViewController.getBarState() == StatusBarState.SHADE_LOCKED) {
             return;
         }
 
@@ -1476,7 +1486,9 @@
     }
 
     protected QS createDefaultQSFragment() {
-        return FragmentHostManager.get(mNotificationShadeWindowView).create(QSFragment.class);
+        return mFragmentService
+                .getFragmentHostManager(mNotificationShadeWindowView)
+                .create(QSFragment.class);
     }
 
     private void setUpPresenter() {
@@ -1571,6 +1583,7 @@
         mCentralSurfacesComponent.getLockIconViewController().init();
         mStackScrollerController =
                 mCentralSurfacesComponent.getNotificationStackScrollLayoutController();
+        mQsController = mCentralSurfacesComponent.getQuickSettingsController();
         mStackScroller = mStackScrollerController.getView();
         mNotifListContainer = mCentralSurfacesComponent.getNotificationListContainer();
         mPresenter = mCentralSurfacesComponent.getNotificationPresenter();
@@ -1690,7 +1703,7 @@
                 && !isShadeDisabled()
                 && ((mDisabled2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) == 0)
                 && !mDozing;
-        mNotificationPanelViewController.setQsExpansionEnabledPolicy(expandEnabled);
+        mQsController.setExpansionEnabledPolicy(expandEnabled);
         Log.d(TAG, "updateQsExpansionEnabled - QS Expand enabled: " + expandEnabled);
     }
 
@@ -3225,12 +3238,12 @@
             mStatusBarKeyguardViewManager.onBackPressed();
             return true;
         }
-        if (mNotificationPanelViewController.isQsCustomizing()) {
-            mNotificationPanelViewController.closeQsCustomizer();
+        if (mQsController.isCustomizing()) {
+            mQsController.closeQsCustomizer();
             return true;
         }
-        if (mNotificationPanelViewController.isQsExpanded()) {
-                mNotificationPanelViewController.animateCloseQs(false /* animateAway */);
+        if (mQsController.getExpanded()) {
+            mNotificationPanelViewController.animateCloseQs(false);
             return true;
         }
         if (mNotificationPanelViewController.closeUserSwitcherIfOpen()) {
@@ -3591,7 +3604,7 @@
             mFalsingCollector.onScreenOff();
             mScrimController.onScreenTurnedOff();
             if (mCloseQsBeforeScreenOff) {
-                mNotificationPanelViewController.closeQs();
+                mQsController.closeQs();
                 mCloseQsBeforeScreenOff = false;
             }
             updateIsKeyguard();
@@ -3770,6 +3783,9 @@
             });
         } else if (mDozing && !unlocking) {
             mScrimController.transitionTo(ScrimState.AOD);
+            // This will cancel the keyguardFadingAway animation if it is running. We need to do
+            // this as otherwise it can remain pending and leave keyguard in a weird state.
+            mUnlockScrimCallback.onCancelled();
         } else if (mKeyguardStateController.isShowing() && !isOccluded() && !unlocking) {
             mScrimController.transitionTo(ScrimState.KEYGUARD);
         } else if (mKeyguardStateController.isShowing() && mKeyguardUpdateMonitor.isDreaming()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index c248a50..b88531e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -352,7 +352,7 @@
     }
 
     private boolean willAnimateFromLockScreenToAod() {
-        return getAlwaysOn() && mKeyguardVisible;
+        return shouldControlScreenOff() && mKeyguardVisible;
     }
 
     private boolean getBoolean(String propName, int resId) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
deleted file mode 100644
index d318759..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ /dev/null
@@ -1,708 +0,0 @@
-/*
- * Copyright (C) 2014 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.statusbar.phone;
-
-import static com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN;
-import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE;
-import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.hardware.biometrics.BiometricSourceType;
-import android.os.Handler;
-import android.os.Trace;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowInsets;
-
-import com.android.internal.policy.SystemBarUtils;
-import com.android.keyguard.KeyguardHostViewController;
-import com.android.keyguard.KeyguardSecurityModel;
-import com.android.keyguard.KeyguardSecurityView;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.keyguard.ViewMediatorCallback;
-import com.android.keyguard.dagger.KeyguardBouncerComponent;
-import com.android.systemui.DejankUtils;
-import com.android.systemui.classifier.FalsingCollector;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.keyguard.DismissCallbackRegistry;
-import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.KeyguardResetCallback;
-import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
-import com.android.systemui.shared.system.SysUiStatsLog;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.ListenerSet;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.inject.Inject;
-
-/**
- * A class which manages the primary (pin/pattern/password) bouncer on the lockscreen.
- * @deprecated Use KeyguardBouncerRepository
- */
-@Deprecated
-public class KeyguardBouncer {
-
-    private static final String TAG = "PrimaryKeyguardBouncer";
-    static final long BOUNCER_FACE_DELAY = 1200;
-    public static final float ALPHA_EXPANSION_THRESHOLD = 0.95f;
-    protected final Context mContext;
-    protected final ViewMediatorCallback mCallback;
-    protected final ViewGroup mContainer;
-    private final FalsingCollector mFalsingCollector;
-    private final DismissCallbackRegistry mDismissCallbackRegistry;
-    private final Handler mHandler;
-    private final List<PrimaryBouncerExpansionCallback> mExpansionCallbacks = new ArrayList<>();
-    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    private final KeyguardStateController mKeyguardStateController;
-    private final KeyguardSecurityModel mKeyguardSecurityModel;
-    private final KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory;
-    private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
-            new KeyguardUpdateMonitorCallback() {
-                @Override
-                public void onStrongAuthStateChanged(int userId) {
-                    mBouncerPromptReason = mCallback.getBouncerPromptReason();
-                }
-
-                @Override
-                public void onLockedOutStateChanged(BiometricSourceType type) {
-                    if (type == BiometricSourceType.FINGERPRINT) {
-                        mBouncerPromptReason = mCallback.getBouncerPromptReason();
-                    }
-                }
-
-                @Override
-                public void onNonStrongBiometricAllowedChanged(int userId) {
-                    mBouncerPromptReason = mCallback.getBouncerPromptReason();
-                }
-            };
-    private final Runnable mRemoveViewRunnable = this::removeView;
-    private final KeyguardBypassController mKeyguardBypassController;
-    private KeyguardHostViewController mKeyguardViewController;
-    private final ListenerSet<KeyguardResetCallback> mResetCallbacks = new ListenerSet<>();
-    private final Runnable mResetRunnable = ()-> {
-        if (mKeyguardViewController != null) {
-            mKeyguardViewController.resetSecurityContainer();
-            for (KeyguardResetCallback callback : mResetCallbacks) {
-                callback.onKeyguardReset();
-            }
-        }
-    };
-
-    private int mStatusBarHeight;
-    private float mExpansion = EXPANSION_HIDDEN;
-    private boolean mShowingSoon;
-    private int mBouncerPromptReason;
-    private boolean mIsAnimatingAway;
-    private boolean mIsScrimmed;
-    private boolean mInitialized;
-
-    private KeyguardBouncer(Context context, ViewMediatorCallback callback,
-            ViewGroup container,
-            DismissCallbackRegistry dismissCallbackRegistry, FalsingCollector falsingCollector,
-            PrimaryBouncerExpansionCallback expansionCallback,
-            KeyguardStateController keyguardStateController,
-            KeyguardUpdateMonitor keyguardUpdateMonitor,
-            KeyguardBypassController keyguardBypassController, @Main Handler handler,
-            KeyguardSecurityModel keyguardSecurityModel,
-            KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory) {
-        mContext = context;
-        mCallback = callback;
-        mContainer = container;
-        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
-        mFalsingCollector = falsingCollector;
-        mDismissCallbackRegistry = dismissCallbackRegistry;
-        mHandler = handler;
-        mKeyguardStateController = keyguardStateController;
-        mKeyguardSecurityModel = keyguardSecurityModel;
-        mKeyguardBouncerComponentFactory = keyguardBouncerComponentFactory;
-        mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
-        mKeyguardBypassController = keyguardBypassController;
-        mExpansionCallbacks.add(expansionCallback);
-    }
-
-    /**
-     * Get the KeyguardBouncer expansion
-     * @return 1=HIDDEN, 0=SHOWING, in between 0 and 1 means the bouncer is in transition.
-     */
-    public float getExpansion() {
-        return mExpansion;
-    }
-
-    /**
-     * Enable/disable only the back button
-     */
-    public void setBackButtonEnabled(boolean enabled) {
-        int vis = mContainer.getSystemUiVisibility();
-        if (enabled) {
-            vis &= ~View.STATUS_BAR_DISABLE_BACK;
-        } else {
-            vis |= View.STATUS_BAR_DISABLE_BACK;
-        }
-        mContainer.setSystemUiVisibility(vis);
-    }
-
-    public void show(boolean resetSecuritySelection) {
-        show(resetSecuritySelection, true /* scrimmed */);
-    }
-
-    /**
-     * Shows the bouncer.
-     *
-     * @param resetSecuritySelection Cleans keyguard view
-     * @param isScrimmed true when the bouncer show show scrimmed, false when the user will be
-     *                 dragging it and translation should be deferred.
-     */
-    public void show(boolean resetSecuritySelection, boolean isScrimmed) {
-        final int keyguardUserId = KeyguardUpdateMonitor.getCurrentUser();
-        if (keyguardUserId == UserHandle.USER_SYSTEM && UserManager.isSplitSystemUser()) {
-            // In split system user mode, we never unlock system user.
-            return;
-        }
-
-        try {
-            Trace.beginSection("KeyguardBouncer#show");
-
-            ensureView();
-            mIsScrimmed = isScrimmed;
-
-            // On the keyguard, we want to show the bouncer when the user drags up, but it's
-            // not correct to end the falsing session. We still need to verify if those touches
-            // are valid.
-            // Later, at the end of the animation, when the bouncer is at the top of the screen,
-            // onFullyShown() will be called and FalsingManager will stop recording touches.
-            if (isScrimmed) {
-                setExpansion(EXPANSION_VISIBLE);
-            }
-
-            if (resetSecuritySelection) {
-                // showPrimarySecurityScreen() updates the current security method. This is needed
-                // in case we are already showing and the current security method changed.
-                showPrimarySecurityScreen();
-            }
-
-            if (mContainer.getVisibility() == View.VISIBLE || mShowingSoon) {
-                // Calls to reset must resume the ViewControllers when in fullscreen mode
-                if (needsFullscreenBouncer()) {
-                    mKeyguardViewController.onResume();
-                }
-                return;
-            }
-
-            final int activeUserId = KeyguardUpdateMonitor.getCurrentUser();
-            final boolean isSystemUser =
-                UserManager.isSplitSystemUser() && activeUserId == UserHandle.USER_SYSTEM;
-            final boolean allowDismissKeyguard = !isSystemUser && activeUserId == keyguardUserId;
-
-            // If allowed, try to dismiss the Keyguard. If no security auth (password/pin/pattern)
-            // is set, this will dismiss the whole Keyguard. Otherwise, show the bouncer.
-            if (allowDismissKeyguard && mKeyguardViewController.dismiss(activeUserId)) {
-                return;
-            }
-
-            // This condition may indicate an error on Android, so log it.
-            if (!allowDismissKeyguard) {
-                Log.w(TAG, "User can't dismiss keyguard: " + activeUserId + " != "
-                        + keyguardUserId);
-            }
-
-            mShowingSoon = true;
-
-            // Split up the work over multiple frames.
-            DejankUtils.removeCallbacks(mResetRunnable);
-            if (mKeyguardStateController.isFaceAuthEnabled()
-                    && !mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
-                            KeyguardUpdateMonitor.getCurrentUser())
-                    && !needsFullscreenBouncer()
-                    && mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
-                            BiometricSourceType.FACE)
-                    && !mKeyguardBypassController.getBypassEnabled()) {
-                mHandler.postDelayed(mShowRunnable, BOUNCER_FACE_DELAY);
-            } else {
-                DejankUtils.postAfterTraversal(mShowRunnable);
-            }
-
-            mKeyguardStateController.notifyBouncerShowing(true /* showing */);
-            dispatchStartingToShow();
-        } finally {
-            Trace.endSection();
-        }
-    }
-
-    public boolean isScrimmed() {
-        return mIsScrimmed;
-    }
-
-    /**
-     * This method must be called at the end of the bouncer animation when
-     * the translation is performed manually by the user, otherwise FalsingManager
-     * will never be notified and its internal state will be out of sync.
-     */
-    private void onFullyShown() {
-        mFalsingCollector.onBouncerShown();
-        if (mKeyguardViewController == null) {
-            Log.e(TAG, "onFullyShown when view was null");
-        } else {
-            mKeyguardViewController.onResume();
-            mContainer.announceForAccessibility(
-                    mKeyguardViewController.getAccessibilityTitleForCurrentMode());
-        }
-    }
-
-    /**
-     * @see #onFullyShown()
-     */
-    private void onFullyHidden() {
-
-    }
-
-    private void setVisibility(@View.Visibility int visibility) {
-        mContainer.setVisibility(visibility);
-        if (mKeyguardViewController != null) {
-            mKeyguardViewController.onBouncerVisibilityChanged(visibility);
-        }
-        dispatchVisibilityChanged();
-    }
-
-    private final Runnable mShowRunnable = new Runnable() {
-        @Override
-        public void run() {
-            setVisibility(View.VISIBLE);
-            showPromptReason(mBouncerPromptReason);
-            final CharSequence customMessage = mCallback.consumeCustomMessage();
-            if (customMessage != null) {
-                mKeyguardViewController.showErrorMessage(customMessage);
-            }
-            mKeyguardViewController.appear(mStatusBarHeight);
-            mShowingSoon = false;
-            if (mExpansion == EXPANSION_VISIBLE) {
-                mKeyguardViewController.onResume();
-                mKeyguardViewController.resetSecurityContainer();
-                showPromptReason(mBouncerPromptReason);
-            }
-        }
-    };
-
-    /**
-     * Show a string explaining why the security view needs to be solved.
-     *
-     * @param reason a flag indicating which string should be shown, see
-     *               {@link KeyguardSecurityView#PROMPT_REASON_NONE}
-     *               and {@link KeyguardSecurityView#PROMPT_REASON_RESTART}
-     */
-    public void showPromptReason(int reason) {
-        if (mKeyguardViewController != null) {
-            mKeyguardViewController.showPromptReason(reason);
-        } else {
-            Log.w(TAG, "Trying to show prompt reason on empty bouncer");
-        }
-    }
-
-    public void showMessage(String message, ColorStateList colorState) {
-        if (mKeyguardViewController != null) {
-            mKeyguardViewController.showMessage(message, colorState);
-        } else {
-            Log.w(TAG, "Trying to show message on empty bouncer");
-        }
-    }
-
-    private void cancelShowRunnable() {
-        DejankUtils.removeCallbacks(mShowRunnable);
-        mHandler.removeCallbacks(mShowRunnable);
-        mShowingSoon = false;
-    }
-
-    public void showWithDismissAction(OnDismissAction r, Runnable cancelAction) {
-        ensureView();
-        setDismissAction(r, cancelAction);
-        show(false /* resetSecuritySelection */);
-    }
-
-    /**
-     * Set the actions to run when the keyguard is dismissed or when the dismiss is cancelled. Those
-     * actions will still be run even if this bouncer is not shown, for instance when authenticating
-     * with an alternate authenticator like the UDFPS.
-     */
-    public void setDismissAction(OnDismissAction r, Runnable cancelAction) {
-        mKeyguardViewController.setOnDismissAction(r, cancelAction);
-    }
-
-    public void hide(boolean destroyView) {
-        Trace.beginSection("KeyguardBouncer#hide");
-        if (isShowing()) {
-            SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED,
-                    SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__HIDDEN);
-            mDismissCallbackRegistry.notifyDismissCancelled();
-        }
-        mIsScrimmed = false;
-        mFalsingCollector.onBouncerHidden();
-        mKeyguardStateController.notifyBouncerShowing(false /* showing */);
-        cancelShowRunnable();
-        if (mKeyguardViewController != null) {
-            mKeyguardViewController.cancelDismissAction();
-            mKeyguardViewController.cleanUp();
-        }
-        mIsAnimatingAway = false;
-        setVisibility(View.INVISIBLE);
-        if (destroyView) {
-
-            // We have a ViewFlipper that unregisters a broadcast when being detached, which may
-            // be slow because of AM lock contention during unlocking. We can delay it a bit.
-            mHandler.postDelayed(mRemoveViewRunnable, 50);
-        }
-        Trace.endSection();
-    }
-
-    /**
-     * See {@link StatusBarKeyguardViewManager#startPreHideAnimation}.
-     */
-    public void startPreHideAnimation(Runnable runnable) {
-        mIsAnimatingAway = true;
-        if (mKeyguardViewController != null) {
-            mKeyguardViewController.startDisappearAnimation(runnable);
-        } else if (runnable != null) {
-            runnable.run();
-        }
-    }
-
-    /**
-     * Reset the state of the view.
-     */
-    public void reset() {
-        cancelShowRunnable();
-        inflateView();
-        mFalsingCollector.onBouncerHidden();
-    }
-
-    public void onScreenTurnedOff() {
-        if (mKeyguardViewController != null && mContainer.getVisibility() == View.VISIBLE) {
-            mKeyguardViewController.onPause();
-        }
-    }
-
-    public boolean isShowing() {
-        return (mShowingSoon || mContainer.getVisibility() == View.VISIBLE)
-                && mExpansion == EXPANSION_VISIBLE && !isAnimatingAway();
-    }
-
-    /**
-     * {@link #show(boolean)} was called but we're not showing yet, or being dragged.
-     */
-    public boolean inTransit() {
-        return mShowingSoon || mExpansion != EXPANSION_HIDDEN && mExpansion != EXPANSION_VISIBLE;
-    }
-
-    /**
-     * @return {@code true} when bouncer's pre-hide animation already started but isn't completely
-     *         hidden yet, {@code false} otherwise.
-     */
-    public boolean isAnimatingAway() {
-        return mIsAnimatingAway;
-    }
-
-    public void prepare() {
-        boolean wasInitialized = mInitialized;
-        ensureView();
-        if (wasInitialized) {
-            showPrimarySecurityScreen();
-        }
-        mBouncerPromptReason = mCallback.getBouncerPromptReason();
-    }
-
-    private void showPrimarySecurityScreen() {
-        mKeyguardViewController.showPrimarySecurityScreen();
-    }
-
-    /**
-     * Current notification panel expansion
-     * @param fraction 0 when notification panel is collapsed and 1 when expanded.
-     * @see StatusBarKeyguardViewManager#onPanelExpansionChanged
-     */
-    public void setExpansion(float fraction) {
-        float oldExpansion = mExpansion;
-        boolean expansionChanged = mExpansion != fraction;
-        mExpansion = fraction;
-        if (mKeyguardViewController != null && !mIsAnimatingAway) {
-            mKeyguardViewController.setExpansion(fraction);
-        }
-
-        if (fraction == EXPANSION_VISIBLE && oldExpansion != EXPANSION_VISIBLE) {
-            onFullyShown();
-            dispatchFullyShown();
-        } else if (fraction == EXPANSION_HIDDEN && oldExpansion != EXPANSION_HIDDEN) {
-            DejankUtils.postAfterTraversal(mResetRunnable);
-            /*
-             * There are cases where #hide() was not invoked, such as when
-             * NotificationPanelViewController controls the hide animation. Make sure the state gets
-             * updated by calling #hide() directly.
-             */
-            hide(false /* destroyView */);
-            dispatchFullyHidden();
-        } else if (fraction != EXPANSION_VISIBLE && oldExpansion == EXPANSION_VISIBLE) {
-            dispatchStartingToHide();
-            if (mKeyguardViewController != null) {
-                mKeyguardViewController.onStartingToHide();
-            }
-        }
-
-        if (expansionChanged) {
-            dispatchExpansionChanged();
-        }
-    }
-
-    public boolean willDismissWithAction() {
-        return mKeyguardViewController != null && mKeyguardViewController.hasDismissActions();
-    }
-
-    public int getTop() {
-        if (mKeyguardViewController == null) {
-            return 0;
-        }
-
-        return mKeyguardViewController.getTop();
-    }
-
-    protected void ensureView() {
-        // Removal of the view might be deferred to reduce unlock latency,
-        // in this case we need to force the removal, otherwise we'll
-        // end up in an unpredictable state.
-        boolean forceRemoval = mHandler.hasCallbacks(mRemoveViewRunnable);
-        if (!mInitialized || forceRemoval) {
-            inflateView();
-        }
-    }
-
-    protected void inflateView() {
-        removeView();
-        mHandler.removeCallbacks(mRemoveViewRunnable);
-
-        KeyguardBouncerComponent component = mKeyguardBouncerComponentFactory.create(mContainer);
-        mKeyguardViewController = component.getKeyguardHostViewController();
-        mKeyguardViewController.init();
-
-        mStatusBarHeight = SystemBarUtils.getStatusBarHeight(mContext);
-        setVisibility(View.INVISIBLE);
-
-        final WindowInsets rootInsets = mContainer.getRootWindowInsets();
-        if (rootInsets != null) {
-            mContainer.dispatchApplyWindowInsets(rootInsets);
-        }
-        mInitialized = true;
-    }
-
-    protected void removeView() {
-        mContainer.removeAllViews();
-        mInitialized = false;
-    }
-
-    /**
-     * @return True if and only if the security method should be shown before showing the
-     * notifications on Keyguard, like SIM PIN/PUK.
-     */
-    public boolean needsFullscreenBouncer() {
-        SecurityMode mode = mKeyguardSecurityModel.getSecurityMode(
-                KeyguardUpdateMonitor.getCurrentUser());
-        return mode == SecurityMode.SimPin || mode == SecurityMode.SimPuk;
-    }
-
-    /**
-     * Like {@link #needsFullscreenBouncer}, but uses the currently visible security method, which
-     * makes this method much faster.
-     */
-    public boolean isFullscreenBouncer() {
-        if (mKeyguardViewController != null) {
-            SecurityMode mode = mKeyguardViewController.getCurrentSecurityMode();
-            return mode == SecurityMode.SimPin || mode == SecurityMode.SimPuk;
-        }
-        return false;
-    }
-
-    /**
-     * WARNING: This method might cause Binder calls.
-     */
-    public boolean isSecure() {
-        return mKeyguardSecurityModel.getSecurityMode(
-                KeyguardUpdateMonitor.getCurrentUser()) != SecurityMode.None;
-    }
-
-    public boolean shouldDismissOnMenuPressed() {
-        return mKeyguardViewController.shouldEnableMenuKey();
-    }
-
-    public boolean interceptMediaKey(KeyEvent event) {
-        ensureView();
-        return mKeyguardViewController.interceptMediaKey(event);
-    }
-
-    /**
-     * @return true if the pre IME back event should be handled
-     */
-    public boolean dispatchBackKeyEventPreIme() {
-        ensureView();
-        return mKeyguardViewController.dispatchBackKeyEventPreIme();
-    }
-
-    public void notifyKeyguardAuthenticated(boolean strongAuth) {
-        ensureView();
-        mKeyguardViewController.finish(strongAuth, KeyguardUpdateMonitor.getCurrentUser());
-    }
-
-    private void dispatchFullyShown() {
-        for (PrimaryBouncerExpansionCallback callback : mExpansionCallbacks) {
-            callback.onFullyShown();
-        }
-    }
-
-    private void dispatchStartingToHide() {
-        for (PrimaryBouncerExpansionCallback callback : mExpansionCallbacks) {
-            callback.onStartingToHide();
-        }
-    }
-
-    private void dispatchStartingToShow() {
-        for (PrimaryBouncerExpansionCallback callback : mExpansionCallbacks) {
-            callback.onStartingToShow();
-        }
-    }
-
-    private void dispatchFullyHidden() {
-        for (PrimaryBouncerExpansionCallback callback : mExpansionCallbacks) {
-            callback.onFullyHidden();
-        }
-    }
-
-    private void dispatchExpansionChanged() {
-        for (PrimaryBouncerExpansionCallback callback : mExpansionCallbacks) {
-            callback.onExpansionChanged(mExpansion);
-        }
-    }
-
-    private void dispatchVisibilityChanged() {
-        for (PrimaryBouncerExpansionCallback callback : mExpansionCallbacks) {
-            callback.onVisibilityChanged(mContainer.getVisibility() == View.VISIBLE);
-        }
-    }
-
-    /**
-     * Apply keyguard configuration from the currently active resources. This can be called when the
-     * device configuration changes, to re-apply some resources that are qualified on the device
-     * configuration.
-     */
-    public void updateResources() {
-        if (mKeyguardViewController != null) {
-            mKeyguardViewController.updateResources();
-        }
-    }
-
-    public void dump(PrintWriter pw) {
-        pw.println("KeyguardBouncer");
-        pw.println("  isShowing(): " + isShowing());
-        pw.println("  mStatusBarHeight: " + mStatusBarHeight);
-        pw.println("  mExpansion: " + mExpansion);
-        pw.println("  mKeyguardViewController; " + mKeyguardViewController);
-        pw.println("  mShowingSoon: " + mShowingSoon);
-        pw.println("  mBouncerPromptReason: " + mBouncerPromptReason);
-        pw.println("  mIsAnimatingAway: " + mIsAnimatingAway);
-        pw.println("  mInitialized: " + mInitialized);
-    }
-
-    /** Update keyguard position based on a tapped X coordinate. */
-    public void updateKeyguardPosition(float x) {
-        if (mKeyguardViewController != null) {
-            mKeyguardViewController.updateKeyguardPosition(x);
-        }
-    }
-
-    public void addKeyguardResetCallback(KeyguardResetCallback callback) {
-        mResetCallbacks.addIfAbsent(callback);
-    }
-
-    public void removeKeyguardResetCallback(KeyguardResetCallback callback) {
-        mResetCallbacks.remove(callback);
-    }
-
-    /**
-     * Adds a callback to listen to bouncer expansion updates.
-     */
-    public void addBouncerExpansionCallback(PrimaryBouncerExpansionCallback callback) {
-        if (!mExpansionCallbacks.contains(callback)) {
-            mExpansionCallbacks.add(callback);
-        }
-    }
-
-    /**
-     * Removes a previously added callback. If the callback was never added, this methood
-     * does nothing.
-     */
-    public void removeBouncerExpansionCallback(PrimaryBouncerExpansionCallback callback) {
-        mExpansionCallbacks.remove(callback);
-    }
-
-    /** Create a {@link KeyguardBouncer} once a container and bouncer callback are available. */
-    public static class Factory {
-        private final Context mContext;
-        private final ViewMediatorCallback mCallback;
-        private final DismissCallbackRegistry mDismissCallbackRegistry;
-        private final FalsingCollector mFalsingCollector;
-        private final KeyguardStateController mKeyguardStateController;
-        private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-        private final KeyguardBypassController mKeyguardBypassController;
-        private final Handler mHandler;
-        private final KeyguardSecurityModel mKeyguardSecurityModel;
-        private final KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory;
-
-        @Inject
-        public Factory(Context context, ViewMediatorCallback callback,
-                DismissCallbackRegistry dismissCallbackRegistry, FalsingCollector falsingCollector,
-                KeyguardStateController keyguardStateController,
-                KeyguardUpdateMonitor keyguardUpdateMonitor,
-                KeyguardBypassController keyguardBypassController, @Main Handler handler,
-                KeyguardSecurityModel keyguardSecurityModel,
-                KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory) {
-            mContext = context;
-            mCallback = callback;
-            mDismissCallbackRegistry = dismissCallbackRegistry;
-            mFalsingCollector = falsingCollector;
-            mKeyguardStateController = keyguardStateController;
-            mKeyguardUpdateMonitor = keyguardUpdateMonitor;
-            mKeyguardBypassController = keyguardBypassController;
-            mHandler = handler;
-            mKeyguardSecurityModel = keyguardSecurityModel;
-            mKeyguardBouncerComponentFactory = keyguardBouncerComponentFactory;
-        }
-
-        /**
-         * Construct a KeyguardBouncer that will exist in the given container.
-         */
-        public KeyguardBouncer create(ViewGroup container,
-                PrimaryBouncerExpansionCallback expansionCallback) {
-            return new KeyguardBouncer(mContext, mCallback, container,
-                    mDismissCallbackRegistry, mFalsingCollector, expansionCallback,
-                    mKeyguardStateController, mKeyguardUpdateMonitor,
-                    mKeyguardBypassController, mHandler, mKeyguardSecurityModel,
-                    mKeyguardBouncerComponentFactory);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 01af486..c163a89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -89,14 +89,19 @@
     private float mPanelExpansion;
 
     /**
-     * Burn-in prevention x translation.
+     * Max burn-in prevention x translation.
      */
-    private int mBurnInPreventionOffsetX;
+    private int mMaxBurnInPreventionOffsetX;
 
     /**
-     * Burn-in prevention y translation for clock layouts.
+     * Max burn-in prevention y translation for clock layouts.
      */
-    private int mBurnInPreventionOffsetYClock;
+    private int mMaxBurnInPreventionOffsetYClock;
+
+    /**
+     * Current burn-in prevention y translation.
+     */
+    private float mCurrentBurnInOffsetY;
 
     /**
      * Doze/AOD transition amount.
@@ -155,9 +160,9 @@
 
         mContainerTopPadding =
                 res.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin);
-        mBurnInPreventionOffsetX = res.getDimensionPixelSize(
+        mMaxBurnInPreventionOffsetX = res.getDimensionPixelSize(
                 R.dimen.burn_in_prevention_offset_x);
-        mBurnInPreventionOffsetYClock = res.getDimensionPixelSize(
+        mMaxBurnInPreventionOffsetYClock = res.getDimensionPixelSize(
                 R.dimen.burn_in_prevention_offset_y_clock);
     }
 
@@ -215,7 +220,10 @@
         if (mBypassEnabled) {
             return (int) (mUnlockedStackScrollerPadding + mOverStretchAmount);
         } else if (mIsSplitShade) {
-            return clockYPosition - mSplitShadeTopNotificationsMargin + mUserSwitchHeight;
+            // mCurrentBurnInOffsetY is subtracted to make notifications not follow clock adjustment
+            // for burn-in. It can make pulsing notification go too high and it will get clipped
+            return clockYPosition - mSplitShadeTopNotificationsMargin + mUserSwitchHeight
+                    - (int) mCurrentBurnInOffsetY;
         } else {
             return clockYPosition + mKeyguardStatusHeight;
         }
@@ -255,11 +263,11 @@
 
         // This will keep the clock at the top but out of the cutout area
         float shift = 0;
-        if (clockY - mBurnInPreventionOffsetYClock < mCutoutTopInset) {
-            shift = mCutoutTopInset - (clockY - mBurnInPreventionOffsetYClock);
+        if (clockY - mMaxBurnInPreventionOffsetYClock < mCutoutTopInset) {
+            shift = mCutoutTopInset - (clockY - mMaxBurnInPreventionOffsetYClock);
         }
 
-        int burnInPreventionOffsetY = mBurnInPreventionOffsetYClock; // requested offset
+        int burnInPreventionOffsetY = mMaxBurnInPreventionOffsetYClock; // requested offset
         final boolean hasUdfps = mUdfpsTop > -1;
         if (hasUdfps && !mIsClockTopAligned) {
             // ensure clock doesn't overlap with the udfps icon
@@ -267,8 +275,8 @@
                 // sometimes the clock textView extends beyond udfps, so let's just use the
                 // space above the KeyguardStatusView/clock as our burn-in offset
                 burnInPreventionOffsetY = (int) (clockY - mCutoutTopInset) / 2;
-                if (mBurnInPreventionOffsetYClock < burnInPreventionOffsetY) {
-                    burnInPreventionOffsetY = mBurnInPreventionOffsetYClock;
+                if (mMaxBurnInPreventionOffsetYClock < burnInPreventionOffsetY) {
+                    burnInPreventionOffsetY = mMaxBurnInPreventionOffsetYClock;
                 }
                 shift = -burnInPreventionOffsetY;
             } else {
@@ -276,16 +284,18 @@
                 float lowerSpace = mUdfpsTop - mClockBottom;
                 // center the burn-in offset within the upper + lower space
                 burnInPreventionOffsetY = (int) (lowerSpace + upperSpace) / 2;
-                if (mBurnInPreventionOffsetYClock < burnInPreventionOffsetY) {
-                    burnInPreventionOffsetY = mBurnInPreventionOffsetYClock;
+                if (mMaxBurnInPreventionOffsetYClock < burnInPreventionOffsetY) {
+                    burnInPreventionOffsetY = mMaxBurnInPreventionOffsetYClock;
                 }
                 shift = (lowerSpace - upperSpace) / 2;
             }
         }
 
+        float fullyDarkBurnInOffset = burnInPreventionOffsetY(burnInPreventionOffsetY);
         float clockYDark = clockY
-                + burnInPreventionOffsetY(burnInPreventionOffsetY)
+                + fullyDarkBurnInOffset
                 + shift;
+        mCurrentBurnInOffsetY = MathUtils.lerp(0, fullyDarkBurnInOffset, darkAmount);
         return (int) (MathUtils.lerp(clockY, clockYDark, darkAmount) + mOverStretchAmount);
     }
 
@@ -325,7 +335,7 @@
     }
 
     private float burnInPreventionOffsetX() {
-        return getBurnInOffset(mBurnInPreventionOffsetX, true /* xAxis */);
+        return getBurnInOffset(mMaxBurnInPreventionOffsetX, true /* xAxis */);
     }
 
     public static class Result {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
index d24469e..b1553b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
@@ -165,6 +165,13 @@
         }
     }
 
+    /**
+     * Get the message that should be shown after the previous text animates out.
+     */
+    public CharSequence getMessage() {
+        return mMessage;
+    }
+
     private AnimatorSet getOutAnimator() {
         AnimatorSet animatorSet = new AnimatorSet();
         Animator fadeOut = ObjectAnimator.ofFloat(this, View.ALPHA, 0f);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
index 4550cb2..8ee2c6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
@@ -76,7 +76,7 @@
                 FaceAuthApiRequestReason.PICK_UP_GESTURE_TRIGGERED
             )
             keyguardUpdateMonitor.requestActiveUnlock(
-                ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.WAKE,
+                ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE,
                 "KeyguardLiftController")
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index 4d14542..fe2a913 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
 
@@ -38,6 +37,7 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.statusbar.policy.BatteryController;
 
 import java.io.PrintWriter;
@@ -94,7 +94,8 @@
             DarkIconDispatcher darkIconDispatcher,
             BatteryController batteryController,
             NavigationModeController navModeController,
-            DumpManager dumpManager) {
+            DumpManager dumpManager,
+            DisplayTracker displayTracker) {
         mDarkIconColor = ctx.getColor(R.color.dark_mode_icon_color_single_tone);
         mLightIconColor = ctx.getColor(R.color.light_mode_icon_color_single_tone);
         mStatusBarIconController = (SysuiDarkIconDispatcher) darkIconDispatcher;
@@ -104,7 +105,7 @@
             mNavigationMode = mode;
         });
 
-        if (ctx.getDisplayId() == DEFAULT_DISPLAY) {
+        if (ctx.getDisplayId() == displayTracker.getDefaultDisplayId()) {
             dumpManager.registerDumpable(getClass().getSimpleName(), this);
         }
     }
@@ -317,24 +318,27 @@
         private final BatteryController mBatteryController;
         private final NavigationModeController mNavModeController;
         private final DumpManager mDumpManager;
+        private final DisplayTracker mDisplayTracker;
 
         @Inject
         public Factory(
                 DarkIconDispatcher darkIconDispatcher,
                 BatteryController batteryController,
                 NavigationModeController navModeController,
-                DumpManager dumpManager) {
+                DumpManager dumpManager,
+                DisplayTracker displayTracker) {
 
             mDarkIconDispatcher = darkIconDispatcher;
             mBatteryController = batteryController;
             mNavModeController = navModeController;
             mDumpManager = dumpManager;
+            mDisplayTracker = displayTracker;
         }
 
         /** Create an {@link LightBarController} */
         public LightBarController create(Context context) {
             return new LightBarController(context, mDarkIconDispatcher, mBatteryController,
-                    mNavModeController, mDumpManager);
+                    mNavModeController, mDumpManager, mDisplayTracker);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 24ad55d..11863627 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -501,7 +501,7 @@
         @VisibleForTesting
         protected StatusIconDisplayable addWifiIcon(int index, String slot, WifiIconState state) {
             if (mStatusBarPipelineFlags.useNewWifiIcon()) {
-                throw new IllegalStateException("Attempting to add a mobile icon while the new "
+                throw new IllegalStateException("Attempting to add a wifi icon while the new "
                         + "icons are enabled is not supported");
             }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index 416bc71..0727c5a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -163,7 +163,7 @@
         for (int i = currentSlots.size() - 1; i >= 0; i--) {
             Slot s = currentSlots.get(i);
             slotsToReAdd.put(s, s.getHolderList());
-            removeAllIconsForSlot(s.getName());
+            removeAllIconsForSlot(s.getName(), /* fromNewPipeline */ false);
         }
 
         // Add them all back
@@ -285,7 +285,7 @@
         // Because of the way we cache the icon holders, we need to remove everything any time
         // we get a new set of subscriptions. This might change in the future, but is required
         // to support demo mode for now
-        removeAllIconsForSlot(slotName);
+        removeAllIconsForSlot(slotName, /* fromNewPipeline */ true);
 
         Collections.reverse(subIds);
 
@@ -428,6 +428,14 @@
     /** */
     @Override
     public void removeIcon(String slot, int tag) {
+        // If the new pipeline is on for this icon, don't allow removal, since the new pipeline
+        // will never call this method
+        if (mStatusBarPipelineFlags.isIconControlledByFlags(slot)) {
+            Log.i(TAG, "Ignoring removal of (" + slot + "). "
+                    + "It should be controlled elsewhere");
+            return;
+        }
+
         if (mStatusBarIconList.getIconHolder(slot, tag) == null) {
             return;
         }
@@ -444,6 +452,18 @@
     /** */
     @Override
     public void removeAllIconsForSlot(String slotName) {
+        removeAllIconsForSlot(slotName, /* fromNewPipeline */ false);
+    }
+
+    private void removeAllIconsForSlot(String slotName, boolean fromNewPipeline) {
+        // If the new pipeline is on for this icon, don't allow removal, since the new pipeline
+        // will never call this method
+        if (!fromNewPipeline && mStatusBarPipelineFlags.isIconControlledByFlags(slotName)) {
+            Log.i(TAG, "Ignoring removal of (" + slotName + "). "
+                    + "It should be controlled elsewhere");
+            return;
+        }
+
         Slot slot = mStatusBarIconList.getSlot(slotName);
         if (!slot.hasIconsInSlot()) {
             return;
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 8e22bf4..39281da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -129,7 +129,6 @@
     private final ConfigurationController mConfigurationController;
     private final NavigationModeController mNavigationModeController;
     private final NotificationShadeWindowController mNotificationShadeWindowController;
-    private final KeyguardBouncer.Factory mKeyguardBouncerFactory;
     private final KeyguardMessageAreaController.Factory mKeyguardMessageAreaFactory;
     private final DreamOverlayStateController mDreamOverlayStateController;
     @Nullable
@@ -206,28 +205,28 @@
                 Log.d(TAG, "onBackInvokedCallback() called, invoking onBackPressed()");
             }
             onBackPressed();
-            if (shouldPlayBackAnimation()) {
+            if (shouldPlayBackAnimation() && mPrimaryBouncerView.getDelegate() != null) {
                 mPrimaryBouncerView.getDelegate().getBackCallback().onBackInvoked();
             }
         }
 
         @Override
         public void onBackProgressed(BackEvent event) {
-            if (shouldPlayBackAnimation()) {
+            if (shouldPlayBackAnimation() && mPrimaryBouncerView.getDelegate() != null) {
                 mPrimaryBouncerView.getDelegate().getBackCallback().onBackProgressed(event);
             }
         }
 
         @Override
         public void onBackCancelled() {
-            if (shouldPlayBackAnimation()) {
+            if (shouldPlayBackAnimation() && mPrimaryBouncerView.getDelegate() != null) {
                 mPrimaryBouncerView.getDelegate().getBackCallback().onBackCancelled();
             }
         }
 
         @Override
         public void onBackStarted(BackEvent event) {
-            if (shouldPlayBackAnimation()) {
+            if (shouldPlayBackAnimation() && mPrimaryBouncerView.getDelegate() != null) {
                 mPrimaryBouncerView.getDelegate().getBackCallback().onBackStarted(event);
             }
         }
@@ -256,7 +255,6 @@
 
     private View mNotificationContainer;
 
-    @Nullable protected KeyguardBouncer mPrimaryBouncer;
     protected boolean mRemoteInputActive;
     private boolean mGlobalActionsVisible = false;
     private boolean mLastGlobalActionsVisible = false;
@@ -281,7 +279,6 @@
     private boolean mLastScreenOffAnimationPlaying;
     private float mQsExpansion;
     final Set<KeyguardViewManagerCallback> mCallbacks = new HashSet<>();
-    private boolean mIsModernBouncerEnabled;
     private boolean mIsUnoccludeTransitionFlagEnabled;
     private boolean mIsModernAlternateBouncerEnabled;
     private boolean mIsBackAnimationEnabled;
@@ -329,7 +326,6 @@
             NotificationShadeWindowController notificationShadeWindowController,
             KeyguardStateController keyguardStateController,
             NotificationMediaManager notificationMediaManager,
-            KeyguardBouncer.Factory keyguardBouncerFactory,
             KeyguardMessageAreaController.Factory keyguardMessageAreaFactory,
             Optional<SysUIUnfoldComponent> sysUIUnfoldComponent,
             Lazy<ShadeController> shadeController,
@@ -352,7 +348,6 @@
         mKeyguardUpdateManager = keyguardUpdateMonitor;
         mStatusBarStateController = sysuiStatusBarStateController;
         mDockManager = dockManager;
-        mKeyguardBouncerFactory = keyguardBouncerFactory;
         mKeyguardMessageAreaFactory = keyguardMessageAreaFactory;
         mShadeController = shadeController;
         mLatencyTracker = latencyTracker;
@@ -362,7 +357,6 @@
         mPrimaryBouncerView = primaryBouncerView;
         mFoldAodAnimationController = sysUIUnfoldComponent
                 .map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null);
-        mIsModernBouncerEnabled = featureFlags.isEnabled(Flags.MODERN_BOUNCER);
         mIsUnoccludeTransitionFlagEnabled = featureFlags.isEnabled(Flags.UNOCCLUSION_TRANSITION);
         mIsModernAlternateBouncerEnabled = featureFlags.isEnabled(Flags.MODERN_ALTERNATE_BOUNCER);
         mAlternateBouncerInteractor = alternateBouncerInteractor;
@@ -381,11 +375,7 @@
         mBiometricUnlockController = biometricUnlockController;
 
         ViewGroup container = mCentralSurfaces.getBouncerContainer();
-        if (mIsModernBouncerEnabled) {
-            mPrimaryBouncerCallbackInteractor.addBouncerExpansionCallback(mExpansionCallback);
-        } else {
-            mPrimaryBouncer = mKeyguardBouncerFactory.create(container, mExpansionCallback);
-        }
+        mPrimaryBouncerCallbackInteractor.addBouncerExpansionCallback(mExpansionCallback);
         mNotificationPanelViewController = notificationPanelViewController;
         if (shadeExpansionStateManager != null) {
             shadeExpansionStateManager.addExpansionListener(this);
@@ -552,11 +542,7 @@
          * show if any subsequent events are to be handled.
          */
         if (beginShowingBouncer(event)) {
-            if (mPrimaryBouncer != null) {
-                mPrimaryBouncer.show(false /* resetSecuritySelection */, false /* scrimmed */);
-            } else {
-                mPrimaryBouncerInteractor.show(/* isScrimmed= */false);
-            }
+            mPrimaryBouncerInteractor.show(/* isScrimmed= */false);
         }
 
         if (!primaryBouncerIsOrWillBeShowing()) {
@@ -564,17 +550,9 @@
         }
 
         if (mKeyguardStateController.isShowing()) {
-            if (mPrimaryBouncer != null) {
-                mPrimaryBouncer.setExpansion(fraction);
-            } else {
-                mPrimaryBouncerInteractor.setPanelExpansion(fraction);
-            }
+            mPrimaryBouncerInteractor.setPanelExpansion(fraction);
         } else {
-            if (mPrimaryBouncer != null) {
-                mPrimaryBouncer.setExpansion(EXPANSION_HIDDEN);
-            } else {
-                mPrimaryBouncerInteractor.setPanelExpansion(EXPANSION_HIDDEN);
-            }
+            mPrimaryBouncerInteractor.setPanelExpansion(EXPANSION_HIDDEN);
         }
     }
 
@@ -604,24 +582,17 @@
 
     /**
      * Shows the notification keyguard or the bouncer depending on
-     * {@link KeyguardBouncer#needsFullscreenBouncer()}.
+     * {@link #needsFullscreenBouncer()}.
      */
     protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing) {
         if (needsFullscreenBouncer() && !mDozing) {
             // The keyguard might be showing (already). So we need to hide it.
             mCentralSurfaces.hideKeyguard();
-            if (mPrimaryBouncer != null) {
-                mPrimaryBouncer.show(true /* resetSecuritySelection */);
-            } else {
-                mPrimaryBouncerInteractor.show(true);
-            }
+            mPrimaryBouncerInteractor.show(true);
         } else {
             mCentralSurfaces.showKeyguard();
             if (hideBouncerWhenShowing) {
                 hideBouncer(false /* destroyView */);
-                if (mPrimaryBouncer != null) {
-                    mPrimaryBouncer.prepare();
-                }
             }
         }
         updateStates();
@@ -648,11 +619,7 @@
      */
     @VisibleForTesting
     void hideBouncer(boolean destroyView) {
-        if (mPrimaryBouncer != null) {
-            mPrimaryBouncer.hide(destroyView);
-        } else {
-            mPrimaryBouncerInteractor.hide();
-        }
+        mPrimaryBouncerInteractor.hide();
         if (mKeyguardStateController.isShowing()) {
             // If we were showing the bouncer and then aborting, we need to also clear out any
             // potential actions unless we actually unlocked.
@@ -671,11 +638,7 @@
         hideAlternateBouncer(false);
 
         if (mKeyguardStateController.isShowing()  && !isBouncerShowing()) {
-            if (mPrimaryBouncer != null) {
-                mPrimaryBouncer.show(false /* resetSecuritySelection */, scrimmed);
-            } else {
-                mPrimaryBouncerInteractor.show(scrimmed);
-            }
+            mPrimaryBouncerInteractor.show(scrimmed);
         }
         updateStates();
     }
@@ -708,13 +671,8 @@
                 // instead of the bouncer.
                 if (mAlternateBouncerInteractor.canShowAlternateBouncerForFingerprint()) {
                     if (!afterKeyguardGone) {
-                        if (mPrimaryBouncer != null) {
-                            mPrimaryBouncer.setDismissAction(mAfterKeyguardGoneAction,
-                                    mKeyguardGoneCancelAction);
-                        } else {
-                            mPrimaryBouncerInteractor.setDismissAction(mAfterKeyguardGoneAction,
-                                    mKeyguardGoneCancelAction);
-                        }
+                        mPrimaryBouncerInteractor.setDismissAction(mAfterKeyguardGoneAction,
+                                mKeyguardGoneCancelAction);
                         mAfterKeyguardGoneAction = null;
                         mKeyguardGoneCancelAction = null;
                     }
@@ -726,22 +684,13 @@
                 if (afterKeyguardGone) {
                     // we'll handle the dismiss action after keyguard is gone, so just show the
                     // bouncer
-                    if (mPrimaryBouncer != null) {
-                        mPrimaryBouncer.show(false /* resetSecuritySelection */);
-                    } else {
-                        mPrimaryBouncerInteractor.show(/* isScrimmed= */true);
-                    }
+                    mPrimaryBouncerInteractor.show(/* isScrimmed= */true);
                 } else {
                     // after authentication success, run dismiss action with the option to defer
                     // hiding the keyguard based on the return value of the OnDismissAction
-                    if (mPrimaryBouncer != null) {
-                        mPrimaryBouncer.showWithDismissAction(mAfterKeyguardGoneAction,
-                                mKeyguardGoneCancelAction);
-                    } else {
-                        mPrimaryBouncerInteractor.setDismissAction(
-                                mAfterKeyguardGoneAction, mKeyguardGoneCancelAction);
-                        mPrimaryBouncerInteractor.show(/* isScrimmed= */true);
-                    }
+                    mPrimaryBouncerInteractor.setDismissAction(
+                            mAfterKeyguardGoneAction, mKeyguardGoneCancelAction);
+                    mPrimaryBouncerInteractor.show(/* isScrimmed= */true);
                     // bouncer will handle the dismiss action, so we no longer need to track it here
                     mAfterKeyguardGoneAction = null;
                     mKeyguardGoneCancelAction = null;
@@ -804,7 +753,7 @@
             mKeyguardMessageAreaController.setMessage("");
         }
         mBypassController.setAltBouncerShowing(isShowingAlternateBouncer);
-        mKeyguardUpdateManager.setUdfpsBouncerShowing(isShowingAlternateBouncer);
+        mKeyguardUpdateManager.setAlternateBouncerShowing(isShowingAlternateBouncer);
 
         if (updateScrim) {
             mCentralSurfaces.updateScrimController();
@@ -841,11 +790,7 @@
 
     @Override
     public void onFinishedGoingToSleep() {
-        if (mPrimaryBouncer != null) {
-            mPrimaryBouncer.onScreenTurnedOff();
-        } else {
-            mPrimaryBouncerInteractor.onScreenTurnedOff();
-        }
+        mPrimaryBouncerInteractor.onScreenTurnedOff();
     }
 
     @Override
@@ -939,11 +884,7 @@
     @Override
     public void startPreHideAnimation(Runnable finishRunnable) {
         if (primaryBouncerIsShowing()) {
-            if (mPrimaryBouncer != null) {
-                mPrimaryBouncer.startPreHideAnimation(finishRunnable);
-            } else {
-                mPrimaryBouncerInteractor.startDisappearAnimation(finishRunnable);
-            }
+            mPrimaryBouncerInteractor.startDisappearAnimation(finishRunnable);
             mNotificationPanelViewController.startBouncerPreHideAnimation();
 
             // We update the state (which will show the keyguard) only if an animation will run on
@@ -1051,17 +992,7 @@
     }
 
     public void onThemeChanged() {
-        if (mIsModernBouncerEnabled) {
-            updateResources();
-            return;
-        }
-        boolean wasShowing = primaryBouncerIsShowing();
-        boolean wasScrimmed = primaryBouncerIsScrimmed();
-
-        hideBouncer(true /* destroyView */);
-        mPrimaryBouncer.prepare();
-
-        if (wasShowing) showPrimaryBouncer(wasScrimmed);
+        updateResources();
     }
 
     public void onKeyguardFadedAway() {
@@ -1106,10 +1037,6 @@
      * WARNING: This method might cause Binder calls.
      */
     public boolean isSecure() {
-        if (mPrimaryBouncer != null) {
-            return mPrimaryBouncer.isSecure();
-        }
-
         return mKeyguardSecurityModel.getSecurityMode(
                 KeyguardUpdateMonitor.getCurrentUser()) != KeyguardSecurityModel.SecurityMode.None;
     }
@@ -1164,10 +1091,8 @@
     }
 
     public boolean isFullscreenBouncer() {
-        if (mPrimaryBouncerView.getDelegate() != null) {
-            return mPrimaryBouncerView.getDelegate().isFullScreenBouncer();
-        }
-        return mPrimaryBouncer != null && mPrimaryBouncer.isFullscreenBouncer();
+        return mPrimaryBouncerView.getDelegate() != null
+                && mPrimaryBouncerView.getDelegate().isFullScreenBouncer();
     }
 
     /**
@@ -1223,17 +1148,9 @@
                 != (mLastBouncerDismissible || !mLastShowing || mLastRemoteInputActive)
                 || mFirstUpdate) {
             if (primaryBouncerDismissible || !showing || remoteInputActive) {
-                if (mPrimaryBouncer != null) {
-                    mPrimaryBouncer.setBackButtonEnabled(true);
-                } else {
-                    mPrimaryBouncerInteractor.setBackButtonEnabled(true);
-                }
+                mPrimaryBouncerInteractor.setBackButtonEnabled(true);
             } else {
-                if (mPrimaryBouncer != null) {
-                    mPrimaryBouncer.setBackButtonEnabled(false);
-                } else {
-                    mPrimaryBouncerInteractor.setBackButtonEnabled(false);
-                }
+                mPrimaryBouncerInteractor.setBackButtonEnabled(false);
             }
         }
 
@@ -1327,27 +1244,21 @@
     }
 
     public boolean shouldDismissOnMenuPressed() {
-        if (mPrimaryBouncerView.getDelegate() != null) {
-            return mPrimaryBouncerView.getDelegate().shouldDismissOnMenuPressed();
-        }
-        return mPrimaryBouncer != null && mPrimaryBouncer.shouldDismissOnMenuPressed();
+        return mPrimaryBouncerView.getDelegate() != null
+                && mPrimaryBouncerView.getDelegate().shouldDismissOnMenuPressed();
     }
 
     public boolean interceptMediaKey(KeyEvent event) {
-        if (mPrimaryBouncerView.getDelegate() != null) {
-            return mPrimaryBouncerView.getDelegate().interceptMediaKey(event);
-        }
-        return mPrimaryBouncer != null && mPrimaryBouncer.interceptMediaKey(event);
+        return mPrimaryBouncerView.getDelegate() != null
+                && mPrimaryBouncerView.getDelegate().interceptMediaKey(event);
     }
 
     /**
      * @return true if the pre IME back event should be handled
      */
     public boolean dispatchBackKeyEventPreIme() {
-        if (mPrimaryBouncerView.getDelegate() != null) {
-            return mPrimaryBouncerView.getDelegate().dispatchBackKeyEventPreIme();
-        }
-        return mPrimaryBouncer != null && mPrimaryBouncer.dispatchBackKeyEventPreIme();
+        return mPrimaryBouncerView.getDelegate() != null
+                && mPrimaryBouncerView.getDelegate().dispatchBackKeyEventPreIme();
     }
 
     public void readyForKeyguardDone() {
@@ -1393,11 +1304,7 @@
      * fingerprint.
      */
     public void notifyKeyguardAuthenticated(boolean strongAuth) {
-        if (mPrimaryBouncer != null) {
-            mPrimaryBouncer.notifyKeyguardAuthenticated(strongAuth);
-        } else {
-            mPrimaryBouncerInteractor.notifyKeyguardAuthenticated(strongAuth);
-        }
+        mPrimaryBouncerInteractor.notifyKeyguardAuthenticated(strongAuth);
 
         if (mAlternateBouncerInteractor.isVisibleState()) {
             hideAlternateBouncer(false);
@@ -1412,11 +1319,7 @@
                 mKeyguardMessageAreaController.setMessage(message);
             }
         } else {
-            if (mPrimaryBouncer != null) {
-                mPrimaryBouncer.showMessage(message, colorState);
-            } else {
-                mPrimaryBouncerInteractor.showMessage(message, colorState);
-            }
+            mPrimaryBouncerInteractor.showMessage(message, colorState);
         }
     }
 
@@ -1472,11 +1375,7 @@
      * configuration.
      */
     public void updateResources() {
-        if (mPrimaryBouncer != null) {
-            mPrimaryBouncer.updateResources();
-        } else {
-            mPrimaryBouncerInteractor.updateResources();
-        }
+        mPrimaryBouncerInteractor.updateResources();
     }
 
     public void dump(PrintWriter pw) {
@@ -1494,11 +1393,6 @@
             pw.println("      " + callback);
         }
 
-        if (mPrimaryBouncer != null) {
-            pw.println("PrimaryBouncer:");
-            mPrimaryBouncer.dump(pw);
-        }
-
         if (mOccludingAppBiometricUI != null) {
             pw.println("mOccludingAppBiometricUI:");
             mOccludingAppBiometricUI.dump(pw);
@@ -1548,11 +1442,6 @@
         }
     }
 
-    @Nullable
-    public KeyguardBouncer getPrimaryBouncer() {
-        return mPrimaryBouncer;
-    }
-
     /**
      * For any touches on the NPVC, show the primary bouncer if the alternate bouncer is currently
      * showing.
@@ -1571,11 +1460,7 @@
 
     /** Update keyguard position based on a tapped X coordinate. */
     public void updateKeyguardPosition(float x) {
-        if (mPrimaryBouncer != null) {
-            mPrimaryBouncer.updateKeyguardPosition(x);
-        } else {
-            mPrimaryBouncerInteractor.setKeyguardPosition(x);
-        }
+        mPrimaryBouncerInteractor.setKeyguardPosition(x);
     }
 
     private static class DismissWithActionRequest {
@@ -1615,56 +1500,35 @@
      * Returns if bouncer expansion is between 0 and 1 non-inclusive.
      */
     public boolean isPrimaryBouncerInTransit() {
-        if (mPrimaryBouncer != null) {
-            return mPrimaryBouncer.inTransit();
-        } else {
-            return mPrimaryBouncerInteractor.isInTransit();
-        }
+        return mPrimaryBouncerInteractor.isInTransit();
     }
 
     /**
      * Returns if bouncer is showing
      */
     public boolean primaryBouncerIsShowing() {
-        if (mPrimaryBouncer != null) {
-            return mPrimaryBouncer.isShowing();
-        } else {
-            return mPrimaryBouncerInteractor.isFullyShowing();
-        }
+        return mPrimaryBouncerInteractor.isFullyShowing();
     }
 
     /**
      * Returns if bouncer is scrimmed
      */
     public boolean primaryBouncerIsScrimmed() {
-        if (mPrimaryBouncer != null) {
-            return mPrimaryBouncer.isScrimmed();
-        } else {
-            return mPrimaryBouncerInteractor.isScrimmed();
-        }
+        return mPrimaryBouncerInteractor.isScrimmed();
     }
 
     /**
      * Returns if bouncer is animating away
      */
     public boolean bouncerIsAnimatingAway() {
-        if (mPrimaryBouncer != null) {
-            return mPrimaryBouncer.isAnimatingAway();
-        } else {
-            return mPrimaryBouncerInteractor.isAnimatingAway();
-        }
-
+        return mPrimaryBouncerInteractor.isAnimatingAway();
     }
 
     /**
      * Returns if bouncer will dismiss with action
      */
     public boolean primaryBouncerWillDismissWithAction() {
-        if (mPrimaryBouncer != null) {
-            return mPrimaryBouncer.willDismissWithAction();
-        } else {
-            return mPrimaryBouncerInteractor.willDismissWithAction();
-        }
+        return mPrimaryBouncerInteractor.willDismissWithAction();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index da1c361..4eed487 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -40,6 +40,7 @@
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.shade.NotificationShadeWindowView;
+import com.android.systemui.shade.QuickSettingsController;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
@@ -102,6 +103,7 @@
     private final IStatusBarService mBarService;
     private final DynamicPrivacyController mDynamicPrivacyController;
     private final NotificationListContainer mNotifListContainer;
+    private final QuickSettingsController mQsController;
 
     protected boolean mVrMode;
 
@@ -109,6 +111,7 @@
     StatusBarNotificationPresenter(
             Context context,
             NotificationPanelViewController panel,
+            QuickSettingsController quickSettingsController,
             HeadsUpManagerPhone headsUp,
             NotificationShadeWindowView statusBarWindow,
             ActivityStarter activityStarter,
@@ -136,6 +139,7 @@
         mActivityStarter = activityStarter;
         mKeyguardStateController = keyguardStateController;
         mNotificationPanel = panel;
+        mQsController = quickSettingsController;
         mHeadsUpManager = headsUp;
         mDynamicPrivacyController = dynamicPrivacyController;
         mKeyguardIndicationController = keyguardIndicationController;
@@ -191,7 +195,7 @@
     private void maybeClosePanelForShadeEmptied() {
         if (CLOSE_PANEL_WHEN_EMPTIED
                 && !mNotificationPanel.isTracking()
-                && !mNotificationPanel.isQsExpanded()
+                && !mQsController.getExpanded()
                 && mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED
                 && !isCollapsing()) {
             mStatusBarStateController.setState(StatusBarState.KEYGUARD);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java
index ae48c2d3..50cce45 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java
@@ -17,5 +17,5 @@
 
 public interface StatusBarWindowCallback {
     void onStateChanged(boolean keyguardShowing, boolean keyguardOccluded, boolean bouncerShowing,
-            boolean isDozing, boolean panelExpanded);
+            boolean isDozing, boolean panelExpanded, boolean isDreaming);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index 608bfa6..924ae4a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -45,8 +45,11 @@
 import com.android.systemui.R;
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.util.DialogKt;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -68,6 +71,7 @@
     private static final boolean DEFAULT_DISMISS_ON_DEVICE_LOCK = true;
 
     private final Context mContext;
+    private final FeatureFlags mFeatureFlags;
     @Nullable private final DismissReceiver mDismissReceiver;
     private final Handler mHandler = new Handler();
     private final SystemUIDialogManager mDialogManager;
@@ -96,16 +100,23 @@
         // TODO(b/219008720): Remove those calls to Dependency.get by introducing a
         // SystemUIDialogFactory and make all other dialogs create a SystemUIDialog to which we set
         // the content and attach listeners.
-        this(context, theme, dismissOnDeviceLock, Dependency.get(SystemUIDialogManager.class),
-                Dependency.get(SysUiState.class), Dependency.get(BroadcastDispatcher.class),
+        this(context, theme, dismissOnDeviceLock,
+                Dependency.get(FeatureFlags.class),
+                Dependency.get(SystemUIDialogManager.class),
+                Dependency.get(SysUiState.class),
+                Dependency.get(BroadcastDispatcher.class),
                 Dependency.get(DialogLaunchAnimator.class));
     }
 
     public SystemUIDialog(Context context, int theme, boolean dismissOnDeviceLock,
-            SystemUIDialogManager dialogManager, SysUiState sysUiState,
-            BroadcastDispatcher broadcastDispatcher, DialogLaunchAnimator dialogLaunchAnimator) {
+            FeatureFlags featureFlags,
+            SystemUIDialogManager dialogManager,
+            SysUiState sysUiState,
+            BroadcastDispatcher broadcastDispatcher,
+            DialogLaunchAnimator dialogLaunchAnimator) {
         super(context, theme);
         mContext = context;
+        mFeatureFlags = featureFlags;
 
         applyFlags(this);
         WindowManager.LayoutParams attrs = getWindow().getAttributes();
@@ -130,6 +141,12 @@
         for (int i = 0; i < mOnCreateRunnables.size(); i++) {
             mOnCreateRunnables.get(i).run();
         }
+        if (mFeatureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_QS_DIALOG_ANIM)) {
+            DialogKt.registerAnimationOnBackInvoked(
+                    /* dialog = */ this,
+                    /* targetView = */ getWindow().getDecorView()
+            );
+        }
     }
 
     private void updateWindowSize() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
index 64b04e9..aec196f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
@@ -26,6 +26,7 @@
 import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.shade.NotificationShadeWindowView;
 import com.android.systemui.shade.NotificationShadeWindowViewController;
+import com.android.systemui.shade.QuickSettingsController;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationShelfController;
 import com.android.systemui.statusbar.core.StatusBarInitializer;
@@ -113,6 +114,9 @@
      */
     NotificationPanelViewController getNotificationPanelViewController();
 
+    /** Creates a QuickSettingsController. */
+    QuickSettingsController getQuickSettingsController();
+
     /**
      * Creates a LockIconViewController. Must be init after creation.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 344d233..c1c6c88 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -23,6 +23,7 @@
 import android.view.View;
 import android.view.ViewStub;
 
+import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.LockIconView;
 import com.android.systemui.R;
 import com.android.systemui.battery.BatteryMeterView;
@@ -67,6 +68,7 @@
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.window.StatusBarWindowStateController;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.util.CarrierConfigTracker;
 import com.android.systemui.util.settings.SecureSettings;
@@ -299,7 +301,9 @@
             OperatorNameViewController.Factory operatorNameViewControllerFactory,
             SecureSettings secureSettings,
             @Main Executor mainExecutor,
-            DumpManager dumpManager
+            DumpManager dumpManager,
+            StatusBarWindowStateController statusBarWindowStateController,
+            KeyguardUpdateMonitor keyguardUpdateMonitor
     ) {
         return new CollapsedStatusBarFragment(statusBarFragmentComponentFactory,
                 ongoingCallController,
@@ -320,7 +324,9 @@
                 operatorNameViewControllerFactory,
                 secureSettings,
                 mainExecutor,
-                dumpManager);
+                dumpManager,
+                statusBarWindowStateController,
+                keyguardUpdateMonitor);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 9354c5e..00fd4ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -44,6 +44,7 @@
 
 import androidx.annotation.VisibleForTesting;
 
+import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.Dumpable;
 import com.android.systemui.R;
 import com.android.systemui.animation.Interpolators;
@@ -72,6 +73,8 @@
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.window.StatusBarWindowStateController;
+import com.android.systemui.statusbar.window.StatusBarWindowStateListener;
 import com.android.systemui.util.CarrierConfigTracker;
 import com.android.systemui.util.CarrierConfigTracker.CarrierConfigChangedListener;
 import com.android.systemui.util.CarrierConfigTracker.DefaultDataSubscriptionChangedListener;
@@ -129,6 +132,8 @@
     private final SecureSettings mSecureSettings;
     private final Executor mMainExecutor;
     private final DumpManager mDumpManager;
+    private final StatusBarWindowStateController mStatusBarWindowStateController;
+    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
 
     private List<String> mBlockedIcons = new ArrayList<>();
     private Map<Startable, Startable.State> mStartableStates = new ArrayMap<>();
@@ -164,6 +169,22 @@
                 }
             };
 
+    /**
+     * Whether we've launched the secure camera over the lockscreen, but haven't yet received a
+     * status bar window state change afterward.
+     *
+     * We wait for this state change (which will tell us whether to show/hide the status bar icons)
+     * so that there is no flickering/jump cutting during the camera launch.
+     */
+    private boolean mWaitingForWindowStateChangeAfterCameraLaunch = false;
+
+    /**
+     * Listener that updates {@link #mWaitingForWindowStateChangeAfterCameraLaunch} when it receives
+     * a new status bar window state.
+     */
+    private final StatusBarWindowStateListener mStatusBarWindowStateListener = state ->
+            mWaitingForWindowStateChangeAfterCameraLaunch = false;
+
     @SuppressLint("ValidFragment")
     public CollapsedStatusBarFragment(
             StatusBarFragmentComponent.Factory statusBarFragmentComponentFactory,
@@ -185,7 +206,9 @@
             OperatorNameViewController.Factory operatorNameViewControllerFactory,
             SecureSettings secureSettings,
             @Main Executor mainExecutor,
-            DumpManager dumpManager
+            DumpManager dumpManager,
+            StatusBarWindowStateController statusBarWindowStateController,
+            KeyguardUpdateMonitor keyguardUpdateMonitor
     ) {
         mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory;
         mOngoingCallController = ongoingCallController;
@@ -207,6 +230,20 @@
         mSecureSettings = secureSettings;
         mMainExecutor = mainExecutor;
         mDumpManager = dumpManager;
+        mStatusBarWindowStateController = statusBarWindowStateController;
+        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mStatusBarWindowStateController.addListener(mStatusBarWindowStateListener);
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        mStatusBarWindowStateController.removeListener(mStatusBarWindowStateListener);
     }
 
     @Override
@@ -254,6 +291,11 @@
         mCarrierConfigTracker.addDefaultDataSubscriptionChangedListener(mDefaultDataListener);
     }
 
+    @Override
+    public void onCameraLaunchGestureDetected(int source) {
+        mWaitingForWindowStateChangeAfterCameraLaunch = true;
+    }
+
     @VisibleForTesting
     void updateBlockedIcons() {
         mBlockedIcons.clear();
@@ -466,6 +508,27 @@
                 && mNotificationPanelViewController.hideStatusBarIconsWhenExpanded()) {
             return true;
         }
+
+        // When launching the camera over the lockscreen, the icons become visible momentarily
+        // before animating out, since we're not yet aware that the launching camera activity is
+        // fullscreen. Even once the activity finishes launching, it takes a short time before WM
+        // decides that the top app wants to hide the icons and tells us to hide them. To ensure
+        // that this high-visibility animation is smooth, keep the icons hidden during a camera
+        // launch until we receive a window state change which indicates that the activity is done
+        // launching and WM has decided to show/hide the icons. For extra safety (to ensure the
+        // icons don't remain hidden somehow) we double check that the camera is still showing, the
+        // status bar window isn't hidden, and we're still occluded as well, though these checks
+        // are typically unnecessary.
+        final boolean hideIconsForSecureCamera =
+                (mWaitingForWindowStateChangeAfterCameraLaunch ||
+                        !mStatusBarWindowStateController.windowIsShowing()) &&
+                        mKeyguardUpdateMonitor.isSecureCameraLaunchedOverKeyguard() &&
+                        mKeyguardStateController.isOccluded();
+
+        if (hideIconsForSecureCamera) {
+            return true;
+        }
+
         return mStatusBarHideIconsForBouncerManager.getShouldHideStatusBarIconsForBouncer();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt
index 2c8677d..270c592 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt
@@ -19,14 +19,14 @@
 import android.content.Context
 import android.util.AttributeSet
 import android.widget.ImageView
-import android.widget.LinearLayout
 import android.widget.TextView
 import com.android.systemui.R
+import com.android.systemui.animation.view.LaunchableLinearLayout
 
 class StatusBarUserSwitcherContainer(
     context: Context?,
     attrs: AttributeSet?
-) : LinearLayout(context, attrs) {
+) : LaunchableLinearLayout(context, attrs) {
     lateinit var text: TextView
         private set
     lateinit var avatar: ImageView
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt
index 15fed32..4a684d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.pipeline
 
+import android.content.Context
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
@@ -23,7 +24,15 @@
 
 /** All flagging methods related to the new status bar pipeline (see b/238425913). */
 @SysUISingleton
-class StatusBarPipelineFlags @Inject constructor(private val featureFlags: FeatureFlags) {
+class StatusBarPipelineFlags
+@Inject
+constructor(
+    context: Context,
+    private val featureFlags: FeatureFlags,
+) {
+    private val mobileSlot = context.getString(com.android.internal.R.string.status_bar_mobile)
+    private val wifiSlot = context.getString(com.android.internal.R.string.status_bar_wifi)
+
     /** True if we should display the mobile icons using the new status bar data pipeline. */
     fun useNewMobileIcons(): Boolean = featureFlags.isEnabled(Flags.NEW_STATUS_BAR_MOBILE_ICONS)
 
@@ -54,4 +63,13 @@
      */
     fun useDebugColoring(): Boolean =
         featureFlags.isEnabled(Flags.NEW_STATUS_BAR_ICONS_DEBUG_COLORING)
+
+    /**
+     * For convenience in the StatusBarIconController, we want to gate some actions based on slot
+     * name and the flag together.
+     *
+     * @return true if this icon is controlled by any of the status bar pipeline flags
+     */
+    fun isIconControlledByFlags(slotName: String): Boolean =
+        slotName == wifiSlot && useNewWifiIcon() || slotName == mobileSlot && useNewMobileIcons()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModel.kt
index 4a5342e..5d5d562 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModel.kt
@@ -18,9 +18,10 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logOutputChange
+import com.android.systemui.statusbar.pipeline.dagger.AirplaneTableLog
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.SharingStarted
@@ -46,7 +47,7 @@
 @Inject
 constructor(
     interactor: AirplaneModeInteractor,
-    logger: ConnectivityPipelineLogger,
+    @AirplaneTableLog logger: TableLogBuffer,
     @Application private val scope: CoroutineScope,
 ) : AirplaneModeViewModel {
     override val isAirplaneModeIconVisible: StateFlow<Boolean> =
@@ -56,6 +57,11 @@
                 isAirplaneMode && !isAirplaneIconForceHidden
             }
             .distinctUntilChanged()
-            .logOutputChange(logger, "isAirplaneModeIconVisible")
+            .logDiffsForTable(
+                logger,
+                columnPrefix = "",
+                columnName = "isAirplaneModeIconVisible",
+                initialValue = false,
+            )
             .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AnimationParams.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/MobileInputLog.kt
similarity index 60%
rename from packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AnimationParams.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/MobileInputLog.kt
index 67733e9..d1aa79e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AnimationParams.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/MobileInputLog.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -11,15 +11,15 @@
  * 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
+ * limitations under the License.
  */
-package com.android.systemui.keyguard.shared.model
 
-import kotlin.time.Duration
-import kotlin.time.Duration.Companion.milliseconds
+package com.android.systemui.statusbar.pipeline.dagger
 
-/** Animation parameters */
-data class AnimationParams(
-    val startTime: Duration = 0.milliseconds,
-    val duration: Duration,
-)
+import javax.inject.Qualifier
+
+/** Logs for inputs into the mobile pipeline. */
+@Qualifier
+@MustBeDocumented
+@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
+annotation class MobileInputLog
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/MobileSummaryLog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/MobileSummaryLog.kt
new file mode 100644
index 0000000..2ac9ab3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/MobileSummaryLog.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.statusbar.pipeline.dagger
+
+import javax.inject.Qualifier
+
+/**
+ * Logs for mobile data that's **the same across all connections**.
+ *
+ * This buffer should only be used for the mobile parent classes like [MobileConnectionsRepository]
+ * and [MobileIconsInteractor]. It should *not* be used for classes that represent an individual
+ * connection, like [MobileConnectionRepository] or [MobileIconInteractor].
+ */
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class MobileSummaryLog
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/SharedConnectivityInputLog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/SharedConnectivityInputLog.kt
new file mode 100644
index 0000000..5face22
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/SharedConnectivityInputLog.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.statusbar.pipeline.dagger
+
+import javax.inject.Qualifier
+
+/** Logs for connectivity-related inputs that are shared across wifi, mobile, etc. */
+@Qualifier
+@MustBeDocumented
+@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
+annotation class SharedConnectivityInputLog
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index 0993ab370..4464751 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -19,12 +19,15 @@
 import android.net.wifi.WifiManager
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.LogBufferFactory
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.log.table.TableLogBufferFactory
+import com.android.systemui.plugins.log.LogBuffer
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepositoryImpl
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigCoreStartable
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileRepositorySwitcher
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository
@@ -82,6 +85,11 @@
     @ClassKey(MobileUiAdapter::class)
     abstract fun bindFeature(impl: MobileUiAdapter): CoreStartable
 
+    @Binds
+    @IntoMap
+    @ClassKey(CarrierConfigCoreStartable::class)
+    abstract fun bindCarrierConfigStartable(impl: CarrierConfigCoreStartable): CoreStartable
+
     companion object {
         @Provides
         @SysUISingleton
@@ -101,6 +109,13 @@
 
         @Provides
         @SysUISingleton
+        @WifiInputLog
+        fun provideWifiInputLogBuffer(factory: LogBufferFactory): LogBuffer {
+            return factory.create("WifiInputLog", 50)
+        }
+
+        @Provides
+        @SysUISingleton
         @WifiTableLog
         fun provideWifiTableLogBuffer(factory: TableLogBufferFactory): TableLogBuffer {
             return factory.create("WifiTableLog", 100)
@@ -112,5 +127,26 @@
         fun provideAirplaneTableLogBuffer(factory: TableLogBufferFactory): TableLogBuffer {
             return factory.create("AirplaneTableLog", 30)
         }
+
+        @Provides
+        @SysUISingleton
+        @SharedConnectivityInputLog
+        fun provideSharedConnectivityTableLogBuffer(factory: LogBufferFactory): LogBuffer {
+            return factory.create("SharedConnectivityInputLog", 30)
+        }
+
+        @Provides
+        @SysUISingleton
+        @MobileSummaryLog
+        fun provideMobileSummaryLogBuffer(factory: TableLogBufferFactory): TableLogBuffer {
+            return factory.create("MobileSummaryLog", 100)
+        }
+
+        @Provides
+        @SysUISingleton
+        @MobileInputLog
+        fun provideMobileInputLogBuffer(factory: LogBufferFactory): LogBuffer {
+            return factory.create("MobileInputLog", 100)
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AnimationParams.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/WifiInputLog.kt
similarity index 60%
copy from packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AnimationParams.kt
copy to packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/WifiInputLog.kt
index 67733e9..6db6944 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AnimationParams.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/WifiInputLog.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -11,15 +11,15 @@
  * 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
+ * limitations under the License.
  */
-package com.android.systemui.keyguard.shared.model
 
-import kotlin.time.Duration
-import kotlin.time.Duration.Companion.milliseconds
+package com.android.systemui.statusbar.pipeline.dagger
 
-/** Animation parameters */
-data class AnimationParams(
-    val startTime: Duration = 0.milliseconds,
-    val duration: Duration,
-)
+import javax.inject.Qualifier
+
+/** Wifi logs for inputs into the wifi pipeline. */
+@Qualifier
+@MustBeDocumented
+@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
+annotation class WifiInputLog
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/DataConnectionState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/DataConnectionState.kt
index 5479b92..85729c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/DataConnectionState.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/DataConnectionState.kt
@@ -20,16 +20,21 @@
 import android.telephony.TelephonyManager.DATA_CONNECTING
 import android.telephony.TelephonyManager.DATA_DISCONNECTED
 import android.telephony.TelephonyManager.DATA_DISCONNECTING
+import android.telephony.TelephonyManager.DATA_HANDOVER_IN_PROGRESS
+import android.telephony.TelephonyManager.DATA_SUSPENDED
 import android.telephony.TelephonyManager.DATA_UNKNOWN
 import android.telephony.TelephonyManager.DataState
 
 /** Internal enum representation of the telephony data connection states */
-enum class DataConnectionState(@DataState val dataState: Int) {
-    Connected(DATA_CONNECTED),
-    Connecting(DATA_CONNECTING),
-    Disconnected(DATA_DISCONNECTED),
-    Disconnecting(DATA_DISCONNECTING),
-    Unknown(DATA_UNKNOWN),
+enum class DataConnectionState {
+    Connected,
+    Connecting,
+    Disconnected,
+    Disconnecting,
+    Suspended,
+    HandoverInProgress,
+    Unknown,
+    Invalid,
 }
 
 fun @receiver:DataState Int.toDataConnectionType(): DataConnectionState =
@@ -38,6 +43,8 @@
         DATA_CONNECTING -> DataConnectionState.Connecting
         DATA_DISCONNECTED -> DataConnectionState.Disconnected
         DATA_DISCONNECTING -> DataConnectionState.Disconnecting
+        DATA_SUSPENDED -> DataConnectionState.Suspended
+        DATA_HANDOVER_IN_PROGRESS -> DataConnectionState.HandoverInProgress
         DATA_UNKNOWN -> DataConnectionState.Unknown
-        else -> throw IllegalArgumentException("unknown data state received $this")
+        else -> DataConnectionState.Invalid
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModel.kt
index 012b9ec..ed7f60b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModel.kt
@@ -26,6 +26,7 @@
 import android.telephony.TelephonyCallback.SignalStrengthsListener
 import android.telephony.TelephonyDisplayInfo
 import android.telephony.TelephonyManager
+import androidx.annotation.VisibleForTesting
 import com.android.systemui.log.table.Diffable
 import com.android.systemui.log.table.TableRowLogger
 import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState.Disconnected
@@ -94,7 +95,7 @@
 ) : Diffable<MobileConnectionModel> {
     override fun logDiffs(prevVal: MobileConnectionModel, row: TableRowLogger) {
         if (prevVal.dataConnectionState != dataConnectionState) {
-            row.logChange(COL_CONNECTION_STATE, dataConnectionState.toString())
+            row.logChange(COL_CONNECTION_STATE, dataConnectionState.name)
         }
 
         if (prevVal.isEmergencyOnly != isEmergencyOnly) {
@@ -125,8 +126,12 @@
             row.logChange(COL_PRIMARY_LEVEL, primaryLevel)
         }
 
-        if (prevVal.dataActivityDirection != dataActivityDirection) {
-            row.logChange(COL_ACTIVITY_DIRECTION, dataActivityDirection.toString())
+        if (prevVal.dataActivityDirection.hasActivityIn != dataActivityDirection.hasActivityIn) {
+            row.logChange(COL_ACTIVITY_DIRECTION_IN, dataActivityDirection.hasActivityIn)
+        }
+
+        if (prevVal.dataActivityDirection.hasActivityOut != dataActivityDirection.hasActivityOut) {
+            row.logChange(COL_ACTIVITY_DIRECTION_OUT, dataActivityDirection.hasActivityOut)
         }
 
         if (prevVal.carrierNetworkChangeActive != carrierNetworkChangeActive) {
@@ -139,7 +144,7 @@
     }
 
     override fun logFull(row: TableRowLogger) {
-        row.logChange(COL_CONNECTION_STATE, dataConnectionState.toString())
+        row.logChange(COL_CONNECTION_STATE, dataConnectionState.name)
         row.logChange(COL_EMERGENCY, isEmergencyOnly)
         row.logChange(COL_ROAMING, isRoaming)
         row.logChange(COL_OPERATOR, operatorAlphaShort)
@@ -147,11 +152,13 @@
         row.logChange(COL_IS_GSM, isGsm)
         row.logChange(COL_CDMA_LEVEL, cdmaLevel)
         row.logChange(COL_PRIMARY_LEVEL, primaryLevel)
-        row.logChange(COL_ACTIVITY_DIRECTION, dataActivityDirection.toString())
+        row.logChange(COL_ACTIVITY_DIRECTION_IN, dataActivityDirection.hasActivityIn)
+        row.logChange(COL_ACTIVITY_DIRECTION_OUT, dataActivityDirection.hasActivityOut)
         row.logChange(COL_CARRIER_NETWORK_CHANGE, carrierNetworkChangeActive)
         row.logChange(COL_RESOLVED_NETWORK_TYPE, resolvedNetworkType.toString())
     }
 
+    @VisibleForTesting
     companion object {
         const val COL_EMERGENCY = "EmergencyOnly"
         const val COL_ROAMING = "Roaming"
@@ -161,7 +168,8 @@
         const val COL_CDMA_LEVEL = "CdmaLevel"
         const val COL_PRIMARY_LEVEL = "PrimaryLevel"
         const val COL_CONNECTION_STATE = "ConnectionState"
-        const val COL_ACTIVITY_DIRECTION = "DataActivity"
+        const val COL_ACTIVITY_DIRECTION_IN = "DataActivity.In"
+        const val COL_ACTIVITY_DIRECTION_OUT = "DataActivity.Out"
         const val COL_CARRIER_NETWORK_CHANGE = "CarrierNetworkChangeActive"
         const val COL_RESOLVED_NETWORK_TYPE = "NetworkType"
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectivityModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectivityModel.kt
index e618905..97a537a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectivityModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectivityModel.kt
@@ -17,6 +17,8 @@
 package com.android.systemui.statusbar.pipeline.mobile.data.model
 
 import android.net.NetworkCapabilities
+import com.android.systemui.log.table.Diffable
+import com.android.systemui.log.table.TableRowLogger
 
 /** Provides information about a mobile network connection */
 data class MobileConnectivityModel(
@@ -24,4 +26,24 @@
     val isConnected: Boolean = false,
     /** Whether the mobile transport is validated [NetworkCapabilities.NET_CAPABILITY_VALIDATED] */
     val isValidated: Boolean = false,
-)
+) : Diffable<MobileConnectivityModel> {
+    // TODO(b/267767715): Can we implement [logDiffs] and [logFull] generically for data classes?
+    override fun logDiffs(prevVal: MobileConnectivityModel, row: TableRowLogger) {
+        if (prevVal.isConnected != isConnected) {
+            row.logChange(COL_IS_CONNECTED, isConnected)
+        }
+        if (prevVal.isValidated != isValidated) {
+            row.logChange(COL_IS_VALIDATED, isValidated)
+        }
+    }
+
+    override fun logFull(row: TableRowLogger) {
+        row.logChange(COL_IS_CONNECTED, isConnected)
+        row.logChange(COL_IS_VALIDATED, isValidated)
+    }
+
+    companion object {
+        private const val COL_IS_CONNECTED = "isConnected"
+        private const val COL_IS_VALIDATED = "isValidated"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt
index c50d82a..78231e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt
@@ -48,15 +48,31 @@
      * This name has been derived from telephony intents. see
      * [android.telephony.TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED]
      */
-    data class Derived(override val name: String) : NetworkNameModel {
+    data class IntentDerived(override val name: String) : NetworkNameModel {
         override fun logDiffs(prevVal: NetworkNameModel, row: TableRowLogger) {
-            if (prevVal !is Derived || prevVal.name != name) {
-                row.logChange(COL_NETWORK_NAME, "Derived($name)")
+            if (prevVal !is IntentDerived || prevVal.name != name) {
+                row.logChange(COL_NETWORK_NAME, "IntentDerived($name)")
             }
         }
 
         override fun logFull(row: TableRowLogger) {
-            row.logChange(COL_NETWORK_NAME, "Derived($name)")
+            row.logChange(COL_NETWORK_NAME, "IntentDerived($name)")
+        }
+    }
+
+    /**
+     * This name has been derived from the sim via
+     * [android.telephony.TelephonyManager.getSimOperatorName].
+     */
+    data class SimDerived(override val name: String) : NetworkNameModel {
+        override fun logDiffs(prevVal: NetworkNameModel, row: TableRowLogger) {
+            if (prevVal !is SimDerived || prevVal.name != name) {
+                row.logChange(COL_NETWORK_NAME, "SimDerived($name)")
+            }
+        }
+
+        override fun logFull(row: TableRowLogger) {
+            row.logChange(COL_NETWORK_NAME, "SimDerived($name)")
         }
     }
 
@@ -84,5 +100,5 @@
         str.append(spn)
     }
 
-    return if (str.isNotEmpty()) NetworkNameModel.Derived(str.toString()) else null
+    return if (str.isNotEmpty()) NetworkNameModel.IntentDerived(str.toString()) else null
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SubscriptionModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SubscriptionModel.kt
index 2f34516..16c4027 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SubscriptionModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SubscriptionModel.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.pipeline.mobile.data.model
 
+import android.os.ParcelUuid
+
 /**
  * SystemUI representation of [SubscriptionInfo]. Currently we only use two fields on the
  * subscriptions themselves: subscriptionId and isOpportunistic. Any new fields that we need can be
@@ -29,4 +31,7 @@
      * filtering in certain cases. See [MobileIconsInteractor] for the filtering logic
      */
     val isOpportunistic: Boolean = false,
+
+    /** Subscriptions in the same group may be filtered or treated as a single subscription */
+    val groupUuid: ParcelUuid? = null,
 )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt
new file mode 100644
index 0000000..8c82fba
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt
@@ -0,0 +1,108 @@
+/*
+ * 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.statusbar.pipeline.mobile.data.model
+
+import android.os.PersistableBundle
+import android.telephony.CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL
+import android.telephony.CarrierConfigManager.KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL
+import androidx.annotation.VisibleForTesting
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/**
+ * Represents, for a given subscription ID, the set of keys about which SystemUI cares.
+ *
+ * Upon first creation, this config represents only the default configuration (see
+ * [android.telephony.CarrierConfigManager.getDefaultConfig]).
+ *
+ * Upon request (see
+ * [com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigRepository]), an
+ * instance of this class may be created for a given subscription Id, and will default to
+ * representing the default carrier configuration. However, once a carrier config is received for
+ * this [subId], all fields will reflect those in the received config, using [PersistableBundle]'s
+ * default of false for any config that is not present in the override.
+ *
+ * To keep things relatively simple, this class defines a wrapper around each config key which
+ * exposes a StateFlow<Boolean> for each config we care about. It also tracks whether or not it is
+ * using the default config for logging purposes.
+ *
+ * NOTE to add new keys to be tracked:
+ * 1. Define a new `private val` wrapping the key using [BooleanCarrierConfig]
+ * 2. Define a public `val` exposing the wrapped flow using [BooleanCarrierConfig.config]
+ * 3. Add the new [BooleanCarrierConfig] to the list of tracked configs, so they are properly
+ * updated when a new carrier config comes down
+ */
+class SystemUiCarrierConfig
+internal constructor(
+    val subId: Int,
+    defaultConfig: PersistableBundle,
+) {
+    @VisibleForTesting
+    var isUsingDefault = true
+        private set
+
+    private val inflateSignalStrength =
+        BooleanCarrierConfig(KEY_INFLATE_SIGNAL_STRENGTH_BOOL, defaultConfig)
+    /** Flow tracking the [KEY_INFLATE_SIGNAL_STRENGTH_BOOL] carrier config */
+    val shouldInflateSignalStrength: StateFlow<Boolean> = inflateSignalStrength.config
+
+    private val showOperatorName =
+        BooleanCarrierConfig(KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL, defaultConfig)
+    /** Flow tracking the [KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL] config */
+    val showOperatorNameInStatusBar: StateFlow<Boolean> = showOperatorName.config
+
+    private val trackedConfigs =
+        listOf(
+            inflateSignalStrength,
+            showOperatorName,
+        )
+
+    /** Ingest a new carrier config, and switch all of the tracked keys over to the new values */
+    fun processNewCarrierConfig(config: PersistableBundle) {
+        isUsingDefault = false
+        trackedConfigs.forEach { it.update(config) }
+    }
+
+    /** For dumpsys, shortcut if we haven't overridden any keys */
+    fun toStringConsideringDefaults(): String {
+        return if (isUsingDefault) {
+            "using defaults"
+        } else {
+            trackedConfigs.joinToString { it.toString() }
+        }
+    }
+
+    override fun toString(): String = trackedConfigs.joinToString { it.toString() }
+}
+
+/** Extracts [key] from the carrier config, and stores it in a flow */
+private class BooleanCarrierConfig(
+    val key: String,
+    defaultConfig: PersistableBundle,
+) {
+    private val _configValue = MutableStateFlow(defaultConfig.getBoolean(key))
+    val config = _configValue.asStateFlow()
+
+    fun update(config: PersistableBundle) {
+        _configValue.value = config.getBoolean(key)
+    }
+
+    override fun toString(): String {
+        return "$key=${config.value}"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigCoreStartable.kt
new file mode 100644
index 0000000..af58999
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigCoreStartable.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.statusbar.pipeline.mobile.data.repository
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.qualifiers.Application
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * Core startable which configures the [CarrierConfigRepository] to listen for updates for the
+ * lifetime of the process
+ */
+class CarrierConfigCoreStartable
+@Inject
+constructor(
+    private val carrierConfigRepository: CarrierConfigRepository,
+    @Application private val scope: CoroutineScope,
+) : CoreStartable {
+
+    override fun start() {
+        scope.launch { carrierConfigRepository.startObservingCarrierConfigUpdates() }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepository.kt
new file mode 100644
index 0000000..bb3b9b2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepository.kt
@@ -0,0 +1,136 @@
+/*
+ * 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.statusbar.pipeline.mobile.data.repository
+
+import android.content.IntentFilter
+import android.os.PersistableBundle
+import android.telephony.CarrierConfigManager
+import android.telephony.SubscriptionManager
+import android.util.SparseArray
+import androidx.annotation.VisibleForTesting
+import androidx.core.util.getOrElse
+import androidx.core.util.isEmpty
+import androidx.core.util.keyIterator
+import com.android.systemui.Dumpable
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
+import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
+import java.io.PrintWriter
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.shareIn
+
+/**
+ * Meant to be the source of truth regarding CarrierConfigs. These are configuration objects defined
+ * on a per-subscriptionId basis, and do not trigger a device configuration event.
+ *
+ * Designed to supplant [com.android.systemui.util.CarrierConfigTracker].
+ *
+ * See [SystemUiCarrierConfig] for details on how to add carrier config keys to be tracked
+ */
+@SysUISingleton
+class CarrierConfigRepository
+@Inject
+constructor(
+    broadcastDispatcher: BroadcastDispatcher,
+    private val carrierConfigManager: CarrierConfigManager,
+    dumpManager: DumpManager,
+    logger: MobileInputLogger,
+    @Application scope: CoroutineScope,
+) : Dumpable {
+    private var isListening = false
+    private val defaultConfig: PersistableBundle by lazy { CarrierConfigManager.getDefaultConfig() }
+    // Used for logging the default config in the dumpsys
+    private val defaultConfigForLogs: SystemUiCarrierConfig by lazy {
+        SystemUiCarrierConfig(-1, defaultConfig)
+    }
+
+    private val configs = SparseArray<SystemUiCarrierConfig>()
+
+    init {
+        dumpManager.registerNormalDumpable(this)
+    }
+
+    @VisibleForTesting
+    val carrierConfigStream: SharedFlow<Pair<Int, PersistableBundle>> =
+        broadcastDispatcher
+            .broadcastFlow(IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
+                intent,
+                _ ->
+                intent.getIntExtra(
+                    CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX,
+                    SubscriptionManager.INVALID_SUBSCRIPTION_ID
+                )
+            }
+            .onEach { logger.logCarrierConfigChanged(it) }
+            .filter { SubscriptionManager.isValidSubscriptionId(it) }
+            .mapNotNull { subId ->
+                val config = carrierConfigManager.getConfigForSubId(subId)
+                config?.let { subId to it }
+            }
+            .shareIn(scope, SharingStarted.WhileSubscribed())
+
+    /**
+     * Start this repository observing broadcasts for **all** carrier configuration updates. Must be
+     * called in order to keep SystemUI in sync with [CarrierConfigManager].
+     */
+    suspend fun startObservingCarrierConfigUpdates() {
+        isListening = true
+        carrierConfigStream.collect { updateCarrierConfig(it.first, it.second) }
+    }
+
+    /** Update or create the [SystemUiCarrierConfig] for subId with the override */
+    private fun updateCarrierConfig(subId: Int, config: PersistableBundle) {
+        val configToUpdate = getOrCreateConfigForSubId(subId)
+        configToUpdate.processNewCarrierConfig(config)
+    }
+
+    /** Gets a cached [SystemUiCarrierConfig], or creates a new one which will track the defaults */
+    fun getOrCreateConfigForSubId(subId: Int): SystemUiCarrierConfig {
+        return configs.getOrElse(subId) {
+            val config = SystemUiCarrierConfig(subId, defaultConfig)
+            val carrierConfig = carrierConfigManager.getConfigForSubId(subId)
+            if (carrierConfig != null) config.processNewCarrierConfig(carrierConfig)
+            configs.put(subId, config)
+            config
+        }
+    }
+
+    override fun dump(pw: PrintWriter, args: Array<out String>) {
+        pw.println("isListening: $isListening")
+        if (configs.isEmpty()) {
+            pw.println("no carrier configs loaded")
+        } else {
+            pw.println("Carrier configs by subId")
+            configs.keyIterator().forEach {
+                pw.println("  subId=$it")
+                pw.println("    config=${configs.get(it).toStringConsideringDefaults()}")
+            }
+            // Finally, print the default config
+            pw.println("Default config:")
+            pw.println("  $defaultConfigForLogs")
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
index e0d156a..be30ea4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.pipeline.mobile.data.repository
 
-import android.provider.Settings
 import android.telephony.CarrierConfigManager
 import android.telephony.SubscriptionManager
 import com.android.settingslib.SignalIcon.MobileIconGroup
@@ -35,8 +34,14 @@
     /** Observable list of current mobile subscriptions */
     val subscriptions: StateFlow<List<SubscriptionModel>>
 
-    /** Observable for the subscriptionId of the current mobile data connection */
-    val activeMobileDataSubscriptionId: StateFlow<Int>
+    /**
+     * Observable for the subscriptionId of the current mobile data connection. Null if we don't
+     * have a valid subscription id
+     */
+    val activeMobileDataSubscriptionId: StateFlow<Int?>
+
+    /** Repo that tracks the current [activeMobileDataSubscriptionId] */
+    val activeMobileDataRepository: StateFlow<MobileConnectionRepository?>
 
     /**
      * Observable event for when the active data sim switches but the group stays the same. E.g.,
@@ -53,9 +58,6 @@
     /** Get or create a repository for the line of service for the given subscription ID */
     fun getRepoForSubId(subId: Int): MobileConnectionRepository
 
-    /** Observe changes to the [Settings.Global.MOBILE_DATA] setting */
-    val globalMobileDataSettingChangedEvent: Flow<Unit>
-
     /**
      * [Config] is an object that tracks relevant configuration flags for a given subscription ID.
      * In the case of [MobileMappings], it's hard-coded to check the default data subscription's
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
index b939856..d54531a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
@@ -47,7 +47,6 @@
  * interface in its own repository, completely separate from the real version, while still using all
  * of the prod implementations for the rest of the pipeline (interactors and onward). Looks
  * something like this:
- *
  * ```
  * RealRepository
  *                 │
@@ -115,7 +114,7 @@
             .flatMapLatest { it.subscriptions }
             .stateIn(scope, SharingStarted.WhileSubscribed(), realRepository.subscriptions.value)
 
-    override val activeMobileDataSubscriptionId: StateFlow<Int> =
+    override val activeMobileDataSubscriptionId: StateFlow<Int?> =
         activeRepo
             .flatMapLatest { it.activeMobileDataSubscriptionId }
             .stateIn(
@@ -124,6 +123,15 @@
                 realRepository.activeMobileDataSubscriptionId.value
             )
 
+    override val activeMobileDataRepository: StateFlow<MobileConnectionRepository?> =
+        activeRepo
+            .flatMapLatest { it.activeMobileDataRepository }
+            .stateIn(
+                scope,
+                SharingStarted.WhileSubscribed(),
+                realRepository.activeMobileDataRepository.value
+            )
+
     override val activeSubChangedInGroupEvent: Flow<Unit> =
         activeRepo.flatMapLatest { it.activeSubChangedInGroupEvent }
 
@@ -156,9 +164,6 @@
                 realRepository.defaultMobileNetworkConnectivity.value
             )
 
-    override val globalMobileDataSettingChangedEvent: Flow<Unit> =
-        activeRepo.flatMapLatest { it.globalMobileDataSettingChangedEvent }
-
     override fun getRepoForSubId(subId: Int): MobileConnectionRepository {
         if (isDemoMode.value) {
             return demoMobileConnectionsRepository.getRepoForSubId(subId)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
index 1088345..e924832 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
@@ -55,6 +55,7 @@
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.mapLatest
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.stateIn
@@ -121,6 +122,15 @@
                 subscriptions.value.firstOrNull()?.subscriptionId ?: INVALID_SUBSCRIPTION_ID
             )
 
+    override val activeMobileDataRepository: StateFlow<MobileConnectionRepository?> =
+        activeMobileDataSubscriptionId
+            .map { getRepoForSubId(it) }
+            .stateIn(
+                scope,
+                SharingStarted.WhileSubscribed(),
+                getRepoForSubId(activeMobileDataSubscriptionId.value)
+            )
+
     // TODO(b/261029387): consider adding a demo command for this
     override val activeSubChangedInGroupEvent: Flow<Unit> = flowOf()
 
@@ -185,8 +195,6 @@
         return CacheContainer(repo, lastMobileState = null)
     }
 
-    override val globalMobileDataSettingChangedEvent = MutableStateFlow(Unit)
-
     fun startProcessingCommands() {
         mobileDemoCommandJob =
             scope.launch {
@@ -242,7 +250,7 @@
 
         // This is always true here, because we split out disabled states at the data-source level
         connection.dataEnabled.value = true
-        connection.networkName.value = NetworkNameModel.Derived(state.name)
+        connection.networkName.value = NetworkNameModel.IntentDerived(state.name)
 
         connection.cdmaRoaming.value = state.roaming
         connection.connectionInfo.value = state.toMobileConnectionModel()
@@ -260,10 +268,13 @@
         maybeCreateSubscription(subId)
         carrierMergedSubId = subId
 
+        // TODO(b/261029387): until we have a command, use the most recent subId
+        defaultDataSubId.value = subId
+
         val connection = getRepoForSubId(subId)
         // This is always true here, because we split out disabled states at the data-source level
         connection.dataEnabled.value = true
-        connection.networkName.value = NetworkNameModel.Derived(CARRIER_MERGED_NAME)
+        connection.networkName.value = NetworkNameModel.IntentDerived(CARRIER_MERGED_NAME)
         connection.numberOfLevels.value = event.numberOfLevels
         connection.cdmaRoaming.value = false
         connection.connectionInfo.value = event.toMobileConnectionModel()
@@ -338,7 +349,10 @@
     }
 
     private fun FakeWifiEventModel.CarrierMerged.toMobileConnectionModel(): MobileConnectionModel {
-        return createCarrierMergedConnectionModel(this.level)
+        return createCarrierMergedConnectionModel(
+            this.level,
+            activity.toMobileDataActivityModel(),
+        )
     }
 
     private fun SignalIcon.MobileIconGroup?.toResolvedNetworkType(): ResolvedNetworkType {
@@ -373,5 +387,5 @@
 
     override val cdmaRoaming = MutableStateFlow(false)
 
-    override val networkName = MutableStateFlow(NetworkNameModel.Derived("demo network"))
+    override val networkName = MutableStateFlow(NetworkNameModel.IntentDerived("demo network"))
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
index c783b12..8f6a87b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
 
+import android.telephony.TelephonyManager
 import android.util.Log
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
@@ -27,8 +28,8 @@
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
@@ -37,7 +38,6 @@
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
@@ -54,10 +54,18 @@
 class CarrierMergedConnectionRepository(
     override val subId: Int,
     override val tableLogBuffer: TableLogBuffer,
-    defaultNetworkName: NetworkNameModel,
+    private val telephonyManager: TelephonyManager,
     @Application private val scope: CoroutineScope,
     val wifiRepository: WifiRepository,
 ) : MobileConnectionRepository {
+    init {
+        if (telephonyManager.subscriptionId != subId) {
+            throw IllegalStateException(
+                "CarrierMergedRepo: TelephonyManager should be created with subId($subId). " +
+                    "Found ${telephonyManager.subscriptionId} instead."
+            )
+        }
+    }
 
     /**
      * Outputs the carrier merged network to use, or null if we don't have a valid carrier merged
@@ -87,20 +95,28 @@
         }
 
     override val connectionInfo: StateFlow<MobileConnectionModel> =
-        network
-            .map { it.toMobileConnectionModel() }
+        combine(network, wifiRepository.wifiActivity) { network, activity ->
+                if (network == null) {
+                    MobileConnectionModel()
+                } else {
+                    createCarrierMergedConnectionModel(network.level, activity)
+                }
+            }
             .stateIn(scope, SharingStarted.WhileSubscribed(), MobileConnectionModel())
 
-    // TODO(b/238425913): Add logging to this class.
-    // TODO(b/238425913): Make sure SignalStrength.getEmptyState is used when appropriate.
+    override val cdmaRoaming: StateFlow<Boolean> = MutableStateFlow(ROAMING).asStateFlow()
 
-    // Carrier merged is never roaming.
-    override val cdmaRoaming: StateFlow<Boolean> = MutableStateFlow(false).asStateFlow()
-
-    // TODO(b/238425913): Fetch the carrier merged network name.
     override val networkName: StateFlow<NetworkNameModel> =
-        flowOf(defaultNetworkName)
-            .stateIn(scope, SharingStarted.WhileSubscribed(), defaultNetworkName)
+        network
+            // The SIM operator name should be the same throughout the lifetime of a subId, **but**
+            // it may not be available when this repo is created because it takes time to load. To
+            // be safe, we re-fetch it each time the network has changed.
+            .map { NetworkNameModel.SimDerived(telephonyManager.simOperatorName) }
+            .stateIn(
+                scope,
+                SharingStarted.WhileSubscribed(),
+                NetworkNameModel.SimDerived(telephonyManager.simOperatorName),
+            )
 
     override val numberOfLevels: StateFlow<Int> =
         wifiRepository.wifiNetwork
@@ -115,37 +131,24 @@
 
     override val dataEnabled: StateFlow<Boolean> = wifiRepository.isWifiEnabled
 
-    private fun WifiNetworkModel.CarrierMerged?.toMobileConnectionModel(): MobileConnectionModel {
-        if (this == null) {
-            return MobileConnectionModel()
-        }
-
-        return createCarrierMergedConnectionModel(level)
-    }
-
     companion object {
         /**
          * Creates an instance of [MobileConnectionModel] that represents a carrier merged network
-         * with the given [level].
+         * with the given [level] and [activity].
          */
-        fun createCarrierMergedConnectionModel(level: Int): MobileConnectionModel {
+        fun createCarrierMergedConnectionModel(
+            level: Int,
+            activity: DataActivityModel,
+        ): MobileConnectionModel {
             return MobileConnectionModel(
                 primaryLevel = level,
                 cdmaLevel = level,
-                // A [WifiNetworkModel.CarrierMerged] instance is always connected.
-                // (A [WifiNetworkModel.Inactive] represents a disconnected network.)
-                dataConnectionState = DataConnectionState.Connected,
-                // TODO(b/238425913): This should come from [WifiRepository.wifiActivity].
-                dataActivityDirection =
-                    DataActivityModel(
-                        hasActivityIn = false,
-                        hasActivityOut = false,
-                    ),
+                dataActivityDirection = activity,
+                // Here and below: These values are always the same for every carrier-merged
+                // connection.
                 resolvedNetworkType = ResolvedNetworkType.CarrierMergedNetworkType,
-                // Carrier merged is never roaming
-                isRoaming = false,
-
-                // TODO(b/238425913): Verify that these fields never change for carrier merged.
+                dataConnectionState = DataConnectionState.Connected,
+                isRoaming = ROAMING,
                 isEmergencyOnly = false,
                 operatorAlphaShort = null,
                 isInService = true,
@@ -153,24 +156,27 @@
                 carrierNetworkChangeActive = false,
             )
         }
+
+        // Carrier merged is never roaming
+        private const val ROAMING = false
     }
 
     @SysUISingleton
     class Factory
     @Inject
     constructor(
+        private val telephonyManager: TelephonyManager,
         @Application private val scope: CoroutineScope,
         private val wifiRepository: WifiRepository,
     ) {
         fun build(
             subId: Int,
             mobileLogger: TableLogBuffer,
-            defaultNetworkName: NetworkNameModel,
         ): MobileConnectionRepository {
             return CarrierMergedConnectionRepository(
                 subId,
                 mobileLogger,
-                defaultNetworkName,
+                telephonyManager.createForSubscriptionId(subId),
                 scope,
                 wifiRepository,
             )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
index 0f30ae2..a39ea0a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
@@ -26,7 +26,6 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
@@ -50,7 +49,6 @@
     override val tableLogBuffer: TableLogBuffer,
     private val defaultNetworkName: NetworkNameModel,
     private val networkNameSeparator: String,
-    private val globalMobileDataSettingChangedEvent: Flow<Unit>,
     @Application scope: CoroutineScope,
     private val mobileRepoFactory: MobileConnectionRepositoryImpl.Factory,
     private val carrierMergedRepoFactory: CarrierMergedConnectionRepository.Factory,
@@ -84,12 +82,11 @@
             tableLogBuffer,
             defaultNetworkName,
             networkNameSeparator,
-            globalMobileDataSettingChangedEvent,
         )
     }
 
     private val carrierMergedRepo: MobileConnectionRepository by lazy {
-        carrierMergedRepoFactory.build(subId, tableLogBuffer, defaultNetworkName)
+        carrierMergedRepoFactory.build(subId, tableLogBuffer)
     }
 
     @VisibleForTesting
@@ -120,11 +117,22 @@
     override val connectionInfo =
         activeRepo
             .flatMapLatest { it.connectionInfo }
+            .logDiffsForTable(
+                tableLogBuffer,
+                columnPrefix = "",
+                initialValue = activeRepo.value.connectionInfo.value,
+            )
             .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.connectionInfo.value)
 
     override val dataEnabled =
         activeRepo
             .flatMapLatest { it.dataEnabled }
+            .logDiffsForTable(
+                tableLogBuffer,
+                columnPrefix = "",
+                columnName = "dataEnabled",
+                initialValue = activeRepo.value.dataEnabled.value,
+            )
             .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.dataEnabled.value)
 
     override val numberOfLevels =
@@ -135,6 +143,11 @@
     override val networkName =
         activeRepo
             .flatMapLatest { it.networkName }
+            .logDiffsForTable(
+                tableLogBuffer,
+                columnPrefix = "",
+                initialValue = activeRepo.value.networkName.value,
+            )
             .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.networkName.value)
 
     class Factory
@@ -150,7 +163,6 @@
             startingIsCarrierMerged: Boolean,
             defaultNetworkName: NetworkNameModel,
             networkNameSeparator: String,
-            globalMobileDataSettingChangedEvent: Flow<Unit>,
         ): FullMobileConnectionRepository {
             val mobileLogger =
                 logFactory.getOrCreate(tableBufferLogName(subId), MOBILE_CONNECTION_BUFFER_SIZE)
@@ -161,7 +173,6 @@
                 mobileLogger,
                 defaultNetworkName,
                 networkNameSeparator,
-                globalMobileDataSettingChangedEvent,
                 scope,
                 mobileRepoFactory,
                 carrierMergedRepoFactory,
@@ -173,7 +184,7 @@
             const val MOBILE_CONNECTION_BUFFER_SIZE = 100
 
             /** Returns a log buffer name for a mobile connection with the given [subId]. */
-            fun tableBufferLogName(subId: Int): String = "MobileConnectionLog [$subId]"
+            fun tableBufferLogName(subId: Int): String = "MobileConnectionLog[$subId]"
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index 3f2ce40..96b96f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -18,8 +18,6 @@
 
 import android.content.Context
 import android.content.IntentFilter
-import android.database.ContentObserver
-import android.provider.Settings.Global
 import android.telephony.CellSignalStrength
 import android.telephony.CellSignalStrengthCdma
 import android.telephony.ServiceState
@@ -38,20 +36,20 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.log.table.TableLogBuffer
-import com.android.systemui.log.table.logDiffsForTable
 import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType
 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.OverrideNetworkType
 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.UnknownNetworkType
+import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
 import com.android.systemui.statusbar.pipeline.mobile.data.model.toDataConnectionType
 import com.android.systemui.statusbar.pipeline.mobile.data.model.toNetworkNameModel
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
+import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
 import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
-import com.android.systemui.util.settings.GlobalSettings
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
@@ -60,13 +58,15 @@
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.SharedFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.mapLatest
-import kotlinx.coroutines.flow.merge
-import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.scan
+import kotlinx.coroutines.flow.shareIn
 import kotlinx.coroutines.flow.stateIn
 
 /**
@@ -81,19 +81,18 @@
     defaultNetworkName: NetworkNameModel,
     networkNameSeparator: String,
     private val telephonyManager: TelephonyManager,
-    private val globalSettings: GlobalSettings,
+    systemUiCarrierConfig: SystemUiCarrierConfig,
     broadcastDispatcher: BroadcastDispatcher,
-    globalMobileDataSettingChangedEvent: Flow<Unit>,
-    mobileMappingsProxy: MobileMappingsProxy,
+    private val mobileMappingsProxy: MobileMappingsProxy,
     bgDispatcher: CoroutineDispatcher,
-    logger: ConnectivityPipelineLogger,
-    mobileLogger: TableLogBuffer,
+    logger: MobileInputLogger,
+    override val tableLogBuffer: TableLogBuffer,
     scope: CoroutineScope,
 ) : MobileConnectionRepository {
     init {
         if (telephonyManager.subscriptionId != subId) {
             throw IllegalStateException(
-                "TelephonyManager should be created with subId($subId). " +
+                "MobileRepo: TelephonyManager should be created with subId($subId). " +
                     "Found ${telephonyManager.subscriptionId} instead."
             )
         }
@@ -101,10 +100,15 @@
 
     private val telephonyCallbackEvent = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
 
-    override val tableLogBuffer: TableLogBuffer = mobileLogger
-
-    override val connectionInfo: StateFlow<MobileConnectionModel> = run {
-        var state = MobileConnectionModel()
+    /**
+     * This flow defines the single shared connection to system_server via TelephonyCallback. Any
+     * new callback should be added to this listener and funneled through callbackEvents via a data
+     * class. See [CallbackEvent] for defining new callbacks.
+     *
+     * The reason we need to do this is because TelephonyManager limits the number of registered
+     * listeners per-process, so we don't want to create a new listener for every callback.
+     */
+    private val callbackEvents: SharedFlow<CallbackEvent> =
         conflatedCallbackFlow {
                 val callback =
                     object :
@@ -114,41 +118,16 @@
                         TelephonyCallback.DataConnectionStateListener,
                         TelephonyCallback.DataActivityListener,
                         TelephonyCallback.CarrierNetworkListener,
-                        TelephonyCallback.DisplayInfoListener {
+                        TelephonyCallback.DisplayInfoListener,
+                        TelephonyCallback.DataEnabledListener {
                         override fun onServiceStateChanged(serviceState: ServiceState) {
                             logger.logOnServiceStateChanged(serviceState, subId)
-                            state =
-                                state.copy(
-                                    isEmergencyOnly = serviceState.isEmergencyOnly,
-                                    isRoaming = serviceState.roaming,
-                                    operatorAlphaShort = serviceState.operatorAlphaShort,
-                                    isInService = Utils.isInService(serviceState),
-                                )
-                            trySend(state)
+                            trySend(CallbackEvent.OnServiceStateChanged(serviceState))
                         }
 
                         override fun onSignalStrengthsChanged(signalStrength: SignalStrength) {
                             logger.logOnSignalStrengthsChanged(signalStrength, subId)
-                            val cdmaLevel =
-                                signalStrength
-                                    .getCellSignalStrengths(CellSignalStrengthCdma::class.java)
-                                    .let { strengths ->
-                                        if (!strengths.isEmpty()) {
-                                            strengths[0].level
-                                        } else {
-                                            CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN
-                                        }
-                                    }
-
-                            val primaryLevel = signalStrength.level
-
-                            state =
-                                state.copy(
-                                    cdmaLevel = cdmaLevel,
-                                    primaryLevel = primaryLevel,
-                                    isGsm = signalStrength.isGsm,
-                                )
-                            trySend(state)
+                            trySend(CallbackEvent.OnSignalStrengthChanged(signalStrength))
                         }
 
                         override fun onDataConnectionStateChanged(
@@ -156,101 +135,131 @@
                             networkType: Int
                         ) {
                             logger.logOnDataConnectionStateChanged(dataState, networkType, subId)
-                            state =
-                                state.copy(dataConnectionState = dataState.toDataConnectionType())
-                            trySend(state)
+                            trySend(CallbackEvent.OnDataConnectionStateChanged(dataState))
                         }
 
                         override fun onDataActivity(direction: Int) {
                             logger.logOnDataActivity(direction, subId)
-                            state =
-                                state.copy(
-                                    dataActivityDirection = direction.toMobileDataActivityModel()
-                                )
-                            trySend(state)
+                            trySend(CallbackEvent.OnDataActivity(direction))
                         }
 
                         override fun onCarrierNetworkChange(active: Boolean) {
                             logger.logOnCarrierNetworkChange(active, subId)
-                            state = state.copy(carrierNetworkChangeActive = active)
-                            trySend(state)
+                            trySend(CallbackEvent.OnCarrierNetworkChange(active))
                         }
 
                         override fun onDisplayInfoChanged(
                             telephonyDisplayInfo: TelephonyDisplayInfo
                         ) {
                             logger.logOnDisplayInfoChanged(telephonyDisplayInfo, subId)
+                            trySend(CallbackEvent.OnDisplayInfoChanged(telephonyDisplayInfo))
+                        }
 
-                            val networkType =
-                                if (telephonyDisplayInfo.networkType == NETWORK_TYPE_UNKNOWN) {
-                                    UnknownNetworkType
-                                } else if (
-                                    telephonyDisplayInfo.overrideNetworkType ==
-                                        OVERRIDE_NETWORK_TYPE_NONE
-                                ) {
-                                    DefaultNetworkType(
-                                        mobileMappingsProxy.toIconKey(
-                                            telephonyDisplayInfo.networkType
-                                        )
-                                    )
-                                } else {
-                                    OverrideNetworkType(
-                                        mobileMappingsProxy.toIconKeyOverride(
-                                            telephonyDisplayInfo.overrideNetworkType
-                                        )
-                                    )
-                                }
-                            state = state.copy(resolvedNetworkType = networkType)
-                            trySend(state)
+                        override fun onDataEnabledChanged(enabled: Boolean, reason: Int) {
+                            logger.logOnDataEnabledChanged(enabled, subId)
+                            trySend(CallbackEvent.OnDataEnabledChanged(enabled))
                         }
                     }
                 telephonyManager.registerTelephonyCallback(bgDispatcher.asExecutor(), callback)
                 awaitClose { telephonyManager.unregisterTelephonyCallback(callback) }
             }
-            .onEach { telephonyCallbackEvent.tryEmit(Unit) }
-            .logDiffsForTable(
-                mobileLogger,
-                columnPrefix = "MobileConnection ($subId)",
-                initialValue = state,
-            )
-            .stateIn(scope, SharingStarted.WhileSubscribed(), state)
+            .shareIn(scope, SharingStarted.WhileSubscribed())
+
+    private fun updateConnectionState(
+        prevState: MobileConnectionModel,
+        callbackEvent: CallbackEvent,
+    ): MobileConnectionModel =
+        when (callbackEvent) {
+            is CallbackEvent.OnServiceStateChanged -> {
+                val serviceState = callbackEvent.serviceState
+                prevState.copy(
+                    isEmergencyOnly = serviceState.isEmergencyOnly,
+                    isRoaming = serviceState.roaming,
+                    operatorAlphaShort = serviceState.operatorAlphaShort,
+                    isInService = Utils.isInService(serviceState),
+                )
+            }
+            is CallbackEvent.OnSignalStrengthChanged -> {
+                val signalStrength = callbackEvent.signalStrength
+                val cdmaLevel =
+                    signalStrength.getCellSignalStrengths(CellSignalStrengthCdma::class.java).let {
+                        strengths ->
+                        if (!strengths.isEmpty()) {
+                            strengths[0].level
+                        } else {
+                            CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN
+                        }
+                    }
+
+                val primaryLevel = signalStrength.level
+
+                prevState.copy(
+                    cdmaLevel = cdmaLevel,
+                    primaryLevel = primaryLevel,
+                    isGsm = signalStrength.isGsm,
+                )
+            }
+            is CallbackEvent.OnDataConnectionStateChanged -> {
+                prevState.copy(dataConnectionState = callbackEvent.dataState.toDataConnectionType())
+            }
+            is CallbackEvent.OnDataActivity -> {
+                prevState.copy(
+                    dataActivityDirection = callbackEvent.direction.toMobileDataActivityModel()
+                )
+            }
+            is CallbackEvent.OnCarrierNetworkChange -> {
+                prevState.copy(carrierNetworkChangeActive = callbackEvent.active)
+            }
+            is CallbackEvent.OnDisplayInfoChanged -> {
+                val telephonyDisplayInfo = callbackEvent.telephonyDisplayInfo
+                val networkType =
+                    if (telephonyDisplayInfo.networkType == NETWORK_TYPE_UNKNOWN) {
+                        UnknownNetworkType
+                    } else if (
+                        telephonyDisplayInfo.overrideNetworkType == OVERRIDE_NETWORK_TYPE_NONE
+                    ) {
+                        DefaultNetworkType(
+                            mobileMappingsProxy.toIconKey(telephonyDisplayInfo.networkType)
+                        )
+                    } else {
+                        OverrideNetworkType(
+                            mobileMappingsProxy.toIconKeyOverride(
+                                telephonyDisplayInfo.overrideNetworkType
+                            )
+                        )
+                    }
+                prevState.copy(resolvedNetworkType = networkType)
+            }
+            is CallbackEvent.OnDataEnabledChanged -> {
+                // Not part of this object, handled in a separate flow
+                prevState
+            }
+        }
+
+    override val connectionInfo = run {
+        val initial = MobileConnectionModel()
+        callbackEvents
+            .scan(initial, ::updateConnectionState)
+            .stateIn(scope, SharingStarted.WhileSubscribed(), initial)
     }
 
-    // This will become variable based on [CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL]
-    // once it's wired up inside of [CarrierConfigTracker].
-    override val numberOfLevels: StateFlow<Int> =
-        flowOf(DEFAULT_NUM_LEVELS)
-            .stateIn(scope, SharingStarted.WhileSubscribed(), DEFAULT_NUM_LEVELS)
-
-    /** Produces whenever the mobile data setting changes for this subId */
-    private val localMobileDataSettingChangedEvent: Flow<Unit> = conflatedCallbackFlow {
-        val observer =
-            object : ContentObserver(null) {
-                override fun onChange(selfChange: Boolean) {
-                    trySend(Unit)
+    override val numberOfLevels =
+        systemUiCarrierConfig.shouldInflateSignalStrength
+            .map { shouldInflate ->
+                if (shouldInflate) {
+                    DEFAULT_NUM_LEVELS + 1
+                } else {
+                    DEFAULT_NUM_LEVELS
                 }
             }
-
-        globalSettings.registerContentObserver(
-            globalSettings.getUriFor("${Global.MOBILE_DATA}$subId"),
-            /* notifyForDescendants */ true,
-            observer
-        )
-
-        awaitClose { context.contentResolver.unregisterContentObserver(observer) }
-    }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), DEFAULT_NUM_LEVELS)
 
     /**
      * There are a few cases where we will need to poll [TelephonyManager] so we can update some
      * internal state where callbacks aren't provided. Any of those events should be merged into
      * this flow, which can be used to trigger the polling.
      */
-    private val telephonyPollingEvent: Flow<Unit> =
-        merge(
-            telephonyCallbackEvent,
-            localMobileDataSettingChangedEvent,
-            globalMobileDataSettingChangedEvent,
-        )
+    private val telephonyPollingEvent: Flow<Unit> = callbackEvents.map { Unit }
 
     override val cdmaRoaming: StateFlow<Boolean> =
         telephonyPollingEvent
@@ -259,47 +268,31 @@
 
     override val networkName: StateFlow<NetworkNameModel> =
         broadcastDispatcher
-            .broadcastFlow(IntentFilter(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED)) {
-                intent,
-                _ ->
-                if (intent.getIntExtra(EXTRA_SUBSCRIPTION_ID, INVALID_SUBSCRIPTION_ID) != subId) {
-                    defaultNetworkName
-                } else {
-                    intent.toNetworkNameModel(networkNameSeparator) ?: defaultNetworkName
-                }
-            }
-            .distinctUntilChanged()
-            .logDiffsForTable(
-                mobileLogger,
-                columnPrefix = "",
-                initialValue = defaultNetworkName,
+            .broadcastFlow(
+                filter = IntentFilter(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED),
+                map = { intent, _ -> intent },
             )
+            .filter { intent ->
+                intent.getIntExtra(EXTRA_SUBSCRIPTION_ID, INVALID_SUBSCRIPTION_ID) == subId
+            }
+            .map { intent -> intent.toNetworkNameModel(networkNameSeparator) ?: defaultNetworkName }
             .stateIn(scope, SharingStarted.WhileSubscribed(), defaultNetworkName)
 
-    override val dataEnabled: StateFlow<Boolean> = run {
-        val initial = dataConnectionAllowed()
-        telephonyPollingEvent
-            .mapLatest { dataConnectionAllowed() }
-            .distinctUntilChanged()
-            .logDiffsForTable(
-                mobileLogger,
-                columnPrefix = "",
-                columnName = "dataEnabled",
-                initialValue = initial,
-            )
+    override val dataEnabled = run {
+        val initial = telephonyManager.isDataConnectionAllowed
+        callbackEvents
+            .mapNotNull { (it as? CallbackEvent.OnDataEnabledChanged)?.enabled }
             .stateIn(scope, SharingStarted.WhileSubscribed(), initial)
     }
 
-    private fun dataConnectionAllowed(): Boolean = telephonyManager.isDataConnectionAllowed
-
     class Factory
     @Inject
     constructor(
         private val broadcastDispatcher: BroadcastDispatcher,
         private val context: Context,
         private val telephonyManager: TelephonyManager,
-        private val logger: ConnectivityPipelineLogger,
-        private val globalSettings: GlobalSettings,
+        private val logger: MobileInputLogger,
+        private val carrierConfigRepository: CarrierConfigRepository,
         private val mobileMappingsProxy: MobileMappingsProxy,
         @Background private val bgDispatcher: CoroutineDispatcher,
         @Application private val scope: CoroutineScope,
@@ -309,7 +302,6 @@
             mobileLogger: TableLogBuffer,
             defaultNetworkName: NetworkNameModel,
             networkNameSeparator: String,
-            globalMobileDataSettingChangedEvent: Flow<Unit>,
         ): MobileConnectionRepository {
             return MobileConnectionRepositoryImpl(
                 context,
@@ -317,9 +309,8 @@
                 defaultNetworkName,
                 networkNameSeparator,
                 telephonyManager.createForSubscriptionId(subId),
-                globalSettings,
+                carrierConfigRepository.getOrCreateConfigForSubId(subId),
                 broadcastDispatcher,
-                globalMobileDataSettingChangedEvent,
                 mobileMappingsProxy,
                 bgDispatcher,
                 logger,
@@ -329,3 +320,17 @@
         }
     }
 }
+
+/**
+ * Wrap every [TelephonyCallback] we care about in a data class so we can accept them in a single
+ * shared flow and then split them back out into other flows.
+ */
+private sealed interface CallbackEvent {
+    data class OnServiceStateChanged(val serviceState: ServiceState) : CallbackEvent
+    data class OnSignalStrengthChanged(val signalStrength: SignalStrength) : CallbackEvent
+    data class OnDataConnectionStateChanged(val dataState: Int) : CallbackEvent
+    data class OnDataActivity(val direction: Int) : CallbackEvent
+    data class OnCarrierNetworkChange(val active: Boolean) : CallbackEvent
+    data class OnDisplayInfoChanged(val telephonyDisplayInfo: TelephonyDisplayInfo) : CallbackEvent
+    data class OnDataEnabledChanged(val enabled: Boolean) : CallbackEvent
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index 510482d..b3d5b1e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -19,14 +19,12 @@
 import android.annotation.SuppressLint
 import android.content.Context
 import android.content.IntentFilter
-import android.database.ContentObserver
 import android.net.ConnectivityManager
 import android.net.ConnectivityManager.NetworkCallback
 import android.net.Network
 import android.net.NetworkCapabilities
 import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
 import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
-import android.provider.Settings.Global.MOBILE_DATA
 import android.telephony.CarrierConfigManager
 import android.telephony.SubscriptionInfo
 import android.telephony.SubscriptionManager
@@ -44,17 +42,18 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.statusbar.pipeline.dagger.MobileSummaryLog
 import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
 import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logInputChange
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
-import com.android.systemui.util.kotlin.pairwiseBy
-import com.android.systemui.util.settings.GlobalSettings
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.util.kotlin.pairwise
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
@@ -66,10 +65,10 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.flow.mapNotNull
 import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.stateIn
@@ -84,10 +83,10 @@
     private val connectivityManager: ConnectivityManager,
     private val subscriptionManager: SubscriptionManager,
     private val telephonyManager: TelephonyManager,
-    private val logger: ConnectivityPipelineLogger,
+    private val logger: MobileInputLogger,
+    @MobileSummaryLog private val tableLogger: TableLogBuffer,
     mobileMappingsProxy: MobileMappingsProxy,
     broadcastDispatcher: BroadcastDispatcher,
-    private val globalSettings: GlobalSettings,
     private val context: Context,
     @Background private val bgDispatcher: CoroutineDispatcher,
     @Application private val scope: CoroutineScope,
@@ -118,6 +117,12 @@
                 }
             }
             .distinctUntilChanged()
+            .logDiffsForTable(
+                tableLogger,
+                LOGGING_PREFIX,
+                columnName = "carrierMergedSubId",
+                initialValue = null,
+            )
             .stateIn(scope, started = SharingStarted.WhileSubscribed(), null)
 
     private val mobileSubscriptionsChangeEvent: Flow<Unit> = conflatedCallbackFlow {
@@ -143,17 +148,26 @@
     override val subscriptions: StateFlow<List<SubscriptionModel>> =
         merge(mobileSubscriptionsChangeEvent, carrierMergedSubId)
             .mapLatest { fetchSubscriptionsList().map { it.toSubscriptionModel() } }
-            .logInputChange(logger, "onSubscriptionsChanged")
             .onEach { infos -> updateRepos(infos) }
+            .distinctUntilChanged()
+            .logDiffsForTable(
+                tableLogger,
+                LOGGING_PREFIX,
+                columnName = "subscriptions",
+                initialValue = listOf(),
+            )
             .stateIn(scope, started = SharingStarted.WhileSubscribed(), listOf())
 
-    /** StateFlow that keeps track of the current active mobile data subscription */
-    override val activeMobileDataSubscriptionId: StateFlow<Int> =
+    override val activeMobileDataSubscriptionId: StateFlow<Int?> =
         conflatedCallbackFlow {
                 val callback =
                     object : TelephonyCallback(), ActiveDataSubscriptionIdListener {
                         override fun onActiveDataSubscriptionIdChanged(subId: Int) {
-                            trySend(subId)
+                            if (subId != INVALID_SUBSCRIPTION_ID) {
+                                trySend(subId)
+                            } else {
+                                trySend(null)
+                            }
                         }
                     }
 
@@ -161,8 +175,24 @@
                 awaitClose { telephonyManager.unregisterTelephonyCallback(callback) }
             }
             .distinctUntilChanged()
-            .logInputChange(logger, "onActiveDataSubscriptionIdChanged")
-            .stateIn(scope, started = SharingStarted.WhileSubscribed(), INVALID_SUBSCRIPTION_ID)
+            .logDiffsForTable(
+                tableLogger,
+                LOGGING_PREFIX,
+                columnName = "activeSubId",
+                initialValue = INVALID_SUBSCRIPTION_ID,
+            )
+            .stateIn(scope, started = SharingStarted.WhileSubscribed(), null)
+
+    override val activeMobileDataRepository =
+        activeMobileDataSubscriptionId
+            .map { activeSubId ->
+                if (activeSubId == null) {
+                    null
+                } else {
+                    getOrCreateRepoForSubId(activeSubId)
+                }
+            }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), null)
 
     private val defaultDataSubIdChangeEvent: MutableSharedFlow<Unit> =
         MutableSharedFlow(extraBufferCapacity = 1)
@@ -175,7 +205,12 @@
                 intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, INVALID_SUBSCRIPTION_ID)
             }
             .distinctUntilChanged()
-            .logInputChange(logger, "ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED")
+            .logDiffsForTable(
+                tableLogger,
+                LOGGING_PREFIX,
+                columnName = "defaultSubId",
+                initialValue = SubscriptionManager.getDefaultDataSubscriptionId(),
+            )
             .onEach { defaultDataSubIdChangeEvent.tryEmit(Unit) }
             .stateIn(
                 scope,
@@ -186,13 +221,13 @@
     private val carrierConfigChangedEvent =
         broadcastDispatcher
             .broadcastFlow(IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED))
-            .logInputChange(logger, "ACTION_CARRIER_CONFIG_CHANGED")
+            .onEach { logger.logActionCarrierConfigChanged() }
 
     override val defaultDataSubRatConfig: StateFlow<Config> =
         merge(defaultDataSubIdChangeEvent, carrierConfigChangedEvent)
             .mapLatest { Config.readConfig(context) }
             .distinctUntilChanged()
-            .logInputChange(logger, "defaultDataSubRatConfig")
+            .onEach { logger.logDefaultDataSubRatConfig(it) }
             .stateIn(
                 scope,
                 SharingStarted.WhileSubscribed(),
@@ -203,13 +238,13 @@
         defaultDataSubRatConfig
             .map { mobileMappingsProxy.mapIconSets(it) }
             .distinctUntilChanged()
-            .logInputChange(logger, "defaultMobileIconMapping")
+            .onEach { logger.logDefaultMobileIconMapping(it) }
 
     override val defaultMobileIconGroup: Flow<MobileIconGroup> =
         defaultDataSubRatConfig
             .map { mobileMappingsProxy.getDefaultIcons(it) }
             .distinctUntilChanged()
-            .logInputChange(logger, "defaultMobileIconGroup")
+            .onEach { logger.logDefaultMobileIconGroup(it) }
 
     override fun getRepoForSubId(subId: Int): FullMobileConnectionRepository {
         if (!isValidSubId(subId)) {
@@ -218,32 +253,12 @@
             )
         }
 
-        return subIdRepositoryCache[subId]
-            ?: createRepositoryForSubId(subId).also { subIdRepositoryCache[subId] = it }
+        return getOrCreateRepoForSubId(subId)
     }
 
-    /**
-     * In single-SIM devices, the [MOBILE_DATA] setting is phone-wide. For multi-SIM, the individual
-     * connection repositories also observe the URI for [MOBILE_DATA] + subId.
-     */
-    override val globalMobileDataSettingChangedEvent: Flow<Unit> =
-        conflatedCallbackFlow {
-                val observer =
-                    object : ContentObserver(null) {
-                        override fun onChange(selfChange: Boolean) {
-                            trySend(Unit)
-                        }
-                    }
-
-                globalSettings.registerContentObserver(
-                    globalSettings.getUriFor(MOBILE_DATA),
-                    true,
-                    observer
-                )
-
-                awaitClose { context.contentResolver.unregisterContentObserver(observer) }
-            }
-            .logInputChange(logger, "globalMobileDataSettingChangedEvent")
+    private fun getOrCreateRepoForSubId(subId: Int) =
+        subIdRepositoryCache[subId]
+            ?: createRepositoryForSubId(subId).also { subIdRepositoryCache[subId] = it }
 
     @SuppressLint("MissingPermission")
     override val defaultMobileNetworkConnectivity: StateFlow<MobileConnectivityModel> =
@@ -274,7 +289,11 @@
                 awaitClose { connectivityManager.unregisterNetworkCallback(callback) }
             }
             .distinctUntilChanged()
-            .logInputChange(logger, "defaultMobileNetworkConnectivity")
+            .logDiffsForTable(
+                tableLogger,
+                columnPrefix = "$LOGGING_PREFIX.defaultConnection",
+                initialValue = MobileConnectivityModel(),
+            )
             .stateIn(scope, SharingStarted.WhileSubscribed(), MobileConnectivityModel())
 
     /**
@@ -287,34 +306,19 @@
      */
     @SuppressLint("MissingPermission")
     override val activeSubChangedInGroupEvent =
-        flow {
-                activeMobileDataSubscriptionId.pairwiseBy { prevVal: Int, newVal: Int ->
-                    if (!defaultMobileNetworkConnectivity.value.isValidated) {
-                        return@pairwiseBy
-                    }
-                    val prevSub = subscriptionManager.getActiveSubscriptionInfo(prevVal)
-                    val nextSub = subscriptionManager.getActiveSubscriptionInfo(newVal)
+        activeMobileDataSubscriptionId
+            .pairwise()
+            .mapNotNull { (prevVal: Int?, newVal: Int?) ->
+                if (prevVal == null || newVal == null) return@mapNotNull null
 
-                    if (prevSub == null || nextSub == null) {
-                        return@pairwiseBy
-                    }
+                val prevSub = subscriptionManager.getActiveSubscriptionInfo(prevVal)?.groupUuid
+                val nextSub = subscriptionManager.getActiveSubscriptionInfo(newVal)?.groupUuid
 
-                    if (prevSub.groupUuid != null && prevSub.groupUuid == nextSub.groupUuid) {
-                        emit(Unit)
-                    }
-                }
+                if (prevSub != null && prevSub == nextSub) Unit else null
             }
             .flowOn(bgDispatcher)
 
-    private fun isValidSubId(subId: Int): Boolean {
-        subscriptions.value.forEach {
-            if (it.subscriptionId == subId) {
-                return true
-            }
-        }
-
-        return false
-    }
+    private fun isValidSubId(subId: Int): Boolean = checkSub(subId, subscriptions.value)
 
     @VisibleForTesting fun getSubIdRepoCache() = subIdRepositoryCache
 
@@ -324,7 +328,6 @@
             isCarrierMerged(subId),
             defaultNetworkName,
             networkNameSeparator,
-            globalMobileDataSettingChangedEvent,
         )
     }
 
@@ -342,12 +345,27 @@
     private fun dropUnusedReposFromCache(newInfos: List<SubscriptionModel>) {
         // Remove any connection repository from the cache that isn't in the new set of IDs. They
         // will get garbage collected once their subscribers go away
-        val currentValidSubscriptionIds = newInfos.map { it.subscriptionId }
-
         subIdRepositoryCache =
-            subIdRepositoryCache
-                .filter { currentValidSubscriptionIds.contains(it.key) }
-                .toMutableMap()
+            subIdRepositoryCache.filter { checkSub(it.key, newInfos) }.toMutableMap()
+    }
+
+    /**
+     * True if the checked subId is in the list of current subs or the active mobile data subId
+     *
+     * @param checkedSubs the list to validate [subId] against. To invalidate the cache, pass in the
+     * new subscription list. Otherwise use [subscriptions.value] to validate a subId against the
+     * current known subscriptions
+     */
+    private fun checkSub(subId: Int, checkedSubs: List<SubscriptionModel>): Boolean {
+        if (activeMobileDataSubscriptionId.value == subId) return true
+
+        checkedSubs.forEach {
+            if (it.subscriptionId == subId) {
+                return true
+            }
+        }
+
+        return false
     }
 
     private suspend fun fetchSubscriptionsList(): List<SubscriptionInfo> =
@@ -357,5 +375,10 @@
         SubscriptionModel(
             subscriptionId = subscriptionId,
             isOpportunistic = isOpportunistic,
+            groupUuid = groupUuid,
         )
+
+    companion object {
+        private const val LOGGING_PREFIX = "Repo"
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
index 9cdff96..7b0f952 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
@@ -33,7 +33,9 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.stateIn
 
 interface MobileIconInteractor {
@@ -109,6 +111,9 @@
 
     /** Based on [CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL], either 4 or 5 */
     val numberOfLevels: StateFlow<Int>
+
+    /** See [MobileIconsInteractor.isForceHidden]. */
+    val isForceHidden: Flow<Boolean>
 }
 
 /** Interactor for a single mobile connection. This connection _should_ have one subscription ID */
@@ -124,6 +129,7 @@
     defaultMobileIconGroup: StateFlow<MobileIconGroup>,
     defaultDataSubId: StateFlow<Int>,
     override val isDefaultConnectionFailed: StateFlow<Boolean>,
+    override val isForceHidden: Flow<Boolean>,
     connectionRepository: MobileConnectionRepository,
 ) : MobileIconInteractor {
     private val connectionInfo = connectionRepository.connectionInfo
@@ -152,7 +158,7 @@
                 if (
                     networkName is NetworkNameModel.Default && connection.operatorAlphaShort != null
                 ) {
-                    NetworkNameModel.Derived(connection.operatorAlphaShort)
+                    NetworkNameModel.IntentDerived(connection.operatorAlphaShort)
                 } else {
                     networkName
                 }
@@ -181,6 +187,16 @@
                     else -> mapping[info.resolvedNetworkType.lookupKey] ?: defaultGroup
                 }
             }
+            .distinctUntilChanged()
+            .onEach {
+                // Doesn't use [logDiffsForTable] because [MobileIconGroup] can't implement the
+                // [Diffable] interface.
+                tableLogBuffer.logChange(
+                    prefix = "",
+                    columnName = "networkTypeIcon",
+                    value = it.name
+                )
+            }
             .stateIn(scope, SharingStarted.WhileSubscribed(), defaultMobileIconGroup.value)
 
     override val isEmergencyOnly: StateFlow<Boolean> =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
index 9ae38e9..142c372 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
@@ -18,16 +18,20 @@
 
 import android.telephony.CarrierConfigManager
 import android.telephony.SubscriptionManager
-import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
 import com.android.settingslib.SignalIcon.MobileIconGroup
 import com.android.settingslib.mobile.TelephonyIcons
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.statusbar.pipeline.dagger.MobileSummaryLog
 import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository
+import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
+import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
 import com.android.systemui.util.CarrierConfigTracker
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -37,9 +41,11 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.mapLatest
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.flow.transformLatest
@@ -85,6 +91,10 @@
     val isDefaultConnectionFailed: StateFlow<Boolean>
     /** True once the user has been set up */
     val isUserSetup: StateFlow<Boolean>
+
+    /** True if we're configured to force-hide the mobile icons and false otherwise. */
+    val isForceHidden: Flow<Boolean>
+
     /**
      * Vends out a [MobileIconInteractor] tracking the [MobileConnectionRepository] for the given
      * subId. Will throw if the ID is invalid
@@ -100,25 +110,13 @@
 constructor(
     private val mobileConnectionsRepo: MobileConnectionsRepository,
     private val carrierConfigTracker: CarrierConfigTracker,
+    @MobileSummaryLog private val tableLogger: TableLogBuffer,
+    connectivityRepository: ConnectivityRepository,
     userSetupRepo: UserSetupRepository,
     @Application private val scope: CoroutineScope,
 ) : MobileIconsInteractor {
-    private val activeMobileDataSubscriptionId =
-        mobileConnectionsRepo.activeMobileDataSubscriptionId
-
-    private val activeMobileDataConnectionRepo: StateFlow<MobileConnectionRepository?> =
-        activeMobileDataSubscriptionId
-            .mapLatest { activeId ->
-                if (activeId == INVALID_SUBSCRIPTION_ID) {
-                    null
-                } else {
-                    mobileConnectionsRepo.getRepoForSubId(activeId)
-                }
-            }
-            .stateIn(scope, SharingStarted.WhileSubscribed(), null)
-
     override val activeDataConnectionHasDataEnabled: StateFlow<Boolean> =
-        activeMobileDataConnectionRepo
+        mobileConnectionsRepo.activeMobileDataRepository
             .flatMapLatest { it?.dataEnabled ?: flowOf(false) }
             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
 
@@ -139,35 +137,51 @@
      * and by checking which subscription is opportunistic, or which one is active.
      */
     override val filteredSubscriptions: Flow<List<SubscriptionModel>> =
-        combine(unfilteredSubscriptions, activeMobileDataSubscriptionId) { unfilteredSubs, activeId
-            ->
-            // Based on the old logic,
-            if (unfilteredSubs.size != 2) {
-                return@combine unfilteredSubs
-            }
+        combine(
+                unfilteredSubscriptions,
+                mobileConnectionsRepo.activeMobileDataSubscriptionId,
+            ) { unfilteredSubs, activeId ->
+                // Based on the old logic,
+                if (unfilteredSubs.size != 2) {
+                    return@combine unfilteredSubs
+                }
 
-            val info1 = unfilteredSubs[0]
-            val info2 = unfilteredSubs[1]
-            // If both subscriptions are primary, show both
-            if (!info1.isOpportunistic && !info2.isOpportunistic) {
-                return@combine unfilteredSubs
-            }
+                val info1 = unfilteredSubs[0]
+                val info2 = unfilteredSubs[1]
 
-            // NOTE: at this point, we are now returning a single SubscriptionInfo
+                // Filtering only applies to subscriptions in the same group
+                if (info1.groupUuid == null || info1.groupUuid != info2.groupUuid) {
+                    return@combine unfilteredSubs
+                }
 
-            // If carrier required, always show the icon of the primary subscription.
-            // Otherwise, show whichever subscription is currently active for internet.
-            if (carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault) {
-                // return the non-opportunistic info
-                return@combine if (info1.isOpportunistic) listOf(info2) else listOf(info1)
-            } else {
-                return@combine if (info1.subscriptionId == activeId) {
-                    listOf(info1)
+                // If both subscriptions are primary, show both
+                if (!info1.isOpportunistic && !info2.isOpportunistic) {
+                    return@combine unfilteredSubs
+                }
+
+                // NOTE: at this point, we are now returning a single SubscriptionInfo
+
+                // If carrier required, always show the icon of the primary subscription.
+                // Otherwise, show whichever subscription is currently active for internet.
+                if (carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault) {
+                    // return the non-opportunistic info
+                    return@combine if (info1.isOpportunistic) listOf(info2) else listOf(info1)
                 } else {
-                    listOf(info2)
+                    return@combine if (info1.subscriptionId == activeId) {
+                        listOf(info1)
+                    } else {
+                        listOf(info2)
+                    }
                 }
             }
-        }
+            .distinctUntilChanged()
+            .logDiffsForTable(
+                tableLogger,
+                LOGGING_PREFIX,
+                columnName = "filteredSubscriptions",
+                initialValue = listOf(),
+            )
+            .stateIn(scope, SharingStarted.WhileSubscribed(), listOf())
 
     override val defaultDataSubId = mobileConnectionsRepo.defaultDataSubId
 
@@ -176,7 +190,7 @@
      * validated bit from the old active network (A) while data is changing to the new one (B).
      *
      * This condition only applies if
-     * 1. A and B are in the same subscription group (e.c. for CBRS data switching) and
+     * 1. A and B are in the same subscription group (e.g. for CBRS data switching) and
      * 2. A was validated before the switch
      *
      * The goal of this is to minimize the flickering in the UI of the cellular indicator
@@ -189,6 +203,12 @@
                 delay(2000)
                 emit(false)
             }
+            .logDiffsForTable(
+                tableLogger,
+                LOGGING_PREFIX,
+                columnName = "forcingValidation",
+                initialValue = false,
+            )
             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
 
     override val defaultMobileNetworkConnectivity: StateFlow<MobileConnectivityModel> =
@@ -205,6 +225,12 @@
                     networkConnectivity
                 }
             }
+            .distinctUntilChanged()
+            .logDiffsForTable(
+                tableLogger,
+                columnPrefix = "$LOGGING_PREFIX.defaultConnection",
+                initialValue = mobileConnectionsRepo.defaultMobileNetworkConnectivity.value,
+            )
             .stateIn(
                 scope,
                 SharingStarted.WhileSubscribed(),
@@ -253,10 +279,21 @@
                     !connectivityModel.isValidated
                 }
             }
+            .logDiffsForTable(
+                tableLogger,
+                LOGGING_PREFIX,
+                columnName = "isDefaultConnectionFailed",
+                initialValue = false,
+            )
             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
 
     override val isUserSetup: StateFlow<Boolean> = userSetupRepo.isUserSetupFlow
 
+    override val isForceHidden: Flow<Boolean> =
+        connectivityRepository.forceHiddenSlots
+            .map { it.contains(ConnectivitySlot.MOBILE) }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
     /** Vends out new [MobileIconInteractor] for a particular subId */
     override fun createMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor =
         MobileIconInteractorImpl(
@@ -269,6 +306,11 @@
             defaultMobileIconGroup,
             defaultDataSubId,
             isDefaultConnectionFailed,
+            isForceHidden,
             mobileConnectionsRepo.getRepoForSubId(subId),
         )
+
+    companion object {
+        private const val LOGGING_PREFIX = "Intr"
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/shared/MobileInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/shared/MobileInputLogger.kt
new file mode 100644
index 0000000..3cbd2b7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/shared/MobileInputLogger.kt
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2022 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.statusbar.pipeline.mobile.shared
+
+import android.net.Network
+import android.net.NetworkCapabilities
+import android.telephony.ServiceState
+import android.telephony.SignalStrength
+import android.telephony.TelephonyDisplayInfo
+import com.android.settingslib.SignalIcon
+import com.android.settingslib.mobile.MobileMappings
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel
+import com.android.systemui.statusbar.pipeline.dagger.MobileInputLog
+import com.android.systemui.statusbar.pipeline.shared.LoggerHelper
+import javax.inject.Inject
+
+/** Logs for inputs into the mobile pipeline. */
+@SysUISingleton
+class MobileInputLogger
+@Inject
+constructor(
+    @MobileInputLog private val buffer: LogBuffer,
+) {
+    fun logOnCapabilitiesChanged(
+        network: Network,
+        networkCapabilities: NetworkCapabilities,
+        isDefaultNetworkCallback: Boolean,
+    ) {
+        LoggerHelper.logOnCapabilitiesChanged(
+            buffer,
+            TAG,
+            network,
+            networkCapabilities,
+            isDefaultNetworkCallback,
+        )
+    }
+
+    fun logOnLost(network: Network) {
+        LoggerHelper.logOnLost(buffer, TAG, network)
+    }
+
+    fun logOnServiceStateChanged(serviceState: ServiceState, subId: Int) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            {
+                int1 = subId
+                bool1 = serviceState.isEmergencyOnly
+                bool2 = serviceState.roaming
+                str1 = serviceState.operatorAlphaShort
+            },
+            {
+                "onServiceStateChanged: subId=$int1 emergencyOnly=$bool1 roaming=$bool2" +
+                    " operator=$str1"
+            }
+        )
+    }
+
+    fun logOnSignalStrengthsChanged(signalStrength: SignalStrength, subId: Int) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            {
+                int1 = subId
+                str1 = signalStrength.toString()
+            },
+            { "onSignalStrengthsChanged: subId=$int1 strengths=$str1" }
+        )
+    }
+
+    fun logOnDataConnectionStateChanged(dataState: Int, networkType: Int, subId: Int) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            {
+                int1 = subId
+                int2 = dataState
+                str1 = networkType.toString()
+            },
+            { "onDataConnectionStateChanged: subId=$int1 dataState=$int2 networkType=$str1" },
+        )
+    }
+
+    fun logOnDataActivity(direction: Int, subId: Int) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            {
+                int1 = subId
+                int2 = direction
+            },
+            { "onDataActivity: subId=$int1 direction=$int2" },
+        )
+    }
+
+    fun logOnCarrierNetworkChange(active: Boolean, subId: Int) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            {
+                int1 = subId
+                bool1 = active
+            },
+            { "onCarrierNetworkChange: subId=$int1 active=$bool1" },
+        )
+    }
+
+    fun logOnDisplayInfoChanged(displayInfo: TelephonyDisplayInfo, subId: Int) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            {
+                int1 = subId
+                str1 = displayInfo.toString()
+            },
+            { "onDisplayInfoChanged: subId=$int1 displayInfo=$str1" },
+        )
+    }
+
+    fun logUiAdapterSubIdsUpdated(subs: List<Int>) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            { str1 = subs.toString() },
+            { "Sub IDs in MobileUiAdapter updated internally: $str1" },
+        )
+    }
+
+    fun logUiAdapterSubIdsSentToIconController(subs: List<Int>) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            { str1 = subs.toString() },
+            { "Sub IDs in MobileUiAdapter being sent to icon controller: $str1" },
+        )
+    }
+
+    fun logCarrierConfigChanged(subId: Int) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            { int1 = subId },
+            { "onCarrierConfigChanged: subId=$int1" },
+        )
+    }
+
+    fun logOnDataEnabledChanged(enabled: Boolean, subId: Int) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            {
+                int1 = subId
+                bool1 = enabled
+            },
+            { "onDataEnabledChanged: subId=$int1 enabled=$bool1" },
+        )
+    }
+
+    fun logActionCarrierConfigChanged() {
+        buffer.log(TAG, LogLevel.INFO, {}, { "Intent received: ACTION_CARRIER_CONFIG_CHANGED" })
+    }
+
+    fun logDefaultDataSubRatConfig(config: MobileMappings.Config) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            { str1 = config.toString() },
+            { "defaultDataSubRatConfig: $str1" }
+        )
+    }
+
+    fun logDefaultMobileIconMapping(mapping: Map<String, SignalIcon.MobileIconGroup>) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            { str1 = mapping.toString() },
+            { "defaultMobileIconMapping: $str1" }
+        )
+    }
+
+    fun logDefaultMobileIconGroup(group: SignalIcon.MobileIconGroup) {
+        buffer.log(TAG, LogLevel.INFO, { str1 = group.name }, { "defaultMobileIconGroup: $str1" })
+    }
+}
+
+private const val TAG = "MobileInputLog"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt
index 829a5ca..da63ab1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt
@@ -22,7 +22,9 @@
 import com.android.systemui.statusbar.phone.StatusBarIconController
 import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
+import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
+import java.io.PrintWriter
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -30,7 +32,9 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
 
@@ -51,13 +55,17 @@
     interactor: MobileIconsInteractor,
     private val iconController: StatusBarIconController,
     private val iconsViewModelFactory: MobileIconsViewModel.Factory,
+    private val logger: MobileInputLogger,
     @Application private val scope: CoroutineScope,
     private val statusBarPipelineFlags: StatusBarPipelineFlags,
 ) : CoreStartable {
     private val mobileSubIds: Flow<List<Int>> =
-        interactor.filteredSubscriptions.mapLatest { subscriptions ->
-            subscriptions.map { subscriptionModel -> subscriptionModel.subscriptionId }
-        }
+        interactor.filteredSubscriptions
+            .mapLatest { subscriptions ->
+                subscriptions.map { subscriptionModel -> subscriptionModel.subscriptionId }
+            }
+            .distinctUntilChanged()
+            .onEach { logger.logUiAdapterSubIdsUpdated(it) }
 
     /**
      * We expose the list of tracked subscriptions as a flow of a list of ints, where each int is
@@ -72,6 +80,9 @@
     /** In order to keep the logs tame, we will reuse the same top-level mobile icons view model */
     val mobileIconsViewModel = iconsViewModelFactory.create(mobileSubIdsState)
 
+    private var isCollecting: Boolean = false
+    private var lastValue: List<Int>? = null
+
     override fun start() {
         // Only notify the icon controller if we want to *render* the new icons.
         // Note that this flow may still run if
@@ -79,8 +90,18 @@
         // get the logging data without rendering.
         if (statusBarPipelineFlags.useNewMobileIcons()) {
             scope.launch {
-                mobileSubIds.collectLatest { iconController.setNewMobileIconSubIds(it) }
+                isCollecting = true
+                mobileSubIds.collectLatest {
+                    logger.logUiAdapterSubIdsSentToIconController(it)
+                    lastValue = it
+                    iconController.setNewMobileIconSubIds(it)
+                }
             }
         }
     }
+
+    override fun dump(pw: PrintWriter, args: Array<out String>) {
+        pw.println("isCollecting=$isCollecting")
+        pw.println("Last values sent to icon controller: $lastValue")
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
index 3e81c7c..db585e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
@@ -29,6 +29,7 @@
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.settingslib.graph.SignalDrawable
 import com.android.systemui.R
+import com.android.systemui.common.ui.binder.ContentDescriptionViewBinder
 import com.android.systemui.common.ui.binder.IconViewBinder
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.statusbar.StatusBarIconView
@@ -90,10 +91,23 @@
                     }
                 }
 
+                launch { viewModel.isVisible.collect { isVisible -> view.isVisible = isVisible } }
+
                 // Set the icon for the triangle
                 launch {
-                    viewModel.iconId.distinctUntilChanged().collect { iconId ->
-                        mobileDrawable.level = iconId
+                    viewModel.icon.distinctUntilChanged().collect { icon ->
+                        mobileDrawable.level =
+                            SignalDrawable.getState(
+                                icon.level,
+                                icon.numberOfLevels,
+                                icon.showExclamationMark,
+                            )
+                    }
+                }
+
+                launch {
+                    viewModel.contentDescription.distinctUntilChanged().collect {
+                        ContentDescriptionViewBinder.bind(it, view)
                     }
                 }
 
@@ -141,8 +155,7 @@
 
         return object : ModernStatusBarViewBinding {
             override fun getShouldIconBeVisible(): Boolean {
-                // If this view model exists, then the icon should be visible.
-                return true
+                return viewModel.isVisible.value
             }
 
             override fun onVisibilityStateChanged(@StatusBarIconView.VisibleState state: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModel.kt
new file mode 100644
index 0000000..16e1766
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModel.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.statusbar.pipeline.mobile.ui.model
+
+import com.android.systemui.log.table.Diffable
+import com.android.systemui.log.table.TableRowLogger
+
+/** A model that will be consumed by [SignalDrawable] to show the mobile triangle icon. */
+data class SignalIconModel(
+    val level: Int,
+    val numberOfLevels: Int,
+    val showExclamationMark: Boolean,
+) : Diffable<SignalIconModel> {
+    // TODO(b/267767715): Can we implement [logDiffs] and [logFull] generically for data classes?
+    override fun logDiffs(prevVal: SignalIconModel, row: TableRowLogger) {
+        if (prevVal.level != level) {
+            row.logChange(COL_LEVEL, level)
+        }
+        if (prevVal.numberOfLevels != numberOfLevels) {
+            row.logChange(COL_NUM_LEVELS, numberOfLevels)
+        }
+        if (prevVal.showExclamationMark != showExclamationMark) {
+            row.logChange(COL_SHOW_EXCLAMATION, showExclamationMark)
+        }
+    }
+
+    override fun logFull(row: TableRowLogger) {
+        row.logChange(COL_LEVEL, level)
+        row.logChange(COL_NUM_LEVELS, numberOfLevels)
+        row.logChange(COL_SHOW_EXCLAMATION, showExclamationMark)
+    }
+
+    companion object {
+        /** Creates a [SignalIconModel] representing an empty and invalidated state. */
+        fun createEmptyState(numberOfLevels: Int) =
+            SignalIconModel(level = 0, numberOfLevels, showExclamationMark = true)
+
+        private const val COL_LEVEL = "level"
+        private const val COL_NUM_LEVELS = "numLevels"
+        private const val COL_SHOW_EXCLAMATION = "showExclamation"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
index 5e935616..0496278 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
@@ -16,14 +16,17 @@
 
 package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
 
+import com.android.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH
+import com.android.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH_NONE
 import com.android.settingslib.graph.SignalDrawable
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractor
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
+import com.android.systemui.statusbar.pipeline.mobile.ui.model.SignalIconModel
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -35,14 +38,15 @@
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.mapLatest
-import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.stateIn
 
 /** Common interface for all of the location-based mobile icon view models. */
 interface MobileIconViewModelCommon {
     val subscriptionId: Int
-    /** An int consumable by [SignalDrawable] for display */
-    val iconId: Flow<Int>
+    /** True if this view should be visible at all. */
+    val isVisible: StateFlow<Boolean>
+    val icon: Flow<SignalIconModel>
+    val contentDescription: Flow<ContentDescription>
     val roaming: Flow<Boolean>
     /** The RAT icon (LTE, 3G, 5G, etc) to be displayed. Null if we shouldn't show anything */
     val networkTypeIcon: Flow<Icon?>
@@ -70,7 +74,7 @@
 constructor(
     override val subscriptionId: Int,
     iconInteractor: MobileIconInteractor,
-    logger: ConnectivityPipelineLogger,
+    airplaneModeInteractor: AirplaneModeInteractor,
     constants: ConnectivityConstants,
     scope: CoroutineScope,
 ) : MobileIconViewModelCommon {
@@ -78,8 +82,28 @@
     private val showExclamationMark: Flow<Boolean> =
         iconInteractor.isDefaultDataEnabled.mapLatest { !it }
 
-    override val iconId: Flow<Int> = run {
-        val initial = SignalDrawable.getEmptyState(iconInteractor.numberOfLevels.value)
+    override val isVisible: StateFlow<Boolean> =
+        if (!constants.hasDataCapabilities) {
+                flowOf(false)
+            } else {
+                combine(
+                    airplaneModeInteractor.isAirplaneMode,
+                    iconInteractor.isForceHidden,
+                ) { isAirplaneMode, isForceHidden ->
+                    !isAirplaneMode && !isForceHidden
+                }
+            }
+            .distinctUntilChanged()
+            .logDiffsForTable(
+                iconInteractor.tableLogBuffer,
+                columnPrefix = "",
+                columnName = "visible",
+                initialValue = false,
+            )
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
+    override val icon: Flow<SignalIconModel> = run {
+        val initial = SignalIconModel.createEmptyState(iconInteractor.numberOfLevels.value)
         combine(
                 iconInteractor.level,
                 iconInteractor.numberOfLevels,
@@ -87,31 +111,55 @@
                 iconInteractor.isInService,
             ) { level, numberOfLevels, showExclamationMark, isInService ->
                 if (!isInService) {
-                    SignalDrawable.getEmptyState(numberOfLevels)
+                    SignalIconModel.createEmptyState(numberOfLevels)
                 } else {
-                    SignalDrawable.getState(level, numberOfLevels, showExclamationMark)
+                    SignalIconModel(level, numberOfLevels, showExclamationMark)
                 }
             }
             .distinctUntilChanged()
             .logDiffsForTable(
                 iconInteractor.tableLogBuffer,
-                columnPrefix = "",
-                columnName = "iconId",
+                columnPrefix = "icon",
                 initialValue = initial,
             )
             .stateIn(scope, SharingStarted.WhileSubscribed(), initial)
     }
 
+    override val contentDescription: Flow<ContentDescription> = run {
+        val initial = ContentDescription.Resource(PHONE_SIGNAL_STRENGTH_NONE)
+        combine(
+                iconInteractor.level,
+                iconInteractor.isInService,
+            ) { level, isInService ->
+                val resId =
+                    when {
+                        isInService -> PHONE_SIGNAL_STRENGTH[level]
+                        else -> PHONE_SIGNAL_STRENGTH_NONE
+                    }
+                ContentDescription.Resource(resId)
+            }
+            .distinctUntilChanged()
+            .stateIn(scope, SharingStarted.WhileSubscribed(), initial)
+    }
+
     private val showNetworkTypeIcon: Flow<Boolean> =
         combine(
-            iconInteractor.isDataConnected,
-            iconInteractor.isDataEnabled,
-            iconInteractor.isDefaultConnectionFailed,
-            iconInteractor.alwaysShowDataRatIcon,
-            iconInteractor.isConnected,
-        ) { dataConnected, dataEnabled, failedConnection, alwaysShow, connected ->
-            alwaysShow || (dataConnected && dataEnabled && !failedConnection && connected)
-        }
+                iconInteractor.isDataConnected,
+                iconInteractor.isDataEnabled,
+                iconInteractor.isDefaultConnectionFailed,
+                iconInteractor.alwaysShowDataRatIcon,
+                iconInteractor.isConnected,
+            ) { dataConnected, dataEnabled, failedConnection, alwaysShow, connected ->
+                alwaysShow || (dataConnected && dataEnabled && !failedConnection && connected)
+            }
+            .distinctUntilChanged()
+            .logDiffsForTable(
+                iconInteractor.tableLogBuffer,
+                columnPrefix = "",
+                columnName = "showNetworkTypeIcon",
+                initialValue = false,
+            )
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
 
     override val networkTypeIcon: Flow<Icon?> =
         combine(
@@ -129,14 +177,6 @@
                 }
             }
             .distinctUntilChanged()
-            .onEach {
-                // This is done as an onEach side effect since Icon is not Diffable (yet)
-                iconInteractor.tableLogBuffer.logChange(
-                    prefix = "",
-                    columnName = "networkTypeIcon",
-                    value = it.toString(),
-                )
-            }
             .stateIn(scope, SharingStarted.WhileSubscribed(), null)
 
     override val roaming: StateFlow<Boolean> =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
index 24370d2..8cb52af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
@@ -17,13 +17,14 @@
 package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
 
 import androidx.annotation.VisibleForTesting
+import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.statusbar.phone.StatusBarLocation
 import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
+import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
 import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.StateFlow
@@ -39,7 +40,7 @@
 constructor(
     val subscriptionIdsFlow: StateFlow<List<Int>>,
     private val interactor: MobileIconsInteractor,
-    private val logger: ConnectivityPipelineLogger,
+    private val airplaneModeInteractor: AirplaneModeInteractor,
     private val constants: ConnectivityConstants,
     @Application private val scope: CoroutineScope,
     private val statusBarPipelineFlags: StatusBarPipelineFlags,
@@ -56,7 +57,7 @@
                 ?: MobileIconViewModel(
                         subId,
                         interactor.createMobileConnectionInteractorForSubId(subId),
-                        logger,
+                        airplaneModeInteractor,
                         constants,
                         scope,
                     )
@@ -74,11 +75,12 @@
         subIdsToRemove.forEach { mobileIconSubIdCache.remove(it) }
     }
 
+    @SysUISingleton
     class Factory
     @Inject
     constructor(
         private val interactor: MobileIconsInteractor,
-        private val logger: ConnectivityPipelineLogger,
+        private val airplaneModeInteractor: AirplaneModeInteractor,
         private val constants: ConnectivityConstants,
         @Application private val scope: CoroutineScope,
         private val statusBarPipelineFlags: StatusBarPipelineFlags,
@@ -87,7 +89,7 @@
             return MobileIconsViewModel(
                 subscriptionIdsFlow,
                 interactor,
-                logger,
+                airplaneModeInteractor,
                 constants,
                 scope,
                 statusBarPipelineFlags,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityConstants.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityConstants.kt
index 0c9b86c..0fe5329 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityConstants.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityConstants.kt
@@ -22,7 +22,6 @@
 import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.SB_LOGGING_TAG
 import java.io.PrintWriter
 import javax.inject.Inject
 
@@ -40,7 +39,7 @@
     telephonyManager: TelephonyManager,
 ) : Dumpable {
     init {
-        dumpManager.registerNormalDumpable("${SB_LOGGING_TAG}Constants", this)
+        dumpManager.registerNormalDumpable("ConnectivityConstants", this)
     }
 
     /** True if this device has the capability for data connections and false otherwise. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityInputLogger.kt
new file mode 100644
index 0000000..95548b8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityInputLogger.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.statusbar.pipeline.shared
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel
+import com.android.systemui.statusbar.pipeline.dagger.SharedConnectivityInputLog
+import javax.inject.Inject
+
+/** Logs for connectivity-related inputs that are shared across wifi, mobile, etc. */
+@SysUISingleton
+class ConnectivityInputLogger
+@Inject
+constructor(
+    @SharedConnectivityInputLog private val buffer: LogBuffer,
+) {
+    fun logTuningChanged(tuningList: String?) {
+        buffer.log(TAG, LogLevel.DEBUG, { str1 = tuningList }, { "onTuningChanged: $str1" })
+    }
+}
+
+private const val TAG = "ConnectivityInputLogger"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt
deleted file mode 100644
index d3ff357..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * Copyright (C) 2022 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.statusbar.pipeline.shared
-
-import android.net.Network
-import android.net.NetworkCapabilities
-import android.telephony.ServiceState
-import android.telephony.SignalStrength
-import android.telephony.TelephonyDisplayInfo
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.log.dagger.StatusBarConnectivityLog
-import com.android.systemui.plugins.log.LogBuffer
-import com.android.systemui.plugins.log.LogLevel
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.toString
-import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.onEach
-
-@SysUISingleton
-class ConnectivityPipelineLogger
-@Inject
-constructor(
-    @StatusBarConnectivityLog private val buffer: LogBuffer,
-) {
-    /**
-     * Logs a change in one of the **raw inputs** to the connectivity pipeline.
-     *
-     * Use this method for inputs that don't have any extra information besides their callback name.
-     */
-    fun logInputChange(callbackName: String) {
-        buffer.log(SB_LOGGING_TAG, LogLevel.INFO, { str1 = callbackName }, { "Input: $str1" })
-    }
-
-    /** Logs a change in one of the **raw inputs** to the connectivity pipeline. */
-    fun logInputChange(callbackName: String, changeInfo: String?) {
-        buffer.log(
-            SB_LOGGING_TAG,
-            LogLevel.INFO,
-            {
-                str1 = callbackName
-                str2 = changeInfo
-            },
-            { "Input: $str1: $str2" }
-        )
-    }
-
-    /** Logs a **data transformation** that we performed within the connectivity pipeline. */
-    fun logTransformation(transformationName: String, oldValue: Any?, newValue: Any?) {
-        if (oldValue == newValue) {
-            buffer.log(
-                SB_LOGGING_TAG,
-                LogLevel.INFO,
-                {
-                    str1 = transformationName
-                    str2 = oldValue.toString()
-                },
-                { "Transform: $str1: $str2 (transformation didn't change it)" }
-            )
-        } else {
-            buffer.log(
-                SB_LOGGING_TAG,
-                LogLevel.INFO,
-                {
-                    str1 = transformationName
-                    str2 = oldValue.toString()
-                    str3 = newValue.toString()
-                },
-                { "Transform: $str1: $str2 -> $str3" }
-            )
-        }
-    }
-
-    /** Logs a change in one of the **outputs** to the connectivity pipeline. */
-    fun logOutputChange(outputParamName: String, changeInfo: String) {
-        buffer.log(
-            SB_LOGGING_TAG,
-            LogLevel.INFO,
-            {
-                str1 = outputParamName
-                str2 = changeInfo
-            },
-            { "Output: $str1: $str2" }
-        )
-    }
-
-    fun logOnCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
-        buffer.log(
-            SB_LOGGING_TAG,
-            LogLevel.INFO,
-            {
-                int1 = network.getNetId()
-                str1 = networkCapabilities.toString()
-            },
-            { "onCapabilitiesChanged: net=$int1 capabilities=$str1" }
-        )
-    }
-
-    fun logOnLost(network: Network) {
-        buffer.log(
-            SB_LOGGING_TAG,
-            LogLevel.INFO,
-            { int1 = network.getNetId() },
-            { "onLost: net=$int1" }
-        )
-    }
-
-    fun logOnServiceStateChanged(serviceState: ServiceState, subId: Int) {
-        buffer.log(
-            SB_LOGGING_TAG,
-            LogLevel.INFO,
-            {
-                int1 = subId
-                bool1 = serviceState.isEmergencyOnly
-                bool2 = serviceState.roaming
-                str1 = serviceState.operatorAlphaShort
-            },
-            {
-                "onServiceStateChanged: subId=$int1 emergencyOnly=$bool1 roaming=$bool2" +
-                    " operator=$str1"
-            }
-        )
-    }
-
-    fun logOnSignalStrengthsChanged(signalStrength: SignalStrength, subId: Int) {
-        buffer.log(
-            SB_LOGGING_TAG,
-            LogLevel.INFO,
-            {
-                int1 = subId
-                str1 = signalStrength.toString()
-            },
-            { "onSignalStrengthsChanged: subId=$int1 strengths=$str1" }
-        )
-    }
-
-    fun logOnDataConnectionStateChanged(dataState: Int, networkType: Int, subId: Int) {
-        buffer.log(
-            SB_LOGGING_TAG,
-            LogLevel.INFO,
-            {
-                int1 = subId
-                int2 = dataState
-                str1 = networkType.toString()
-            },
-            { "onDataConnectionStateChanged: subId=$int1 dataState=$int2 networkType=$str1" },
-        )
-    }
-
-    fun logOnDataActivity(direction: Int, subId: Int) {
-        buffer.log(
-            SB_LOGGING_TAG,
-            LogLevel.INFO,
-            {
-                int1 = subId
-                int2 = direction
-            },
-            { "onDataActivity: subId=$int1 direction=$int2" },
-        )
-    }
-
-    fun logOnCarrierNetworkChange(active: Boolean, subId: Int) {
-        buffer.log(
-            SB_LOGGING_TAG,
-            LogLevel.INFO,
-            {
-                int1 = subId
-                bool1 = active
-            },
-            { "onCarrierNetworkChange: subId=$int1 active=$bool1" },
-        )
-    }
-
-    fun logOnDisplayInfoChanged(displayInfo: TelephonyDisplayInfo, subId: Int) {
-        buffer.log(
-            SB_LOGGING_TAG,
-            LogLevel.INFO,
-            {
-                int1 = subId
-                str1 = displayInfo.toString()
-            },
-            { "onDisplayInfoChanged: subId=$int1 displayInfo=$str1" },
-        )
-    }
-
-    companion object {
-        const val SB_LOGGING_TAG = "SbConnectivity"
-
-        /** Log a change in one of the **inputs** to the connectivity pipeline. */
-        fun Flow<Unit>.logInputChange(
-            logger: ConnectivityPipelineLogger,
-            inputParamName: String,
-        ): Flow<Unit> {
-            return this.onEach { logger.logInputChange(inputParamName) }
-        }
-
-        /**
-         * Log a change in one of the **inputs** to the connectivity pipeline.
-         *
-         * @param prettyPrint an optional function to transform the value into a readable string.
-         * [toString] is used if no custom function is provided.
-         */
-        fun <T> Flow<T>.logInputChange(
-            logger: ConnectivityPipelineLogger,
-            inputParamName: String,
-            prettyPrint: (T) -> String = { it.toString() }
-        ): Flow<T> {
-            return this.onEach { logger.logInputChange(inputParamName, prettyPrint(it)) }
-        }
-
-        /**
-         * Log a change in one of the **outputs** to the connectivity pipeline.
-         *
-         * @param prettyPrint an optional function to transform the value into a readable string.
-         * [toString] is used if no custom function is provided.
-         */
-        fun <T> Flow<T>.logOutputChange(
-            logger: ConnectivityPipelineLogger,
-            outputParamName: String,
-            prettyPrint: (T) -> String = { it.toString() }
-        ): Flow<T> {
-            return this.onEach { logger.logOutputChange(outputParamName, prettyPrint(it)) }
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/LoggerHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/LoggerHelper.kt
new file mode 100644
index 0000000..6f29e33
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/LoggerHelper.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.statusbar.pipeline.shared
+
+import android.net.Network
+import android.net.NetworkCapabilities
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel
+
+/** Helper object for logs that are shared between wifi and mobile. */
+object LoggerHelper {
+    fun logOnCapabilitiesChanged(
+        buffer: LogBuffer,
+        tag: String,
+        network: Network,
+        networkCapabilities: NetworkCapabilities,
+        isDefaultNetworkCallback: Boolean,
+    ) {
+        buffer.log(
+            tag,
+            LogLevel.INFO,
+            {
+                bool1 = isDefaultNetworkCallback
+                int1 = network.getNetId()
+                str1 = networkCapabilities.toString()
+            },
+            { "onCapabilitiesChanged[default=$bool1]: net=$int1 capabilities=$str1" }
+        )
+    }
+
+    fun logOnLost(buffer: LogBuffer, tag: String, network: Network) {
+        buffer.log(tag, LogLevel.INFO, { int1 = network.getNetId() }, { "onLost: net=$int1" })
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt
index 45c6d46..5d9ba018 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt
@@ -26,8 +26,7 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.statusbar.phone.StatusBarIconController
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.SB_LOGGING_TAG
+import com.android.systemui.statusbar.pipeline.shared.ConnectivityInputLogger
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlots
 import com.android.systemui.tuner.TunerService
@@ -45,59 +44,61 @@
  * types of connectivity (wifi, mobile, ethernet, etc.)
  */
 interface ConnectivityRepository {
-    /**
-     * Observable for the current set of connectivity icons that should be force-hidden.
-     */
+    /** Observable for the current set of connectivity icons that should be force-hidden. */
     val forceHiddenSlots: StateFlow<Set<ConnectivitySlot>>
 }
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
-class ConnectivityRepositoryImpl @Inject constructor(
+class ConnectivityRepositoryImpl
+@Inject
+constructor(
     private val connectivitySlots: ConnectivitySlots,
     context: Context,
     dumpManager: DumpManager,
-    logger: ConnectivityPipelineLogger,
+    logger: ConnectivityInputLogger,
     @Application scope: CoroutineScope,
     tunerService: TunerService,
 ) : ConnectivityRepository, Dumpable {
     init {
-        dumpManager.registerDumpable("${SB_LOGGING_TAG}Repository", this)
+        dumpManager.registerDumpable("ConnectivityRepository", this)
     }
 
     // The default set of hidden icons to use if we don't get any from [TunerService].
     private val defaultHiddenIcons: Set<ConnectivitySlot> =
-            context.resources.getStringArray(DEFAULT_HIDDEN_ICONS_RESOURCE)
-                .asList()
-                .toSlotSet(connectivitySlots)
+        context.resources
+            .getStringArray(DEFAULT_HIDDEN_ICONS_RESOURCE)
+            .asList()
+            .toSlotSet(connectivitySlots)
 
-    override val forceHiddenSlots: StateFlow<Set<ConnectivitySlot>> = conflatedCallbackFlow {
-        val callback = object : TunerService.Tunable {
-            override fun onTuningChanged(key: String, newHideList: String?) {
-                if (key != HIDDEN_ICONS_TUNABLE_KEY) {
-                    return
-                }
-                logger.logInputChange("onTuningChanged", newHideList)
+    override val forceHiddenSlots: StateFlow<Set<ConnectivitySlot>> =
+        conflatedCallbackFlow {
+                val callback =
+                    object : TunerService.Tunable {
+                        override fun onTuningChanged(key: String, newHideList: String?) {
+                            if (key != HIDDEN_ICONS_TUNABLE_KEY) {
+                                return
+                            }
+                            logger.logTuningChanged(newHideList)
 
-                val outputList = newHideList?.split(",")?.toSlotSet(connectivitySlots)
-                    ?: defaultHiddenIcons
-                trySend(outputList)
+                            val outputList =
+                                newHideList?.split(",")?.toSlotSet(connectivitySlots)
+                                    ?: defaultHiddenIcons
+                            trySend(outputList)
+                        }
+                    }
+                tunerService.addTunable(callback, HIDDEN_ICONS_TUNABLE_KEY)
+
+                awaitClose { tunerService.removeTunable(callback) }
             }
-        }
-        tunerService.addTunable(callback, HIDDEN_ICONS_TUNABLE_KEY)
-
-        awaitClose { tunerService.removeTunable(callback) }
-    }
-        .stateIn(
-            scope,
-            started = SharingStarted.WhileSubscribed(),
-            initialValue = defaultHiddenIcons
-        )
+            .stateIn(
+                scope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = defaultHiddenIcons
+            )
 
     override fun dump(pw: PrintWriter, args: Array<out String>) {
-        pw.apply {
-            println("defaultHiddenIcons=$defaultHiddenIcons")
-        }
+        pw.apply { println("defaultHiddenIcons=$defaultHiddenIcons") }
     }
 
     companion object {
@@ -111,8 +112,7 @@
         private fun List<String>.toSlotSet(
             connectivitySlots: ConnectivitySlots
         ): Set<ConnectivitySlot> {
-            return this
-                .filter { it.isNotBlank() }
+            return this.filter { it.isNotBlank() }
                 .mapNotNull { connectivitySlots.getSlotFromName(it) }
                 .toSet()
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt
index cc0ec54..b1e2812 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt
@@ -77,6 +77,17 @@
         return binding.getShouldIconBeVisible()
     }
 
+    /** See [StatusBarIconView.getDrawingRect]. */
+    override fun getDrawingRect(outRect: Rect) {
+        super.getDrawingRect(outRect)
+        val translationX = translationX.toInt()
+        val translationY = translationY.toInt()
+        outRect.left += translationX
+        outRect.right += translationX
+        outRect.top += translationY
+        outRect.bottom += translationY
+    }
+
     /**
      * Initializes this view.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
index ac4d55c..08c14e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.statusbar.pipeline.wifi.data.repository
 
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import kotlinx.coroutines.flow.StateFlow
 
 /** Provides data related to the wifi state. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt
index 2cb81c8..e0e0ed7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt
@@ -23,9 +23,9 @@
 import com.android.systemui.demomode.DemoMode
 import com.android.systemui.demomode.DemoModeController
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoWifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoModeWifiDataSource.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoModeWifiDataSource.kt
index caac8fa..7d2501ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoModeWifiDataSource.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoModeWifiDataSource.kt
@@ -53,7 +53,7 @@
 
     private fun Bundle.activeWifiEvent(): FakeWifiEventModel.Wifi {
         val level = getString("level")?.toInt()
-        val activity = getString("activity")?.toActivity()
+        val activity = getString("activity").toActivity()
         val ssid = getString("ssid")
         val validated = getString("fully").toBoolean()
 
@@ -69,11 +69,12 @@
         val subId = getString("slot")?.toInt() ?: DEFAULT_CARRIER_MERGED_SUB_ID
         val level = getString("level")?.toInt() ?: 0
         val numberOfLevels = getString("numlevels")?.toInt() ?: DEFAULT_NUM_LEVELS
+        val activity = getString("activity").toActivity()
 
-        return FakeWifiEventModel.CarrierMerged(subId, level, numberOfLevels)
+        return FakeWifiEventModel.CarrierMerged(subId, level, numberOfLevels, activity)
     }
 
-    private fun String.toActivity(): Int =
+    private fun String?.toActivity(): Int =
         when (this) {
             "inout" -> WifiManager.TrafficStateCallback.DATA_ACTIVITY_INOUT
             "in" -> WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
index e161b3e..a4fbc2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
@@ -19,9 +19,9 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import com.android.systemui.statusbar.pipeline.shared.data.model.toWifiDataActivityModel
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
@@ -80,17 +80,14 @@
     private fun processEnabledWifiState(event: FakeWifiEventModel.Wifi) {
         _isWifiEnabled.value = true
         _isWifiDefault.value = true
-        _wifiActivity.value =
-            event.activity?.toWifiDataActivityModel()
-                ?: DataActivityModel(hasActivityIn = false, hasActivityOut = false)
+        _wifiActivity.value = event.activity.toWifiDataActivityModel()
         _wifiNetwork.value = event.toWifiNetworkModel()
     }
 
     private fun processCarrierMergedWifiState(event: FakeWifiEventModel.CarrierMerged) {
         _isWifiEnabled.value = true
         _isWifiDefault.value = true
-        // TODO(b/238425913): Support activity in demo mode.
-        _wifiActivity.value = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
+        _wifiActivity.value = event.activity.toWifiDataActivityModel()
         _wifiNetwork.value = event.toCarrierMergedModel()
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/model/FakeWifiEventModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/model/FakeWifiEventModel.kt
index 518f8ce..f5035cbc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/model/FakeWifiEventModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/model/FakeWifiEventModel.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model
 
+import android.telephony.Annotation
+
 /**
  * Model for demo wifi commands, ported from [NetworkControllerImpl]
  *
@@ -24,7 +26,7 @@
 sealed interface FakeWifiEventModel {
     data class Wifi(
         val level: Int?,
-        val activity: Int?,
+        @Annotation.DataActivityType val activity: Int,
         val ssid: String?,
         val validated: Boolean?,
     ) : FakeWifiEventModel
@@ -33,6 +35,7 @@
         val subscriptionId: Int,
         val level: Int,
         val numberOfLevels: Int,
+        @Annotation.DataActivityType val activity: Int,
     ) : FakeWifiEventModel
 
     object WifiDisabled : FakeWifiEventModel
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepository.kt
index 5d4a666..86a668a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepository.kt
@@ -18,8 +18,8 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.RealWifiRepository
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import javax.inject.Inject
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
index d26499c..ee58160 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
@@ -39,13 +39,12 @@
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.log.table.logDiffsForTable
 import com.android.systemui.statusbar.pipeline.dagger.WifiTableLog
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logInputChange
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import com.android.systemui.statusbar.pipeline.shared.data.model.toWifiDataActivityModel
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.RealWifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
+import com.android.systemui.statusbar.pipeline.wifi.shared.WifiInputLogger
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import java.util.concurrent.Executor
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -58,6 +57,7 @@
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.mapLatest
 import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.stateIn
 
 /** Real implementation of [WifiRepository]. */
@@ -70,7 +70,7 @@
 constructor(
     broadcastDispatcher: BroadcastDispatcher,
     connectivityManager: ConnectivityManager,
-    logger: ConnectivityPipelineLogger,
+    logger: WifiInputLogger,
     @WifiTableLog wifiTableLogBuffer: TableLogBuffer,
     @Main mainExecutor: Executor,
     @Application scope: CoroutineScope,
@@ -80,7 +80,7 @@
     private val wifiStateChangeEvents: Flow<Unit> =
         broadcastDispatcher
             .broadcastFlow(IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION))
-            .logInputChange(logger, "WIFI_STATE_CHANGED_ACTION intent")
+            .onEach { logger.logIntent("WIFI_STATE_CHANGED_ACTION") }
 
     private val wifiNetworkChangeEvents: MutableSharedFlow<Unit> =
         MutableSharedFlow(extraBufferCapacity = 1)
@@ -95,7 +95,7 @@
             .logDiffsForTable(
                 wifiTableLogBuffer,
                 columnPrefix = "",
-                columnName = "isWifiEnabled",
+                columnName = "isEnabled",
                 initialValue = wifiManager.isWifiEnabled,
             )
             .stateIn(
@@ -114,13 +114,17 @@
                             network: Network,
                             networkCapabilities: NetworkCapabilities
                         ) {
+                            logger.logOnCapabilitiesChanged(
+                                network,
+                                networkCapabilities,
+                                isDefaultNetworkCallback = true,
+                            )
+
                             // This method will always be called immediately after the network
                             // becomes the default, in addition to any time the capabilities change
                             // while the network is the default.
-                            // If this network contains valid wifi info, then wifi is the default
-                            // network.
-                            val wifiInfo = networkCapabilitiesToWifiInfo(networkCapabilities)
-                            trySend(wifiInfo != null)
+                            // If this network is a wifi network, then wifi is the default network.
+                            trySend(isWifiNetwork(networkCapabilities))
                         }
 
                         override fun onLost(network: Network) {
@@ -137,7 +141,7 @@
             .logDiffsForTable(
                 wifiTableLogBuffer,
                 columnPrefix = "",
-                columnName = "isWifiDefault",
+                columnName = "isDefault",
                 initialValue = false,
             )
             .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false)
@@ -152,7 +156,11 @@
                             network: Network,
                             networkCapabilities: NetworkCapabilities
                         ) {
-                            logger.logOnCapabilitiesChanged(network, networkCapabilities)
+                            logger.logOnCapabilitiesChanged(
+                                network,
+                                networkCapabilities,
+                                isDefaultNetworkCallback = false,
+                            )
 
                             wifiNetworkChangeEvents.tryEmit(Unit)
 
@@ -165,11 +173,6 @@
                                         networkCapabilities,
                                         wifiManager,
                                     )
-                                logger.logTransformation(
-                                    WIFI_NETWORK_CALLBACK_NAME,
-                                    oldValue = currentWifi,
-                                    newValue = wifiNetworkModel,
-                                )
                                 currentWifi = wifiNetworkModel
                                 trySend(wifiNetworkModel)
                             }
@@ -186,11 +189,6 @@
                                     wifi.networkId == network.getNetId()
                             ) {
                                 val newNetworkModel = WifiNetworkModel.Inactive
-                                logger.logTransformation(
-                                    WIFI_NETWORK_CALLBACK_NAME,
-                                    oldValue = wifi,
-                                    newValue = newNetworkModel,
-                                )
                                 currentWifi = newNetworkModel
                                 trySend(newNetworkModel)
                             }
@@ -204,7 +202,7 @@
             .distinctUntilChanged()
             .logDiffsForTable(
                 wifiTableLogBuffer,
-                columnPrefix = "wifiNetwork",
+                columnPrefix = "",
                 initialValue = WIFI_NETWORK_DEFAULT,
             )
             // There will be multiple wifi icons in different places that will frequently
@@ -220,7 +218,7 @@
     override val wifiActivity: StateFlow<DataActivityModel> =
         conflatedCallbackFlow {
                 val callback = TrafficStateCallback { state ->
-                    logger.logInputChange("onTrafficStateChange", prettyPrintActivity(state))
+                    logger.logActivity(prettyPrintActivity(state))
                     trySend(state.toWifiDataActivityModel())
                 }
                 wifiManager.registerTrafficStateCallback(mainExecutor, callback)
@@ -253,16 +251,30 @@
             networkCapabilities: NetworkCapabilities
         ): WifiInfo? {
             return when {
-                networkCapabilities.hasTransport(TRANSPORT_WIFI) ->
-                    networkCapabilities.transportInfo as WifiInfo?
                 networkCapabilities.hasTransport(TRANSPORT_CELLULAR) ->
                     // Sometimes, cellular networks can act as wifi networks (known as VCN --
                     // virtual carrier network). So, see if this cellular network has wifi info.
                     Utils.tryGetWifiInfoForVcn(networkCapabilities)
+                networkCapabilities.hasTransport(TRANSPORT_WIFI) ->
+                    if (networkCapabilities.transportInfo is WifiInfo) {
+                        networkCapabilities.transportInfo as WifiInfo
+                    } else {
+                        null
+                    }
                 else -> null
             }
         }
 
+        /** True if these capabilities represent a wifi network. */
+        private fun isWifiNetwork(networkCapabilities: NetworkCapabilities): Boolean {
+            return when {
+                networkCapabilities.hasTransport(TRANSPORT_WIFI) -> true
+                networkCapabilities.hasTransport(TRANSPORT_CELLULAR) ->
+                    Utils.tryGetWifiInfoForVcn(networkCapabilities) != null
+                else -> false
+            }
+        }
+
         private fun createWifiNetworkModel(
             wifiInfo: WifiInfo,
             network: Network,
@@ -314,8 +326,6 @@
                 .addTransportType(TRANSPORT_CELLULAR)
                 .build()
 
-        private const val WIFI_NETWORK_CALLBACK_NAME = "wifiNetworkModel"
-
         private const val CARRIER_MERGED_INVALID_SUB_ID_REASON =
             "Wifi network was carrier merged but had invalid sub ID"
     }
@@ -326,7 +336,7 @@
     constructor(
         private val broadcastDispatcher: BroadcastDispatcher,
         private val connectivityManager: ConnectivityManager,
-        private val logger: ConnectivityPipelineLogger,
+        private val logger: WifiInputLogger,
         @WifiTableLog private val wifiTableLogBuffer: TableLogBuffer,
         @Main private val mainExecutor: Executor,
         @Application private val scope: CoroutineScope,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
index 86dcd18..96ab074 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
@@ -21,8 +21,8 @@
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
@@ -58,25 +58,29 @@
 }
 
 @SysUISingleton
-class WifiInteractorImpl @Inject constructor(
+class WifiInteractorImpl
+@Inject
+constructor(
     connectivityRepository: ConnectivityRepository,
     wifiRepository: WifiRepository,
 ) : WifiInteractor {
 
-    override val ssid: Flow<String?> = wifiRepository.wifiNetwork.map { info ->
-        when (info) {
-            is WifiNetworkModel.Unavailable -> null
-            is WifiNetworkModel.Invalid -> null
-            is WifiNetworkModel.Inactive -> null
-            is WifiNetworkModel.CarrierMerged -> null
-            is WifiNetworkModel.Active -> when {
-                info.isPasspointAccessPoint || info.isOnlineSignUpForPasspointAccessPoint ->
-                    info.passpointProviderFriendlyName
-                info.ssid != WifiManager.UNKNOWN_SSID -> info.ssid
-                else -> null
+    override val ssid: Flow<String?> =
+        wifiRepository.wifiNetwork.map { info ->
+            when (info) {
+                is WifiNetworkModel.Unavailable -> null
+                is WifiNetworkModel.Invalid -> null
+                is WifiNetworkModel.Inactive -> null
+                is WifiNetworkModel.CarrierMerged -> null
+                is WifiNetworkModel.Active ->
+                    when {
+                        info.isPasspointAccessPoint || info.isOnlineSignUpForPasspointAccessPoint ->
+                            info.passpointProviderFriendlyName
+                        info.ssid != WifiManager.UNKNOWN_SSID -> info.ssid
+                        else -> null
+                    }
             }
         }
-    }
 
     override val isEnabled: Flow<Boolean> = wifiRepository.isWifiEnabled
 
@@ -86,7 +90,6 @@
 
     override val activity: StateFlow<DataActivityModel> = wifiRepository.wifiActivity
 
-    override val isForceHidden: Flow<Boolean> = connectivityRepository.forceHiddenSlots.map {
-        it.contains(ConnectivitySlot.WIFI)
-    }
+    override val isForceHidden: Flow<Boolean> =
+        connectivityRepository.forceHiddenSlots.map { it.contains(ConnectivitySlot.WIFI) }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiConstants.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiConstants.kt
index 4f7fe28..8bea772 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiConstants.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiConstants.kt
@@ -21,7 +21,6 @@
 import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.SB_LOGGING_TAG
 import java.io.PrintWriter
 import javax.inject.Inject
 
@@ -30,12 +29,14 @@
  * logging purposes.
  */
 @SysUISingleton
-class WifiConstants @Inject constructor(
-        context: Context,
-        dumpManager: DumpManager,
+class WifiConstants
+@Inject
+constructor(
+    context: Context,
+    dumpManager: DumpManager,
 ) : Dumpable {
     init {
-        dumpManager.registerDumpable("${SB_LOGGING_TAG}WifiConstants", this)
+        dumpManager.registerNormalDumpable("WifiConstants", this)
     }
 
     /** True if we should always show the wifi icon when wifi is enabled and false otherwise. */
@@ -43,8 +44,6 @@
         context.resources.getBoolean(R.bool.config_showWifiIndicatorWhenEnabled)
 
     override fun dump(pw: PrintWriter, args: Array<out String>) {
-        pw.apply {
-            println("alwaysShowIconIfEnabled=$alwaysShowIconIfEnabled")
-        }
+        pw.apply { println("alwaysShowIconIfEnabled=$alwaysShowIconIfEnabled") }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt
new file mode 100644
index 0000000..a32e475
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt
@@ -0,0 +1,64 @@
+/*
+ * 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.statusbar.pipeline.wifi.shared
+
+import android.net.Network
+import android.net.NetworkCapabilities
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel
+import com.android.systemui.statusbar.pipeline.dagger.WifiInputLog
+import com.android.systemui.statusbar.pipeline.shared.LoggerHelper
+import javax.inject.Inject
+
+/**
+ * Logger for all the wifi-related inputs (intents, callbacks, etc.) that the wifi repo receives.
+ */
+@SysUISingleton
+class WifiInputLogger
+@Inject
+constructor(
+    @WifiInputLog val buffer: LogBuffer,
+) {
+    fun logOnCapabilitiesChanged(
+        network: Network,
+        networkCapabilities: NetworkCapabilities,
+        isDefaultNetworkCallback: Boolean,
+    ) {
+        LoggerHelper.logOnCapabilitiesChanged(
+            buffer,
+            TAG,
+            network,
+            networkCapabilities,
+            isDefaultNetworkCallback,
+        )
+    }
+
+    fun logOnLost(network: Network) {
+        LoggerHelper.logOnLost(buffer, TAG, network)
+    }
+
+    fun logIntent(intentName: String) {
+        buffer.log(TAG, LogLevel.DEBUG, { str1 = intentName }, { "Intent received: $str1" })
+    }
+
+    fun logActivity(activity: String) {
+        buffer.log(TAG, LogLevel.DEBUG, { str1 = activity }, { "Activity: $str1" })
+    }
+}
+
+private const val TAG = "WifiInputLog"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModel.kt
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModel.kt
index da2daf2..0923d78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModel.kt
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.pipeline.wifi.data.model
+package com.android.systemui.statusbar.pipeline.wifi.shared.model
 
-import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
+import android.telephony.SubscriptionManager
 import androidx.annotation.VisibleForTesting
-import com.android.systemui.log.table.TableRowLogger
 import com.android.systemui.log.table.Diffable
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
+import com.android.systemui.log.table.TableRowLogger
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
 
 /** Provides information about the current wifi network. */
 sealed class WifiNetworkModel : Diffable<WifiNetworkModel> {
@@ -57,9 +57,7 @@
         }
     }
 
-    /**
-     * A model representing that the wifi information we received was invalid in some way.
-     */
+    /** A model representing that the wifi information we received was invalid in some way. */
     data class Invalid(
         /** A description of why the wifi information was invalid. */
         val invalidReason: String,
@@ -142,21 +140,17 @@
          */
         val subscriptionId: Int,
 
-        /**
-         * The signal level, guaranteed to be 0 <= level <= numberOfLevels.
-         */
+        /** The signal level, guaranteed to be 0 <= level <= numberOfLevels. */
         val level: Int,
 
-        /**
-         * The maximum possible level.
-         */
-        val numberOfLevels: Int = DEFAULT_NUM_LEVELS,
+        /** The maximum possible level. */
+        val numberOfLevels: Int = MobileConnectionRepository.DEFAULT_NUM_LEVELS,
     ) : WifiNetworkModel() {
         init {
             require(level in MIN_VALID_LEVEL..numberOfLevels) {
                 "0 <= wifi level <= $numberOfLevels required; level was $level"
             }
-            require(subscriptionId != INVALID_SUBSCRIPTION_ID) {
+            require(subscriptionId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
                 "subscription ID cannot be invalid"
             }
         }
@@ -208,9 +202,7 @@
         /** See [android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED]. */
         val isValidated: Boolean = false,
 
-        /**
-         * The wifi signal level, guaranteed to be 0 <= level <= 4.
-         */
+        /** The wifi signal level, guaranteed to be 0 <= level <= 4. */
         val level: Int,
 
         /** See [android.net.wifi.WifiInfo.ssid]. */
@@ -255,8 +247,10 @@
             if (prevVal.isPasspointAccessPoint != isPasspointAccessPoint) {
                 row.logChange(COL_PASSPOINT_ACCESS_POINT, isPasspointAccessPoint)
             }
-            if (prevVal.isOnlineSignUpForPasspointAccessPoint !=
-                isOnlineSignUpForPasspointAccessPoint) {
+            if (
+                prevVal.isOnlineSignUpForPasspointAccessPoint !=
+                    isOnlineSignUpForPasspointAccessPoint
+            ) {
                 row.logChange(COL_ONLINE_SIGN_UP, isOnlineSignUpForPasspointAccessPoint)
             }
             if (prevVal.passpointProviderFriendlyName != passpointProviderFriendlyName) {
@@ -281,29 +275,29 @@
             // Only include the passpoint-related values in the string if we have them. (Most
             // networks won't have them so they'll be mostly clutter.)
             val passpointString =
-                if (isPasspointAccessPoint ||
-                    isOnlineSignUpForPasspointAccessPoint ||
-                    passpointProviderFriendlyName != null) {
+                if (
+                    isPasspointAccessPoint ||
+                        isOnlineSignUpForPasspointAccessPoint ||
+                        passpointProviderFriendlyName != null
+                ) {
                     ", isPasspointAp=$isPasspointAccessPoint, " +
                         "isOnlineSignUpForPasspointAp=$isOnlineSignUpForPasspointAccessPoint, " +
                         "passpointName=$passpointProviderFriendlyName"
-            } else {
-                ""
-            }
+                } else {
+                    ""
+                }
 
             return "WifiNetworkModel.Active(networkId=$networkId, isValidated=$isValidated, " +
                 "level=$level, ssid=$ssid$passpointString)"
         }
 
         companion object {
-            @VisibleForTesting
-            internal const val MAX_VALID_LEVEL = 4
+            @VisibleForTesting internal const val MAX_VALID_LEVEL = 4
         }
     }
 
     companion object {
-        @VisibleForTesting
-        internal const val MIN_VALID_LEVEL = 0
+        @VisibleForTesting internal const val MIN_VALID_LEVEL = 0
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt
index e491d2b..094bcf9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt
@@ -53,4 +53,4 @@
     }
 }
 
-private const val COL_ICON = "wifiIcon"
+private const val COL_ICON = "icon"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
index 95431af..1057231 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
@@ -25,8 +25,6 @@
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.statusbar.pipeline.dagger.WifiTableLog
-import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.log.table.logDiffsForTable
 import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS
@@ -34,13 +32,13 @@
 import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_NETWORK
 import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
+import com.android.systemui.statusbar.pipeline.dagger.WifiTableLog
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logOutputChange
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
 import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
@@ -55,15 +53,12 @@
 /**
  * Models the UI state for the status bar wifi icon.
  *
- * This class exposes three view models, one per status bar location:
- *  - [home]
- *  - [keyguard]
- *  - [qs]
- *  In order to get the UI state for the wifi icon, you must use one of those view models (whichever
- *  is correct for your location).
+ * This class exposes three view models, one per status bar location: [home], [keyguard], and [qs].
+ * In order to get the UI state for the wifi icon, you must use one of those view models (whichever
+ * is correct for your location).
  *
- * Internally, this class maintains the current state of the wifi icon and notifies those three
- * view models of any changes.
+ * Internally, this class maintains the current state of the wifi icon and notifies those three view
+ * models of any changes.
  */
 @SysUISingleton
 class WifiViewModel
@@ -72,7 +67,6 @@
     airplaneModeViewModel: AirplaneModeViewModel,
     connectivityConstants: ConnectivityConstants,
     private val context: Context,
-    logger: ConnectivityPipelineLogger,
     @WifiTableLog wifiTableLogBuffer: TableLogBuffer,
     interactor: WifiInteractor,
     @Application private val scope: CoroutineScope,
@@ -85,12 +79,13 @@
             is WifiNetworkModel.Unavailable -> WifiIcon.Hidden
             is WifiNetworkModel.Invalid -> WifiIcon.Hidden
             is WifiNetworkModel.CarrierMerged -> WifiIcon.Hidden
-            is WifiNetworkModel.Inactive -> WifiIcon.Visible(
-                res = WIFI_NO_NETWORK,
-                ContentDescription.Loaded(
-                    "${context.getString(WIFI_NO_CONNECTION)},${context.getString(NO_INTERNET)}"
+            is WifiNetworkModel.Inactive ->
+                WifiIcon.Visible(
+                    res = WIFI_NO_NETWORK,
+                    ContentDescription.Loaded(
+                        "${context.getString(WIFI_NO_CONNECTION)},${context.getString(NO_INTERNET)}"
+                    )
                 )
-            )
             is WifiNetworkModel.Active -> {
                 val levelDesc = context.getString(WIFI_CONNECTION_STRENGTH[this.level])
                 when {
@@ -114,25 +109,25 @@
     /** The wifi icon that should be displayed. */
     private val wifiIcon: StateFlow<WifiIcon> =
         combine(
-            interactor.isEnabled,
-            interactor.isDefault,
-            interactor.isForceHidden,
-            interactor.wifiNetwork,
-        ) { isEnabled, isDefault, isForceHidden, wifiNetwork ->
-            if (!isEnabled || isForceHidden || wifiNetwork is WifiNetworkModel.CarrierMerged) {
-                return@combine WifiIcon.Hidden
-            }
+                interactor.isEnabled,
+                interactor.isDefault,
+                interactor.isForceHidden,
+                interactor.wifiNetwork,
+            ) { isEnabled, isDefault, isForceHidden, wifiNetwork ->
+                if (!isEnabled || isForceHidden || wifiNetwork is WifiNetworkModel.CarrierMerged) {
+                    return@combine WifiIcon.Hidden
+                }
 
-            val icon = wifiNetwork.icon()
+                val icon = wifiNetwork.icon()
 
-            return@combine when {
-                isDefault -> icon
-                wifiConstants.alwaysShowIconIfEnabled -> icon
-                !connectivityConstants.hasDataCapabilities -> icon
-                wifiNetwork is WifiNetworkModel.Active && wifiNetwork.isValidated -> icon
-                else -> WifiIcon.Hidden
+                return@combine when {
+                    isDefault -> icon
+                    wifiConstants.alwaysShowIconIfEnabled -> icon
+                    !connectivityConstants.hasDataCapabilities -> icon
+                    wifiNetwork is WifiNetworkModel.Active && wifiNetwork.isValidated -> icon
+                    else -> WifiIcon.Hidden
+                }
             }
-        }
             .logDiffsForTable(
                 wifiTableLogBuffer,
                 columnPrefix = "",
@@ -145,36 +140,42 @@
             )
 
     /** The wifi activity status. Null if we shouldn't display the activity status. */
-    private val activity: Flow<DataActivityModel?> =
+    private val activity: Flow<DataActivityModel> = run {
+        val default = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
         if (!connectivityConstants.shouldShowActivityConfig) {
-            flowOf(null)
-        } else {
-            combine(interactor.activity, interactor.ssid) { activity, ssid ->
-                when (ssid) {
-                    null -> null
-                    else -> activity
+                flowOf(default)
+            } else {
+                combine(interactor.activity, interactor.ssid) { activity, ssid ->
+                    when (ssid) {
+                        null -> default
+                        else -> activity
+                    }
                 }
             }
-        }
-        .distinctUntilChanged()
-        .logOutputChange(logger, "activity")
-        .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = null)
+            .distinctUntilChanged()
+            .logDiffsForTable(
+                wifiTableLogBuffer,
+                columnPrefix = "VM.activity",
+                initialValue = default,
+            )
+            .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = default)
+    }
 
     private val isActivityInViewVisible: Flow<Boolean> =
-         activity
-             .map { it?.hasActivityIn == true }
-             .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false)
+        activity
+            .map { it.hasActivityIn }
+            .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false)
 
     private val isActivityOutViewVisible: Flow<Boolean> =
-       activity
-           .map { it?.hasActivityOut == true }
-           .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false)
+        activity
+            .map { it.hasActivityOut }
+            .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false)
 
     private val isActivityContainerVisible: Flow<Boolean> =
-         combine(isActivityInViewVisible, isActivityOutViewVisible) { activityIn, activityOut ->
-                    activityIn || activityOut
-                }
-             .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false)
+        combine(isActivityInViewVisible, isActivityOutViewVisible) { activityIn, activityOut ->
+                activityIn || activityOut
+            }
+            .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false)
 
     // TODO(b/238425913): It isn't ideal for the wifi icon to need to know about whether the
     //  airplane icon is visible. Instead, we should have a parent StatusBarSystemIconsViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsController.kt
index e2bebbe..f0949ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsController.kt
@@ -25,6 +25,8 @@
          * If controls become available, initiate this callback with the desired position
          */
         fun onControlsUpdate(position: Int?)
+
+        fun removeControlsAutoTracker()
     }
 
     /** Add callback, supporting only a single callback at once */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt
index 341eb3b..4950482 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt
@@ -21,16 +21,15 @@
 import android.content.SharedPreferences
 import android.provider.Settings
 import android.util.Log
-
 import com.android.systemui.R
 import com.android.systemui.controls.ControlsServiceInfo
 import com.android.systemui.controls.dagger.ControlsComponent
 import com.android.systemui.controls.management.ControlsListingController
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.settings.UserContextProvider
+import com.android.systemui.statusbar.phone.AutoTileManager
 import com.android.systemui.statusbar.policy.DeviceControlsController.Callback
 import com.android.systemui.util.settings.SecureSettings
-
 import javax.inject.Inject
 
 /**
@@ -87,6 +86,10 @@
      * incorrect.
      */
     override fun setCallback(callback: Callback) {
+        if (!controlsComponent.isEnabled()) {
+            callback.removeControlsAutoTracker()
+            return
+        }
         // Treat any additional call as a reset before recalculating
         removeCallback()
         this.callback = callback
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java
index 9946b4b..5dcafb3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java
@@ -17,7 +17,6 @@
 package com.android.systemui.statusbar.policy;
 
 import android.annotation.WorkerThread;
-import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.hardware.camera2.CameraAccessException;
 import android.hardware.camera2.CameraCharacteristics;
@@ -255,7 +254,6 @@
                 setTorchMode(enabled);
                 mSecureSettings.putInt(Settings.Secure.FLASHLIGHT_AVAILABLE, 1);
                 mSecureSettings.putInt(Secure.FLASHLIGHT_ENABLED, enabled ? 1 : 0);
-                mBroadcastSender.sendBroadcast(new Intent(ACTION_FLASHLIGHT_CHANGED));
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerExt.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerExt.kt
new file mode 100644
index 0000000..5e36750
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerExt.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.statusbar.policy
+
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Returns a [Flow] that emits events whenever a [NotificationEntry] enters or exists the "heads up"
+ * state.
+ */
+val HeadsUpManager.headsUpEvents: Flow<Pair<NotificationEntry, Boolean>>
+    get() = conflatedCallbackFlow {
+        val listener =
+            object : OnHeadsUpChangedListener {
+                override fun onHeadsUpStateChanged(entry: NotificationEntry, isHeadsUp: Boolean) {
+                    trySend(entry to isHeadsUp)
+                }
+            }
+        addListener(listener)
+        awaitClose { removeListener(listener) }
+    }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
index 1ae1eae..8929e02 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
@@ -56,7 +56,7 @@
     /**
      * Whether the bouncer (PIN/password entry) is currently visible.
      */
-    boolean isBouncerShowing();
+    boolean isPrimaryBouncerShowing();
 
     /**
      * If swiping up will unlock without asking for a password.
@@ -196,7 +196,7 @@
     /** **/
     default void notifyKeyguardState(boolean showing, boolean occluded) {}
     /** **/
-    default void notifyBouncerShowing(boolean showing) {}
+    default void notifyPrimaryBouncerShowing(boolean showing) {}
 
     /**
      * Updates the keyguard state to reflect that it's in the process of being dismissed, either by
@@ -244,7 +244,7 @@
         /**
          * Called when the bouncer (PIN/password entry) is shown or hidden.
          */
-        default void onBouncerShowingChanged() {}
+        default void onPrimaryBouncerShowingChanged() {}
 
         /**
          * Triggered when the device was just unlocked and the lock screen is being dismissed.
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 cc6fdcc..805368c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -65,7 +65,7 @@
 
     private boolean mCanDismissLockScreen;
     private boolean mShowing;
-    private boolean mBouncerShowing;
+    private boolean mPrimaryBouncerShowing;
     private boolean mSecure;
     private boolean mOccluded;
 
@@ -157,8 +157,8 @@
     }
 
     @Override
-    public boolean isBouncerShowing() {
-        return mBouncerShowing;
+    public boolean isPrimaryBouncerShowing() {
+        return mPrimaryBouncerShowing;
     }
 
     @Override
@@ -339,11 +339,11 @@
     }
 
     @Override
-    public void notifyBouncerShowing(boolean showing) {
-        if (mBouncerShowing != showing) {
-            mBouncerShowing = showing;
+    public void notifyPrimaryBouncerShowing(boolean showing) {
+        if (mPrimaryBouncerShowing != showing) {
+            mPrimaryBouncerShowing = showing;
 
-            new ArrayList<>(mCallbacks).forEach(Callback::onBouncerShowingChanged);
+            new ArrayList<>(mCallbacks).forEach(Callback::onPrimaryBouncerShowingChanged);
         }
     }
 
@@ -438,6 +438,11 @@
         }
 
         @Override
+        public void onLockedOutStateChanged(BiometricSourceType biometricSourceType) {
+            update(false /* updateAlways */);
+        }
+
+        @Override
         public void onKeyguardVisibilityChanged(boolean visible) {
             update(false /* updateAlways */);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
new file mode 100644
index 0000000..2a18b81
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use mHost 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.statusbar.policy
+
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.AlarmTile
+import com.android.systemui.qs.tiles.CameraToggleTile
+import com.android.systemui.qs.tiles.DndTile
+import com.android.systemui.qs.tiles.FlashlightTile
+import com.android.systemui.qs.tiles.LocationTile
+import com.android.systemui.qs.tiles.MicrophoneToggleTile
+import com.android.systemui.qs.tiles.UiModeNightTile
+import com.android.systemui.qs.tiles.WorkModeTile
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
+
+@Module
+interface PolicyModule {
+
+    /** Inject DndTile into tileMap in QSModule */
+    @Binds @IntoMap @StringKey(DndTile.TILE_SPEC) fun bindDndTile(dndTile: DndTile): QSTileImpl<*>
+
+    /** Inject WorkModeTile into tileMap in QSModule */
+    @Binds
+    @IntoMap
+    @StringKey(WorkModeTile.TILE_SPEC)
+    fun bindWorkModeTile(workModeTile: WorkModeTile): QSTileImpl<*>
+
+    /** Inject FlashlightTile into tileMap in QSModule */
+    @Binds
+    @IntoMap
+    @StringKey(FlashlightTile.TILE_SPEC)
+    fun bindFlashlightTile(flashlightTile: FlashlightTile): QSTileImpl<*>
+
+    /** Inject LocationTile into tileMap in QSModule */
+    @Binds
+    @IntoMap
+    @StringKey(LocationTile.TILE_SPEC)
+    fun bindLocationTile(locationTile: LocationTile): QSTileImpl<*>
+
+    /** Inject CameraToggleTile into tileMap in QSModule */
+    @Binds
+    @IntoMap
+    @StringKey(CameraToggleTile.TILE_SPEC)
+    fun bindCameraToggleTile(cameraToggleTile: CameraToggleTile): QSTileImpl<*>
+
+    /** Inject MicrophoneToggleTile into tileMap in QSModule */
+    @Binds
+    @IntoMap
+    @StringKey(MicrophoneToggleTile.TILE_SPEC)
+    fun bindMicrophoneToggleTile(microphoneToggleTile: MicrophoneToggleTile): QSTileImpl<*>
+
+    /** Inject AlarmTile into tileMap in QSModule */
+    @Binds
+    @IntoMap
+    @StringKey(AlarmTile.TILE_SPEC)
+    fun bindAlarmTile(alarmTile: AlarmTile): QSTileImpl<*>
+
+    @Binds
+    @IntoMap
+    @StringKey(UiModeNightTile.TILE_SPEC)
+    fun bindUiModeNightTile(uiModeNightTile: UiModeNightTile): QSTileImpl<*>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
index e0d780a..7a4e35f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
@@ -48,6 +48,7 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.fragments.FragmentService;
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
 import com.android.systemui.unfold.util.JankMonitorTransitionProgressListener;
@@ -73,6 +74,7 @@
     private boolean mIsAttached;
 
     private final ViewGroup mStatusBarWindowView;
+    private final FragmentService mFragmentService;
     // The container in which we should run launch animations started from the status bar and
     //   expanding into the opening window.
     private final ViewGroup mLaunchAnimationContainer;
@@ -86,6 +88,7 @@
             WindowManager windowManager,
             IWindowManager iWindowManager,
             StatusBarContentInsetsProvider contentInsetsProvider,
+            FragmentService fragmentService,
             @Main Resources resources,
             Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider) {
         mContext = context;
@@ -93,6 +96,7 @@
         mIWindowManager = iWindowManager;
         mContentInsetsProvider = contentInsetsProvider;
         mStatusBarWindowView = statusBarWindowView;
+        mFragmentService = fragmentService;
         mLaunchAnimationContainer = mStatusBarWindowView.findViewById(
                 R.id.status_bar_launch_animation_container);
         mLpChanged = new WindowManager.LayoutParams();
@@ -157,7 +161,7 @@
 
     /** Returns a fragment host manager for the status bar window view. */
     public FragmentHostManager getFragmentHostManager() {
-        return FragmentHostManager.get(mStatusBarWindowView);
+        return mFragmentService.getFragmentHostManager(mStatusBarWindowView);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt
index 60f6df6..8f424b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt
@@ -61,6 +61,10 @@
         listeners.add(listener)
     }
 
+    fun removeListener(listener: StatusBarWindowStateListener) {
+        listeners.remove(listener)
+    }
+
     /** Returns true if the window is currently showing. */
     fun windowIsShowing() = windowState == WINDOW_STATE_SHOWING
 
diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerStartable.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerStartable.kt
index 5a8850a..dde2a80 100644
--- a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerStartable.kt
@@ -39,6 +39,17 @@
     private val featureFlags: FeatureFlags,
 ) : CoreStartable, StylusManager.StylusCallback, StylusManager.StylusBatteryCallback {
 
+    override fun onStylusAdded(deviceId: Int) {
+        // On some devices, the addition of a new internal stylus indicates the use of a
+        // USI stylus with a different vendor/product ID. We would therefore like to reset
+        // the battery notification suppression, in case the user has dismissed a low battery
+        // notification of the previous stylus.
+        val device = inputManager.getInputDevice(deviceId) ?: return
+        if (!device.isExternal) {
+            stylusUsiPowerUi.updateSuppression(false)
+        }
+    }
+
     override fun onStylusBluetoothConnected(deviceId: Int, btAddress: String) {
         stylusUsiPowerUi.refresh()
     }
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
index df8d161..1065d33 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
@@ -327,7 +327,7 @@
         // appropriately.
         activeViews.remove(displayInfo)
         listeners.forEach {
-            it.onInfoPermanentlyRemoved(id)
+            it.onInfoPermanentlyRemoved(id, removalReason)
         }
 
         // No need to time the view out since it's already gone
@@ -393,7 +393,7 @@
             activeViews.remove(it)
             logger.logViewExpiration(it.info)
             listeners.forEach { listener ->
-                listener.onInfoPermanentlyRemoved(it.info.id)
+                listener.onInfoPermanentlyRemoved(it.info.id, REMOVAL_REASON_TIME_EXPIRED)
             }
         }
     }
@@ -457,7 +457,7 @@
          * Called whenever a [DisplayInfo] with the given [id] has been removed and will never be
          * displayed again (unless another call to [updateView] is made).
          */
-        fun onInfoPermanentlyRemoved(id: String)
+        fun onInfoPermanentlyRemoved(id: String, reason: String)
     }
 
     /** A container for all the display-related state objects. */
@@ -494,6 +494,7 @@
 }
 
 private const val REMOVAL_REASON_TIMEOUT = "TIMEOUT"
+private const val REMOVAL_REASON_TIME_EXPIRED = "TIMEOUT_EXPIRED_BEFORE_REDISPLAY"
 private const val MIN_REQUIRED_TIME_FOR_REDISPLAY = 1000
 
 private data class IconInfo(
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt
index ec6965a..899b0c2 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt
@@ -80,6 +80,26 @@
         )
     }
 
+    /** Logs that there was a failure to animate the view in. */
+    fun logAnimateInFailure() {
+        buffer.log(
+            tag,
+            LogLevel.WARNING,
+            {},
+            { "View's appearance animation failed. Forcing view display manually." },
+        )
+    }
+
+    /** Logs that there was a failure to animate the view out. */
+    fun logAnimateOutFailure() {
+        buffer.log(
+            tag,
+            LogLevel.WARNING,
+            {},
+            { "View's disappearance animation failed." },
+        )
+    }
+
     fun logViewHidden(info: T) {
         buffer.log(
             tag,
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarAnimator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarAnimator.kt
new file mode 100644
index 0000000..01a81de
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarAnimator.kt
@@ -0,0 +1,83 @@
+/*
+ * 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.temporarydisplay.chipbar
+
+import android.view.View
+import android.view.ViewGroup
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.animation.ViewHierarchyAnimator
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.util.children
+import javax.inject.Inject
+
+/**
+ * A class controlling chipbar animations. Typically delegates to [ViewHierarchyAnimator].
+ *
+ * Used so that animations can be mocked out in tests.
+ */
+@SysUISingleton
+open class ChipbarAnimator @Inject constructor() {
+    /**
+     * Animates [innerView] and its children into view.
+     *
+     * @return true if the animation was successfully started and false if the animation can't be
+     * run for any reason.
+     *
+     * See [ViewHierarchyAnimator.animateAddition].
+     */
+    open fun animateViewIn(innerView: ViewGroup, onAnimationEnd: Runnable): Boolean {
+        return ViewHierarchyAnimator.animateAddition(
+            innerView,
+            ViewHierarchyAnimator.Hotspot.TOP,
+            Interpolators.EMPHASIZED_DECELERATE,
+            duration = ANIMATION_IN_DURATION,
+            includeMargins = true,
+            includeFadeIn = true,
+            onAnimationEnd = onAnimationEnd,
+        )
+    }
+
+    /**
+     * Animates [innerView] and its children out of view.
+     *
+     * @return true if the animation was successfully started and false if the animation can't be
+     * run for any reason.
+     *
+     * See [ViewHierarchyAnimator.animateRemoval].
+     */
+    open fun animateViewOut(innerView: ViewGroup, onAnimationEnd: Runnable): Boolean {
+        return ViewHierarchyAnimator.animateRemoval(
+            innerView,
+            ViewHierarchyAnimator.Hotspot.TOP,
+            Interpolators.EMPHASIZED_ACCELERATE,
+            ANIMATION_OUT_DURATION,
+            includeMargins = true,
+            onAnimationEnd,
+        )
+    }
+
+    /** Force shows this view and all child views. Should be used in case [animateViewIn] fails. */
+    fun forceDisplayView(innerView: View) {
+        innerView.alpha = 1f
+        if (innerView is ViewGroup) {
+            innerView.children.forEach { forceDisplayView(it) }
+        }
+    }
+}
+
+private const val ANIMATION_IN_DURATION = 500L
+private const val ANIMATION_OUT_DURATION = 250L
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
index 9e0bbb7..a20a5b2 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.temporarydisplay.chipbar
 
+import android.animation.ObjectAnimator
+import android.animation.ValueAnimator
 import android.content.Context
 import android.graphics.Rect
 import android.os.PowerManager
@@ -27,13 +29,14 @@
 import android.view.ViewGroup
 import android.view.WindowManager
 import android.view.accessibility.AccessibilityManager
+import android.widget.ImageView
 import android.widget.TextView
 import androidx.annotation.IdRes
+import androidx.annotation.VisibleForTesting
 import com.android.internal.widget.CachingIconView
 import com.android.systemui.Gefingerpoken
 import com.android.systemui.R
 import com.android.systemui.animation.Interpolators
-import com.android.systemui.animation.ViewHierarchyAnimator
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
 import com.android.systemui.common.shared.model.Text.Companion.loadText
@@ -78,6 +81,7 @@
     configurationController: ConfigurationController,
     dumpManager: DumpManager,
     powerManager: PowerManager,
+    private val chipbarAnimator: ChipbarAnimator,
     private val falsingManager: FalsingManager,
     private val falsingCollector: FalsingCollector,
     private val swipeChipbarAwayGestureHandler: SwipeChipbarAwayGestureHandler?,
@@ -102,6 +106,15 @@
 
     private lateinit var parent: ChipbarRootView
 
+    /** The current loading information, or null we're not currently loading. */
+    @VisibleForTesting
+    internal var loadingDetails: LoadingDetails? = null
+        private set(value) {
+            // Always cancel the old one before updating
+            field?.animator?.cancel()
+            field = value
+        }
+
     override val windowLayoutParams =
         commonWindowLayoutParams.apply { gravity = Gravity.TOP.or(Gravity.CENTER_HORIZONTAL) }
 
@@ -144,8 +157,22 @@
 
         // ---- End item ----
         // Loading
-        currentView.requireViewById<View>(R.id.loading).visibility =
-            (newInfo.endItem == ChipbarEndItem.Loading).visibleIfTrue()
+        val isLoading = newInfo.endItem == ChipbarEndItem.Loading
+        val loadingView = currentView.requireViewById<ImageView>(R.id.loading)
+        loadingView.visibility = isLoading.visibleIfTrue()
+
+        if (isLoading) {
+            val currentLoadingDetails = loadingDetails
+            // Since there can be multiple chipbars, we need to check if the loading view is the
+            // same and possibly re-start the loading animation on the new view.
+            if (currentLoadingDetails == null || currentLoadingDetails.loadingView != loadingView) {
+                val newDetails = createLoadingDetails(loadingView)
+                newDetails.animator.start()
+                loadingDetails = newDetails
+            }
+        } else {
+            loadingDetails = null
+        }
 
         // Error
         currentView.requireViewById<View>(R.id.error).visibility =
@@ -206,31 +233,36 @@
     }
 
     override fun animateViewIn(view: ViewGroup) {
-        ViewHierarchyAnimator.animateAddition(
-            view.getInnerView(),
-            ViewHierarchyAnimator.Hotspot.TOP,
-            Interpolators.EMPHASIZED_DECELERATE,
-            duration = ANIMATION_IN_DURATION,
-            includeMargins = true,
-            includeFadeIn = true,
-            // We can only request focus once the animation finishes.
-            onAnimationEnd = {
-                maybeGetAccessibilityFocus(view.getTag(INFO_TAG) as ChipbarInfo?, view)
-            },
-        )
+        // We can only request focus once the animation finishes.
+        val onAnimationEnd = Runnable {
+            maybeGetAccessibilityFocus(view.getTag(INFO_TAG) as ChipbarInfo?, view)
+        }
+        val animatedIn = chipbarAnimator.animateViewIn(view.getInnerView(), onAnimationEnd)
+
+        // If the view doesn't get animated, the [onAnimationEnd] runnable won't get run and the
+        // views would remain un-displayed. So, just force-set/run those items immediately.
+        if (!animatedIn) {
+            logger.logAnimateInFailure()
+            chipbarAnimator.forceDisplayView(view.getInnerView())
+            onAnimationEnd.run()
+        }
     }
 
     override fun animateViewOut(view: ViewGroup, removalReason: String?, onAnimationEnd: Runnable) {
         val innerView = view.getInnerView()
         innerView.accessibilityLiveRegion = ACCESSIBILITY_LIVE_REGION_NONE
-        ViewHierarchyAnimator.animateRemoval(
-            innerView,
-            ViewHierarchyAnimator.Hotspot.TOP,
-            Interpolators.EMPHASIZED_ACCELERATE,
-            ANIMATION_OUT_DURATION,
-            includeMargins = true,
-            onAnimationEnd,
-        )
+
+        val fullEndRunnable = Runnable {
+            loadingDetails = null
+            onAnimationEnd.run()
+        }
+        val removed = chipbarAnimator.animateViewOut(innerView, fullEndRunnable)
+        // If the view doesn't get animated, the [onAnimationEnd] runnable won't get run. So, just
+        // run it immediately.
+        if (!removed) {
+            logger.logAnimateOutFailure()
+            fullEndRunnable.run()
+        }
 
         updateGestureListening()
     }
@@ -270,7 +302,7 @@
     }
 
     private fun ViewGroup.getInnerView(): ViewGroup {
-        return requireViewById(R.id.chipbar_inner)
+        return this.requireViewById(R.id.chipbar_inner)
     }
 
     override fun getTouchableRegion(view: View, outRect: Rect) {
@@ -284,10 +316,28 @@
             View.GONE
         }
     }
+
+    private fun createLoadingDetails(loadingView: View): LoadingDetails {
+        // Ideally, we would use a <ProgressBar> view, which would automatically handle the loading
+        // spinner rotation for us. However, due to b/243983980, the ProgressBar animation
+        // unexpectedly pauses when SysUI starts another window. ObjectAnimator is a workaround that
+        // won't pause.
+        val animator =
+            ObjectAnimator.ofFloat(loadingView, View.ROTATION, 0f, 360f).apply {
+                duration = LOADING_ANIMATION_DURATION_MS
+                repeatCount = ValueAnimator.INFINITE
+                interpolator = Interpolators.LINEAR
+            }
+        return LoadingDetails(loadingView, animator)
+    }
+
+    internal data class LoadingDetails(
+        val loadingView: View,
+        val animator: ObjectAnimator,
+    )
 }
 
-private const val ANIMATION_IN_DURATION = 500L
-private const val ANIMATION_OUT_DURATION = 250L
 @IdRes private val INFO_TAG = R.id.tag_chipbar_info
 private const val SWIPE_UP_GESTURE_REASON = "SWIPE_UP_GESTURE_DETECTED"
 private const val TAG = "ChipbarCoordinator"
+private const val LOADING_ANIMATION_DURATION_MS = 1000L
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandler.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandler.kt
index 6e3cb48..9dbc4b3 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandler.kt
@@ -19,6 +19,7 @@
 import android.content.Context
 import android.view.MotionEvent
 import android.view.View
+import com.android.systemui.settings.DisplayTracker
 import com.android.systemui.statusbar.gesture.SwipeUpGestureHandler
 import com.android.systemui.statusbar.gesture.SwipeUpGestureLogger
 import com.android.systemui.util.boundsOnScreen
@@ -31,8 +32,9 @@
  */
 class SwipeChipbarAwayGestureHandler(
     context: Context,
+    displayTracker: DisplayTracker,
     logger: SwipeUpGestureLogger,
-) : SwipeUpGestureHandler(context, logger, loggerTag = LOGGER_TAG) {
+) : SwipeUpGestureHandler(context, displayTracker, logger, loggerTag = LOGGER_TAG) {
 
     private var viewFetcher: () -> View? = { null }
 
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/dagger/TemporaryDisplayModule.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/dagger/TemporaryDisplayModule.kt
index 933c060..b1be404 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/dagger/TemporaryDisplayModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/dagger/TemporaryDisplayModule.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.log.LogBufferFactory
 import com.android.systemui.media.taptotransfer.MediaTttFlags
 import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.settings.DisplayTracker
 import com.android.systemui.statusbar.gesture.SwipeUpGestureLogger
 import com.android.systemui.temporarydisplay.chipbar.SwipeChipbarAwayGestureHandler
 import dagger.Module
@@ -41,10 +42,11 @@
         fun provideSwipeChipbarAwayGestureHandler(
             mediaTttFlags: MediaTttFlags,
             context: Context,
+            displayTracker: DisplayTracker,
             logger: SwipeUpGestureLogger,
         ): SwipeChipbarAwayGestureHandler? {
             return if (mediaTttFlags.isMediaTttDismissGestureEnabled()) {
-                SwipeChipbarAwayGestureHandler(context, logger)
+                SwipeChipbarAwayGestureHandler(context, displayTracker, logger)
             } else {
                 null
             }
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index f29ca4d..2b7ea2a 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -357,15 +357,24 @@
     };
 
     @Inject
-    public ThemeOverlayController(Context context, BroadcastDispatcher broadcastDispatcher,
-            @Background Handler bgHandler, @Main Executor mainExecutor,
-            @Background Executor bgExecutor, ThemeOverlayApplier themeOverlayApplier,
-            SecureSettings secureSettings, WallpaperManager wallpaperManager,
-            UserManager userManager, DeviceProvisionedController deviceProvisionedController,
-            UserTracker userTracker, DumpManager dumpManager, FeatureFlags featureFlags,
-            @Main Resources resources, WakefulnessLifecycle wakefulnessLifecycle) {
+    public ThemeOverlayController(
+            Context context,
+            BroadcastDispatcher broadcastDispatcher,
+            @Background Handler bgHandler,
+            @Main Executor mainExecutor,
+            @Background Executor bgExecutor,
+            ThemeOverlayApplier themeOverlayApplier,
+            SecureSettings secureSettings,
+            WallpaperManager wallpaperManager,
+            UserManager userManager,
+            DeviceProvisionedController deviceProvisionedController,
+            UserTracker userTracker,
+            DumpManager dumpManager,
+            FeatureFlags featureFlags,
+            @Main Resources resources,
+            WakefulnessLifecycle wakefulnessLifecycle) {
         mContext = context;
-        mIsMonochromaticEnabled = featureFlags.isEnabled(Flags.MONOCHROMATIC_THEMES);
+        mIsMonochromaticEnabled = featureFlags.isEnabled(Flags.MONOCHROMATIC_THEME);
         mIsMonetEnabled = featureFlags.isEnabled(Flags.MONET);
         mDeviceProvisionedController = deviceProvisionedController;
         mBroadcastDispatcher = broadcastDispatcher;
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/RadioListPreference.java b/packages/SystemUI/src/com/android/systemui/tuner/RadioListPreference.java
index 79811c5..2475890 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/RadioListPreference.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/RadioListPreference.java
@@ -28,8 +28,9 @@
 import androidx.preference.PreferenceScreen;
 
 import com.android.settingslib.Utils;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.fragments.FragmentService;
 
 import java.util.Objects;
 
@@ -74,7 +75,7 @@
 
         RadioFragment f = new RadioFragment();
         f.setPreference(this);
-        FragmentHostManager.get(v).getFragmentManager()
+        Dependency.get(FragmentService.class).getFragmentHostManager(v).getFragmentManager()
                 .beginTransaction()
                 .add(android.R.id.content, f)
                 .commit();
@@ -86,8 +87,10 @@
             Bundle savedInstanceState) {
         super.onDialogStateRestored(fragment, dialog, savedInstanceState);
         View view = dialog.findViewById(R.id.content);
-        RadioFragment radioFragment = (RadioFragment) FragmentHostManager.get(view)
-                .getFragmentManager().findFragmentById(R.id.content);
+        RadioFragment radioFragment = (RadioFragment) Dependency.get(FragmentService.class)
+                .getFragmentHostManager(view)
+                .getFragmentManager()
+                .findFragmentById(R.id.content);
         if (radioFragment != null) {
             radioFragment.setPreference(this);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
index 0069bb5..19a0866 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -38,6 +38,7 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.settings.DisplayTracker
 import com.android.systemui.statusbar.LightRevealEffect
 import com.android.systemui.statusbar.LightRevealScrim
 import com.android.systemui.statusbar.LinearLightRevealEffect
@@ -68,6 +69,7 @@
     @Main private val executor: Executor,
     private val threadFactory: ThreadFactory,
     private val rotationChangeProvider: RotationChangeProvider,
+    private val displayTracker: DisplayTracker
 ) {
 
     private val transitionListener = TransitionListener()
@@ -104,7 +106,7 @@
                 .setName("unfold-overlay-container")
 
         displayAreaHelper.get().attachToRootDisplayArea(
-            Display.DEFAULT_DISPLAY,
+            displayTracker.defaultDisplayId,
             containerBuilder
         ) { builder ->
             executor.execute {
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserModule.java b/packages/SystemUI/src/com/android/systemui/user/UserModule.java
index 2b29885..f7c8bac 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserModule.java
+++ b/packages/SystemUI/src/com/android/systemui/user/UserModule.java
@@ -21,6 +21,7 @@
 
 import com.android.settingslib.users.EditUserInfoController;
 import com.android.systemui.user.data.repository.UserRepositoryModule;
+import com.android.systemui.user.domain.interactor.HeadlessSystemUserModeModule;
 import com.android.systemui.user.ui.dialog.UserDialogModule;
 
 import dagger.Binds;
@@ -36,6 +37,7 @@
         includes = {
                 UserDialogModule.class,
                 UserRepositoryModule.class,
+                HeadlessSystemUserModeModule.class,
         }
 )
 public abstract class UserModule {
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
index c0f0390..ad1e5fe 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
@@ -17,8 +17,11 @@
 
 package com.android.systemui.user.data.repository
 
+import android.app.IActivityManager
+import android.app.UserSwitchObserver
 import android.content.Context
 import android.content.pm.UserInfo
+import android.os.IRemoteCallback
 import android.os.UserHandle
 import android.os.UserManager
 import android.provider.Settings
@@ -30,6 +33,8 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.user.data.model.UserSwitcherSettingsModel
 import com.android.systemui.util.settings.GlobalSettings
@@ -42,12 +47,14 @@
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.withContext
@@ -68,6 +75,9 @@
     /** [UserInfo] of the currently-selected user. */
     val selectedUserInfo: Flow<UserInfo>
 
+    /** Whether user switching is currently in progress. */
+    val userSwitchingInProgress: Flow<Boolean>
+
     /** User ID of the last non-guest selected user. */
     val lastSelectedNonGuestUserId: Int
 
@@ -108,11 +118,29 @@
     @Background private val backgroundDispatcher: CoroutineDispatcher,
     private val globalSettings: GlobalSettings,
     private val tracker: UserTracker,
+    private val activityManager: IActivityManager,
+    featureFlags: FeatureFlags,
 ) : UserRepository {
 
-    private val _userSwitcherSettings = MutableStateFlow(runBlocking { getSettings() })
-    override val userSwitcherSettings: Flow<UserSwitcherSettingsModel> =
-        _userSwitcherSettings.asStateFlow().filterNotNull()
+    private val _userSwitcherSettings: StateFlow<UserSwitcherSettingsModel> =
+        globalSettings
+            .observerFlow(
+                names =
+                    arrayOf(
+                        SETTING_SIMPLE_USER_SWITCHER,
+                        Settings.Global.ADD_USERS_WHEN_LOCKED,
+                        Settings.Global.USER_SWITCHER_ENABLED,
+                    ),
+                userId = UserHandle.USER_SYSTEM,
+            )
+            .onStart { emit(Unit) } // Forces an initial update.
+            .map { getSettings() }
+            .stateIn(
+                scope = applicationScope,
+                started = SharingStarted.Eagerly,
+                initialValue = runBlocking { getSettings() },
+            )
+    override val userSwitcherSettings: Flow<UserSwitcherSettingsModel> = _userSwitcherSettings
 
     private val _userInfos = MutableStateFlow<List<UserInfo>?>(null)
     override val userInfos: Flow<List<UserInfo>> = _userInfos.filterNotNull()
@@ -129,6 +157,10 @@
     private var _isGuestUserResetting: Boolean = false
     override var isGuestUserResetting: Boolean = _isGuestUserResetting
 
+    private val _isUserSwitchingInProgress = MutableStateFlow(false)
+    override val userSwitchingInProgress: Flow<Boolean>
+        get() = _isUserSwitchingInProgress
+
     override val isGuestUserCreationScheduled = AtomicBoolean()
 
     override val isStatusBarUserChipEnabled: Boolean =
@@ -140,7 +172,9 @@
 
     init {
         observeSelectedUser()
-        observeUserSettings()
+        if (featureFlags.isEnabled(FACE_AUTH_REFACTOR)) {
+            observeUserSwitching()
+        }
     }
 
     override fun refreshUsers() {
@@ -166,6 +200,28 @@
         return _userSwitcherSettings.value.isSimpleUserSwitcher
     }
 
+    private fun observeUserSwitching() {
+        conflatedCallbackFlow {
+                val callback =
+                    object : UserSwitchObserver() {
+                        override fun onUserSwitching(newUserId: Int, reply: IRemoteCallback) {
+                            trySendWithFailureLogging(true, TAG, "userSwitching started")
+                        }
+
+                        override fun onUserSwitchComplete(newUserId: Int) {
+                            trySendWithFailureLogging(false, TAG, "userSwitching completed")
+                        }
+                    }
+                activityManager.registerUserSwitchObserver(callback, TAG)
+                trySendWithFailureLogging(false, TAG, "initial value defaulting to false")
+                awaitClose { activityManager.unregisterUserSwitchObserver(callback) }
+            }
+            .onEach { _isUserSwitchingInProgress.value = it }
+            // TODO (b/262838215), Make this stateIn and initialize directly in field declaration
+            //  once the flag is launched
+            .launchIn(applicationScope)
+    }
+
     private fun observeSelectedUser() {
         conflatedCallbackFlow {
                 fun send() {
@@ -174,7 +230,7 @@
 
                 val callback =
                     object : UserTracker.Callback {
-                        override fun onUserChanged(newUser: Int, userContext: Context) {
+                        override fun onUserChanging(newUser: Int, userContext: Context) {
                             send()
                         }
 
@@ -198,23 +254,6 @@
             .launchIn(applicationScope)
     }
 
-    private fun observeUserSettings() {
-        globalSettings
-            .observerFlow(
-                names =
-                    arrayOf(
-                        SETTING_SIMPLE_USER_SWITCHER,
-                        Settings.Global.ADD_USERS_WHEN_LOCKED,
-                        Settings.Global.USER_SWITCHER_ENABLED,
-                    ),
-                userId = UserHandle.USER_SYSTEM,
-            )
-            .onStart { emit(Unit) } // Forces an initial update.
-            .map { getSettings() }
-            .onEach { _userSwitcherSettings.value = it }
-            .launchIn(applicationScope)
-    }
-
     private suspend fun getSettings(): UserSwitcherSettingsModel {
         return withContext(backgroundDispatcher) {
             val isSimpleUserSwitcher =
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/HeadlessSystemUserMode.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/HeadlessSystemUserMode.kt
new file mode 100644
index 0000000..756e6a1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/HeadlessSystemUserMode.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.user.domain.interactor
+
+import android.os.UserManager
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+interface HeadlessSystemUserMode {
+
+    fun isHeadlessSystemUserMode(): Boolean
+}
+
+@SysUISingleton
+class HeadlessSystemUserModeImpl @Inject constructor() : HeadlessSystemUserMode {
+    override fun isHeadlessSystemUserMode(): Boolean {
+        return UserManager.isHeadlessSystemUserMode()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AnimationParams.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/HeadlessSystemUserModeModule.kt
similarity index 60%
copy from packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AnimationParams.kt
copy to packages/SystemUI/src/com/android/systemui/user/domain/interactor/HeadlessSystemUserModeModule.kt
index 67733e9..0efa2d8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AnimationParams.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/HeadlessSystemUserModeModule.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -11,15 +11,16 @@
  * 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
+ * limitations under the License.
  */
-package com.android.systemui.keyguard.shared.model
 
-import kotlin.time.Duration
-import kotlin.time.Duration.Companion.milliseconds
+package com.android.systemui.user.domain.interactor
 
-/** Animation parameters */
-data class AnimationParams(
-    val startTime: Duration = 0.milliseconds,
-    val duration: Duration,
-)
+import dagger.Binds
+
+@dagger.Module
+interface HeadlessSystemUserModeModule {
+
+    @Binds
+    fun bindIsHeadlessSystemUserMode(impl: HeadlessSystemUserModeImpl): HeadlessSystemUserMode
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
index c0ba3cc..3f895ad 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
@@ -86,6 +86,7 @@
     private val keyguardInteractor: KeyguardInteractor,
     private val featureFlags: FeatureFlags,
     private val manager: UserManager,
+    private val headlessSystemUserMode: HeadlessSystemUserMode,
     @Application private val applicationScope: CoroutineScope,
     telephonyInteractor: TelephonyInteractor,
     broadcastDispatcher: BroadcastDispatcher,
@@ -560,7 +561,10 @@
             actionType = action,
             isRestricted = isRestricted,
             isSwitchToEnabled =
-                canSwitchUsers(selectedUserId) &&
+                canSwitchUsers(
+                    selectedUserId = selectedUserId,
+                    isAction = true,
+                ) &&
                     // If the user is auto-created is must not be currently resetting.
                     !(isGuestUserAutoCreated && isGuestUserResetting),
         )
@@ -712,10 +716,32 @@
         }
     }
 
-    private suspend fun canSwitchUsers(selectedUserId: Int): Boolean {
-        return withContext(backgroundDispatcher) {
-            manager.getUserSwitchability(UserHandle.of(selectedUserId))
-        } == UserManager.SWITCHABILITY_STATUS_OK
+    private suspend fun canSwitchUsers(
+        selectedUserId: Int,
+        isAction: Boolean = false,
+    ): Boolean {
+        val isHeadlessSystemUserMode =
+            withContext(backgroundDispatcher) { headlessSystemUserMode.isHeadlessSystemUserMode() }
+        // Whether menu item should be active. True if item is a user or if any user has
+        // signed in since reboot or in all cases for non-headless system user mode.
+        val isItemEnabled = !isAction || !isHeadlessSystemUserMode || isAnyUserUnlocked()
+        return isItemEnabled &&
+            withContext(backgroundDispatcher) {
+                manager.getUserSwitchability(UserHandle.of(selectedUserId))
+            } == UserManager.SWITCHABILITY_STATUS_OK
+    }
+
+    private suspend fun isAnyUserUnlocked(): Boolean {
+        return manager
+            .getUsers(
+                /* excludePartial= */ true,
+                /* excludeDying= */ true,
+                /* excludePreCreated= */ true
+            )
+            .any { user ->
+                user.id != UserHandle.USER_SYSTEM &&
+                    withContext(backgroundDispatcher) { manager.isUserUnlocked(user.userHandle) }
+            }
     }
 
     @SuppressLint("UseCompatLoadingForDrawables")
diff --git a/packages/SystemUI/src/com/android/systemui/util/BackupManagerProxy.kt b/packages/SystemUI/src/com/android/systemui/util/BackupManagerProxy.kt
new file mode 100644
index 0000000..f542434
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/BackupManagerProxy.kt
@@ -0,0 +1,17 @@
+package com.android.systemui.util
+
+import android.app.backup.BackupManager
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/** Wrapper around [BackupManager] useful for testing. */
+@SysUISingleton
+class BackupManagerProxy @Inject constructor() {
+
+    /** Wrapped version of [BackupManager.dataChanged] */
+    fun dataChanged(packageName: String) = BackupManager.dataChanged(packageName)
+
+    /** Wrapped version of [BackupManager.dataChangedForUser] */
+    fun dataChangedForUser(userId: Int, packageName: String) =
+        BackupManager.dataChangedForUser(userId, packageName)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index d54de3fa..c2727fc 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -14,8 +14,6 @@
 
 package com.android.systemui.util;
 
-import static android.view.Display.DEFAULT_DISPLAY;
-
 import android.Manifest;
 import android.content.Context;
 import android.content.Intent;
@@ -26,6 +24,7 @@
 
 import com.android.internal.policy.SystemBarUtils;
 import com.android.systemui.R;
+import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.shared.system.QuickStepContract;
 
 import java.util.List;
@@ -71,8 +70,9 @@
      * {@link android.view.WindowManagerPolicyConstants#NAV_BAR_MODE_GESTURAL} AND
      * the context is that of the default display
      */
-    public static boolean isGesturalModeOnDefaultDisplay(Context context, int navMode) {
-        return context.getDisplayId() == DEFAULT_DISPLAY
+    public static boolean isGesturalModeOnDefaultDisplay(Context context,
+            DisplayTracker displayTracker, int navMode) {
+        return context.getDisplayId() == displayTracker.getDefaultDisplayId()
                 && QuickStepContract.isGesturalMode(navMode);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt
index 08ee0af..56c5d3b 100644
--- a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt
@@ -27,6 +27,8 @@
 import android.widget.TextView
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.animation.LaunchableView
+import com.android.systemui.animation.LaunchableViewDelegate
 import com.android.systemui.statusbar.CrossFadeHelper
 
 /**
@@ -38,7 +40,7 @@
     context: Context,
     attrs: AttributeSet? = null,
     defStyleAttr: Int = 0
-) : ConstraintLayout(context, attrs, defStyleAttr) {
+) : ConstraintLayout(context, attrs, defStyleAttr), LaunchableView {
 
     private val boundsRect = Rect()
     private val originalGoneChildrenSet: MutableSet<Int> = mutableSetOf()
@@ -50,7 +52,11 @@
 
     private var desiredMeasureWidth = 0
     private var desiredMeasureHeight = 0
-    private var transitionVisibility = View.VISIBLE
+    private val delegate =
+        LaunchableViewDelegate(
+            this,
+            superSetVisibility = { super.setVisibility(it) },
+        )
 
     /**
      * The measured state of this view which is the one we will lay ourselves out with. This
@@ -83,11 +89,12 @@
         }
     }
 
-    override fun setTransitionVisibility(visibility: Int) {
-        // We store the last transition visibility assigned to this view to restore it later if
-        // necessary.
-        super.setTransitionVisibility(visibility)
-        transitionVisibility = visibility
+    override fun setShouldBlockVisibilityChanges(block: Boolean) {
+        delegate.setShouldBlockVisibilityChanges(block)
+    }
+
+    override fun setVisibility(visibility: Int) {
+        delegate.setVisibility(visibility)
     }
 
     override fun onFinishInflate() {
@@ -173,14 +180,6 @@
         translationY = currentState.translation.y
 
         CrossFadeHelper.fadeIn(this, currentState.alpha)
-
-        // CrossFadeHelper#fadeIn will change this view visibility, which overrides the transition
-        // visibility. We set the transition visibility again to make sure that this view plays well
-        // with GhostView, which sets the transition visibility and is used for activity launch
-        // animations.
-        if (transitionVisibility != View.VISIBLE) {
-            setTransitionVisibility(transitionVisibility)
-        }
     }
 
     private fun applyCurrentStateOnPredraw() {
diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/ConditionalCoreStartable.java b/packages/SystemUI/src/com/android/systemui/util/condition/ConditionalCoreStartable.java
new file mode 100644
index 0000000..8d32a48
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/condition/ConditionalCoreStartable.java
@@ -0,0 +1,74 @@
+/*
+ * 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.condition;
+
+import com.android.systemui.CoreStartable;
+import com.android.systemui.shared.condition.Condition;
+import com.android.systemui.shared.condition.Monitor;
+
+import java.util.Set;
+
+/**
+ * {@link ConditionalCoreStartable} is a {@link com.android.systemui.CoreStartable} abstract
+ * implementation where conditions must be met before routines are executed.
+ */
+public abstract class ConditionalCoreStartable implements CoreStartable {
+    private final Monitor mMonitor;
+    private final Set<Condition> mConditionSet;
+    private Monitor.Subscription.Token mStartToken;
+    private Monitor.Subscription.Token mBootCompletedToken;
+
+    public ConditionalCoreStartable(Monitor monitor) {
+        this(monitor, null);
+    }
+
+    public ConditionalCoreStartable(Monitor monitor, Set<Condition> conditionSet) {
+        mMonitor = monitor;
+        mConditionSet = conditionSet;
+    }
+
+    @Override
+    public final void start() {
+        mStartToken = mMonitor.addSubscription(
+                new Monitor.Subscription.Builder(allConditionsMet -> {
+                    if (allConditionsMet) {
+                        mMonitor.removeSubscription(mStartToken);
+                        mStartToken = null;
+                        onStart();
+                    }
+                }).addConditions(mConditionSet)
+                        .build());
+    }
+
+    protected abstract void onStart();
+
+    @Override
+    public final void onBootCompleted() {
+        mBootCompletedToken = mMonitor.addSubscription(
+                new Monitor.Subscription.Builder(allConditionsMet -> {
+                    if (allConditionsMet) {
+                        mMonitor.removeSubscription(mBootCompletedToken);
+                        mBootCompletedToken = null;
+                        bootCompleted();
+                    }
+                }).addConditions(mConditionSet)
+                        .build());
+    }
+
+    protected void bootCompleted() {
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitorModule.kt b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitorModule.kt
new file mode 100644
index 0000000..c74e71f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitorModule.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.leak
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
+
+@Module
+interface GarbageMonitorModule {
+    /** Inject into GarbageMonitor.Service. */
+    @Binds
+    @IntoMap
+    @ClassKey(GarbageMonitor::class)
+    fun bindGarbageMonitorService(sysui: GarbageMonitor.Service): CoreStartable
+
+    @Binds
+    @IntoMap
+    @StringKey(GarbageMonitor.MemoryTile.TILE_SPEC)
+    fun bindMemoryTile(memoryTile: GarbageMonitor.MemoryTile): QSTileImpl<*>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java b/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java
index 2c901d2..9429d89 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java
@@ -22,6 +22,8 @@
 
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.qs.tiles.QuickAccessWalletTile;
 import com.android.systemui.wallet.ui.WalletActivity;
 
 import java.util.concurrent.Executor;
@@ -31,6 +33,7 @@
 import dagger.Provides;
 import dagger.multibindings.ClassKey;
 import dagger.multibindings.IntoMap;
+import dagger.multibindings.StringKey;
 
 
 /**
@@ -52,4 +55,11 @@
             @Background Executor bgExecutor) {
         return QuickAccessWalletClient.create(context, bgExecutor);
     }
+
+    /** */
+    @Binds
+    @IntoMap
+    @StringKey(QuickAccessWalletTile.TILE_SPEC)
+    public abstract QSTileImpl<?> bindQuickAccessWalletTile(
+            QuickAccessWalletTile quickAccessWalletTile);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index 7033ccd..5d896cb 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -236,7 +236,8 @@
 
         // Store callback in a field so it won't get GC'd
         mStatusBarWindowCallback =
-                (keyguardShowing, keyguardOccluded, bouncerShowing, isDozing, panelExpanded) ->
+                (keyguardShowing, keyguardOccluded, bouncerShowing, isDozing, panelExpanded,
+                        isDreaming) ->
                         mBubbles.onNotificationPanelExpandedChanged(panelExpanded);
         notificationShadeWindowController.registerCallback(mStatusBarWindowCallback);
 
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 8ef98f0..bd60401 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.wmshell;
 
-import static android.view.Display.DEFAULT_DISPLAY;
-
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED;
@@ -50,6 +48,7 @@
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.notetask.NoteTaskInitializer;
+import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shared.tracing.ProtoTraceable;
 import com.android.systemui.statusbar.CommandQueue;
@@ -124,6 +123,7 @@
     private final WakefulnessLifecycle mWakefulnessLifecycle;
     private final ProtoTracer mProtoTracer;
     private final UserTracker mUserTracker;
+    private final DisplayTracker mDisplayTracker;
     private final NoteTaskInitializer mNoteTaskInitializer;
     private final Executor mSysUiMainExecutor;
 
@@ -186,6 +186,7 @@
             ProtoTracer protoTracer,
             WakefulnessLifecycle wakefulnessLifecycle,
             UserTracker userTracker,
+            DisplayTracker displayTracker,
             NoteTaskInitializer noteTaskInitializer,
             @Main Executor sysUiMainExecutor) {
         mContext = context;
@@ -203,6 +204,7 @@
         mWakefulnessLifecycle = wakefulnessLifecycle;
         mProtoTracer = protoTracer;
         mUserTracker = userTracker;
+        mDisplayTracker = displayTracker;
         mNoteTaskInitializer = noteTaskInitializer;
         mSysUiMainExecutor = sysUiMainExecutor;
     }
@@ -268,7 +270,7 @@
             public void onStartTransition(boolean isEntering) {
                 mSysUiMainExecutor.execute(() -> {
                     mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE,
-                            true).commitUpdate(DEFAULT_DISPLAY);
+                            true).commitUpdate(mDisplayTracker.getDefaultDisplayId());
                 });
             }
 
@@ -276,7 +278,7 @@
             public void onStartFinished(Rect bounds) {
                 mSysUiMainExecutor.execute(() -> {
                     mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE,
-                            true).commitUpdate(DEFAULT_DISPLAY);
+                            true).commitUpdate(mDisplayTracker.getDefaultDisplayId());
                 });
             }
 
@@ -284,7 +286,7 @@
             public void onStopFinished(Rect bounds) {
                 mSysUiMainExecutor.execute(() -> {
                     mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE,
-                            false).commitUpdate(DEFAULT_DISPLAY);
+                            false).commitUpdate(mDisplayTracker.getDefaultDisplayId());
                 });
             }
         });
@@ -333,7 +335,8 @@
             @Override
             public void setImeWindowStatus(int displayId, IBinder token, int vis,
                     int backDisposition, boolean showImeSwitcher) {
-                if (displayId == DEFAULT_DISPLAY && (vis & InputMethodService.IME_VISIBLE) != 0) {
+                if (displayId == mDisplayTracker.getDefaultDisplayId()
+                        && (vis & InputMethodService.IME_VISIBLE) != 0) {
                     oneHanded.stopOneHanded(
                             OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_POP_IME_OUT);
                 }
@@ -346,7 +349,7 @@
             @Override
             public void onVisibilityChanged(boolean hasFreeformTasks) {
                 mSysUiState.setFlag(SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE, hasFreeformTasks)
-                        .commitUpdate(DEFAULT_DISPLAY);
+                        .commitUpdate(mDisplayTracker.getDefaultDisplayId());
             }
         }, mSysUiMainExecutor);
     }
diff --git a/packages/SystemUI/tests/res/layout/invalid_notification_height.xml b/packages/SystemUI/tests/res/layout/invalid_notification_height.xml
new file mode 100644
index 0000000..aac43bf
--- /dev/null
+++ b/packages/SystemUI/tests/res/layout/invalid_notification_height.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="5dp"/>
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
index 39cc34b..e84a975 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
@@ -21,14 +21,23 @@
 import android.hardware.biometrics.BiometricFaceConstants
 import android.net.Uri
 import android.os.Handler
+import android.os.PowerManager
+import android.os.PowerManager.WAKE_REASON_BIOMETRIC
 import android.os.UserHandle
-import android.provider.Settings
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_WAKE
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.settings.FakeSettings
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
 import org.junit.Before
@@ -39,18 +48,12 @@
 import org.mockito.Mockito.`when`
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
+import java.io.PrintWriter
 
 @SmallTest
 class ActiveUnlockConfigTest : SysuiTestCase() {
-    private val fakeWakeUri = Uri.Builder().appendPath("wake").build()
-    private val fakeUnlockIntentUri = Uri.Builder().appendPath("unlock-intent").build()
-    private val fakeBioFailUri = Uri.Builder().appendPath("bio-fail").build()
-    private val fakeFaceErrorsUri = Uri.Builder().appendPath("face-errors").build()
-    private val fakeFaceAcquiredUri = Uri.Builder().appendPath("face-acquired").build()
-    private val fakeUnlockIntentBioEnroll = Uri.Builder().appendPath("unlock-intent-bio").build()
 
-    @Mock
-    private lateinit var secureSettings: SecureSettings
+    private lateinit var secureSettings: FakeSettings
     @Mock
     private lateinit var contentResolver: ContentResolver
     @Mock
@@ -59,30 +62,19 @@
     private lateinit var dumpManager: DumpManager
     @Mock
     private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+    @Mock private lateinit var mockPrintWriter: PrintWriter
 
     @Captor
     private lateinit var settingsObserverCaptor: ArgumentCaptor<ContentObserver>
 
     private lateinit var activeUnlockConfig: ActiveUnlockConfig
+    private var currentUser: Int = 0
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-
-        `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_WAKE))
-                .thenReturn(fakeWakeUri)
-        `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT))
-                .thenReturn(fakeUnlockIntentUri)
-        `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
-                .thenReturn(fakeBioFailUri)
-        `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS))
-                .thenReturn(fakeFaceErrorsUri)
-        `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO))
-                .thenReturn(fakeFaceAcquiredUri)
-        `when`(secureSettings.getUriFor(
-                Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED))
-                .thenReturn(fakeUnlockIntentBioEnroll)
-
+        currentUser = KeyguardUpdateMonitor.getCurrentUser()
+        secureSettings = FakeSettings()
         activeUnlockConfig = ActiveUnlockConfig(
                 handler,
                 secureSettings,
@@ -92,104 +84,93 @@
     }
 
     @Test
-    fun testRegsitersForSettingsChanges() {
+    fun registersForSettingsChanges() {
         verifyRegisterSettingObserver()
     }
 
     @Test
-    fun testOnWakeupSettingChanged() {
-        verifyRegisterSettingObserver()
-
+    fun onWakeupSettingChanged() {
         // GIVEN no active unlock settings enabled
         assertFalse(
                 activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
-                        ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.WAKE)
+                        ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE)
         )
 
         // WHEN unlock on wake is allowed
-        `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_WAKE,
-                0, 0)).thenReturn(1)
-        updateSetting(fakeWakeUri)
+        secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_WAKE, 1, currentUser)
+        updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_WAKE))
 
         // THEN active unlock triggers allowed on: wake, unlock-intent, and biometric failure
         assertTrue(
                 activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
-                        ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.WAKE)
+                        ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE)
         )
         assertTrue(
                 activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
-                        ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT)
+                        ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT)
         )
         assertTrue(
                 activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
-                        ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL)
+                        ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL)
         )
     }
 
     @Test
-    fun testOnUnlockIntentSettingChanged() {
-        verifyRegisterSettingObserver()
-
+    fun onUnlockIntentSettingChanged() {
         // GIVEN no active unlock settings enabled
         assertFalse(
                 activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
-                        ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT)
+                        ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT)
         )
 
         // WHEN unlock on biometric failed is allowed
-        `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT,
-                0, 0)).thenReturn(1)
-        updateSetting(fakeUnlockIntentUri)
+        secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_UNLOCK_INTENT, 1, currentUser)
+        updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT))
 
         // THEN active unlock triggers allowed on: biometric failure ONLY
         assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
-                ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.WAKE))
+                ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE))
         assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
-                ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT))
+                ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT))
         assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
-                ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL))
+                ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL))
     }
 
     @Test
-    fun testOnBioFailSettingChanged() {
-        verifyRegisterSettingObserver()
-
+    fun onBioFailSettingChanged() {
         // GIVEN no active unlock settings enabled and triggering unlock intent on biometric
         // enrollment setting is disabled (empty string is disabled, null would use the default)
-        `when`(secureSettings.getStringForUser(
-                Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
-                0)).thenReturn("")
-        updateSetting(fakeUnlockIntentBioEnroll)
+        secureSettings.putStringForUser(
+                ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED, "", currentUser)
+        updateSetting(secureSettings.getUriFor(
+            ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
+        ))
         assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
-                ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL))
+                ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL))
 
         // WHEN unlock on biometric failed is allowed
-        `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
-                0, 0)).thenReturn(1)
-        updateSetting(fakeBioFailUri)
+        secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 1, currentUser)
+        updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
 
         // THEN active unlock triggers allowed on: biometric failure ONLY
         assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
-                ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.WAKE))
+                ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE))
         assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
-                ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT))
+                ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT))
         assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
-                ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL))
+                ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL))
     }
 
     @Test
-    fun testFaceErrorSettingsChanged() {
-        verifyRegisterSettingObserver()
-
+    fun faceErrorSettingsChanged() {
         // GIVEN unlock on biometric fail
-        `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
-                0, 0)).thenReturn(1)
-        updateSetting(fakeBioFailUri)
+        secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 1, currentUser)
+        updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
 
         // WHEN face error timeout (3), allow trigger active unlock
-        `when`(secureSettings.getStringForUser(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS,
-                0)).thenReturn("3")
-        updateSetting(fakeFaceAcquiredUri)
+        secureSettings.putStringForUser(
+            ACTIVE_UNLOCK_ON_FACE_ERRORS, "3", currentUser)
+        updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ERRORS))
 
         // THEN active unlock triggers allowed on error TIMEOUT
         assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceError(
@@ -200,20 +181,18 @@
     }
 
     @Test
-    fun testFaceAcquiredSettingsChanged() {
-        verifyRegisterSettingObserver()
-
+    fun faceAcquiredSettingsChanged() {
         // GIVEN unlock on biometric fail
-        `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
-                0, 0)).thenReturn(1)
-        updateSetting(fakeBioFailUri)
+        secureSettings.putStringForUser(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, "1", currentUser)
+        updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
 
         // WHEN face acquiredMsg DARK_GLASSESand MOUTH_COVERING are allowed to trigger
-        `when`(secureSettings.getStringForUser(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
-                0)).thenReturn(
+        secureSettings.putStringForUser(
+            ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
                 "${BiometricFaceConstants.FACE_ACQUIRED_MOUTH_COVERING_DETECTED}" +
-                        "|${BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED}")
-        updateSetting(fakeFaceAcquiredUri)
+                        "|${BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED}",
+            currentUser)
+        updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO))
 
         // THEN active unlock triggers allowed on acquired messages DARK_GLASSES & MOUTH_COVERING
         assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
@@ -228,75 +207,209 @@
     }
 
     @Test
-    fun testTriggerOnUnlockIntentWhenBiometricEnrolledNone() {
-        verifyRegisterSettingObserver()
-
+    fun triggerOnUnlockIntentWhenBiometricEnrolledNone() {
         // GIVEN unlock on biometric fail
-        `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
-                0, 0)).thenReturn(1)
-        updateSetting(fakeBioFailUri)
+        secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 1, currentUser)
+        updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
 
         // GIVEN fingerprint and face are NOT enrolled
         activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor
-        `when`(keyguardUpdateMonitor.isFaceEnrolled()).thenReturn(false)
+        `when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(false)
         `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(false)
 
         // WHEN unlock intent is allowed when NO biometrics are enrolled (0)
-        `when`(secureSettings.getStringForUser(
-                Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
-                0)).thenReturn("${ActiveUnlockConfig.BIOMETRIC_TYPE_NONE}")
-        updateSetting(fakeUnlockIntentBioEnroll)
+
+        secureSettings.putStringForUser(
+            ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
+            "${ActiveUnlockConfig.BiometricType.NONE.intValue}", currentUser)
+        updateSetting(secureSettings.getUriFor(
+            ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
+        ))
 
         // THEN active unlock triggers allowed on unlock intent
         assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
-                ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT))
+                ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT))
     }
 
     @Test
-    fun testTriggerOnUnlockIntentWhenBiometricEnrolledFingerprintOrFaceOnly() {
-        verifyRegisterSettingObserver()
-
+    fun triggerOnUnlockIntentWhenBiometricEnrolledFingerprintOrFaceOnly() {
         // GIVEN unlock on biometric fail
-        `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
-                0, 0)).thenReturn(1)
-        updateSetting(fakeBioFailUri)
+        secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 1, currentUser)
+        updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
 
         // GIVEN fingerprint and face are both enrolled
         activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor
-        `when`(keyguardUpdateMonitor.isFaceEnrolled()).thenReturn(true)
+        `when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(true)
         `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(true)
 
         // WHEN unlock intent is allowed when ONLY fingerprint is enrolled or NO biometircs
         // are enrolled
-        `when`(secureSettings.getStringForUser(
-                Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
-                0)).thenReturn(
-                "${ActiveUnlockConfig.BIOMETRIC_TYPE_ANY_FACE}" +
-                        "|${ActiveUnlockConfig.BIOMETRIC_TYPE_ANY_FINGERPRINT}")
-        updateSetting(fakeUnlockIntentBioEnroll)
+        secureSettings.putStringForUser(
+                ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
+                "${ActiveUnlockConfig.BiometricType.ANY_FACE.intValue}" +
+                        "|${ActiveUnlockConfig.BiometricType.ANY_FINGERPRINT.intValue}",
+            currentUser)
+        updateSetting(secureSettings.getUriFor(
+            ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
+        ))
 
         // THEN active unlock triggers NOT allowed on unlock intent
         assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
-                ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT))
+                ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT))
 
         // WHEN fingerprint ONLY enrolled
-        `when`(keyguardUpdateMonitor.isFaceEnrolled()).thenReturn(false)
+        `when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(false)
         `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(true)
 
         // THEN active unlock triggers allowed on unlock intent
         assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
-                ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT))
+                ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT))
 
         // WHEN face ONLY enrolled
-        `when`(keyguardUpdateMonitor.isFaceEnrolled()).thenReturn(true)
+        `when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(true)
         `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(false)
 
         // THEN active unlock triggers allowed on unlock intent
         assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
-                ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT))
+                ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT))
+    }
+
+    @Test
+    fun isWakeupConsideredUnlockIntent_singleValue() {
+        // GIVEN lift is considered an unlock intent
+        secureSettings.putIntForUser(
+            ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
+            PowerManager.WAKE_REASON_LIFT,
+            currentUser)
+        updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
+
+        // THEN only WAKE_REASON_LIFT is considered an unlock intent
+        for (wakeReason in 0..WAKE_REASON_BIOMETRIC) {
+            if (wakeReason == PowerManager.WAKE_REASON_LIFT) {
+                assertTrue(activeUnlockConfig.isWakeupConsideredUnlockIntent(wakeReason))
+            } else {
+                assertFalse(activeUnlockConfig.isWakeupConsideredUnlockIntent(wakeReason))
+            }
+        }
+    }
+
+    @Test
+    fun isWakeupConsideredUnlockIntent_multiValue() {
+        // GIVEN lift and tap are considered an unlock intent
+        secureSettings.putStringForUser(
+            ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
+            PowerManager.WAKE_REASON_LIFT.toString() +
+                    "|" +
+                    PowerManager.WAKE_REASON_TAP.toString(),
+            currentUser
+        )
+        updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
+
+        // THEN WAKE_REASON_LIFT and WAKE_REASON TAP are considered an unlock intent
+        for (wakeReason in 0..WAKE_REASON_BIOMETRIC) {
+            if (wakeReason == PowerManager.WAKE_REASON_LIFT ||
+                wakeReason == PowerManager.WAKE_REASON_TAP) {
+                assertTrue(activeUnlockConfig.isWakeupConsideredUnlockIntent(wakeReason))
+            } else {
+                assertFalse(activeUnlockConfig.isWakeupConsideredUnlockIntent(wakeReason))
+            }
+        }
+    }
+
+    @Test
+    fun isWakeupConsideredUnlockIntent_emptyValues() {
+        // GIVEN lift and tap are considered an unlock intent
+        secureSettings.putStringForUser(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS, " ",
+            currentUser)
+        updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
+
+        // THEN no wake up gestures are considered an unlock intent
+        for (wakeReason in 0..WAKE_REASON_BIOMETRIC) {
+            assertFalse(activeUnlockConfig.isWakeupConsideredUnlockIntent(wakeReason))
+        }
+    }
+
+    @Test
+    fun isWakeupForceDismissKeyguard_singleValue() {
+        verifyRegisterSettingObserver()
+
+        // GIVEN lift is considered an unlock intent
+        secureSettings.putStringForUser(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
+            PowerManager.WAKE_REASON_LIFT.toString(), currentUser)
+        updateSetting(secureSettings.getUriFor(
+            ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD
+        ))
+
+        // THEN only WAKE_REASON_LIFT is considered an unlock intent
+        for (wakeReason in 0..WAKE_REASON_BIOMETRIC) {
+            if (wakeReason == PowerManager.WAKE_REASON_LIFT) {
+                assertTrue(activeUnlockConfig.shouldWakeupForceDismissKeyguard(wakeReason))
+            } else {
+                assertFalse(activeUnlockConfig.shouldWakeupForceDismissKeyguard(wakeReason))
+            }
+        }
+    }
+
+    @Test
+    fun isWakeupForceDismissKeyguard_emptyValues() {
+        verifyRegisterSettingObserver()
+
+        // GIVEN lift and tap are considered an unlock intent
+        secureSettings.putStringForUser(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
+            " ", currentUser)
+        updateSetting(secureSettings.getUriFor(
+            ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD
+        ))
+
+        // THEN no wake up gestures are considered an unlock intent
+        for (wakeReason in 0..WAKE_REASON_BIOMETRIC) {
+            assertFalse(activeUnlockConfig.shouldWakeupForceDismissKeyguard(wakeReason))
+        }
+    }
+
+    @Test
+    fun isWakeupForceDismissKeyguard_multiValue() {
+        verifyRegisterSettingObserver()
+
+        // GIVEN lift and tap are considered an unlock intent
+        secureSettings.putStringForUser(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
+            PowerManager.WAKE_REASON_LIFT.toString() +
+                    "|" +
+                    PowerManager.WAKE_REASON_TAP.toString(),
+            currentUser
+        )
+        updateSetting(secureSettings.getUriFor(
+            ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD
+        ))
+
+        // THEN WAKE_REASON_LIFT and WAKE_REASON TAP are considered an unlock intent
+        for (wakeReason in 0..WAKE_REASON_BIOMETRIC) {
+            if (wakeReason == PowerManager.WAKE_REASON_LIFT ||
+                wakeReason == PowerManager.WAKE_REASON_TAP) {
+                assertTrue(activeUnlockConfig.shouldWakeupForceDismissKeyguard(wakeReason))
+            } else {
+                assertFalse(activeUnlockConfig.shouldWakeupForceDismissKeyguard(wakeReason))
+            }
+        }
+    }
+
+    @Test
+    fun dump_onUnlockIntentWhenBiometricEnrolled_invalidNum_noArrayOutOfBoundsException() {
+        // GIVEN an invalid input (-1)
+        secureSettings.putStringForUser(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
+            "-1", currentUser)
+
+        // WHEN the setting updates
+        updateSetting(secureSettings.getUriFor(
+            ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
+        ))
+
+        // THEN no exception thrown
+        activeUnlockConfig.dump(mockPrintWriter, emptyArray())
     }
 
     private fun updateSetting(uri: Uri) {
+        verifyRegisterSettingObserver()
         settingsObserverCaptor.value.onChange(
                 false,
                 listOf(uri),
@@ -306,12 +419,17 @@
     }
 
     private fun verifyRegisterSettingObserver() {
-        verifyRegisterSettingObserver(fakeWakeUri)
-        verifyRegisterSettingObserver(fakeUnlockIntentUri)
-        verifyRegisterSettingObserver(fakeBioFailUri)
-        verifyRegisterSettingObserver(fakeFaceErrorsUri)
-        verifyRegisterSettingObserver(fakeFaceAcquiredUri)
-        verifyRegisterSettingObserver(fakeUnlockIntentBioEnroll)
+        verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_WAKE))
+        verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT))
+        verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
+        verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ERRORS))
+        verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO))
+        verifyRegisterSettingObserver(secureSettings.getUriFor(
+            ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
+        ))
+        verifyRegisterSettingObserver(secureSettings.getUriFor(
+            ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS
+        ))
     }
 
     private fun verifyRegisterSettingObserver(uri: Uri) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 00b2fbe..480b8f9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -15,7 +15,6 @@
  */
 package com.android.keyguard
 
-import com.android.systemui.statusbar.CommandQueue
 import android.content.BroadcastReceiver
 import android.testing.AndroidTestingRunner
 import android.view.View
@@ -24,6 +23,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -33,16 +33,17 @@
 import com.android.systemui.plugins.ClockEvents
 import com.android.systemui.plugins.ClockFaceController
 import com.android.systemui.plugins.ClockFaceEvents
+import com.android.systemui.plugins.ClockTickRate
 import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.policy.BatteryController
 import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
-import java.util.TimeZone
-import java.util.concurrent.Executor
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.yield
@@ -57,8 +58,10 @@
 import org.mockito.Mockito.never
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
 import org.mockito.junit.MockitoJUnit
+import java.util.TimeZone
+import java.util.concurrent.Executor
+import org.mockito.Mockito.`when` as whenever
 
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
@@ -72,7 +75,7 @@
     @Mock private lateinit var animations: ClockAnimations
     @Mock private lateinit var events: ClockEvents
     @Mock private lateinit var clock: ClockController
-    @Mock private lateinit var mainExecutor: Executor
+    @Mock private lateinit var mainExecutor: DelayableExecutor
     @Mock private lateinit var bgExecutor: Executor
     @Mock private lateinit var featureFlags: FeatureFlags
     @Mock private lateinit var smallClockController: ClockFaceController
@@ -83,6 +86,7 @@
     @Mock private lateinit var transitionRepository: KeyguardTransitionRepository
     @Mock private lateinit var commandQueue: CommandQueue
     private lateinit var repository: FakeKeyguardRepository
+    private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
     @Mock private lateinit var smallLogBuffer: LogBuffer
     @Mock private lateinit var largeLogBuffer: LogBuffer
     private lateinit var underTest: ClockEventController
@@ -97,11 +101,19 @@
         whenever(largeClockController.events).thenReturn(largeClockEvents)
         whenever(clock.events).thenReturn(events)
         whenever(clock.animations).thenReturn(animations)
+        whenever(smallClockEvents.tickRate).thenReturn(ClockTickRate.PER_MINUTE)
+        whenever(largeClockEvents.tickRate).thenReturn(ClockTickRate.PER_MINUTE)
 
         repository = FakeKeyguardRepository()
+        bouncerRepository = FakeKeyguardBouncerRepository()
 
         underTest = ClockEventController(
-            KeyguardInteractor(repository = repository, commandQueue = commandQueue),
+            KeyguardInteractor(
+                repository = repository,
+                commandQueue = commandQueue,
+                featureFlags = featureFlags,
+                bouncerRepository = bouncerRepository,
+            ),
             KeyguardTransitionInteractor(repository = transitionRepository),
             broadcastDispatcher,
             batteryController,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
index 1059543..50645e5 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
@@ -150,4 +150,11 @@
                 getContext().getResources().getString(R.string.kg_prompt_reason_restart_password),
                 false);
     }
+
+
+    @Test
+    public void testReset() {
+        mKeyguardAbsKeyInputViewController.reset();
+        verify(mKeyguardMessageAreaController).setMessage("", false);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index a4180fd..a5f90f8 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -33,6 +33,7 @@
 import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 import android.widget.RelativeLayout;
@@ -47,6 +48,7 @@
 import com.android.systemui.plugins.ClockController;
 import com.android.systemui.plugins.ClockEvents;
 import com.android.systemui.plugins.ClockFaceController;
+import com.android.systemui.plugins.ClockFaceEvents;
 import com.android.systemui.plugins.log.LogBuffer;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.shared.clocks.AnimatableClockView;
@@ -99,6 +101,8 @@
     @Mock
     private ClockEvents mClockEvents;
     @Mock
+    private ClockFaceEvents mClockFaceEvents;
+    @Mock
     DumpManager mDumpManager;
     @Mock
     ClockEventController mClockEventController;
@@ -118,6 +122,11 @@
     @Mock
     private LogBuffer mLogBuffer;
 
+    private final View mFakeDateView = (View) (new ViewGroup(mContext) {
+        @Override
+        protected void onLayout(boolean changed, int l, int t, int r, int b) {}
+    });
+    private final View mFakeWeatherView = new View(mContext);
     private final View mFakeSmartspaceView = new View(mContext);
 
     private KeyguardClockSwitchController mController;
@@ -145,6 +154,8 @@
         when(mLargeClockView.getContext()).thenReturn(getContext());
 
         when(mView.isAttachedToWindow()).thenReturn(true);
+        when(mSmartspaceController.buildAndConnectDateView(any())).thenReturn(mFakeDateView);
+        when(mSmartspaceController.buildAndConnectWeatherView(any())).thenReturn(mFakeWeatherView);
         when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView);
         mExecutor = new FakeExecutor(new FakeSystemClock());
         mController = new KeyguardClockSwitchController(
@@ -168,6 +179,8 @@
         when(mClockController.getLargeClock()).thenReturn(mLargeClockController);
         when(mClockController.getSmallClock()).thenReturn(mSmallClockController);
         when(mClockController.getEvents()).thenReturn(mClockEvents);
+        when(mSmallClockController.getEvents()).thenReturn(mClockFaceEvents);
+        when(mLargeClockController.getEvents()).thenReturn(mClockFaceEvents);
         when(mClockController.getAnimations()).thenReturn(mClockAnimations);
         when(mClockRegistry.createCurrentClock()).thenReturn(mClockController);
         when(mClockEventController.getClock()).thenReturn(mClockController);
@@ -228,7 +241,7 @@
         mController.init();
         verify(mClockRegistry).registerClockChangeListener(listenerArgumentCaptor.capture());
 
-        listenerArgumentCaptor.getValue().onClockChanged();
+        listenerArgumentCaptor.getValue().onCurrentClockChanged();
         verify(mView, times(2)).setClock(mClockController, StatusBarState.SHADE);
         verify(mClockEventController, times(2)).setClock(mClockController);
     }
@@ -252,6 +265,19 @@
     }
 
     @Test
+    public void onLocaleListChanged_rebuildsSmartspaceViews_whenDecouplingEnabled() {
+        when(mSmartspaceController.isEnabled()).thenReturn(true);
+        when(mSmartspaceController.isDateWeatherDecoupled()).thenReturn(true);
+        mController.init();
+
+        mController.onLocaleListChanged();
+        // Should be called once on initial setup, then once again for locale change
+        verify(mSmartspaceController, times(2)).buildAndConnectDateView(mView);
+        verify(mSmartspaceController, times(2)).buildAndConnectWeatherView(mView);
+        verify(mSmartspaceController, times(2)).buildAndConnectView(mView);
+    }
+
+    @Test
     public void testSmartspaceDisabledShowsKeyguardStatusArea() {
         when(mSmartspaceController.isEnabled()).thenReturn(false);
         mController.init();
@@ -274,8 +300,9 @@
         ArgumentCaptor<ContentObserver> observerCaptor =
                 ArgumentCaptor.forClass(ContentObserver.class);
         mController.init();
-        verify(mSecureSettings).registerContentObserverForUser(any(String.class),
-                anyBoolean(), observerCaptor.capture(), eq(UserHandle.USER_ALL));
+        verify(mSecureSettings).registerContentObserverForUser(
+                eq(Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK),
+                    anyBoolean(), observerCaptor.capture(), eq(UserHandle.USER_ALL));
         ContentObserver observer = observerCaptor.getValue();
         mExecutor.runAllReady();
 
@@ -321,6 +348,22 @@
         assertEquals(0, mController.getClockBottom(10));
     }
 
+    @Test
+    public void testChangeLockscreenWeatherEnabledSetsWeatherViewVisible() {
+        when(mSmartspaceController.isWeatherEnabled()).thenReturn(true);
+        ArgumentCaptor<ContentObserver> observerCaptor =
+                ArgumentCaptor.forClass(ContentObserver.class);
+        mController.init();
+        verify(mSecureSettings).registerContentObserverForUser(
+                eq(Settings.Secure.LOCK_SCREEN_WEATHER_ENABLED), anyBoolean(),
+                    observerCaptor.capture(), eq(UserHandle.USER_ALL));
+        ContentObserver observer = observerCaptor.getValue();
+        mExecutor.runAllReady();
+        // When a settings change has occurred, check that view is visible.
+        observer.onChange(true);
+        mExecutor.runAllReady();
+        assertEquals(View.VISIBLE, mFakeWeatherView.getVisibility());
+    }
 
     private void verifyAttachment(VerificationMode times) {
         verify(mClockRegistry, times).registerClockChangeListener(
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index 8dc1e8f..254f953 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -16,7 +16,6 @@
 
 package com.android.keyguard;
 
-import static android.view.View.INVISIBLE;
 import static android.view.View.VISIBLE;
 
 import static com.android.keyguard.KeyguardClockSwitch.LARGE;
@@ -190,7 +189,6 @@
         assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1);
         assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE);
         assertThat(mSmallClockFrame.getAlpha()).isEqualTo(0);
-        assertThat(mSmallClockFrame.getVisibility()).isEqualTo(INVISIBLE);
     }
 
     @Test
@@ -200,7 +198,6 @@
         assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1);
         assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE);
         assertThat(mSmallClockFrame.getAlpha()).isEqualTo(0);
-        assertThat(mSmallClockFrame.getVisibility()).isEqualTo(INVISIBLE);
     }
 
     @Test
@@ -215,7 +212,6 @@
         // only big clock is removed at switch
         assertThat(mLargeClockFrame.getParent()).isNull();
         assertThat(mLargeClockFrame.getAlpha()).isEqualTo(0);
-        assertThat(mLargeClockFrame.getVisibility()).isEqualTo(INVISIBLE);
     }
 
     @Test
@@ -227,7 +223,6 @@
         // only big clock is removed at switch
         assertThat(mLargeClockFrame.getParent()).isNull();
         assertThat(mLargeClockFrame.getAlpha()).isEqualTo(0);
-        assertThat(mLargeClockFrame.getVisibility()).isEqualTo(INVISIBLE);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
index 01365b4..1a365ef 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
@@ -25,9 +25,7 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
-import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerGlobal;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -39,6 +37,7 @@
 import com.android.keyguard.dagger.KeyguardStatusViewComponent;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.navigationbar.NavigationBarController;
+import com.android.systemui.settings.FakeDisplayTracker;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -58,12 +57,12 @@
     @Mock
     private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
     @Mock
-    private DisplayManager mDisplayManager;
-    @Mock
     private KeyguardDisplayManager.KeyguardPresentation mKeyguardPresentation;
 
+    private Executor mMainExecutor = Runnable::run;
     private Executor mBackgroundExecutor = Runnable::run;
     private KeyguardDisplayManager mManager;
+    private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
 
     // The default and secondary displays are both in the default group
     private Display mDefaultDisplay;
@@ -75,9 +74,9 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mContext.addMockSystemService(DisplayManager.class, mDisplayManager);
         mManager = spy(new KeyguardDisplayManager(mContext, () -> mNavigationBarController,
-                mKeyguardStatusViewComponentFactory, mBackgroundExecutor));
+                mKeyguardStatusViewComponentFactory, mDisplayTracker, mMainExecutor,
+                mBackgroundExecutor));
         doReturn(mKeyguardPresentation).when(mManager).createPresentation(any());
 
         mDefaultDisplay = new Display(DisplayManagerGlobal.getInstance(), Display.DEFAULT_DISPLAY,
@@ -96,23 +95,21 @@
 
     @Test
     public void testShow_defaultDisplayOnly() {
-        when(mDisplayManager.getDisplays()).thenReturn(new Display[]{mDefaultDisplay});
+        mDisplayTracker.setAllDisplays(new Display[]{mDefaultDisplay});
         mManager.show();
         verify(mManager, never()).createPresentation(any());
     }
 
     @Test
     public void testShow_includeSecondaryDisplay() {
-        when(mDisplayManager.getDisplays()).thenReturn(
-                new Display[]{mDefaultDisplay, mSecondaryDisplay});
+        mDisplayTracker.setAllDisplays(new Display[]{mDefaultDisplay, mSecondaryDisplay});
         mManager.show();
         verify(mManager, times(1)).createPresentation(eq(mSecondaryDisplay));
     }
 
     @Test
     public void testShow_includeAlwaysUnlockedDisplay() {
-        when(mDisplayManager.getDisplays()).thenReturn(
-                new Display[]{mDefaultDisplay, mAlwaysUnlockedDisplay});
+        mDisplayTracker.setAllDisplays(new Display[]{mDefaultDisplay, mAlwaysUnlockedDisplay});
 
         mManager.show();
         verify(mManager, never()).createPresentation(any());
@@ -120,9 +117,8 @@
 
     @Test
     public void testShow_includeSecondaryAndAlwaysUnlockedDisplays() {
-        when(mDisplayManager.getDisplays()).thenReturn(
+        mDisplayTracker.setAllDisplays(
                 new Display[]{mDefaultDisplay, mSecondaryDisplay, mAlwaysUnlockedDisplay});
-
         mManager.show();
         verify(mManager, times(1)).createPresentation(eq(mSecondaryDisplay));
     }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java
deleted file mode 100644
index 4021652..0000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright (C) 2018 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.keyguard;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.media.AudioManager;
-import android.telephony.TelephonyManager;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.testing.TestableResources;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class KeyguardHostViewControllerTest extends SysuiTestCase {
-    @Mock
-    private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-
-    private KeyguardHostView mKeyguardHostView;
-    @Mock
-    private AudioManager mAudioManager;
-    @Mock
-    private TelephonyManager mTelephonyManager;
-    @Mock
-    private ViewMediatorCallback mViewMediatorCallback;
-    @Mock
-    KeyguardSecurityContainerController.Factory mKeyguardSecurityContainerControllerFactory;
-    @Mock
-    private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
-
-    @Rule
-    public MockitoRule mMockitoRule = MockitoJUnit.rule();
-
-    private TestableResources mTestableResources;
-    private KeyguardHostViewController mKeyguardHostViewController;
-
-    @Before
-    public void setup() {
-        mTestableResources = mContext.getOrCreateTestableResources();
-
-        mKeyguardHostView = new KeyguardHostView(mContext);
-
-        // Explicitly disable one handed keyguard.
-        mTestableResources.addOverride(
-                R.bool.can_use_one_handed_bouncer, false);
-
-        when(mKeyguardSecurityContainerControllerFactory.create(any(
-                KeyguardSecurityContainer.SecurityCallback.class)))
-                .thenReturn(mKeyguardSecurityContainerController);
-        mKeyguardHostViewController = new KeyguardHostViewController(
-                mKeyguardHostView, mKeyguardUpdateMonitor, mAudioManager, mTelephonyManager,
-                mViewMediatorCallback, mKeyguardSecurityContainerControllerFactory);
-    }
-
-    @Test
-    public void testHasDismissActions() {
-        assertFalse("Action not set yet", mKeyguardHostViewController.hasDismissActions());
-        mKeyguardHostViewController.setOnDismissAction(mock(OnDismissAction.class),
-                null /* cancelAction */);
-        assertTrue("Action should exist", mKeyguardHostViewController.hasDismissActions());
-    }
-
-    @Test
-    public void testOnStartingToHide() {
-        mKeyguardHostViewController.onStartingToHide();
-        verify(mKeyguardSecurityContainerController).onStartingToHide();
-    }
-
-    @Test
-    public void onBouncerVisible_propagatesToKeyguardSecurityContainerController() {
-        mKeyguardHostViewController.onBouncerVisibilityChanged(ViewGroup.VISIBLE);
-        mKeyguardHostViewController.onBouncerVisibilityChanged(ViewGroup.INVISIBLE);
-
-        InOrder order = inOrder(mKeyguardSecurityContainerController);
-        order.verify(mKeyguardSecurityContainerController).onBouncerVisibilityChanged(View.VISIBLE);
-        order.verify(mKeyguardSecurityContainerController).onBouncerVisibilityChanged(
-                View.INVISIBLE);
-    }
-
-    @Test
-    public void testGravityReappliedOnConfigurationChange() {
-        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT,
-                ViewGroup.LayoutParams.MATCH_PARENT);
-        mKeyguardHostView.setLayoutParams(lp);
-
-        // Set initial gravity
-        mTestableResources.addOverride(R.integer.keyguard_host_view_gravity,
-                Gravity.CENTER);
-
-        // Kick off the initial pass...
-        mKeyguardHostViewController.init();
-        assertEquals(
-                ((FrameLayout.LayoutParams) mKeyguardHostView.getLayoutParams()).gravity,
-                Gravity.CENTER);
-
-        // Now simulate a config change
-        mTestableResources.addOverride(R.integer.keyguard_host_view_gravity,
-                Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
-
-        mKeyguardHostViewController.updateResources();
-        assertEquals(
-                ((FrameLayout.LayoutParams) mKeyguardHostView.getLayoutParams()).gravity,
-                Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
-    }
-
-    @Test
-    public void testGravityUsesOneHandGravityWhenApplicable() {
-        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT,
-                ViewGroup.LayoutParams.MATCH_PARENT);
-        mKeyguardHostView.setLayoutParams(lp);
-
-        mTestableResources.addOverride(
-                R.integer.keyguard_host_view_gravity,
-                Gravity.CENTER);
-        mTestableResources.addOverride(
-                R.integer.keyguard_host_view_one_handed_gravity,
-                Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
-
-        // Start disabled.
-        mTestableResources.addOverride(
-                R.bool.can_use_one_handed_bouncer, false);
-
-        mKeyguardHostViewController.init();
-        assertEquals(
-                ((FrameLayout.LayoutParams) mKeyguardHostView.getLayoutParams()).gravity,
-                Gravity.CENTER);
-
-        // And enable
-        mTestableResources.addOverride(
-                R.bool.can_use_one_handed_bouncer, true);
-
-        mKeyguardHostViewController.updateResources();
-        assertEquals(
-                ((FrameLayout.LayoutParams) mKeyguardHostView.getLayoutParams()).gravity,
-                Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
-    }
-
-    @Test
-    public void testUpdateKeyguardPositionDelegatesToSecurityContainer() {
-        mKeyguardHostViewController.updateKeyguardPosition(1.0f);
-
-        verify(mKeyguardSecurityContainerController).updateKeyguardPosition(1.0f);
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index 075ef9d..bffbe17 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -23,12 +23,17 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
@@ -38,13 +43,19 @@
 
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.hardware.biometrics.BiometricOverlayConstants;
 import android.hardware.biometrics.BiometricSourceType;
+import android.media.AudioManager;
+import android.telephony.TelephonyManager;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.testing.TestableResources;
+import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.WindowInsetsController;
+import android.widget.FrameLayout;
 
 import androidx.test.filters.SmallTest;
 
@@ -60,6 +71,7 @@
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.log.SessionTracker;
+import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -71,6 +83,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
 import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
@@ -108,8 +121,6 @@
     @Mock
     private KeyguardInputViewController mInputViewController;
     @Mock
-    private KeyguardSecurityContainer.SecurityCallback mSecurityCallback;
-    @Mock
     private WindowInsetsController mWindowInsetsController;
     @Mock
     private KeyguardSecurityViewFlipper mSecurityViewFlipper;
@@ -126,8 +137,6 @@
     @Mock
     private EmergencyButtonController mEmergencyButtonController;
     @Mock
-    private Resources mResources;
-    @Mock
     private FalsingCollector mFalsingCollector;
     @Mock
     private FalsingManager mFalsingManager;
@@ -147,6 +156,12 @@
     private KeyguardPasswordViewController mKeyguardPasswordViewControllerMock;
     @Mock
     private FalsingA11yDelegate mFalsingA11yDelegate;
+    @Mock
+    private TelephonyManager mTelephonyManager;
+    @Mock
+    private ViewMediatorCallback mViewMediatorCallback;
+    @Mock
+    private AudioManager mAudioManager;
 
     @Captor
     private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallback;
@@ -158,18 +173,25 @@
     private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
     private KeyguardPasswordViewController mKeyguardPasswordViewController;
     private KeyguardPasswordView mKeyguardPasswordView;
+    private TestableResources mTestableResources;
 
     @Before
     public void setup() {
         mConfiguration = new Configuration();
         mConfiguration.setToDefaults(); // Defaults to ORIENTATION_UNDEFINED.
+        mTestableResources = mContext.getOrCreateTestableResources();
 
-        when(mResources.getConfiguration()).thenReturn(mConfiguration);
         when(mView.getContext()).thenReturn(mContext);
-        when(mView.getResources()).thenReturn(mResources);
+        when(mView.getResources()).thenReturn(mContext.getResources());
+        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(/* width=  */ 0, /* height= */
+                0);
+        lp.gravity = 0;
+        when(mView.getLayoutParams()).thenReturn(lp);
         when(mAdminSecondaryLockScreenControllerFactory.create(any(KeyguardSecurityCallback.class)))
                 .thenReturn(mAdminSecondaryLockScreenController);
         when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController);
+        when(mKeyguardSecurityViewFlipperController.getSecurityView(any(SecurityMode.class),
+                any(KeyguardSecurityCallback.class))).thenReturn(mInputViewController);
         mKeyguardPasswordView = spy((KeyguardPasswordView) LayoutInflater.from(mContext).inflate(
                 R.layout.keyguard_password_view, null));
         when(mKeyguardPasswordView.getRootView()).thenReturn(mSecurityViewFlipper);
@@ -178,20 +200,21 @@
         when(mKeyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea.class)))
                 .thenReturn(mKeyguardMessageAreaController);
         when(mKeyguardPasswordView.getWindowInsetsController()).thenReturn(mWindowInsetsController);
+        when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(SecurityMode.PIN);
         mKeyguardPasswordViewController = new KeyguardPasswordViewController(
                 (KeyguardPasswordView) mKeyguardPasswordView, mKeyguardUpdateMonitor,
                 SecurityMode.Password, mLockPatternUtils, null,
                 mKeyguardMessageAreaControllerFactory, null, null, mEmergencyButtonController,
                 null, mock(Resources.class), null, mKeyguardViewController);
 
-        mKeyguardSecurityContainerController = new KeyguardSecurityContainerController.Factory(
+        mKeyguardSecurityContainerController = new KeyguardSecurityContainerController(
                 mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
                 mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
                 mKeyguardStateController, mKeyguardSecurityViewFlipperController,
                 mConfigurationController, mFalsingCollector, mFalsingManager,
                 mUserSwitcherController, mFeatureFlags, mGlobalSettings,
-                mSessionTracker, Optional.of(mSideFpsController), mFalsingA11yDelegate).create(
-                mSecurityCallback);
+                mSessionTracker, Optional.of(mSideFpsController), mFalsingA11yDelegate,
+                mTelephonyManager, mViewMediatorCallback, mAudioManager);
     }
 
     @Test
@@ -243,7 +266,8 @@
                 eq(mFalsingA11yDelegate));
 
         // Update rotation. Should trigger update
-        mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;
+        mTestableResources.getResources().getConfiguration().orientation =
+                Configuration.ORIENTATION_LANDSCAPE;
 
         mKeyguardSecurityContainerController.updateResources();
         verify(mView).initMode(eq(MODE_DEFAULT), eq(mGlobalSettings), eq(mFalsingManager),
@@ -277,7 +301,7 @@
 
     @Test
     public void showSecurityScreen_oneHandedMode_flagDisabled_noOneHandedMode() {
-        when(mResources.getBoolean(R.bool.can_use_one_handed_bouncer)).thenReturn(false);
+        mTestableResources.addOverride(R.bool.can_use_one_handed_bouncer, false);
         when(mKeyguardSecurityViewFlipperController.getSecurityView(
                 eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class)))
                 .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
@@ -291,7 +315,7 @@
 
     @Test
     public void showSecurityScreen_oneHandedMode_flagEnabled_oneHandedMode() {
-        when(mResources.getBoolean(R.bool.can_use_one_handed_bouncer)).thenReturn(true);
+        mTestableResources.addOverride(R.bool.can_use_one_handed_bouncer, true);
         when(mKeyguardSecurityViewFlipperController.getSecurityView(
                 eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class)))
                 .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
@@ -305,7 +329,7 @@
 
     @Test
     public void showSecurityScreen_twoHandedMode_flagEnabled_noOneHandedMode() {
-        when(mResources.getBoolean(R.bool.can_use_one_handed_bouncer)).thenReturn(true);
+        mTestableResources.addOverride(R.bool.can_use_one_handed_bouncer, true);
         setupGetSecurityView();
 
         mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Password);
@@ -350,7 +374,8 @@
 
         mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
 
-        verify(mSideFpsController).show(SideFpsUiRequestSource.PRIMARY_BOUNCER);
+        verify(mSideFpsController).show(SideFpsUiRequestSource.PRIMARY_BOUNCER,
+                BiometricOverlayConstants.REASON_AUTH_KEYGUARD);
         verify(mSideFpsController, never()).hide(any());
     }
 
@@ -363,7 +388,7 @@
         mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
 
         verify(mSideFpsController).hide(SideFpsUiRequestSource.PRIMARY_BOUNCER);
-        verify(mSideFpsController, never()).show(any());
+        verify(mSideFpsController, never()).show(any(), anyInt());
     }
 
     @Test
@@ -375,7 +400,7 @@
         mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
 
         verify(mSideFpsController).hide(SideFpsUiRequestSource.PRIMARY_BOUNCER);
-        verify(mSideFpsController, never()).show(any());
+        verify(mSideFpsController, never()).show(any(), anyInt());
     }
 
     @Test
@@ -387,7 +412,7 @@
         mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
 
         verify(mSideFpsController).hide(SideFpsUiRequestSource.PRIMARY_BOUNCER);
-        verify(mSideFpsController, never()).show(any());
+        verify(mSideFpsController, never()).show(any(), anyInt());
     }
 
     @Test
@@ -395,13 +420,14 @@
         setupGetSecurityView();
         setupConditionsToEnableSideFpsHint();
         mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
-        verify(mSideFpsController, atLeastOnce()).show(SideFpsUiRequestSource.PRIMARY_BOUNCER);
+        verify(mSideFpsController, atLeastOnce()).show(SideFpsUiRequestSource.PRIMARY_BOUNCER,
+                BiometricOverlayConstants.REASON_AUTH_KEYGUARD);
         reset(mSideFpsController);
 
         mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.INVISIBLE);
 
         verify(mSideFpsController).hide(SideFpsUiRequestSource.PRIMARY_BOUNCER);
-        verify(mSideFpsController, never()).show(any());
+        verify(mSideFpsController, never()).show(any(), anyInt());
     }
 
     @Test
@@ -416,13 +442,14 @@
         setupGetSecurityView();
         setupConditionsToEnableSideFpsHint();
         mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
-        verify(mSideFpsController, atLeastOnce()).show(SideFpsUiRequestSource.PRIMARY_BOUNCER);
+        verify(mSideFpsController, atLeastOnce()).show(SideFpsUiRequestSource.PRIMARY_BOUNCER,
+                BiometricOverlayConstants.REASON_AUTH_KEYGUARD);
         reset(mSideFpsController);
 
         mKeyguardSecurityContainerController.onStartingToHide();
 
         verify(mSideFpsController).hide(SideFpsUiRequestSource.PRIMARY_BOUNCER);
-        verify(mSideFpsController, never()).show(any());
+        verify(mSideFpsController, never()).show(any(), anyInt());
     }
 
     @Test
@@ -430,13 +457,14 @@
         setupGetSecurityView();
         setupConditionsToEnableSideFpsHint();
         mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
-        verify(mSideFpsController, atLeastOnce()).show(SideFpsUiRequestSource.PRIMARY_BOUNCER);
+        verify(mSideFpsController, atLeastOnce()).show(SideFpsUiRequestSource.PRIMARY_BOUNCER,
+                BiometricOverlayConstants.REASON_AUTH_KEYGUARD);
         reset(mSideFpsController);
 
         mKeyguardSecurityContainerController.onPause();
 
         verify(mSideFpsController).hide(SideFpsUiRequestSource.PRIMARY_BOUNCER);
-        verify(mSideFpsController, never()).show(any());
+        verify(mSideFpsController, never()).show(any(), anyInt());
     }
 
     @Test
@@ -448,7 +476,8 @@
 
         mKeyguardSecurityContainerController.onResume(0);
 
-        verify(mSideFpsController).show(SideFpsUiRequestSource.PRIMARY_BOUNCER);
+        verify(mSideFpsController).show(SideFpsUiRequestSource.PRIMARY_BOUNCER,
+                BiometricOverlayConstants.REASON_AUTH_KEYGUARD);
         verify(mSideFpsController, never()).hide(any());
     }
 
@@ -463,7 +492,7 @@
         mKeyguardSecurityContainerController.onResume(0);
 
         verify(mSideFpsController).hide(SideFpsUiRequestSource.PRIMARY_BOUNCER);
-        verify(mSideFpsController, never()).show(any());
+        verify(mSideFpsController, never()).show(any(), anyInt());
     }
 
     @Test
@@ -482,7 +511,9 @@
                 SecurityMode.SimPin);
 
         // THEN the next security method of PIN is set, and the keyguard is not marked as done
-        verify(mSecurityCallback, never()).finish(anyBoolean(), anyInt());
+
+        verify(mViewMediatorCallback, never()).keyguardDonePending(anyBoolean(), anyInt());
+        verify(mViewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt());
         assertThat(mKeyguardSecurityContainerController.getCurrentSecurityMode())
                 .isEqualTo(SecurityMode.PIN);
     }
@@ -556,17 +587,19 @@
     }
 
     @Test
-    public void onDensityorFontScaleChanged() {
+    public void onDensityOrFontScaleChanged() {
         ArgumentCaptor<ConfigurationController.ConfigurationListener>
                 configurationListenerArgumentCaptor = ArgumentCaptor.forClass(
                 ConfigurationController.ConfigurationListener.class);
         mKeyguardSecurityContainerController.onViewAttached();
         verify(mConfigurationController).addCallback(configurationListenerArgumentCaptor.capture());
+        clearInvocations(mKeyguardSecurityViewFlipperController);
+
         configurationListenerArgumentCaptor.getValue().onDensityOrFontScaleChanged();
 
         verify(mView).onDensityOrFontScaleChanged();
         verify(mKeyguardSecurityViewFlipperController).clearViews();
-        verify(mKeyguardSecurityViewFlipperController).getSecurityView(any(SecurityMode.class),
+        verify(mKeyguardSecurityViewFlipperController).getSecurityView(eq(SecurityMode.PIN),
                 any(KeyguardSecurityCallback.class));
     }
 
@@ -577,11 +610,13 @@
                 ConfigurationController.ConfigurationListener.class);
         mKeyguardSecurityContainerController.onViewAttached();
         verify(mConfigurationController).addCallback(configurationListenerArgumentCaptor.capture());
+        clearInvocations(mKeyguardSecurityViewFlipperController);
+
         configurationListenerArgumentCaptor.getValue().onThemeChanged();
 
         verify(mView).reloadColors();
         verify(mKeyguardSecurityViewFlipperController).clearViews();
-        verify(mKeyguardSecurityViewFlipperController).getSecurityView(any(SecurityMode.class),
+        verify(mKeyguardSecurityViewFlipperController).getSecurityView(eq(SecurityMode.PIN),
                 any(KeyguardSecurityCallback.class));
     }
 
@@ -592,10 +627,94 @@
                 ConfigurationController.ConfigurationListener.class);
         mKeyguardSecurityContainerController.onViewAttached();
         verify(mConfigurationController).addCallback(configurationListenerArgumentCaptor.capture());
+        clearInvocations(mKeyguardSecurityViewFlipperController);
+
         configurationListenerArgumentCaptor.getValue().onUiModeChanged();
 
         verify(mView).reloadColors();
         verify(mKeyguardSecurityViewFlipperController).clearViews();
+        verify(mKeyguardSecurityViewFlipperController).getSecurityView(eq(SecurityMode.PIN),
+                any(KeyguardSecurityCallback.class));
+    }
+
+    @Test
+    public void testHasDismissActions() {
+        assertFalse("Action not set yet", mKeyguardSecurityContainerController.hasDismissActions());
+        mKeyguardSecurityContainerController.setOnDismissAction(mock(
+                        ActivityStarter.OnDismissAction.class),
+                null /* cancelAction */);
+        assertTrue("Action should exist", mKeyguardSecurityContainerController.hasDismissActions());
+    }
+
+    @Test
+    public void testOnStartingToHide() {
+        mKeyguardSecurityContainerController.onStartingToHide();
+        verify(mInputViewController).onStartingToHide();
+    }
+
+    @Test
+    public void testGravityReappliedOnConfigurationChange() {
+        // Set initial gravity
+        mTestableResources.addOverride(R.integer.keyguard_host_view_gravity,
+                Gravity.CENTER);
+
+        // Kick off the initial pass...
+        mKeyguardSecurityContainerController.onInit();
+        verify(mView).setLayoutParams(argThat(
+                (ArgumentMatcher<FrameLayout.LayoutParams>) argument ->
+                        argument.gravity == Gravity.CENTER));
+        clearInvocations(mView);
+
+        // Now simulate a config change
+        mTestableResources.addOverride(R.integer.keyguard_host_view_gravity,
+                Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
+
+        mKeyguardSecurityContainerController.updateResources();
+        verify(mView).setLayoutParams(argThat(
+                (ArgumentMatcher<FrameLayout.LayoutParams>) argument ->
+                        argument.gravity == (Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM)));
+    }
+
+    @Test
+    public void testGravityUsesOneHandGravityWhenApplicable() {
+        mTestableResources.addOverride(
+                R.integer.keyguard_host_view_gravity,
+                Gravity.CENTER);
+        mTestableResources.addOverride(
+                R.integer.keyguard_host_view_one_handed_gravity,
+                Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
+
+        // Start disabled.
+        mTestableResources.addOverride(
+                R.bool.can_use_one_handed_bouncer, false);
+
+        mKeyguardSecurityContainerController.onInit();
+        verify(mView).setLayoutParams(argThat(
+                (ArgumentMatcher<FrameLayout.LayoutParams>) argument ->
+                        argument.gravity == Gravity.CENTER));
+        clearInvocations(mView);
+
+        // And enable
+        mTestableResources.addOverride(
+                R.bool.can_use_one_handed_bouncer, true);
+
+        mKeyguardSecurityContainerController.updateResources();
+        verify(mView).setLayoutParams(argThat(
+                (ArgumentMatcher<FrameLayout.LayoutParams>) argument ->
+                        argument.gravity == (Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM)));
+    }
+
+    @Test
+    public void testUpdateKeyguardPositionDelegatesToSecurityContainer() {
+        mKeyguardSecurityContainerController.updateKeyguardPosition(1.0f);
+        verify(mView).updatePositionByTouchX(1.0f);
+    }
+
+
+    @Test
+    public void testReinflateViewFlipper() {
+        mKeyguardSecurityContainerController.reinflateViewFlipper();
+        verify(mKeyguardSecurityViewFlipperController).clearViews();
         verify(mKeyguardSecurityViewFlipperController).getSecurityView(any(SecurityMode.class),
                 any(KeyguardSecurityCallback.class));
     }
@@ -625,7 +744,7 @@
     }
 
     private void setSideFpsHintEnabledFromResources(boolean enabled) {
-        when(mResources.getBoolean(R.bool.config_show_sidefps_hint_on_bouncer)).thenReturn(
+        mTestableResources.addOverride(R.bool.config_show_sidefps_hint_on_bouncer,
                 enabled);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
index 06082b6..68dc6c0 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
@@ -29,6 +29,7 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.KeyguardSliceProvider;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.settings.FakeDisplayTracker;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.tuner.TunerService;
 
@@ -52,6 +53,7 @@
     private ConfigurationController mConfigurationController;
     @Mock
     private ActivityStarter mActivityStarter;
+    private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
     private DumpManager mDumpManager = new DumpManager();
 
     private KeyguardSliceViewController mController;
@@ -63,7 +65,7 @@
         when(mView.getContext()).thenReturn(mContext);
         mController = new KeyguardSliceViewController(
                 mView, mActivityStarter, mConfigurationController,
-                mTunerService, mDumpManager);
+                mTunerService, mDumpManager, mDisplayTracker);
         mController.setupUri(KeyguardSliceProvider.KEYGUARD_SLICE_URI);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index df6752a..dc90e2d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -17,7 +17,6 @@
 package com.android.keyguard;
 
 import static android.app.StatusBarManager.SESSION_KEYGUARD;
-import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START;
 import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
 import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
 import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON;
@@ -28,6 +27,8 @@
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
 import static com.android.keyguard.FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED;
+import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_AVAILABLE;
+import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_CANCELLING;
 import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_CANCELLING_RESTARTING;
 import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT;
 import static com.android.keyguard.KeyguardUpdateMonitor.HAL_POWER_PRESS_TIMEOUT;
@@ -41,7 +42,6 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyObject;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
@@ -95,7 +95,6 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.provider.Settings;
 import android.service.dreams.IDreamManager;
 import android.service.trust.TrustAgentService;
 import android.telephony.ServiceState;
@@ -238,14 +237,12 @@
     @Mock
     private UiEventLogger mUiEventLogger;
     @Mock
-    private PowerManager mPowerManager;
-    @Mock
     private GlobalSettings mGlobalSettings;
     private FaceWakeUpTriggersConfig mFaceWakeUpTriggersConfig;
     @Mock
     private FingerprintInteractiveToAuthProvider mInteractiveToAuthProvider;
 
-
+    private List<FingerprintSensorPropertiesInternal> mFingerprintSensorProperties;
     private final int mCurrentUserId = 100;
     private final UserInfo mCurrentUserInfo = new UserInfo(mCurrentUserId, "Test user", 0);
 
@@ -285,9 +282,7 @@
         when(mFaceSensorProperties.get(anyInt())).thenReturn(
                 createFaceSensorProperties(/* supportsFaceDetection = */ false));
 
-        when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
-        when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
-        when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(List.of(
+        mFingerprintSensorProperties = List.of(
                 new FingerprintSensorPropertiesInternal(1 /* sensorId */,
                         FingerprintSensorProperties.STRENGTH_STRONG,
                         1 /* maxEnrollmentsPerUser */,
@@ -296,7 +291,11 @@
                                 "1.01" /* firmwareVersion */,
                                 "00000001" /* serialNumber */, "" /* softwareVersion */)),
                         FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
-                        false /* resetLockoutRequiresHAT */)));
+                        false /* resetLockoutRequiresHAT */));
+        when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+        when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+        when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(
+                mFingerprintSensorProperties);
         when(mUserManager.isUserUnlocked(anyInt())).thenReturn(true);
         when(mUserManager.isPrimaryUser()).thenReturn(true);
         when(mStrongAuthTracker.getStub()).thenReturn(mock(IStrongAuthTracker.Stub.class));
@@ -690,12 +689,36 @@
         // WHEN fingerprint is locked out
         fingerprintErrorLockedOut();
 
-        // THEN unlocking with fingeprint is not allowed
+        // THEN unlocking with fingerprint is not allowed
         Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
                 BiometricSourceType.FINGERPRINT));
     }
 
     @Test
+    public void trustAgentHasTrust() {
+        // WHEN user has trust
+        mKeyguardUpdateMonitor.onTrustChanged(true, true, getCurrentUser(), 0, null);
+
+        // THEN user is considered as "having trust" and bouncer can be skipped
+        Assert.assertTrue(mKeyguardUpdateMonitor.getUserHasTrust(getCurrentUser()));
+        Assert.assertTrue(mKeyguardUpdateMonitor.getUserCanSkipBouncer(getCurrentUser()));
+    }
+
+    @Test
+    public void trustAgentHasTrust_fingerprintLockout() {
+        // GIVEN user has trust
+        mKeyguardUpdateMonitor.onTrustChanged(true, true, getCurrentUser(), 0, null);
+        Assert.assertTrue(mKeyguardUpdateMonitor.getUserHasTrust(getCurrentUser()));
+
+        // WHEN fingerprint is locked out
+        fingerprintErrorLockedOut();
+
+        // THEN user is NOT considered as "having trust" and bouncer cannot be skipped
+        Assert.assertFalse(mKeyguardUpdateMonitor.getUserHasTrust(getCurrentUser()));
+        Assert.assertFalse(mKeyguardUpdateMonitor.getUserCanSkipBouncer(getCurrentUser()));
+    }
+
+    @Test
     public void testTriesToAuthenticate_whenBouncer() {
         setKeyguardBouncerVisibility(true);
 
@@ -751,12 +774,43 @@
     }
 
     @Test
+    public void nofaceDetect_whenStrongAuthRequiredAndBypassUdfpsSupportedAndFpRunning() {
+        // GIVEN mocked keyguardUpdateMonitorCallback
+        KeyguardUpdateMonitorCallback keyguardUpdateMonitorCallback =
+                mock(KeyguardUpdateMonitorCallback.class);
+        mKeyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback);
+
+        // GIVEN bypass is enabled, face detection is supported
+        lockscreenBypassIsAllowed();
+        supportsFaceDetection();
+        keyguardIsVisible();
+
+        // GIVEN udfps is supported and strong auth required for weak biometrics (face) only
+        givenUdfpsSupported();
+        strongAuthRequiredForWeakBiometricOnly(); // this allows fingerprint to run but not face
+
+        // WHEN the device wakes up
+        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
+        mTestableLooper.processAllMessages();
+
+        // THEN face detect and authenticate are NOT triggered
+        verify(mFaceManager, never()).detectFace(any(), any(), anyInt());
+        verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
+                anyBoolean());
+
+        // THEN biometric help message sent to callback
+        verify(keyguardUpdateMonitorCallback).onBiometricHelp(
+                eq(BIOMETRIC_HELP_FACE_NOT_AVAILABLE), anyString(), eq(BiometricSourceType.FACE));
+    }
+
+    @Test
     public void faceDetect_whenStrongAuthRequiredAndBypass() {
         // GIVEN bypass is enabled, face detection is supported and strong auth is required
         lockscreenBypassIsAllowed();
         supportsFaceDetection();
         strongAuthRequiredEncrypted();
         keyguardIsVisible();
+        // fingerprint is NOT running, UDFPS is NOT supported
 
         // WHEN the device wakes up
         mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
@@ -834,6 +888,38 @@
     }
 
     @Test
+    public void doesNotTryToAuthenticateWhenKeyguardIsNotShowingButOccluded_whenAssistant() {
+        mKeyguardUpdateMonitor.setKeyguardShowing(false, true);
+        mKeyguardUpdateMonitor.setAssistantVisible(true);
+
+        verify(mFaceManager, never()).authenticate(any(),
+                any(),
+                any(),
+                any(),
+                anyInt(),
+                anyBoolean());
+    }
+
+    @Test
+    public void noFpListeningWhenKeyguardIsOccluded_unlessAlternateBouncerShowing() {
+        // GIVEN device is awake but occluded
+        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
+        mKeyguardUpdateMonitor.setKeyguardShowing(false, true);
+
+        // THEN fingerprint shouldn't listen
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isFalse();
+        verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
+                anyInt(), anyInt());
+
+        // WHEN alternate bouncer is shown
+        mKeyguardUpdateMonitor.setAlternateBouncerShowing(true);
+
+        // THEN make sure FP listening begins
+        verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
+                anyInt());
+    }
+
+    @Test
     public void testTriesToAuthenticate_whenTrustOnAgentKeyguard_ifBypass() {
         mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
         mTestableLooper.processAllMessages();
@@ -846,6 +932,32 @@
     }
 
     @Test
+    public void faceUnlockDoesNotRunWhenDeviceIsGoingToSleepWithAssistantVisible() {
+        mKeyguardUpdateMonitor.setKeyguardShowing(true, true);
+        mKeyguardUpdateMonitor.setAssistantVisible(true);
+
+        verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
+        mTestableLooper.processAllMessages();
+        clearInvocations(mFaceManager);
+
+        // Device going to sleep while assistant is visible
+        mKeyguardUpdateMonitor.handleStartedGoingToSleep(0);
+        mKeyguardUpdateMonitor.handleFinishedGoingToSleep(0);
+        mTestableLooper.moveTimeForward(DEFAULT_CANCEL_SIGNAL_TIMEOUT);
+        mTestableLooper.processAllMessages();
+
+        mKeyguardUpdateMonitor.handleKeyguardReset();
+
+        assertThat(mKeyguardUpdateMonitor.isFaceDetectionRunning()).isFalse();
+        verify(mFaceManager, never()).authenticate(any(),
+                any(),
+                any(),
+                any(),
+                anyInt(),
+                anyBoolean());
+    }
+
+    @Test
     public void testIgnoresAuth_whenTrustAgentOnKeyguard_withoutBypass() {
         mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
         mTestableLooper.processAllMessages();
@@ -1055,10 +1167,11 @@
         assertThat(mKeyguardUpdateMonitor.isFingerprintLockedOut()).isEqualTo(fpLocked);
         assertThat(mKeyguardUpdateMonitor.isFaceLockedOut()).isEqualTo(faceLocked);
 
-        // Fingerprint should be restarted once its cancelled bc on lockout, the device
-        // can still detectFingerprint (and if it's not locked out, fingerprint can listen)
+        // Fingerprint should be cancelled on lockout if going to lockout state, else
+        // restarted if it's not
         assertThat(mKeyguardUpdateMonitor.mFingerprintRunningState)
-                .isEqualTo(BIOMETRIC_STATE_CANCELLING_RESTARTING);
+                .isEqualTo(fpLocked
+                        ? BIOMETRIC_STATE_CANCELLING : BIOMETRIC_STATE_CANCELLING_RESTARTING);
     }
 
     @Test
@@ -1675,7 +1788,7 @@
     }
 
     @Test
-    public void testShouldListenForFace_whenOccludingAppRequestsFaceAuth_returnsTrue()
+    public void shouldListenForFace_secureCameraLaunchedButAlternateBouncerIsLaunched_returnsTrue()
             throws RemoteException {
         // Face auth should run when the following is true.
         keyguardNotGoingAway();
@@ -1691,7 +1804,7 @@
 
         assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
 
-        occludingAppRequestsFaceAuth();
+        alternateBouncerVisible();
         mTestableLooper.processAllMessages();
 
         assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
@@ -1781,7 +1894,7 @@
     }
 
     @Test
-    public void testShouldListenForFace_whenUdfpsBouncerIsShowing_returnsTrue()
+    public void testShouldListenForFace_whenAlternateBouncerIsShowing_returnsTrue()
             throws RemoteException {
         // Preconditions for face auth to run
         keyguardNotGoingAway();
@@ -1793,13 +1906,13 @@
         mTestableLooper.processAllMessages();
         assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
 
-        mKeyguardUpdateMonitor.setUdfpsBouncerShowing(true);
+        mKeyguardUpdateMonitor.setAlternateBouncerShowing(true);
 
         assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
     }
 
     @Test
-    public void testShouldListenForFace_udfpsBouncerIsShowingButDeviceGoingToSleep_returnsFalse()
+    public void testShouldListenForFace_alternateBouncerShowingButDeviceGoingToSleep_returnsFalse()
             throws RemoteException {
         // Preconditions for face auth to run
         keyguardNotGoingAway();
@@ -1809,7 +1922,7 @@
         biometricsEnabledForCurrentUser();
         userNotCurrentlySwitching();
         deviceNotGoingToSleep();
-        mKeyguardUpdateMonitor.setUdfpsBouncerShowing(true);
+        alternateBouncerVisible();
         mTestableLooper.processAllMessages();
         assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
 
@@ -1819,6 +1932,10 @@
         assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
     }
 
+    private void alternateBouncerVisible() {
+        mKeyguardUpdateMonitor.setAlternateBouncerShowing(true);
+    }
+
     @Test
     public void testShouldListenForFace_whenFaceIsLockedOut_returnsTrue()
             throws RemoteException {
@@ -1829,7 +1946,7 @@
         biometricsNotDisabledThroughDevicePolicyManager();
         biometricsEnabledForCurrentUser();
         userNotCurrentlySwitching();
-        mKeyguardUpdateMonitor.setUdfpsBouncerShowing(true);
+        mKeyguardUpdateMonitor.setAlternateBouncerShowing(true);
         mTestableLooper.processAllMessages();
         assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
 
@@ -1872,28 +1989,6 @@
     }
 
     @Test
-    public void testFingerAcquired_wakesUpPowerManager() {
-        cleanupKeyguardUpdateMonitor();
-        mContext.getOrCreateTestableResources().addOverride(
-                com.android.internal.R.bool.kg_wake_on_acquire_start, true);
-        mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mContext);
-        fingerprintAcquireStart();
-
-        verify(mPowerManager).wakeUp(anyLong(), anyInt(), anyString());
-    }
-
-    @Test
-    public void testFingerAcquired_doesNotWakeUpPowerManager() {
-        cleanupKeyguardUpdateMonitor();
-        mContext.getOrCreateTestableResources().addOverride(
-                com.android.internal.R.bool.kg_wake_on_acquire_start, false);
-        mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mContext);
-        fingerprintAcquireStart();
-
-        verify(mPowerManager, never()).wakeUp(anyLong(), anyInt(), anyString());
-    }
-
-    @Test
     public void testDreamingStopped_faceDoesNotRun() {
         mKeyguardUpdateMonitor.dispatchDreamingStopped();
         mTestableLooper.processAllMessages();
@@ -2171,7 +2266,7 @@
 
         // GIVEN active unlock triggers on biometric failures
         when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
-                ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL))
+                ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL))
                 .thenReturn(true);
 
         // WHEN fingerprint fails
@@ -2194,7 +2289,7 @@
 
         // GIVEN active unlock triggers on biometric failures
         when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
-                ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL))
+                ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL))
                 .thenReturn(true);
 
         // WHEN face fails & bypass is not allowed
@@ -2218,7 +2313,7 @@
 
         // GIVEN active unlock triggers on biometric failures
         when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
-                ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL))
+                ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL))
                 .thenReturn(true);
 
         // WHEN face fails & bypass is not allowed
@@ -2240,7 +2335,7 @@
 
         // GIVEN active unlock triggers on biometric failures
         when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
-                ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL))
+                ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL))
                 .thenReturn(true);
 
         // WHEN face fails & on the bouncer
@@ -2300,6 +2395,56 @@
         assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
     }
 
+    @Test
+    public void unfoldWakeup_requestActiveUnlock_forceDismissKeyguard()
+            throws RemoteException {
+        // GIVEN shouldTriggerActiveUnlock
+        keyguardIsVisible();
+        when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+
+        // GIVEN active unlock triggers on wakeup
+        when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+                ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE))
+                .thenReturn(true);
+
+        // GIVEN an unfold should force dismiss the keyguard
+        when(mActiveUnlockConfig.shouldWakeupForceDismissKeyguard(
+                PowerManager.WAKE_REASON_UNFOLD_DEVICE)).thenReturn(true);
+
+        // WHEN device wakes up from an unfold
+        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_UNFOLD_DEVICE);
+        mTestableLooper.processAllMessages();
+
+        // THEN request unlock with a keyguard dismissal
+        verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+                eq(true));
+    }
+
+    @Test
+    public void unfoldWakeup_requestActiveUnlock_noDismissKeyguard()
+            throws RemoteException {
+        // GIVEN shouldTriggerActiveUnlock on wake from UNFOLD_DEVICE
+        keyguardIsVisible();
+        when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+
+        // GIVEN active unlock triggers on wakeup
+        when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+                ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE))
+                .thenReturn(true);
+
+        // GIVEN an unfold should NOT force dismiss the keyguard
+        when(mActiveUnlockConfig.shouldWakeupForceDismissKeyguard(
+                PowerManager.WAKE_REASON_UNFOLD_DEVICE)).thenReturn(false);
+
+        // WHEN device wakes up from an unfold
+        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_UNFOLD_DEVICE);
+        mTestableLooper.processAllMessages();
+
+        // THEN request unlock WITHOUT a keyguard dismissal
+        verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+                eq(false));
+    }
+
     private void userDeviceLockDown() {
         when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
         when(mStrongAuthTracker.getStrongAuthForUser(mCurrentUserId))
@@ -2374,11 +2519,6 @@
                 .onAuthenticationError(FINGERPRINT_ERROR_LOCKOUT, "Fingerprint locked out");
     }
 
-    private void fingerprintAcquireStart() {
-        mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
-                .onAuthenticationAcquired(FINGERPRINT_ACQUIRED_START);
-    }
-
     private void deviceInPostureStateOpened() {
         mKeyguardUpdateMonitor.mPostureCallback.onPostureChanged(DEVICE_POSTURE_OPENED);
     }
@@ -2435,6 +2575,11 @@
         when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
     }
 
+    private void strongAuthRequiredForWeakBiometricOnly() {
+        when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(eq(true))).thenReturn(true);
+        when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(eq(false))).thenReturn(false);
+    }
+
     private void strongAuthNotRequired() {
         when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
                 .thenReturn(0);
@@ -2489,6 +2634,12 @@
         mTestableLooper.processAllMessages();
     }
 
+    private void givenUdfpsSupported() {
+        Assert.assertFalse(mFingerprintSensorProperties.isEmpty());
+        when(mAuthController.getUdfpsProps()).thenReturn(mFingerprintSensorProperties);
+        Assert.assertTrue(mKeyguardUpdateMonitor.isUdfpsSupported());
+    }
+
     private void setBroadcastReceiverPendingResult(BroadcastReceiver receiver) {
         BroadcastReceiver.PendingResult pendingResult =
                 new BroadcastReceiver.PendingResult(Activity.RESULT_OK,
@@ -2525,7 +2676,7 @@
                     mAuthController, mTelephonyListenerManager,
                     mInteractionJankMonitor, mLatencyTracker, mActiveUnlockConfig,
                     mKeyguardUpdateMonitorLogger, mUiEventLogger, () -> mSessionTracker,
-                    mPowerManager, mTrustManager, mSubscriptionManager, mUserManager,
+                    mTrustManager, mSubscriptionManager, mUserManager,
                     mDreamManager, mDevicePolicyManager, mSensorPrivacyManager, mTelephonyManager,
                     mPackageManager, mFaceManager, mFingerprintManager, mBiometricManager,
                     mFaceWakeUpTriggersConfig, mDevicePostureController,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
index 05bd1e4..456702b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
@@ -43,6 +43,7 @@
 import com.android.systemui.doze.util.BurnInHelperKt;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository;
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
@@ -159,7 +160,12 @@
                 mAuthRippleController,
                 mResources,
                 new KeyguardTransitionInteractor(mTransitionRepository),
-                new KeyguardInteractor(new FakeKeyguardRepository(), mCommandQueue),
+                new KeyguardInteractor(
+                        new FakeKeyguardRepository(),
+                        mCommandQueue,
+                        mFeatureFlags,
+                        new FakeKeyguardBouncerRepository()
+                ),
                 mFeatureFlags
         );
     }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
index b69491e..b7d0059 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
@@ -284,4 +284,26 @@
         // THEN the lock icon is shown
         verify(mLockIconView).setContentDescription(LOCKED_LABEL);
     }
+
+    @Test
+    public void lockIconShows_afterUnlockStateChanges() {
+        // GIVEN lock icon controller is initialized and view is attached
+        init(/* useMigrationFlag= */false);
+        captureKeyguardStateCallback();
+        captureKeyguardUpdateMonitorCallback();
+
+        // GIVEN user has unlocked with a biometric auth (ie: face auth)
+        // and biometric running state changes
+        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true);
+        mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false,
+                BiometricSourceType.FACE);
+        reset(mLockIconView);
+
+        // WHEN the unlocked state changes
+        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false);
+        mKeyguardStateCallback.onUnlockedChanged();
+
+        // THEN the lock icon is shown
+        verify(mLockIconView).setContentDescription(LOCKED_LABEL);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/NumPadAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/NumPadAnimatorTest.kt
new file mode 100644
index 0000000..9fcb9c8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/NumPadAnimatorTest.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.keyguard
+
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.GradientDrawable
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.anyFloat
+import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class NumPadAnimatorTest : SysuiTestCase() {
+    @Mock lateinit var background: GradientDrawable
+    @Mock lateinit var buttonImage: Drawable
+    private lateinit var underTest: NumPadAnimator
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        underTest = NumPadAnimator(context, background, 0, buttonImage)
+    }
+
+    @Test
+    fun testOnLayout() {
+        underTest.onLayout(100)
+        verify(background).cornerRadius = 50f
+        reset(background)
+        underTest.onLayout(100)
+        verify(background, never()).cornerRadius = anyFloat()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ChooserSelectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/ChooserSelectorTest.kt
index 81d0034..babbe45 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ChooserSelectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/ChooserSelectorTest.kt
@@ -11,6 +11,7 @@
 import com.android.systemui.flags.Flag
 import com.android.systemui.flags.FlagListenable
 import com.android.systemui.flags.Flags
+import com.android.systemui.flags.ReleasedFlag
 import com.android.systemui.flags.UnreleasedFlag
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.util.mockito.any
@@ -102,7 +103,7 @@
     @Test
     fun initialize_enablesUnbundledChooser_whenFlagEnabled() {
         // Arrange
-        whenever(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(true)
+        setFlagMock(true)
 
         // Act
         chooserSelector.start()
@@ -118,7 +119,7 @@
     @Test
     fun initialize_disablesUnbundledChooser_whenFlagDisabled() {
         // Arrange
-        whenever(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
+        setFlagMock(false)
 
         // Act
         chooserSelector.start()
@@ -134,7 +135,7 @@
     @Test
     fun enablesUnbundledChooser_whenFlagBecomesEnabled() {
         // Arrange
-        whenever(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
+        setFlagMock(false)
         chooserSelector.start()
         verify(mockFeatureFlags).addListener(
                 eq<Flag<*>>(Flags.CHOOSER_UNBUNDLED),
@@ -147,8 +148,8 @@
         )
 
         // Act
-        whenever(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(true)
-        flagListener.value.onFlagChanged(TestFlagEvent(Flags.CHOOSER_UNBUNDLED.id))
+        setFlagMock(true)
+        flagListener.value.onFlagChanged(TestFlagEvent(Flags.CHOOSER_UNBUNDLED.name))
 
         // Assert
         verify(mockPackageManager, times(2)).setComponentEnabledSetting(
@@ -161,7 +162,7 @@
     @Test
     fun disablesUnbundledChooser_whenFlagBecomesDisabled() {
         // Arrange
-        whenever(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(true)
+        setFlagMock(true)
         chooserSelector.start()
         verify(mockFeatureFlags).addListener(
                 eq<Flag<*>>(Flags.CHOOSER_UNBUNDLED),
@@ -174,8 +175,8 @@
         )
 
         // Act
-        whenever(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
-        flagListener.value.onFlagChanged(TestFlagEvent(Flags.CHOOSER_UNBUNDLED.id))
+        setFlagMock(false)
+        flagListener.value.onFlagChanged(TestFlagEvent(Flags.CHOOSER_UNBUNDLED.name))
 
         // Assert
         verify(mockPackageManager, times(2)).setComponentEnabledSetting(
@@ -188,7 +189,7 @@
     @Test
     fun doesNothing_whenAnotherFlagChanges() {
         // Arrange
-        whenever(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
+        setFlagMock(false)
         chooserSelector.start()
         verify(mockFeatureFlags).addListener(
                 eq<Flag<*>>(Flags.CHOOSER_UNBUNDLED),
@@ -197,14 +198,18 @@
         clearInvocations(mockPackageManager)
 
         // Act
-        whenever(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
-        flagListener.value.onFlagChanged(TestFlagEvent(Flags.CHOOSER_UNBUNDLED.id + 1))
+        flagListener.value.onFlagChanged(TestFlagEvent("other flag"))
 
         // Assert
         verifyZeroInteractions(mockPackageManager)
     }
 
-    private class TestFlagEvent(override val flagId: Int) : FlagListenable.FlagEvent {
+    private fun setFlagMock(enabled: Boolean) {
+        whenever(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(enabled)
+        whenever(mockFeatureFlags.isEnabled(any<ReleasedFlag>())).thenReturn(enabled)
+    }
+
+    private class TestFlagEvent(override val flagName: String) : FlagListenable.FlagEvent {
         override fun requestNoRestart() {}
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 0627fc6..4cf5a4b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -21,6 +21,7 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -33,8 +34,10 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isA;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
@@ -88,7 +91,9 @@
 import com.android.systemui.decor.PrivacyDotCornerDecorProviderImpl;
 import com.android.systemui.decor.PrivacyDotDecorProviderFactory;
 import com.android.systemui.decor.RoundedCornerResDelegate;
+import com.android.systemui.log.ScreenDecorationsLogger;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.settings.FakeDisplayTracker;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.events.PrivacyDotViewController;
 import com.android.systemui.tuner.TunerService;
@@ -101,8 +106,12 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -116,7 +125,8 @@
     private WindowManager mWindowManager;
     private DisplayManager mDisplayManager;
     private SecureSettings mSecureSettings;
-    private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+    private FakeExecutor mExecutor;
+    private final FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
     private FakeThreadFactory mThreadFactory;
     private ArrayList<DecorProvider> mPrivacyDecorProviders;
     private ArrayList<DecorProvider> mFaceScanningProviders;
@@ -157,6 +167,8 @@
     private PrivacyDotViewController.ShowingListener mPrivacyDotShowingListener;
     @Mock
     private CutoutDecorProviderFactory mCutoutFactory;
+    @Captor
+    private ArgumentCaptor<AuthController.Callback> mAuthControllerCallback;
     private List<DecorProvider> mMockCutoutList;
 
     @Before
@@ -165,6 +177,7 @@
 
         Handler mainHandler = new Handler(TestableLooper.get(this).getLooper());
         mSecureSettings = new FakeSettings();
+        mExecutor = new FakeExecutor(new FakeSystemClock());
         mThreadFactory = new FakeThreadFactory(mExecutor);
         mThreadFactory.setHandler(mainHandler);
 
@@ -217,11 +230,14 @@
                 mAuthController,
                 mStatusBarStateController,
                 mKeyguardUpdateMonitor,
-                mExecutor));
+                mExecutor,
+                new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer"))));
 
         mScreenDecorations = spy(new ScreenDecorations(mContext, mExecutor, mSecureSettings,
-                mTunerService, mUserTracker, mDotViewController, mThreadFactory,
-                mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory) {
+                mTunerService, mUserTracker, mDisplayTracker, mDotViewController, mThreadFactory,
+                mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory,
+                new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")),
+                mAuthController) {
             @Override
             public void start() {
                 super.start();
@@ -1159,6 +1175,44 @@
     }
 
     @Test
+    public void faceSensorLocationChangesReloadsFaceScanningOverlay() {
+        mFaceScanningProviders = new ArrayList<>();
+        mFaceScanningProviders.add(mFaceScanningDecorProvider);
+        when(mFaceScanningProviderFactory.getProviders()).thenReturn(mFaceScanningProviders);
+        when(mFaceScanningProviderFactory.getHasProviders()).thenReturn(true);
+        ScreenDecorations screenDecorations = new ScreenDecorations(mContext, mExecutor,
+                mSecureSettings, mTunerService, mUserTracker, mDisplayTracker, mDotViewController,
+                mThreadFactory, mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory,
+                new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")), mAuthController);
+        screenDecorations.start();
+        verify(mAuthController).addCallback(mAuthControllerCallback.capture());
+        when(mContext.getDisplay()).thenReturn(mDisplay);
+        when(mDisplay.getDisplayInfo(any())).thenAnswer(new Answer<Boolean>() {
+            @Override
+            public Boolean answer(InvocationOnMock invocation) throws Throwable {
+                DisplayInfo displayInfo = invocation.getArgument(0);
+                int modeId = 1;
+                displayInfo.modeId = modeId;
+                displayInfo.supportedModes = new Display.Mode[]{new Display.Mode(modeId, 1024, 1024,
+                        90)};
+                return false;
+            }
+        });
+        mExecutor.runAllReady();
+        clearInvocations(mFaceScanningDecorProvider);
+
+        AuthController.Callback callback = mAuthControllerCallback.getValue();
+        callback.onFaceSensorLocationChanged();
+        mExecutor.runAllReady();
+
+        verify(mFaceScanningDecorProvider).onReloadResAndMeasure(any(),
+                anyInt(),
+                anyInt(),
+                anyInt(),
+                any());
+    }
+
+    @Test
     public void testPrivacyDotShowingListenerWorkWellWithNullParameter() {
         mPrivacyDotShowingListener.onPrivacyDotShown(null);
         mPrivacyDotShowingListener.onPrivacyDotHidden(null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
index 58b4af4..da419d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
@@ -39,6 +39,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.FakeDisplayTracker;
 import com.android.systemui.statusbar.CommandQueue;
 
 import org.junit.Before;
@@ -76,6 +77,7 @@
 
     private IWindowMagnificationConnection mIWindowMagnificationConnection;
     private WindowMagnification mWindowMagnification;
+    private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
 
     @Before
     public void setUp() throws Exception {
@@ -88,7 +90,7 @@
                 any(IWindowMagnificationConnection.class));
         mWindowMagnification = new WindowMagnification(getContext(),
                 getContext().getMainThreadHandler(), mCommandQueue,
-                mModeSwitchesController, mSysUiState, mOverviewProxyService);
+                mModeSwitchesController, mSysUiState, mOverviewProxyService, mDisplayTracker);
         mWindowMagnification.mMagnificationControllerSupplier = new FakeControllerSupplier(
                 mContext.getSystemService(DisplayManager.class));
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index cdf3f65..f4505f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -78,6 +78,7 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.model.SysUiState;
+import com.android.systemui.settings.FakeDisplayTracker;
 import com.android.systemui.util.leak.ReferenceTestUtils;
 import com.android.systemui.util.settings.SecureSettings;
 import com.android.systemui.utils.os.FakeHandler;
@@ -120,11 +121,12 @@
 
     private Handler mHandler;
     private TestableWindowManager mWindowManager;
-    private SysUiState mSysUiState = new SysUiState();
+    private SysUiState mSysUiState;
     private Resources mResources;
     private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
     private WindowMagnificationController mWindowMagnificationController;
     private Instrumentation mInstrumentation;
+    private final FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
     private final ValueAnimator mValueAnimator = ValueAnimator.ofFloat(0, 1.0f).setDuration(0);
 
     @Before
@@ -143,6 +145,7 @@
             return null;
         }).when(mSfVsyncFrameProvider).postFrameCallback(
                 any(FrameCallback.class));
+        mSysUiState = new SysUiState(mDisplayTracker);
         mSysUiState.addCallback(Mockito.mock(SysUiState.SysUiStateCallback.class));
         when(mSecureSettings.getIntForUser(anyString(), anyInt(), anyInt())).then(
                 returnsSecondArg());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
index ccf2f8b..f75dc03 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
@@ -45,6 +45,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.FakeDisplayTracker;
 import com.android.systemui.statusbar.CommandQueue;
 
 import org.junit.Before;
@@ -74,6 +75,8 @@
     private CommandQueue mCommandQueue;
     private WindowMagnification mWindowMagnification;
     private OverviewProxyListener mOverviewProxyListener;
+    private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -87,10 +90,10 @@
 
         when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState);
 
-        mCommandQueue = new CommandQueue(getContext());
+        mCommandQueue = new CommandQueue(getContext(), new FakeDisplayTracker(getContext()));
         mWindowMagnification = new WindowMagnification(getContext(),
                 getContext().getMainThreadHandler(), mCommandQueue, mModeSwitchesController,
-                mSysUiState, mOverviewProxyService);
+                mSysUiState, mOverviewProxyService, mDisplayTracker);
         mWindowMagnification.start();
 
         final ArgumentCaptor<OverviewProxyListener> listenerArgumentCaptor =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogTest.kt
new file mode 100644
index 0000000..777dd4e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogTest.kt
@@ -0,0 +1,107 @@
+/*
+ * 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.accessibility.fontscaling
+
+import android.os.Handler
+import android.provider.Settings
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.widget.ImageView
+import android.widget.SeekBar
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView
+import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.SystemSettings
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Tests for [FontScalingDialog]. */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class FontScalingDialogTest : SysuiTestCase() {
+    private lateinit var fontScalingDialog: FontScalingDialog
+    private lateinit var systemSettings: SystemSettings
+    private val fontSizeValueArray: Array<String> =
+        mContext
+            .getResources()
+            .getStringArray(com.android.settingslib.R.array.entryvalues_font_size)
+
+    @Before
+    fun setUp() {
+        val mainHandler = Handler(TestableLooper.get(this).getLooper())
+        systemSettings = FakeSettings()
+        fontScalingDialog = FontScalingDialog(mContext, systemSettings as FakeSettings)
+    }
+
+    @Test
+    fun showTheDialog_seekbarIsShowingCorrectProgress() {
+        fontScalingDialog.show()
+
+        val seekBar: SeekBar = fontScalingDialog.findViewById<SeekBar>(R.id.seekbar)!!
+        val progress: Int = seekBar.getProgress()
+        val currentScale = systemSettings.getFloat(Settings.System.FONT_SCALE, /* def = */ 1.0f)
+
+        assertThat(currentScale).isEqualTo(fontSizeValueArray[progress].toFloat())
+
+        fontScalingDialog.dismiss()
+    }
+
+    @Test
+    fun progressIsZero_clickIconEnd_seekBarProgressIncreaseOne_fontSizeScaled() {
+        fontScalingDialog.show()
+
+        val iconEnd: ImageView = fontScalingDialog.findViewById(R.id.icon_end)!!
+        val seekBarWithIconButtonsView: SeekBarWithIconButtonsView =
+            fontScalingDialog.findViewById(R.id.font_scaling_slider)!!
+        val seekBar: SeekBar = fontScalingDialog.findViewById(R.id.seekbar)!!
+
+        seekBarWithIconButtonsView.setProgress(0)
+
+        iconEnd.performClick()
+
+        val currentScale = systemSettings.getFloat(Settings.System.FONT_SCALE, /* def = */ 1.0f)
+        assertThat(seekBar.getProgress()).isEqualTo(1)
+        assertThat(currentScale).isEqualTo(fontSizeValueArray[1].toFloat())
+
+        fontScalingDialog.dismiss()
+    }
+
+    @Test
+    fun progressIsMax_clickIconStart_seekBarProgressDecreaseOne_fontSizeScaled() {
+        fontScalingDialog.show()
+
+        val iconStart: ImageView = fontScalingDialog.findViewById(R.id.icon_start)!!
+        val seekBarWithIconButtonsView: SeekBarWithIconButtonsView =
+            fontScalingDialog.findViewById(R.id.font_scaling_slider)!!
+        val seekBar: SeekBar = fontScalingDialog.findViewById(R.id.seekbar)!!
+
+        seekBarWithIconButtonsView.setProgress(fontSizeValueArray.size - 1)
+
+        iconStart.performClick()
+
+        val currentScale = systemSettings.getFloat(Settings.System.FONT_SCALE, /* def = */ 1.0f)
+        assertThat(seekBar.getProgress()).isEqualTo(fontSizeValueArray.size - 2)
+        assertThat(currentScale)
+            .isEqualTo(fontSizeValueArray[fontSizeValueArray.size - 2].toFloat())
+
+        fontScalingDialog.dismiss()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
index a61cd23..578e1d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
@@ -15,6 +15,7 @@
 import android.view.RemoteAnimationTarget
 import android.view.SurfaceControl
 import android.view.ViewGroup
+import android.widget.FrameLayout
 import android.widget.LinearLayout
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -26,6 +27,7 @@
 import junit.framework.AssertionFailedError
 import kotlin.concurrent.thread
 import org.junit.After
+import org.junit.Assert.assertThrows
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -195,6 +197,13 @@
         verify(controller).onLaunchAnimationStart(anyBoolean())
     }
 
+    @Test
+    fun creatingControllerFromNormalViewThrows() {
+        assertThrows(IllegalArgumentException::class.java) {
+            ActivityLaunchAnimator.Controller.fromView(FrameLayout(mContext))
+        }
+    }
+
     private fun fakeWindow(): RemoteAnimationTarget {
         val bounds = Rect(10 /* left */, 20 /* top */, 30 /* right */, 40 /* bottom */)
         val taskInfo = ActivityManager.RunningTaskInfo()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
index cac4a0e..316de59 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
@@ -26,6 +26,7 @@
 import junit.framework.Assert.assertTrue
 import org.junit.After
 import org.junit.Assert.assertNotEquals
+import org.junit.Assert.assertThrows
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -260,6 +261,19 @@
         assertThat(touchSurface.visibility).isEqualTo(View.GONE)
     }
 
+    @Test
+    fun creatingControllerFromNormalViewThrows() {
+        assertThrows(IllegalArgumentException::class.java) {
+            DialogLaunchAnimator.Controller.fromView(FrameLayout(mContext))
+        }
+    }
+
+    @Test
+    fun showFromDialogDoesNotCrashWhenShownFromRandomDialog() {
+        val dialog = createDialogAndShowFromDialog(animateFrom = TestDialog(context))
+        dialog.dismiss()
+    }
+
     private fun createAndShowDialog(
         animator: DialogLaunchAnimator = dialogLaunchAnimator,
     ): TestDialog {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt
index 3696ec5..0798d73 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt
@@ -16,58 +16,34 @@
 
 package com.android.systemui.animation
 
-import android.graphics.drawable.Drawable
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
-import android.view.View
-import android.view.ViewGroup
-import android.view.ViewParent
+import android.widget.FrameLayout
 import androidx.test.filters.SmallTest
-import com.android.internal.jank.InteractionJankMonitor
 import com.android.systemui.SysuiTestCase
-import org.junit.Before
+import org.junit.Assert.assertThrows
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.Mock
-import org.mockito.Mockito.`when` as whenever
-import org.mockito.MockitoAnnotations
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper
 class GhostedViewLaunchAnimatorControllerTest : SysuiTestCase() {
-    @Mock lateinit var interactionJankMonitor: InteractionJankMonitor
-    @Mock lateinit var view: View
-    @Mock lateinit var rootView: ViewGroup
-    @Mock lateinit var viewParent: ViewParent
-    @Mock lateinit var drawable: Drawable
-    lateinit var controller: GhostedViewLaunchAnimatorController
-
-    @Before
-    fun setup() {
-        MockitoAnnotations.initMocks(this)
-        whenever(view.rootView).thenReturn(rootView)
-        whenever(view.background).thenReturn(drawable)
-        whenever(view.height).thenReturn(0)
-        whenever(view.width).thenReturn(0)
-        whenever(view.parent).thenReturn(viewParent)
-        whenever(view.visibility).thenReturn(View.VISIBLE)
-        whenever(view.invalidate()).then { /* NO-OP */ }
-        whenever(view.getLocationOnScreen(any())).then { /* NO-OP */ }
-        whenever(interactionJankMonitor.begin(any(), anyInt())).thenReturn(true)
-        whenever(interactionJankMonitor.end(anyInt())).thenReturn(true)
-        controller = GhostedViewLaunchAnimatorController(view, 0, interactionJankMonitor)
-    }
-
     @Test
     fun animatingOrphanViewDoesNotCrash() {
         val state = LaunchAnimator.State(top = 0, bottom = 0, left = 0, right = 0)
 
+        val controller = GhostedViewLaunchAnimatorController(LaunchableFrameLayout(mContext))
         controller.onIntentStarted(willAnimate = true)
         controller.onLaunchAnimationStart(isExpandingFullyAbove = true)
         controller.onLaunchAnimationProgress(state, progress = 0f, linearProgress = 0f)
         controller.onLaunchAnimationEnd(isExpandingFullyAbove = true)
     }
+
+    @Test
+    fun creatingControllerFromNormalViewThrows() {
+        assertThrows(IllegalArgumentException::class.java) {
+            GhostedViewLaunchAnimatorController(FrameLayout(mContext))
+        }
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt
index ed0cd7e..31d0d12 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt
@@ -19,6 +19,7 @@
 import android.animation.AnimatorListenerAdapter
 import android.animation.ValueAnimator
 import android.graphics.Typeface
+import android.graphics.fonts.FontVariationAxis
 import android.testing.AndroidTestingRunner
 import android.text.Layout
 import android.text.StaticLayout
@@ -178,4 +179,71 @@
 
         assertThat(paint.typeface).isSameInstanceAs(prevTypeface)
     }
+
+    @Test
+    fun testSetTextStyle_addWeight() {
+        testWeightChange("", 100, FontVariationAxis.fromFontVariationSettings("'wght' 100")!!)
+    }
+
+    @Test
+    fun testSetTextStyle_changeWeight() {
+        testWeightChange(
+                "'wght' 500",
+                100,
+                FontVariationAxis.fromFontVariationSettings("'wght' 100")!!
+        )
+    }
+
+    @Test
+    fun testSetTextStyle_addWeightWithOtherAxis() {
+        testWeightChange(
+                "'wdth' 100",
+                100,
+                FontVariationAxis.fromFontVariationSettings("'wght' 100, 'wdth' 100")!!
+        )
+    }
+
+    @Test
+    fun testSetTextStyle_changeWeightWithOtherAxis() {
+        testWeightChange(
+                "'wght' 500, 'wdth' 100",
+                100,
+                FontVariationAxis.fromFontVariationSettings("'wght' 100, 'wdth' 100")!!
+        )
+    }
+
+    private fun testWeightChange(
+            initialFontVariationSettings: String,
+            weight: Int,
+            expectedFontVariationSettings: Array<FontVariationAxis>
+    ) {
+        val layout = makeLayout("Hello, World", PAINT)
+        val valueAnimator = mock(ValueAnimator::class.java)
+        val textInterpolator = mock(TextInterpolator::class.java)
+        val paint =
+                TextPaint().apply {
+                    typeface = Typeface.createFromFile("/system/fonts/Roboto-Regular.ttf")
+                    fontVariationSettings = initialFontVariationSettings
+                }
+        `when`(textInterpolator.targetPaint).thenReturn(paint)
+
+        val textAnimator =
+                TextAnimator(layout, {}).apply {
+                    this.textInterpolator = textInterpolator
+                    this.animator = valueAnimator
+                }
+        textAnimator.setTextStyle(weight = weight, animate = false)
+
+        val resultFontVariationList =
+                FontVariationAxis.fromFontVariationSettings(
+                        textInterpolator.targetPaint.fontVariationSettings
+                )
+        expectedFontVariationSettings.forEach { expectedAxis ->
+            val resultAxis = resultFontVariationList?.filter { it.tag == expectedAxis.tag }?.get(0)
+            assertThat(resultAxis).isNotNull()
+            if (resultAxis != null) {
+                assertThat(resultAxis.styleValue).isEqualTo(expectedAxis.styleValue)
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index ace0ccb..489efd71 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -25,6 +25,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotSame;
 import static junit.framework.Assert.assertNull;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -33,6 +34,7 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -49,6 +51,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.graphics.Point;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
@@ -166,6 +169,8 @@
     private ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor;
     @Captor
     private ArgumentCaptor<WakefulnessLifecycle.Observer> mWakefullnessObserverCaptor;
+    @Mock
+    private Resources mResources;
 
     private TestableContext mContextSpy;
     private Execution mExecution;
@@ -879,6 +884,25 @@
         );
     }
 
+    @Test
+    public void testUpdateFingerprintLocation_defaultPointChanges_whenConfigChanges() {
+        when(mContextSpy.getResources()).thenReturn(mResources);
+
+        doReturn(500).when(mResources)
+                .getDimensionPixelSize(eq(com.android.systemui.R.dimen
+                        .physical_fingerprint_sensor_center_screen_location_y));
+        mAuthController.onConfigurationChanged(null /* newConfig */);
+
+        final Point firstFpLocation = mAuthController.getFingerprintSensorLocation();
+
+        doReturn(1000).when(mResources)
+                .getDimensionPixelSize(eq(com.android.systemui.R.dimen
+                        .physical_fingerprint_sensor_center_screen_location_y));
+        mAuthController.onConfigurationChanged(null /* newConfig */);
+
+        assertNotSame(firstFpLocation, mAuthController.getFingerprintSensorLocation());
+    }
+
     private void showDialog(int[] sensorIds, boolean credentialAllowed) {
         mAuthController.showAuthenticationDialog(createTestPromptInfo(),
                 mReceiver /* receiver */,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
index b92c5d0..612e557 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
@@ -51,14 +51,22 @@
 import android.view.WindowMetrics
 import androidx.test.filters.SmallTest
 import com.airbnb.lottie.LottieAnimationView
+import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.SysuiTestableContext
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags.MODERN_ALTERNATE_BOUNCER
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
 import com.android.systemui.recents.OverviewProxyService
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestCoroutineScope
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -101,6 +109,9 @@
     @Captor lateinit var overlayCaptor: ArgumentCaptor<View>
     @Captor lateinit var overlayViewParamsCaptor: ArgumentCaptor<WindowManager.LayoutParams>
 
+    private lateinit var keyguardBouncerRepository: FakeKeyguardBouncerRepository
+    private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
+    private val featureFlags = FakeFeatureFlags()
     private val executor = FakeExecutor(FakeSystemClock())
     private lateinit var overlayController: ISidefpsController
     private lateinit var sideFpsController: SideFpsController
@@ -121,6 +132,18 @@
 
     @Before
     fun setup() {
+        featureFlags.set(MODERN_ALTERNATE_BOUNCER, true)
+        keyguardBouncerRepository = FakeKeyguardBouncerRepository()
+        alternateBouncerInteractor =
+            AlternateBouncerInteractor(
+                keyguardBouncerRepository,
+                FakeBiometricSettingsRepository(),
+                FakeDeviceEntryFingerprintAuthRepository(),
+                FakeSystemClock(),
+                mock(KeyguardUpdateMonitor::class.java),
+                featureFlags,
+            )
+
         context.addMockSystemService(DisplayManager::class.java, displayManager)
         context.addMockSystemService(WindowManager::class.java, windowManager)
 
@@ -217,7 +240,10 @@
                 displayManager,
                 executor,
                 handler,
-                dumpManager
+                alternateBouncerInteractor,
+                TestCoroutineScope(),
+                featureFlags,
+                dumpManager,
             )
 
         overlayController =
@@ -241,6 +267,17 @@
     }
 
     @Test
+    fun testShowOverlayReasonWhenDisplayChanged() = testWithDisplay {
+        sideFpsController.show(SideFpsUiRequestSource.AUTO_SHOW, REASON_AUTH_KEYGUARD)
+        executor.runAllReady()
+        sideFpsController.orientationListener.onDisplayChanged(1 /* displayId */)
+        executor.runAllReady()
+
+        assertThat(sideFpsController.orientationReasonListener.reason)
+            .isEqualTo(REASON_AUTH_KEYGUARD)
+    }
+
+    @Test
     fun testShowsAndHides() = testWithDisplay {
         overlayController.show(SENSOR_ID, REASON_UNKNOWN)
         executor.runAllReady()
@@ -507,6 +544,26 @@
 
     private fun verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible: Boolean) {
         sideFpsController.overlayOffsets = sensorLocation
+    }
+
+    fun alternateBouncerVisibility_showAndHideSideFpsUI() = testWithDisplay {
+        // WHEN alternate bouncer is visible
+        keyguardBouncerRepository.setAlternateVisible(true)
+        executor.runAllReady()
+
+        // THEN side fps shows UI
+        verify(windowManager).addView(any(), any())
+        verify(windowManager, never()).removeView(any())
+
+        // WHEN alternate bouncer is no longer visible
+        keyguardBouncerRepository.setAlternateVisible(false)
+        executor.runAllReady()
+
+        // THEN side fps UI is hidden
+        verify(windowManager).removeView(any())
+    }
+
+    private fun hidesWithTaskbar(visible: Boolean) {
         overlayController.show(SENSOR_ID, REASON_UNKNOWN)
         executor.runAllReady()
 
@@ -515,7 +572,7 @@
 
         verify(windowManager).addView(any(), any())
         verify(windowManager, never()).removeView(any())
-        verify(sideFpsView).visibility = if (sfpsViewVisible) View.VISIBLE else View.GONE
+        verify(sideFpsView).visibility = if (visible) View.VISIBLE else View.GONE
     }
 
     /**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java
index 9c32c38..dbbc266 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java
@@ -37,7 +37,6 @@
 import com.android.systemui.shade.ShadeExpansionListener;
 import com.android.systemui.shade.ShadeExpansionStateManager;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
-import com.android.systemui.statusbar.phone.KeyguardBouncer;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
@@ -71,7 +70,6 @@
     protected @Mock SystemUIDialogManager mDialogManager;
     protected @Mock UdfpsController mUdfpsController;
     protected @Mock ActivityLaunchAnimator mActivityLaunchAnimator;
-    protected @Mock KeyguardBouncer mBouncer;
     protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor;
     protected @Mock AlternateBouncerInteractor mAlternateBouncerInteractor;
 
@@ -149,11 +147,8 @@
 
     protected UdfpsKeyguardViewController createUdfpsKeyguardViewController(
             boolean useModernBouncer, boolean useExpandedOverlay) {
-        mFeatureFlags.set(Flags.MODERN_BOUNCER, useModernBouncer);
         mFeatureFlags.set(Flags.MODERN_ALTERNATE_BOUNCER, useModernBouncer);
         mFeatureFlags.set(Flags.UDFPS_NEW_TOUCH_DETECTION, useExpandedOverlay);
-        when(mStatusBarKeyguardViewManager.getPrimaryBouncer()).thenReturn(
-                useModernBouncer ? null : mBouncer);
         UdfpsKeyguardViewController controller = new UdfpsKeyguardViewController(
                 mView,
                 mStatusBarStateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index 7715f7f..f437a8f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -32,24 +32,17 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
 import com.android.systemui.shade.ShadeExpansionListener;
 import com.android.systemui.statusbar.StatusBarState;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class UdfpsKeyguardViewControllerTest extends UdfpsKeyguardViewControllerBaseTest {
-    private @Captor ArgumentCaptor<PrimaryBouncerExpansionCallback>
-            mBouncerExpansionCallbackCaptor;
-    private PrimaryBouncerExpansionCallback mBouncerExpansionCallback;
-
     @Override
     public UdfpsKeyguardViewController createUdfpsKeyguardViewController() {
         return createUdfpsKeyguardViewController(/* useModernBouncer */ false,
@@ -62,11 +55,9 @@
         captureStatusBarStateListeners();
         sendStatusBarStateChanged(StatusBarState.KEYGUARD);
 
-        captureBouncerExpansionCallback();
         when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
         when(mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()).thenReturn(true);
-        mBouncerExpansionCallback.onVisibilityChanged(true);
-
+        when(mView.getUnpausedAlpha()).thenReturn(0);
         assertTrue(mController.shouldPauseAuth());
     }
 
@@ -304,11 +295,6 @@
         verify(mView, atLeastOnce()).setPauseAuth(false);
     }
 
-    private void captureBouncerExpansionCallback() {
-        verify(mBouncer).addBouncerExpansionCallback(mBouncerExpansionCallbackCaptor.capture());
-        mBouncerExpansionCallback = mBouncerExpansionCallbackCaptor.getValue();
-    }
-
     @Test
     // TODO(b/259264861): Tracking Bug
     public void testUdfpsExpandedOverlayOn() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt
index 9060922..c73ff1d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt
@@ -26,8 +26,10 @@
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.BouncerView
-import com.android.systemui.keyguard.data.repository.BiometricRepository
+import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
+import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepositoryImpl
 import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
 import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor
 import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
@@ -64,7 +66,7 @@
         allowTestableLooperAsMainThread() // repeatWhenAttached requires the main thread
         MockitoAnnotations.initMocks(this)
         keyguardBouncerRepository =
-            KeyguardBouncerRepository(
+            KeyguardBouncerRepositoryImpl(
                 mock(com.android.keyguard.ViewMediatorCallback::class.java),
                 FakeSystemClock(),
                 TestCoroutineScope(),
@@ -90,7 +92,8 @@
         mAlternateBouncerInteractor =
             AlternateBouncerInteractor(
                 keyguardBouncerRepository,
-                mock(BiometricRepository::class.java),
+                mock(BiometricSettingsRepository::class.java),
+                mock(DeviceEntryFingerprintAuthRepository::class.java),
                 mock(SystemClock::class.java),
                 mock(KeyguardUpdateMonitor::class.java),
                 mock(FeatureFlags::class.java)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
index 34ddf79..8e20303 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
@@ -110,6 +110,28 @@
                         expectedInteractionEvent = InteractionEvent.UP,
                         expectedPointerOnSensorId = INVALID_POINTER_ID,
                     ),
+                    // MotionEvent.ACTION_HOVER_ENTER
+                    genPositiveTestCases(
+                        motionEventAction = MotionEvent.ACTION_HOVER_ENTER,
+                        previousPointerOnSensorId = INVALID_POINTER_ID,
+                        currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
+                        expectedInteractionEvent = InteractionEvent.DOWN,
+                        expectedPointerOnSensorId = POINTER_ID_1,
+                    ),
+                    genPositiveTestCases(
+                        motionEventAction = MotionEvent.ACTION_HOVER_ENTER,
+                        previousPointerOnSensorId = INVALID_POINTER_ID,
+                        currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
+                        expectedInteractionEvent = InteractionEvent.UNCHANGED,
+                        expectedPointerOnSensorId = INVALID_POINTER_ID,
+                    ),
+                    genPositiveTestCases(
+                        motionEventAction = MotionEvent.ACTION_HOVER_ENTER,
+                        previousPointerOnSensorId = POINTER_ID_1,
+                        currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
+                        expectedInteractionEvent = InteractionEvent.UP,
+                        expectedPointerOnSensorId = INVALID_POINTER_ID,
+                    ),
                     // MotionEvent.ACTION_MOVE
                     genPositiveTestCases(
                         motionEventAction = MotionEvent.ACTION_MOVE,
@@ -161,6 +183,35 @@
                         expectedInteractionEvent = InteractionEvent.UNCHANGED,
                         expectedPointerOnSensorId = POINTER_ID_2,
                     ),
+                    // MotionEvent.ACTION_HOVER_MOVE
+                    genPositiveTestCases(
+                        motionEventAction = MotionEvent.ACTION_HOVER_MOVE,
+                        previousPointerOnSensorId = INVALID_POINTER_ID,
+                        currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
+                        expectedInteractionEvent = InteractionEvent.DOWN,
+                        expectedPointerOnSensorId = POINTER_ID_1,
+                    ),
+                    genPositiveTestCases(
+                        motionEventAction = MotionEvent.ACTION_HOVER_MOVE,
+                        previousPointerOnSensorId = POINTER_ID_1,
+                        currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
+                        expectedInteractionEvent = InteractionEvent.UNCHANGED,
+                        expectedPointerOnSensorId = POINTER_ID_1,
+                    ),
+                    genPositiveTestCases(
+                        motionEventAction = MotionEvent.ACTION_HOVER_MOVE,
+                        previousPointerOnSensorId = INVALID_POINTER_ID,
+                        currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
+                        expectedInteractionEvent = InteractionEvent.UNCHANGED,
+                        expectedPointerOnSensorId = INVALID_POINTER_ID,
+                    ),
+                    genPositiveTestCases(
+                        motionEventAction = MotionEvent.ACTION_HOVER_MOVE,
+                        previousPointerOnSensorId = POINTER_ID_1,
+                        currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
+                        expectedInteractionEvent = InteractionEvent.UP,
+                        expectedPointerOnSensorId = INVALID_POINTER_ID,
+                    ),
                     // MotionEvent.ACTION_UP
                     genPositiveTestCases(
                         motionEventAction = MotionEvent.ACTION_UP,
@@ -183,6 +234,28 @@
                         expectedInteractionEvent = InteractionEvent.UNCHANGED,
                         expectedPointerOnSensorId = INVALID_POINTER_ID,
                     ),
+                    // MotionEvent.ACTION_HOVER_EXIT
+                    genPositiveTestCases(
+                        motionEventAction = MotionEvent.ACTION_HOVER_EXIT,
+                        previousPointerOnSensorId = INVALID_POINTER_ID,
+                        currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
+                        expectedInteractionEvent = InteractionEvent.UP,
+                        expectedPointerOnSensorId = INVALID_POINTER_ID,
+                    ),
+                    genPositiveTestCases(
+                        motionEventAction = MotionEvent.ACTION_HOVER_EXIT,
+                        previousPointerOnSensorId = POINTER_ID_1,
+                        currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
+                        expectedInteractionEvent = InteractionEvent.UP,
+                        expectedPointerOnSensorId = INVALID_POINTER_ID,
+                    ),
+                    genPositiveTestCases(
+                        motionEventAction = MotionEvent.ACTION_HOVER_EXIT,
+                        previousPointerOnSensorId = INVALID_POINTER_ID,
+                        currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
+                        expectedInteractionEvent = InteractionEvent.UNCHANGED,
+                        expectedPointerOnSensorId = INVALID_POINTER_ID,
+                    ),
                     // MotionEvent.ACTION_CANCEL
                     genPositiveTestCases(
                         motionEventAction = MotionEvent.ACTION_CANCEL,
@@ -315,13 +388,7 @@
                         expectedPointerOnSensorId = POINTER_ID_2
                     )
                 )
-                .flatten() +
-                listOf(
-                        genTestCasesForUnsupportedAction(MotionEvent.ACTION_HOVER_ENTER),
-                        genTestCasesForUnsupportedAction(MotionEvent.ACTION_HOVER_MOVE),
-                        genTestCasesForUnsupportedAction(MotionEvent.ACTION_HOVER_EXIT)
-                    )
-                    .flatten()
+                .flatten()
     }
 }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
index e4df754..8cb9130 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
@@ -106,7 +106,7 @@
         mClassifiers.add(mClassifierB);
         when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(mMotionEventList);
         when(mKeyguardStateController.isShowing()).thenReturn(true);
-        when(mFalsingDataProvider.isFolded()).thenReturn(true);
+        when(mFalsingDataProvider.isUnfolded()).thenReturn(false);
         mBrightLineFalsingManager = new BrightLineFalsingManager(mFalsingDataProvider,
                 mMetricsLogger, mClassifiers, mSingleTapClassfier, mLongTapClassifier,
                 mDoubleTapClassifier, mHistoryTracker, mKeyguardStateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
index ae38eb6..315774a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
@@ -89,7 +89,7 @@
         mClassifiers.add(mClassifierA);
         when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(mMotionEventList);
         when(mKeyguardStateController.isShowing()).thenReturn(true);
-        when(mFalsingDataProvider.isFolded()).thenReturn(true);
+        when(mFalsingDataProvider.isUnfolded()).thenReturn(false);
         mBrightLineFalsingManager = new BrightLineFalsingManager(mFalsingDataProvider,
                 mMetricsLogger, mClassifiers, mSingleTapClassifier, mLongTapClassifier,
                 mDoubleTapClassifier, mHistoryTracker, mKeyguardStateController,
@@ -185,7 +185,7 @@
     @Test
     public void testSkipUnfolded() {
         assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isTrue();
-        when(mFalsingDataProvider.isFolded()).thenReturn(false);
+        when(mFalsingDataProvider.isUnfolded()).thenReturn(true);
         assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isFalse();
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
index c451a1e7..2edc3d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
@@ -324,12 +324,18 @@
     @Test
     public void test_FoldedState_Folded() {
         when(mFoldStateListener.getFolded()).thenReturn(true);
-        assertThat(mDataProvider.isFolded()).isTrue();
+        assertThat(mDataProvider.isUnfolded()).isFalse();
     }
 
     @Test
     public void test_FoldedState_Unfolded() {
         when(mFoldStateListener.getFolded()).thenReturn(false);
-        assertThat(mDataProvider.isFolded()).isFalse();
+        assertThat(mDataProvider.isUnfolded()).isTrue();
+    }
+
+    @Test
+    public void test_FoldedState_NotFoldable() {
+        when(mFoldStateListener.getFolded()).thenReturn(null);
+        assertThat(mDataProvider.isUnfolded()).isFalse();
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java
index bdd496e..fd6e31b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.clipboardoverlay;
 
-import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLIPBOARD_OVERLAY_ENABLED;
+import static com.android.systemui.flags.Flags.CLIPBOARD_MINIMIZED_LAYOUT;
 
 import static com.google.android.setupcompat.util.WizardManagerHelper.SETTINGS_SECURE_USER_SETUP_COMPLETE;
 
@@ -33,7 +33,6 @@
 import android.content.ClipDescription;
 import android.content.ClipboardManager;
 import android.os.PersistableBundle;
-import android.provider.DeviceConfig;
 import android.provider.Settings;
 
 import androidx.test.filters.SmallTest;
@@ -41,9 +40,7 @@
 
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
-import com.android.systemui.util.DeviceConfigProxyFake;
+import com.android.systemui.flags.FakeFeatureFlags;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -54,6 +51,8 @@
 import org.mockito.MockitoAnnotations;
 import org.mockito.Spy;
 
+import java.util.ArrayList;
+
 import javax.inject.Provider;
 
 @SmallTest
@@ -63,18 +62,12 @@
     @Mock
     private ClipboardManager mClipboardManager;
     @Mock
-    private ClipboardOverlayControllerLegacyFactory mClipboardOverlayControllerLegacyFactory;
-    @Mock
-    private ClipboardOverlayControllerLegacy mOverlayControllerLegacy;
-    @Mock
     private ClipboardOverlayController mOverlayController;
     @Mock
     private ClipboardToast mClipboardToast;
+    private FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
     @Mock
     private UiEventLogger mUiEventLogger;
-    @Mock
-    private FeatureFlags mFeatureFlags;
-    private DeviceConfigProxyFake mDeviceConfigProxy;
 
     private ClipData mSampleClipData;
     private String mSampleSource = "Example source";
@@ -97,8 +90,6 @@
         mOverlayControllerProvider = () -> mOverlayController;
 
         MockitoAnnotations.initMocks(this);
-        when(mClipboardOverlayControllerLegacyFactory.create(any()))
-                .thenReturn(mOverlayControllerLegacy);
         when(mClipboardManager.hasPrimaryClip()).thenReturn(true);
         Settings.Secure.putInt(
                 mContext.getContentResolver(), SETTINGS_SECURE_USER_SETUP_COMPLETE, 1);
@@ -108,26 +99,15 @@
         when(mClipboardManager.getPrimaryClip()).thenReturn(mSampleClipData);
         when(mClipboardManager.getPrimaryClipSource()).thenReturn(mSampleSource);
 
-        mDeviceConfigProxy = new DeviceConfigProxyFake();
+        mFeatureFlags.set(CLIPBOARD_MINIMIZED_LAYOUT, true);
 
-        mClipboardListener = new ClipboardListener(getContext(), mDeviceConfigProxy,
-                mOverlayControllerProvider, mClipboardOverlayControllerLegacyFactory,
-                mClipboardToast, mClipboardManager, mUiEventLogger, mFeatureFlags);
+        mClipboardListener = new ClipboardListener(getContext(), mOverlayControllerProvider,
+                mClipboardToast, mClipboardManager, mFeatureFlags, mUiEventLogger);
     }
 
-    @Test
-    public void test_disabled() {
-        mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED,
-                "false", false);
-        mClipboardListener.start();
-        verifyZeroInteractions(mClipboardManager);
-        verifyZeroInteractions(mUiEventLogger);
-    }
 
     @Test
-    public void test_enabled() {
-        mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED,
-                "true", false);
+    public void test_initialization() {
         mClipboardListener.start();
         verify(mClipboardManager).addPrimaryClipChangedListener(any());
         verifyZeroInteractions(mUiEventLogger);
@@ -135,45 +115,6 @@
 
     @Test
     public void test_consecutiveCopies() {
-        when(mFeatureFlags.isEnabled(Flags.CLIPBOARD_OVERLAY_REFACTOR)).thenReturn(false);
-
-        mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED,
-                "true", false);
-        mClipboardListener.start();
-        mClipboardListener.onPrimaryClipChanged();
-
-        verify(mClipboardOverlayControllerLegacyFactory).create(any());
-
-        verify(mOverlayControllerLegacy).setClipData(
-                mClipDataCaptor.capture(), mStringCaptor.capture());
-
-        assertEquals(mSampleClipData, mClipDataCaptor.getValue());
-        assertEquals(mSampleSource, mStringCaptor.getValue());
-
-        verify(mOverlayControllerLegacy).setOnSessionCompleteListener(mRunnableCaptor.capture());
-
-        // Should clear the overlay controller
-        mRunnableCaptor.getValue().run();
-
-        mClipboardListener.onPrimaryClipChanged();
-
-        verify(mClipboardOverlayControllerLegacyFactory, times(2)).create(any());
-
-        // Not calling the runnable here, just change the clip again and verify that the overlay is
-        // NOT recreated.
-
-        mClipboardListener.onPrimaryClipChanged();
-
-        verify(mClipboardOverlayControllerLegacyFactory, times(2)).create(any());
-        verifyZeroInteractions(mOverlayControllerProvider);
-    }
-
-    @Test
-    public void test_consecutiveCopies_new() {
-        when(mFeatureFlags.isEnabled(Flags.CLIPBOARD_OVERLAY_REFACTOR)).thenReturn(true);
-
-        mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED,
-                "true", false);
         mClipboardListener.start();
         mClipboardListener.onPrimaryClipChanged();
 
@@ -200,7 +141,6 @@
         mClipboardListener.onPrimaryClipChanged();
 
         verify(mOverlayControllerProvider, times(2)).get();
-        verifyZeroInteractions(mClipboardOverlayControllerLegacyFactory);
     }
 
     @Test
@@ -231,23 +171,6 @@
 
     @Test
     public void test_logging_enterAndReenter() {
-        when(mFeatureFlags.isEnabled(Flags.CLIPBOARD_OVERLAY_REFACTOR)).thenReturn(false);
-
-        mClipboardListener.start();
-
-        mClipboardListener.onPrimaryClipChanged();
-        mClipboardListener.onPrimaryClipChanged();
-
-        verify(mUiEventLogger, times(1)).log(
-                ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ENTERED, 0, mSampleSource);
-        verify(mUiEventLogger, times(1)).log(
-                ClipboardOverlayEvent.CLIPBOARD_OVERLAY_UPDATED, 0, mSampleSource);
-    }
-
-    @Test
-    public void test_logging_enterAndReenter_new() {
-        when(mFeatureFlags.isEnabled(Flags.CLIPBOARD_OVERLAY_REFACTOR)).thenReturn(true);
-
         mClipboardListener.start();
 
         mClipboardListener.onPrimaryClipChanged();
@@ -271,6 +194,62 @@
                 ClipboardOverlayEvent.CLIPBOARD_TOAST_SHOWN, 0, mSampleSource);
         verify(mClipboardToast, times(1)).showCopiedToast();
         verifyZeroInteractions(mOverlayControllerProvider);
-        verifyZeroInteractions(mClipboardOverlayControllerLegacyFactory);
+    }
+
+    @Test
+    public void test_nullClipData_showsNothing() {
+        when(mClipboardManager.getPrimaryClip()).thenReturn(null);
+
+        mClipboardListener.start();
+        mClipboardListener.onPrimaryClipChanged();
+
+        verifyZeroInteractions(mUiEventLogger);
+        verifyZeroInteractions(mClipboardToast);
+        verifyZeroInteractions(mOverlayControllerProvider);
+    }
+
+    @Test
+    public void test_emptyClipData_showsToast() {
+        ClipDescription description = new ClipDescription("Test", new String[0]);
+        ClipData noItems = new ClipData(description, new ArrayList<>());
+        when(mClipboardManager.getPrimaryClip()).thenReturn(noItems);
+
+        mClipboardListener.start();
+        mClipboardListener.onPrimaryClipChanged();
+
+        verify(mUiEventLogger, times(1)).log(
+                ClipboardOverlayEvent.CLIPBOARD_TOAST_SHOWN, 0, mSampleSource);
+        verify(mClipboardToast, times(1)).showCopiedToast();
+        verifyZeroInteractions(mOverlayControllerProvider);
+    }
+
+    @Test
+    public void test_minimizedLayoutFlagOff_usesLegacy() {
+        mFeatureFlags.set(CLIPBOARD_MINIMIZED_LAYOUT, false);
+
+        mClipboardListener.start();
+        mClipboardListener.onPrimaryClipChanged();
+
+        verify(mOverlayControllerProvider).get();
+
+        verify(mOverlayController).setClipDataLegacy(
+                mClipDataCaptor.capture(), mStringCaptor.capture());
+
+        assertEquals(mSampleClipData, mClipDataCaptor.getValue());
+        assertEquals(mSampleSource, mStringCaptor.getValue());
+    }
+
+    @Test
+    public void test_minimizedLayoutFlagOn_usesNew() {
+        mClipboardListener.start();
+        mClipboardListener.onPrimaryClipChanged();
+
+        verify(mOverlayControllerProvider).get();
+
+        verify(mOverlayController).setClipData(
+                mClipDataCaptor.capture(), mStringCaptor.capture());
+
+        assertEquals(mSampleClipData, mClipDataCaptor.getValue());
+        assertEquals(mSampleSource, mStringCaptor.getValue());
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardModelTest.kt
new file mode 100644
index 0000000..c0dada4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardModelTest.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2022 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.clipboardoverlay
+
+import android.content.ClipData
+import android.content.ClipDescription
+import android.content.ContentResolver
+import android.content.Context
+import android.graphics.Bitmap
+import android.net.Uri
+import android.os.PersistableBundle
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.whenever
+import java.io.IOException
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ClipboardModelTest : SysuiTestCase() {
+    @Mock private lateinit var mClipboardUtils: ClipboardOverlayUtils
+    @Mock private lateinit var mMockContext: Context
+    @Mock private lateinit var mMockContentResolver: ContentResolver
+    private lateinit var mSampleClipData: ClipData
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        mSampleClipData = ClipData("Test", arrayOf("text/plain"), ClipData.Item("Test Item"))
+    }
+
+    @Test
+    fun test_textClipData() {
+        val source = "test source"
+        val model = ClipboardModel.fromClipData(mContext, mClipboardUtils, mSampleClipData, source)
+        assertEquals(mSampleClipData, model.clipData)
+        assertEquals(source, model.source)
+        assertEquals(ClipboardModel.Type.TEXT, model.type)
+        assertEquals(mSampleClipData.getItemAt(0).text, model.text)
+        assertEquals(mSampleClipData.getItemAt(0).textLinks, model.textLinks)
+        assertEquals(mSampleClipData.getItemAt(0).uri, model.uri)
+        assertFalse(model.isSensitive)
+        assertFalse(model.isRemote)
+        assertNull(model.loadThumbnail(mContext))
+    }
+
+    @Test
+    fun test_sensitiveExtra() {
+        val description = mSampleClipData.description
+        val b = PersistableBundle()
+        b.putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true)
+        description.extras = b
+        val data = ClipData(description, mSampleClipData.getItemAt(0))
+        val (_, _, _, _, _, _, sensitive) =
+            ClipboardModel.fromClipData(mContext, mClipboardUtils, data, "")
+        assertTrue(sensitive)
+    }
+
+    @Test
+    fun test_remoteExtra() {
+        whenever(mClipboardUtils.isRemoteCopy(any(), any(), any())).thenReturn(true)
+        val model = ClipboardModel.fromClipData(mContext, mClipboardUtils, mSampleClipData, "")
+        assertTrue(model.isRemote)
+    }
+
+    @Test
+    @Throws(IOException::class)
+    fun test_imageClipData() {
+        val testBitmap = Bitmap.createBitmap(50, 50, Bitmap.Config.ARGB_8888)
+        whenever(mMockContext.contentResolver).thenReturn(mMockContentResolver)
+        whenever(mMockContext.resources).thenReturn(mContext.resources)
+        whenever(mMockContentResolver.loadThumbnail(any(), any(), any())).thenReturn(testBitmap)
+        whenever(mMockContentResolver.getType(any())).thenReturn("image")
+        val imageClipData =
+            ClipData("Test", arrayOf("text/plain"), ClipData.Item(Uri.parse("test")))
+        val model = ClipboardModel.fromClipData(mMockContext, mClipboardUtils, imageClipData, "")
+        assertEquals(ClipboardModel.Type.IMAGE, model.type)
+        assertEquals(testBitmap, model.loadThumbnail(mMockContext))
+    }
+
+    @Test
+    @Throws(IOException::class)
+    fun test_imageClipData_loadFailure() {
+        whenever(mMockContext.contentResolver).thenReturn(mMockContentResolver)
+        whenever(mMockContext.resources).thenReturn(mContext.resources)
+        whenever(mMockContentResolver.loadThumbnail(any(), any(), any())).thenThrow(IOException())
+        whenever(mMockContentResolver.getType(any())).thenReturn("image")
+        val imageClipData =
+            ClipData("Test", arrayOf("text/plain"), ClipData.Item(Uri.parse("test")))
+        val model = ClipboardModel.fromClipData(mMockContext, mClipboardUtils, imageClipData, "")
+        assertEquals(ClipboardModel.Type.IMAGE, model.type)
+        assertNull(model.loadThumbnail(mMockContext))
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
index b4e85c0..2099281 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
@@ -16,13 +16,17 @@
 
 package com.android.systemui.clipboardoverlay;
 
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ACTION_SHOWN;
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_DISMISS_TAPPED;
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SHARE_TAPPED;
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SWIPE_DISMISSED;
+import static com.android.systemui.flags.Flags.CLIPBOARD_MINIMIZED_LAYOUT;
 import static com.android.systemui.flags.Flags.CLIPBOARD_REMOTE_BEHAVIOR;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -35,8 +39,11 @@
 import android.content.ClipData;
 import android.content.ClipDescription;
 import android.content.Context;
+import android.graphics.Insets;
+import android.graphics.Rect;
 import android.net.Uri;
 import android.os.PersistableBundle;
+import android.view.WindowInsets;
 import android.view.textclassifier.TextLinks;
 
 import androidx.test.filters.SmallTest;
@@ -47,6 +54,7 @@
 import com.android.systemui.broadcast.BroadcastSender;
 import com.android.systemui.flags.FakeFeatureFlags;
 import com.android.systemui.screenshot.TimeoutHandler;
+import com.android.systemui.settings.FakeDisplayTracker;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
 
@@ -81,6 +89,7 @@
     private ClipboardOverlayUtils mClipboardUtils;
     @Mock
     private UiEventLogger mUiEventLogger;
+    private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
     private FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
 
     @Mock
@@ -100,11 +109,14 @@
 
         when(mClipboardOverlayView.getEnterAnimation()).thenReturn(mAnimator);
         when(mClipboardOverlayView.getExitAnimation()).thenReturn(mAnimator);
+        when(mClipboardOverlayWindow.getWindowInsets()).thenReturn(
+                getImeInsets(new Rect(0, 0, 0, 0)));
 
         mSampleClipData = new ClipData("Test", new String[]{"text/plain"},
                 new ClipData.Item("Test Item"));
 
         mFeatureFlags.set(CLIPBOARD_REMOTE_BEHAVIOR, false);
+        mFeatureFlags.set(CLIPBOARD_MINIMIZED_LAYOUT, true); // turned off for legacy tests
 
         mOverlayController = new ClipboardOverlayController(
                 mContext,
@@ -127,16 +139,178 @@
     }
 
     @Test
-    public void test_setClipData_nullData() {
-        ClipData clipData = null;
-        mOverlayController.setClipData(clipData, "");
+    public void test_setClipData_invalidImageData_legacy() {
+        mFeatureFlags.set(CLIPBOARD_MINIMIZED_LAYOUT, false);
+        ClipData clipData = new ClipData("", new String[]{"image/png"},
+                new ClipData.Item(Uri.parse("")));
+
+        mOverlayController.setClipDataLegacy(clipData, "");
 
         verify(mClipboardOverlayView, times(1)).showDefaultTextPreview();
-        verify(mClipboardOverlayView, times(0)).showShareChip();
+        verify(mClipboardOverlayView, times(1)).showShareChip();
         verify(mClipboardOverlayView, times(1)).getEnterAnimation();
     }
 
     @Test
+    public void test_setClipData_nonImageUri_legacy() {
+        mFeatureFlags.set(CLIPBOARD_MINIMIZED_LAYOUT, false);
+        ClipData clipData = new ClipData("", new String[]{"resource/png"},
+                new ClipData.Item(Uri.parse("")));
+
+        mOverlayController.setClipDataLegacy(clipData, "");
+
+        verify(mClipboardOverlayView, times(1)).showDefaultTextPreview();
+        verify(mClipboardOverlayView, times(1)).showShareChip();
+        verify(mClipboardOverlayView, times(1)).getEnterAnimation();
+    }
+
+    @Test
+    public void test_setClipData_textData_legacy() {
+        mFeatureFlags.set(CLIPBOARD_MINIMIZED_LAYOUT, false);
+
+        mOverlayController.setClipDataLegacy(mSampleClipData, "");
+
+        verify(mClipboardOverlayView, times(1)).showTextPreview("Test Item", false);
+        verify(mClipboardOverlayView, times(1)).showShareChip();
+        verify(mClipboardOverlayView, times(1)).getEnterAnimation();
+    }
+
+    @Test
+    public void test_setClipData_sensitiveTextData_legacy() {
+        mFeatureFlags.set(CLIPBOARD_MINIMIZED_LAYOUT, false);
+
+        ClipDescription description = mSampleClipData.getDescription();
+        PersistableBundle b = new PersistableBundle();
+        b.putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true);
+        description.setExtras(b);
+        ClipData data = new ClipData(description, mSampleClipData.getItemAt(0));
+        mOverlayController.setClipDataLegacy(data, "");
+
+        verify(mClipboardOverlayView, times(1)).showTextPreview("••••••", true);
+        verify(mClipboardOverlayView, times(1)).showShareChip();
+        verify(mClipboardOverlayView, times(1)).getEnterAnimation();
+    }
+
+    @Test
+    public void test_setClipData_repeatedCalls_legacy() {
+        mFeatureFlags.set(CLIPBOARD_MINIMIZED_LAYOUT, false);
+        when(mAnimator.isRunning()).thenReturn(true);
+
+        mOverlayController.setClipDataLegacy(mSampleClipData, "");
+        mOverlayController.setClipDataLegacy(mSampleClipData, "");
+
+        verify(mClipboardOverlayView, times(1)).getEnterAnimation();
+    }
+
+    @Test
+    public void test_viewCallbacks_onShareTapped_legacy() {
+        mFeatureFlags.set(CLIPBOARD_MINIMIZED_LAYOUT, false);
+        mOverlayController.setClipDataLegacy(mSampleClipData, "");
+
+        mCallbacks.onShareButtonTapped();
+
+        verify(mUiEventLogger, times(1)).log(CLIPBOARD_OVERLAY_SHARE_TAPPED, 0, "");
+        verify(mClipboardOverlayView, times(1)).getExitAnimation();
+    }
+
+    @Test
+    public void test_viewCallbacks_onDismissTapped_legacy() {
+        mFeatureFlags.set(CLIPBOARD_MINIMIZED_LAYOUT, false);
+        mOverlayController.setClipDataLegacy(mSampleClipData, "");
+
+        mCallbacks.onDismissButtonTapped();
+
+        verify(mUiEventLogger, times(1)).log(CLIPBOARD_OVERLAY_DISMISS_TAPPED, 0, "");
+        verify(mClipboardOverlayView, times(1)).getExitAnimation();
+    }
+
+    @Test
+    public void test_multipleDismissals_dismissesOnce_legacy() {
+        mFeatureFlags.set(CLIPBOARD_MINIMIZED_LAYOUT, false);
+
+        mCallbacks.onSwipeDismissInitiated(mAnimator);
+        mCallbacks.onDismissButtonTapped();
+        mCallbacks.onSwipeDismissInitiated(mAnimator);
+        mCallbacks.onDismissButtonTapped();
+
+        verify(mUiEventLogger, times(1)).log(CLIPBOARD_OVERLAY_SWIPE_DISMISSED, 0, null);
+        verify(mUiEventLogger, never()).log(CLIPBOARD_OVERLAY_DISMISS_TAPPED);
+    }
+
+    @Test
+    public void test_remoteCopy_withFlagOn_legacy() {
+        mFeatureFlags.set(CLIPBOARD_MINIMIZED_LAYOUT, false);
+        mFeatureFlags.set(CLIPBOARD_REMOTE_BEHAVIOR, true);
+        when(mClipboardUtils.isRemoteCopy(any(), any(), any())).thenReturn(true);
+
+        mOverlayController.setClipDataLegacy(mSampleClipData, "");
+
+        verify(mTimeoutHandler, never()).resetTimeout();
+    }
+
+    @Test
+    public void test_remoteCopy_withFlagOff_legacy() {
+        mFeatureFlags.set(CLIPBOARD_MINIMIZED_LAYOUT, false);
+        when(mClipboardUtils.isRemoteCopy(any(), any(), any())).thenReturn(true);
+
+        mOverlayController.setClipDataLegacy(mSampleClipData, "");
+
+        verify(mTimeoutHandler).resetTimeout();
+    }
+
+    @Test
+    public void test_nonRemoteCopy_legacy() {
+        mFeatureFlags.set(CLIPBOARD_MINIMIZED_LAYOUT, false);
+        mFeatureFlags.set(CLIPBOARD_REMOTE_BEHAVIOR, true);
+        when(mClipboardUtils.isRemoteCopy(any(), any(), any())).thenReturn(false);
+
+        mOverlayController.setClipDataLegacy(mSampleClipData, "");
+
+        verify(mTimeoutHandler).resetTimeout();
+    }
+
+    @Test
+    public void test_logsUseLastClipSource_legacy() {
+        mFeatureFlags.set(CLIPBOARD_MINIMIZED_LAYOUT, false);
+
+        mOverlayController.setClipDataLegacy(mSampleClipData, "first.package");
+        mCallbacks.onDismissButtonTapped();
+        mOverlayController.setClipDataLegacy(mSampleClipData, "second.package");
+        mCallbacks.onDismissButtonTapped();
+
+        verify(mUiEventLogger).log(CLIPBOARD_OVERLAY_DISMISS_TAPPED, 0, "first.package");
+        verify(mUiEventLogger).log(CLIPBOARD_OVERLAY_DISMISS_TAPPED, 0, "second.package");
+        verifyNoMoreInteractions(mUiEventLogger);
+    }
+
+    @Test
+    public void test_logOnClipboardActionsShown_legacy() {
+        mFeatureFlags.set(CLIPBOARD_MINIMIZED_LAYOUT, false);
+        ClipData.Item item = mSampleClipData.getItemAt(0);
+        item.setTextLinks(Mockito.mock(TextLinks.class));
+        mFeatureFlags.set(CLIPBOARD_REMOTE_BEHAVIOR, true);
+        when(mClipboardUtils.isRemoteCopy(any(Context.class), any(ClipData.class), anyString()))
+                .thenReturn(true);
+        when(mClipboardUtils.getAction(any(ClipData.Item.class), anyString()))
+                .thenReturn(Optional.of(Mockito.mock(RemoteAction.class)));
+        when(mClipboardOverlayView.post(any(Runnable.class))).thenAnswer(new Answer<Object>() {
+            @Override
+            public Object answer(InvocationOnMock invocation) throws Throwable {
+                ((Runnable) invocation.getArgument(0)).run();
+                return null;
+            }
+        });
+
+        mOverlayController.setClipDataLegacy(
+                new ClipData(mSampleClipData.getDescription(), item), "actionShownSource");
+        mExecutor.runAllReady();
+
+        verify(mUiEventLogger).log(CLIPBOARD_OVERLAY_ACTION_SHOWN, 0, "actionShownSource");
+        verifyNoMoreInteractions(mUiEventLogger);
+    }
+
+    // start of refactored setClipData tests
+    @Test
     public void test_setClipData_invalidImageData() {
         ClipData clipData = new ClipData("", new String[]{"image/png"},
                 new ClipData.Item(Uri.parse("")));
@@ -144,7 +318,19 @@
         mOverlayController.setClipData(clipData, "");
 
         verify(mClipboardOverlayView, times(1)).showDefaultTextPreview();
-        verify(mClipboardOverlayView, times(0)).showShareChip();
+        verify(mClipboardOverlayView, times(1)).showShareChip();
+        verify(mClipboardOverlayView, times(1)).getEnterAnimation();
+    }
+
+    @Test
+    public void test_setClipData_nonImageUri() {
+        ClipData clipData = new ClipData("", new String[]{"resource/png"},
+                new ClipData.Item(Uri.parse("")));
+
+        mOverlayController.setClipData(clipData, "");
+
+        verify(mClipboardOverlayView, times(1)).showDefaultTextPreview();
+        verify(mClipboardOverlayView, times(1)).showShareChip();
         verify(mClipboardOverlayView, times(1)).getEnterAnimation();
     }
 
@@ -260,7 +446,7 @@
         mFeatureFlags.set(CLIPBOARD_REMOTE_BEHAVIOR, true);
         when(mClipboardUtils.isRemoteCopy(any(Context.class), any(ClipData.class), anyString()))
                 .thenReturn(true);
-        when(mClipboardUtils.getAction(any(ClipData.Item.class), anyString()))
+        when(mClipboardUtils.getAction(any(CharSequence.class), any(TextLinks.class), anyString()))
                 .thenReturn(Optional.of(Mockito.mock(RemoteAction.class)));
         when(mClipboardOverlayView.post(any(Runnable.class))).thenAnswer(new Answer<Object>() {
             @Override
@@ -277,4 +463,43 @@
         verify(mUiEventLogger).log(CLIPBOARD_OVERLAY_ACTION_SHOWN, 0, "actionShownSource");
         verifyNoMoreInteractions(mUiEventLogger);
     }
+
+    @Test
+    public void test_noInsets_showsExpanded() {
+        mOverlayController.setClipData(mSampleClipData, "");
+
+        verify(mClipboardOverlayView, never()).setMinimized(true);
+        verify(mClipboardOverlayView).setMinimized(false);
+        verify(mClipboardOverlayView).showTextPreview("Test Item", false);
+    }
+
+    @Test
+    public void test_insets_showsMinimized() {
+        when(mClipboardOverlayWindow.getWindowInsets()).thenReturn(
+                getImeInsets(new Rect(0, 0, 0, 1)));
+        mOverlayController.setClipData(mSampleClipData, "");
+
+        verify(mClipboardOverlayView).setMinimized(true);
+        verify(mClipboardOverlayView, never()).setMinimized(false);
+        verify(mClipboardOverlayView, never()).showTextPreview(any(), anyBoolean());
+
+        mCallbacks.onMinimizedViewTapped();
+
+        verify(mClipboardOverlayView).setMinimized(false);
+        verify(mClipboardOverlayView).showTextPreview("Test Item", false);
+    }
+
+    @Test
+    public void test_insetsChanged_minimizes() {
+        mOverlayController.setClipData(mSampleClipData, "");
+        verify(mClipboardOverlayView, never()).setMinimized(true);
+
+        WindowInsets insetsWithKeyboard = getImeInsets(new Rect(0, 0, 0, 1));
+        mOverlayController.onInsetsChanged(insetsWithKeyboard, ORIENTATION_PORTRAIT);
+        verify(mClipboardOverlayView).setMinimized(true);
+    }
+
+    private static WindowInsets getImeInsets(Rect r) {
+        return new WindowInsets.Builder().setInsets(WindowInsets.Type.ime(), Insets.of(r)).build();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/coroutine/CoroutineResultTest.kt b/packages/SystemUI/tests/src/com/android/systemui/common/coroutine/CoroutineResultTest.kt
new file mode 100644
index 0000000..d552c9d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/common/coroutine/CoroutineResultTest.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.common.coroutine
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** atest SystemUITests:CoroutineResultTest */
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class CoroutineResultTest : SysuiTestCase() {
+
+    @Test
+    fun suspendRunCatching_shouldReturnSuccess() = runTest {
+        val actual = suspendRunCatching { "Placeholder" }
+        assertThat(actual.isSuccess).isTrue()
+        assertThat(actual.getOrNull()).isEqualTo("Placeholder")
+    }
+
+    @Test
+    fun suspendRunCatching_whenExceptionThrow_shouldResumeWithException() = runTest {
+        val actual = suspendRunCatching { throw Exception() }
+        assertThat(actual.isFailure).isTrue()
+        assertThat(actual.exceptionOrNull()).isInstanceOf(Exception::class.java)
+    }
+
+    @Test(expected = CancellationException::class)
+    fun suspendRunCatching_whenCancelled_shouldResumeWithException() = runTest {
+        suspendRunCatching { cancel() }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
new file mode 100644
index 0000000..fe352fd
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
@@ -0,0 +1,163 @@
+/*
+ * 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.ui.view
+
+import android.view.ViewConfiguration
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.view.LongPressHandlingViewInteractionHandler.MotionEventModel
+import com.android.systemui.common.ui.view.LongPressHandlingViewInteractionHandler.MotionEventModel.Down
+import com.android.systemui.common.ui.view.LongPressHandlingViewInteractionHandler.MotionEventModel.Move
+import com.android.systemui.common.ui.view.LongPressHandlingViewInteractionHandler.MotionEventModel.Up
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class LongPressHandlingViewInteractionHandlerTest : SysuiTestCase() {
+
+    @Mock private lateinit var postDelayed: (Runnable, Long) -> DisposableHandle
+    @Mock private lateinit var onLongPressDetected: (Int, Int) -> Unit
+    @Mock private lateinit var onSingleTapDetected: () -> Unit
+
+    private lateinit var underTest: LongPressHandlingViewInteractionHandler
+
+    private var isAttachedToWindow: Boolean = true
+    private var delayedRunnable: Runnable? = null
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        whenever(postDelayed.invoke(any(), any())).thenAnswer { invocation ->
+            delayedRunnable = invocation.arguments[0] as Runnable
+            DisposableHandle { delayedRunnable = null }
+        }
+
+        underTest =
+            LongPressHandlingViewInteractionHandler(
+                postDelayed = postDelayed,
+                isAttachedToWindow = { isAttachedToWindow },
+                onLongPressDetected = onLongPressDetected,
+                onSingleTapDetected = onSingleTapDetected,
+            )
+        underTest.isLongPressHandlingEnabled = true
+    }
+
+    @Test
+    fun `long-press`() = runTest {
+        val downX = 123
+        val downY = 456
+        dispatchTouchEvents(
+            Down(
+                x = downX,
+                y = downY,
+            ),
+            Move(
+                distanceMoved = ViewConfiguration.getTouchSlop() - 0.1f,
+            ),
+        )
+        delayedRunnable?.run()
+
+        verify(onLongPressDetected).invoke(downX, downY)
+        verify(onSingleTapDetected, never()).invoke()
+    }
+
+    @Test
+    fun `long-press but feature not enabled`() = runTest {
+        underTest.isLongPressHandlingEnabled = false
+        dispatchTouchEvents(
+            Down(
+                x = 123,
+                y = 456,
+            ),
+        )
+
+        assertThat(delayedRunnable).isNull()
+        verify(onLongPressDetected, never()).invoke(any(), any())
+        verify(onSingleTapDetected, never()).invoke()
+    }
+
+    @Test
+    fun `long-press but view not attached`() = runTest {
+        isAttachedToWindow = false
+        dispatchTouchEvents(
+            Down(
+                x = 123,
+                y = 456,
+            ),
+        )
+        delayedRunnable?.run()
+
+        verify(onLongPressDetected, never()).invoke(any(), any())
+        verify(onSingleTapDetected, never()).invoke()
+    }
+
+    @Test
+    fun `dragged too far to be considered a long-press`() = runTest {
+        dispatchTouchEvents(
+            Down(
+                x = 123,
+                y = 456,
+            ),
+            Move(
+                distanceMoved = ViewConfiguration.getTouchSlop() + 0.1f,
+            ),
+        )
+
+        assertThat(delayedRunnable).isNull()
+        verify(onLongPressDetected, never()).invoke(any(), any())
+        verify(onSingleTapDetected, never()).invoke()
+    }
+
+    @Test
+    fun `held down too briefly to be considered a long-press`() = runTest {
+        dispatchTouchEvents(
+            Down(
+                x = 123,
+                y = 456,
+            ),
+            Up(
+                distanceMoved = ViewConfiguration.getTouchSlop().toFloat(),
+                gestureDuration = ViewConfiguration.getLongPressTimeout() - 1L,
+            ),
+        )
+
+        assertThat(delayedRunnable).isNull()
+        verify(onLongPressDetected, never()).invoke(any(), any())
+        verify(onSingleTapDetected).invoke()
+    }
+
+    private fun dispatchTouchEvents(
+        vararg models: MotionEventModel,
+    ) {
+        models.forEach { model -> underTest.onTouchEvent(model) }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java
new file mode 100644
index 0000000..2ed0346
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.ui.view;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.widget.ImageView;
+import android.widget.SeekBar;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link SeekBarWithIconButtonsView}
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class SeekBarWithIconButtonsViewTest extends SysuiTestCase {
+
+    private ImageView mIconStart;
+    private ImageView mIconEnd;
+    private SeekBar mSeekbar;
+    private SeekBarWithIconButtonsView mIconDiscreteSliderLinearLayout;
+
+    @Before
+    public void setUp() {
+        mIconDiscreteSliderLinearLayout = new SeekBarWithIconButtonsView(mContext);
+        mIconStart = mIconDiscreteSliderLinearLayout.findViewById(R.id.icon_start);
+        mIconEnd = mIconDiscreteSliderLinearLayout.findViewById(R.id.icon_end);
+        mSeekbar = mIconDiscreteSliderLinearLayout.findViewById(R.id.seekbar);
+    }
+
+    @Test
+    public void setSeekBarProgressZero_startIconAndFrameDisabled() {
+        mIconDiscreteSliderLinearLayout.setProgress(0);
+
+        assertThat(mIconStart.isEnabled()).isFalse();
+        assertThat(mIconEnd.isEnabled()).isTrue();
+    }
+
+    @Test
+    public void setSeekBarProgressMax_endIconAndFrameDisabled() {
+        mIconDiscreteSliderLinearLayout.setProgress(mSeekbar.getMax());
+
+        assertThat(mIconEnd.isEnabled()).isFalse();
+        assertThat(mIconStart.isEnabled()).isTrue();
+    }
+
+    @Test
+    public void setSeekBarProgressMax_allIconsAndFramesEnabled() {
+        // We are using the default value for the max of seekbar.
+        // Therefore, the max value will be DEFAULT_SEEKBAR_MAX = 6.
+        mIconDiscreteSliderLinearLayout.setProgress(1);
+
+        assertThat(mIconStart.isEnabled()).isTrue();
+        assertThat(mIconEnd.isEnabled()).isTrue();
+    }
+
+    @Test
+    public void clickIconEnd_currentProgressIsOneToMax_reachesMax() {
+        mIconDiscreteSliderLinearLayout.setProgress(mSeekbar.getMax() - 1);
+        mIconEnd.performClick();
+
+        assertThat(mSeekbar.getProgress()).isEqualTo(mSeekbar.getMax());
+    }
+
+    @Test
+    public void clickIconStart_currentProgressIsOne_reachesZero() {
+        mIconDiscreteSliderLinearLayout.setProgress(1);
+        mIconStart.performClick();
+
+        assertThat(mSeekbar.getProgress()).isEqualTo(0);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
index 25f471b..e35b2a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.controls.ControlStatus
 import com.android.systemui.controls.ControlsServiceInfo
 import com.android.systemui.controls.management.ControlsListingController
+import com.android.systemui.controls.panels.AuthorizedPanelsRepository
 import com.android.systemui.controls.ui.ControlsUiController
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.settings.UserFileManager
@@ -66,6 +67,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyNoMoreInteractions
 import org.mockito.Mockito.`when`
+import org.mockito.Mockito.clearInvocations
 import org.mockito.MockitoAnnotations
 
 @SmallTest
@@ -88,6 +90,8 @@
     private lateinit var userTracker: UserTracker
     @Mock
     private lateinit var userFileManager: UserFileManager
+    @Mock
+    private lateinit var authorizedPanelsRepository: AuthorizedPanelsRepository
 
     @Captor
     private lateinit var structureInfoCaptor: ArgumentCaptor<StructureInfo>
@@ -100,8 +104,6 @@
             ArgumentCaptor<ControlsBindingController.LoadCallback>
 
     @Captor
-    private lateinit var userTrackerCallbackCaptor: ArgumentCaptor<UserTracker.Callback>
-    @Captor
     private lateinit var listingCallbackCaptor:
             ArgumentCaptor<ControlsListingController.ControlsListingCallback>
 
@@ -168,15 +170,12 @@
                 listingController,
                 userFileManager,
                 userTracker,
+                authorizedPanelsRepository,
                 Optional.of(persistenceWrapper),
                 mock(DumpManager::class.java)
         )
         controller.auxiliaryPersistenceWrapper = auxiliaryPersistenceWrapper
 
-        verify(userTracker).addCallback(
-            capture(userTrackerCallbackCaptor), any()
-        )
-
         verify(listingController).addCallback(capture(listingCallbackCaptor))
     }
 
@@ -224,6 +223,7 @@
                 listingController,
                 userFileManager,
                 userTracker,
+                authorizedPanelsRepository,
                 Optional.of(persistenceWrapper),
                 mock(DumpManager::class.java)
         )
@@ -231,6 +231,26 @@
     }
 
     @Test
+    fun testAddAuthorizedPackagesFromSavedFavoritesOnStart() {
+        clearInvocations(authorizedPanelsRepository)
+        `when`(persistenceWrapper.readFavorites()).thenReturn(listOf(TEST_STRUCTURE_INFO))
+        ControlsControllerImpl(
+                mContext,
+                delayableExecutor,
+                uiController,
+                bindingController,
+                listingController,
+                userFileManager,
+                userTracker,
+                authorizedPanelsRepository,
+                Optional.of(persistenceWrapper),
+                mock(DumpManager::class.java)
+        )
+        verify(authorizedPanelsRepository)
+                .addAuthorizedPanels(setOf(TEST_STRUCTURE_INFO.componentName.packageName))
+    }
+
+    @Test
     fun testOnActionResponse() {
         controller.onActionResponse(TEST_COMPONENT, TEST_CONTROL_ID, ControlAction.RESPONSE_OK)
 
@@ -513,7 +533,7 @@
 
         reset(persistenceWrapper)
 
-        userTrackerCallbackCaptor.value.onUserChanged(otherUser, mContext)
+        controller.changeUser(UserHandle.of(otherUser))
 
         verify(persistenceWrapper).changeFileAndBackupManager(any(), any())
         verify(persistenceWrapper).readFavorites()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt
index 4439586..2283746 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt
@@ -18,9 +18,13 @@
 
 import android.app.job.JobParameters
 import android.content.Context
+import android.os.PersistableBundle
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.controls.controller.AuxiliaryPersistenceWrapper.DeletionJobService.Companion.USER
+import com.android.systemui.util.mockito.whenever
+import java.util.concurrent.TimeUnit
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
@@ -28,18 +32,15 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
-import org.mockito.Mockito.`when`
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
-import java.util.concurrent.TimeUnit
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 class DeletionJobServiceTest : SysuiTestCase() {
 
-    @Mock
-    private lateinit var context: Context
+    @Mock private lateinit var context: Context
 
     private lateinit var service: AuxiliaryPersistenceWrapper.DeletionJobService
 
@@ -53,6 +54,10 @@
 
     @Test
     fun testOnStartJob() {
+        val bundle = PersistableBundle().also { it.putInt(USER, 0) }
+        val params = mock(JobParameters::class.java)
+        whenever(params.getExtras()).thenReturn(bundle)
+
         // false means job is terminated
         assertFalse(service.onStartJob(mock(JobParameters::class.java)))
         verify(context).deleteFile(AuxiliaryPersistenceWrapper.AUXILIARY_FILE_NAME)
@@ -67,13 +72,17 @@
     @Test
     fun testJobHasRightParameters() {
         val userId = 10
-        `when`(context.userId).thenReturn(userId)
-        `when`(context.packageName).thenReturn(mContext.packageName)
+        whenever(context.userId).thenReturn(userId)
+        whenever(context.packageName).thenReturn(mContext.packageName)
 
-        val jobInfo = AuxiliaryPersistenceWrapper.DeletionJobService.getJobForContext(context)
+        val jobInfo =
+            AuxiliaryPersistenceWrapper.DeletionJobService.getJobForContext(context, userId)
         assertEquals(
-            AuxiliaryPersistenceWrapper.DeletionJobService.DELETE_FILE_JOB_ID + userId, jobInfo.id)
+            AuxiliaryPersistenceWrapper.DeletionJobService.DELETE_FILE_JOB_ID + userId,
+            jobInfo.id
+        )
         assertTrue(jobInfo.isPersisted)
+        assertEquals(userId, jobInfo.getExtras().getInt(USER))
         assertEquals(TimeUnit.DAYS.toMillis(7), jobInfo.minLatencyMillis)
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AppAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AppAdapterTest.kt
index 765c4c0..226ef3b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AppAdapterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AppAdapterTest.kt
@@ -21,12 +21,15 @@
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.LayoutInflater
+import android.view.View
 import androidx.test.filters.SmallTest
 import com.android.settingslib.core.lifecycle.Lifecycle
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.controls.ControlsServiceInfo
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
+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
@@ -36,8 +39,10 @@
 import org.mockito.ArgumentCaptor
 import org.mockito.Mock
 import org.mockito.Mockito.`when`
+import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
+import java.text.Collator
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -49,25 +54,18 @@
     @Mock lateinit var lifecycle: Lifecycle
     @Mock lateinit var controlsListingController: ControlsListingController
     @Mock lateinit var layoutInflater: LayoutInflater
-    @Mock lateinit var onAppSelected: (ComponentName?) -> Unit
+    @Mock lateinit var onAppSelected: (ControlsServiceInfo) -> Unit
     @Mock lateinit var favoritesRenderer: FavoritesRenderer
     val resources: Resources = context.resources
     lateinit var adapter: AppAdapter
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        adapter = AppAdapter(backgroundExecutor,
-            uiExecutor,
-            lifecycle,
-            controlsListingController,
-            layoutInflater,
-            onAppSelected,
-            favoritesRenderer,
-            resources)
     }
 
     @Test
     fun testOnServicesUpdated_nullLoadLabel() {
+        adapter = createAdapterWithAuthorizedPanels(emptySet())
         val captor = ArgumentCaptor
             .forClass(ControlsListingController.ControlsListingCallback::class.java)
         val controlsServiceInfo = mock<ControlsServiceInfo>()
@@ -76,14 +74,14 @@
         verify(controlsListingController).observe(any(Lifecycle::class.java), captor.capture())
 
         captor.value.onServicesUpdated(serviceInfo)
-        backgroundExecutor.runAllReady()
-        uiExecutor.runAllReady()
+        FakeExecutor.exhaustExecutors(backgroundExecutor, uiExecutor)
 
         assertThat(adapter.itemCount).isEqualTo(serviceInfo.size)
     }
 
     @Test
-    fun testOnServicesUpdatedDoesntHavePanels() {
+    fun testOnServicesUpdated_showsNotAuthorizedPanels() {
+        adapter = createAdapterWithAuthorizedPanels(emptySet())
         val captor = ArgumentCaptor
                 .forClass(ControlsListingController.ControlsListingCallback::class.java)
         val serviceInfo = listOf(
@@ -93,20 +91,88 @@
         verify(controlsListingController).observe(any(Lifecycle::class.java), captor.capture())
 
         captor.value.onServicesUpdated(serviceInfo)
-        backgroundExecutor.runAllReady()
-        uiExecutor.runAllReady()
+        FakeExecutor.exhaustExecutors(backgroundExecutor, uiExecutor)
+
+        assertThat(adapter.itemCount).isEqualTo(2)
+    }
+
+    @Test
+    fun testOnServicesUpdated_doesntShowAuthorizedPanels() {
+        adapter = createAdapterWithAuthorizedPanels(setOf(TEST_PACKAGE))
+
+        val captor = ArgumentCaptor
+                .forClass(ControlsListingController.ControlsListingCallback::class.java)
+        val serviceInfo = listOf(
+                ControlsServiceInfo("no panel", null),
+                ControlsServiceInfo("panel", ComponentName(TEST_PACKAGE, "cls"))
+        )
+        verify(controlsListingController).observe(any(Lifecycle::class.java), captor.capture())
+
+        captor.value.onServicesUpdated(serviceInfo)
+        FakeExecutor.exhaustExecutors(backgroundExecutor, uiExecutor)
 
         assertThat(adapter.itemCount).isEqualTo(1)
     }
 
-    fun ControlsServiceInfo(
-        label: CharSequence,
-        panelComponentName: ComponentName? = null
-    ): ControlsServiceInfo {
-        return mock {
-            `when`(this.loadLabel()).thenReturn(label)
-            `when`(this.panelActivity).thenReturn(panelComponentName)
-            `when`(this.loadIcon()).thenReturn(mock())
+    @Test
+    fun testOnBindSetsClickListenerToCallOnAppSelected() {
+        adapter = createAdapterWithAuthorizedPanels(emptySet())
+
+        val captor = ArgumentCaptor
+                .forClass(ControlsListingController.ControlsListingCallback::class.java)
+        val serviceInfo = listOf(
+                ControlsServiceInfo("no panel", null),
+                ControlsServiceInfo("panel", ComponentName(TEST_PACKAGE, "cls"))
+        )
+        verify(controlsListingController).observe(any(Lifecycle::class.java), captor.capture())
+
+        captor.value.onServicesUpdated(serviceInfo)
+        FakeExecutor.exhaustExecutors(backgroundExecutor, uiExecutor)
+
+        val sorted = serviceInfo.sortedWith(
+                compareBy(Collator.getInstance(resources.configuration.locales[0])) {
+                    it.loadLabel() ?: ""
+                })
+
+        sorted.forEachIndexed { index, info ->
+            val fakeView: View = mock()
+            val fakeHolder: AppAdapter.Holder = mock()
+            `when`(fakeHolder.view).thenReturn(fakeView)
+
+            clearInvocations(onAppSelected)
+            adapter.onBindViewHolder(fakeHolder, index)
+            val listenerCaptor: ArgumentCaptor<View.OnClickListener> = argumentCaptor()
+            verify(fakeView).setOnClickListener(capture(listenerCaptor))
+            listenerCaptor.value.onClick(fakeView)
+
+            verify(onAppSelected).invoke(info)
         }
     }
+
+    private fun createAdapterWithAuthorizedPanels(packages: Set<String>): AppAdapter {
+        return AppAdapter(backgroundExecutor,
+                uiExecutor,
+                lifecycle,
+                controlsListingController,
+                layoutInflater,
+                onAppSelected,
+                favoritesRenderer,
+                resources,
+                packages)
+    }
+
+    companion object {
+        private fun ControlsServiceInfo(
+                label: CharSequence,
+                panelComponentName: ComponentName? = null
+        ): ControlsServiceInfo {
+            return mock {
+                `when`(loadLabel()).thenReturn(label)
+                `when`(panelActivity).thenReturn(panelComponentName)
+                `when`(loadIcon()).thenReturn(mock())
+            }
+        }
+
+        private const val TEST_PACKAGE = "package"
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt
index 56c3efe..8dfd223 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt
@@ -16,7 +16,13 @@
 
 package com.android.systemui.controls.management
 
+import android.app.Dialog
+import android.content.ComponentName
 import android.content.Intent
+import android.content.pm.ApplicationInfo
+import android.content.pm.ServiceInfo
+import android.graphics.drawable.Drawable
+import android.os.Bundle
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.window.OnBackInvokedCallback
@@ -25,14 +31,23 @@
 import androidx.test.rule.ActivityTestRule
 import androidx.test.runner.intercepting.SingleActivityFactory
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.controls.ControlsServiceInfo
 import com.android.systemui.controls.controller.ControlsController
-import com.android.systemui.controls.ui.ControlsUiController
+import com.android.systemui.controls.panels.AuthorizedPanelsRepository
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
 import com.google.common.util.concurrent.MoreExecutors
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.Executor
+import java.util.function.Consumer
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -41,7 +56,11 @@
 import org.mockito.ArgumentMatchers
 import org.mockito.Captor
 import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
 import org.mockito.MockitoAnnotations
 
 @SmallTest
@@ -58,9 +77,10 @@
 
     @Mock lateinit var userTracker: UserTracker
 
-    @Mock lateinit var uiController: ControlsUiController
+    @Mock lateinit var authorizedPanelsRepository: AuthorizedPanelsRepository
 
-    private lateinit var controlsProviderSelectorActivity: ControlsProviderSelectorActivity_Factory
+    @Mock lateinit var dialogFactory: PanelConfirmationDialogFactory
+
     private var latch: CountDownLatch = CountDownLatch(1)
 
     @Mock private lateinit var mockDispatcher: OnBackInvokedDispatcher
@@ -81,7 +101,8 @@
                         listingController,
                         controlsController,
                         userTracker,
-                        uiController,
+                        authorizedPanelsRepository,
+                        dialogFactory,
                         mockDispatcher,
                         latch
                     )
@@ -113,13 +134,99 @@
         verify(mockDispatcher).unregisterOnBackInvokedCallback(captureCallback.value)
     }
 
-    public class TestableControlsProviderSelectorActivity(
+    @Test
+    fun testOnAppSelectedForNonPanelStartsFavoritingActivity() {
+        val info = ControlsServiceInfo(ComponentName("test_pkg", "service"), "", null)
+        activityRule.activity.onAppSelected(info)
+
+        verifyNoMoreInteractions(dialogFactory)
+
+        assertThat(activityRule.activity.lastStartedActivity?.component?.className)
+            .isEqualTo(ControlsFavoritingActivity::class.java.name)
+
+        assertThat(activityRule.activity.triedToFinish).isTrue()
+    }
+
+    @Test
+    fun testOnAppSelectedForPanelTriggersDialog() {
+        val label = "label"
+        val info =
+            ControlsServiceInfo(
+                ComponentName("test_pkg", "service"),
+                label,
+                ComponentName("test_pkg", "activity")
+            )
+
+        val dialog: Dialog = mock()
+        whenever(dialogFactory.createConfirmationDialog(any(), any(), any())).thenReturn(dialog)
+
+        activityRule.activity.onAppSelected(info)
+        verify(dialogFactory).createConfirmationDialog(any(), eq(label), any())
+        verify(dialog).show()
+
+        assertThat(activityRule.activity.triedToFinish).isFalse()
+    }
+
+    @Test
+    fun dialogAcceptAddsPackage() {
+        val label = "label"
+        val info =
+            ControlsServiceInfo(
+                ComponentName("test_pkg", "service"),
+                label,
+                ComponentName("test_pkg", "activity")
+            )
+
+        val dialog: Dialog = mock()
+        whenever(dialogFactory.createConfirmationDialog(any(), any(), any())).thenReturn(dialog)
+
+        activityRule.activity.onAppSelected(info)
+
+        val captor: ArgumentCaptor<Consumer<Boolean>> = argumentCaptor()
+        verify(dialogFactory).createConfirmationDialog(any(), any(), capture(captor))
+
+        captor.value.accept(true)
+
+        val setCaptor: ArgumentCaptor<Set<String>> = argumentCaptor()
+        verify(authorizedPanelsRepository).addAuthorizedPanels(capture(setCaptor))
+        assertThat(setCaptor.value).containsExactly(info.componentName.packageName)
+
+        assertThat(activityRule.activity.triedToFinish).isTrue()
+    }
+
+    @Test
+    fun dialogCancelDoesntAddPackage() {
+        val label = "label"
+        val info =
+            ControlsServiceInfo(
+                ComponentName("test_pkg", "service"),
+                label,
+                ComponentName("test_pkg", "activity")
+            )
+
+        val dialog: Dialog = mock()
+        whenever(dialogFactory.createConfirmationDialog(any(), any(), any())).thenReturn(dialog)
+
+        activityRule.activity.onAppSelected(info)
+
+        val captor: ArgumentCaptor<Consumer<Boolean>> = argumentCaptor()
+        verify(dialogFactory).createConfirmationDialog(any(), any(), capture(captor))
+
+        captor.value.accept(false)
+
+        verify(authorizedPanelsRepository, never()).addAuthorizedPanels(any())
+
+        assertThat(activityRule.activity.triedToFinish).isFalse()
+    }
+
+    class TestableControlsProviderSelectorActivity(
         executor: Executor,
         backExecutor: Executor,
         listingController: ControlsListingController,
         controlsController: ControlsController,
         userTracker: UserTracker,
-        uiController: ControlsUiController,
+        authorizedPanelsRepository: AuthorizedPanelsRepository,
+        dialogFactory: PanelConfirmationDialogFactory,
         private val mockDispatcher: OnBackInvokedDispatcher,
         private val latch: CountDownLatch
     ) :
@@ -129,16 +236,50 @@
             listingController,
             controlsController,
             userTracker,
-            uiController
+            authorizedPanelsRepository,
+            dialogFactory
         ) {
+
+        var lastStartedActivity: Intent? = null
+        var triedToFinish = false
+
         override fun getOnBackInvokedDispatcher(): OnBackInvokedDispatcher {
             return mockDispatcher
         }
 
+        override fun startActivity(intent: Intent?, options: Bundle?) {
+            lastStartedActivity = intent
+        }
+
         override fun onStop() {
             super.onStop()
             // ensures that test runner thread does not proceed until ui thread is done
             latch.countDown()
         }
+
+        override fun animateExitAndFinish() {
+            // Activity should only be finished from the rule.
+            triedToFinish = true
+        }
+    }
+
+    companion object {
+        private fun ControlsServiceInfo(
+            componentName: ComponentName,
+            label: CharSequence,
+            panelComponentName: ComponentName? = null
+        ): ControlsServiceInfo {
+            val serviceInfo =
+                ServiceInfo().apply {
+                    applicationInfo = ApplicationInfo()
+                    packageName = componentName.packageName
+                    name = componentName.className
+                }
+            return Mockito.spy(ControlsServiceInfo(mock(), serviceInfo)).apply {
+                doReturn(label).`when`(this).loadLabel()
+                doReturn(mock<Drawable>()).`when`(this).loadIcon()
+                doReturn(panelComponentName).`when`(this).panelActivity
+            }
+        }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt
new file mode 100644
index 0000000..756f267
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt
@@ -0,0 +1,106 @@
+/*
+ * 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.controls.management
+
+import android.content.DialogInterface
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class PanelConfirmationDialogFactoryTest : SysuiTestCase() {
+
+    @Test
+    fun testDialogHasCorrectInfo() {
+        val mockDialog: SystemUIDialog = mock() { `when`(context).thenReturn(mContext) }
+        val factory = PanelConfirmationDialogFactory { mockDialog }
+        val appName = "appName"
+
+        factory.createConfirmationDialog(context, appName) {}
+
+        verify(mockDialog).setCanceledOnTouchOutside(true)
+        verify(mockDialog)
+            .setTitle(context.getString(R.string.controls_panel_authorization_title, appName))
+        verify(mockDialog)
+            .setMessage(context.getString(R.string.controls_panel_authorization, appName))
+    }
+
+    @Test
+    fun testDialogPositiveButton() {
+        val mockDialog: SystemUIDialog = mock() { `when`(context).thenReturn(mContext) }
+        val factory = PanelConfirmationDialogFactory { mockDialog }
+
+        var response: Boolean? = null
+
+        factory.createConfirmationDialog(context, "") { response = it }
+
+        val captor: ArgumentCaptor<DialogInterface.OnClickListener> = argumentCaptor()
+        verify(mockDialog).setPositiveButton(eq(R.string.controls_dialog_ok), capture(captor))
+
+        captor.value.onClick(mockDialog, DialogInterface.BUTTON_POSITIVE)
+
+        assertThat(response).isTrue()
+    }
+
+    @Test
+    fun testDialogNeutralButton() {
+        val mockDialog: SystemUIDialog = mock() { `when`(context).thenReturn(mContext) }
+        val factory = PanelConfirmationDialogFactory { mockDialog }
+
+        var response: Boolean? = null
+
+        factory.createConfirmationDialog(context, "") { response = it }
+
+        val captor: ArgumentCaptor<DialogInterface.OnClickListener> = argumentCaptor()
+        verify(mockDialog).setNeutralButton(eq(R.string.cancel), capture(captor))
+
+        captor.value.onClick(mockDialog, DialogInterface.BUTTON_NEUTRAL)
+
+        assertThat(response).isFalse()
+    }
+
+    @Test
+    fun testDialogCancel() {
+        val mockDialog: SystemUIDialog = mock() { `when`(context).thenReturn(mContext) }
+        val factory = PanelConfirmationDialogFactory { mockDialog }
+
+        var response: Boolean? = null
+
+        factory.createConfirmationDialog(context, "") { response = it }
+
+        val captor: ArgumentCaptor<DialogInterface.OnCancelListener> = argumentCaptor()
+        verify(mockDialog).setOnCancelListener(capture(captor))
+
+        captor.value.onCancel(mockDialog)
+
+        assertThat(response).isFalse()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt
new file mode 100644
index 0000000..b91a3fd
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt
@@ -0,0 +1,145 @@
+/*
+ * 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.controls.panels
+
+import android.content.SharedPreferences
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.UserFileManager
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.FakeSharedPreferences
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import java.io.File
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class AuthorizedPanelsRepositoryImplTest : SysuiTestCase() {
+
+    @Mock private lateinit var userTracker: UserTracker
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        mContext.orCreateTestableResources.addOverride(
+            R.array.config_controlsPreferredPackages,
+            arrayOf<String>()
+        )
+        whenever(userTracker.userId).thenReturn(0)
+    }
+
+    @Test
+    fun testPreApprovedPackagesAreSeededIfNoSavedPreferences() {
+        mContext.orCreateTestableResources.addOverride(
+            R.array.config_controlsPreferredPackages,
+            arrayOf(TEST_PACKAGE)
+        )
+        val sharedPrefs = FakeSharedPreferences()
+        val fileManager = FakeUserFileManager(mapOf(0 to sharedPrefs))
+        val repository = createRepository(fileManager)
+
+        assertThat(repository.getAuthorizedPanels()).containsExactly(TEST_PACKAGE)
+        assertThat(sharedPrefs.getStringSet(KEY, null)).containsExactly(TEST_PACKAGE)
+    }
+
+    @Test
+    fun testPreApprovedPackagesNotSeededIfEmptySavedPreferences() {
+        mContext.orCreateTestableResources.addOverride(
+            R.array.config_controlsPreferredPackages,
+            arrayOf(TEST_PACKAGE)
+        )
+        val sharedPrefs = FakeSharedPreferences()
+        sharedPrefs.edit().putStringSet(KEY, emptySet()).apply()
+        val fileManager = FakeUserFileManager(mapOf(0 to sharedPrefs))
+        createRepository(fileManager)
+
+        assertThat(sharedPrefs.getStringSet(KEY, null)).isEmpty()
+    }
+
+    @Test
+    fun testPreApprovedPackagesOnlySetForUserThatDoesntHaveThem() {
+        mContext.orCreateTestableResources.addOverride(
+            R.array.config_controlsPreferredPackages,
+            arrayOf(TEST_PACKAGE)
+        )
+        val sharedPrefs_0 = FakeSharedPreferences()
+        val sharedPrefs_1 = FakeSharedPreferences()
+        sharedPrefs_1.edit().putStringSet(KEY, emptySet()).apply()
+        val fileManager = FakeUserFileManager(mapOf(0 to sharedPrefs_0, 1 to sharedPrefs_1))
+        val repository = createRepository(fileManager)
+
+        assertThat(repository.getAuthorizedPanels()).containsExactly(TEST_PACKAGE)
+        whenever(userTracker.userId).thenReturn(1)
+        assertThat(repository.getAuthorizedPanels()).isEmpty()
+    }
+
+    @Test
+    fun testGetAuthorizedPackages() {
+        val sharedPrefs = FakeSharedPreferences()
+        sharedPrefs.edit().putStringSet(KEY, mutableSetOf(TEST_PACKAGE)).apply()
+        val fileManager = FakeUserFileManager(mapOf(0 to sharedPrefs))
+
+        val repository = createRepository(fileManager)
+        assertThat(repository.getAuthorizedPanels()).containsExactly(TEST_PACKAGE)
+    }
+
+    @Test
+    fun testSetAuthorizedPackage() {
+        val sharedPrefs = FakeSharedPreferences()
+        val fileManager = FakeUserFileManager(mapOf(0 to sharedPrefs))
+
+        val repository = createRepository(fileManager)
+        repository.addAuthorizedPanels(setOf(TEST_PACKAGE))
+        assertThat(sharedPrefs.getStringSet(KEY, null)).containsExactly(TEST_PACKAGE)
+    }
+
+    private fun createRepository(userFileManager: UserFileManager): AuthorizedPanelsRepositoryImpl {
+        return AuthorizedPanelsRepositoryImpl(mContext, userFileManager, userTracker)
+    }
+
+    private class FakeUserFileManager(private val sharedPrefs: Map<Int, SharedPreferences>) :
+        UserFileManager {
+        override fun getFile(fileName: String, userId: Int): File {
+            throw UnsupportedOperationException()
+        }
+
+        override fun getSharedPreferences(
+            fileName: String,
+            mode: Int,
+            userId: Int
+        ): SharedPreferences {
+            if (fileName != FILE_NAME) {
+                throw IllegalArgumentException("Preference files must be $FILE_NAME")
+            }
+            return sharedPrefs.getValue(userId)
+        }
+    }
+
+    companion object {
+        private const val FILE_NAME = "controls_prefs"
+        private const val KEY = "authorized_panels"
+        private const val TEST_PACKAGE = "package"
+    }
+}
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
new file mode 100644
index 0000000..7ecaca6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt
@@ -0,0 +1,281 @@
+/*
+ * 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.controls.start
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.pm.ServiceInfo
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+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.ui.SelectedItem
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.time.FakeSystemClock
+import java.util.Optional
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ControlsStartableTest : SysuiTestCase() {
+
+    @Mock private lateinit var controlsController: ControlsController
+    @Mock private lateinit var controlsListingController: ControlsListingController
+    @Mock private lateinit var userTracker: UserTracker
+
+    private lateinit var fakeExecutor: FakeExecutor
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        context.orCreateTestableResources.addOverride(
+            R.array.config_controlsPreferredPackages,
+            arrayOf<String>()
+        )
+
+        fakeExecutor = FakeExecutor(FakeSystemClock())
+    }
+
+    @Test
+    fun testDisabledNothingIsCalled() {
+        createStartable(enabled = false).start()
+
+        verifyZeroInteractions(controlsController, controlsListingController, userTracker)
+    }
+
+    @Test
+    fun testNoPreferredPackagesNoDefaultSelected_noNewSelection() {
+        `when`(controlsController.getPreferredSelection()).thenReturn(SelectedItem.EMPTY_SELECTION)
+        val listings = listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true))
+        `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+
+        createStartable(enabled = true).start()
+
+        verify(controlsController, never()).setPreferredSelection(any())
+    }
+
+    @Test
+    fun testPreferredPackagesNotInstalled_noNewSelection() {
+        context.orCreateTestableResources.addOverride(
+            R.array.config_controlsPreferredPackages,
+            arrayOf(TEST_PACKAGE_PANEL)
+        )
+        `when`(controlsController.getPreferredSelection()).thenReturn(SelectedItem.EMPTY_SELECTION)
+        `when`(controlsListingController.getCurrentServices()).thenReturn(emptyList())
+
+        createStartable(enabled = true).start()
+
+        verify(controlsController, never()).setPreferredSelection(any())
+    }
+
+    @Test
+    fun testPreferredPackageNotPanel_noNewSelection() {
+        context.orCreateTestableResources.addOverride(
+            R.array.config_controlsPreferredPackages,
+            arrayOf(TEST_PACKAGE_PANEL)
+        )
+        `when`(controlsController.getPreferredSelection()).thenReturn(SelectedItem.EMPTY_SELECTION)
+        val listings = listOf(ControlsServiceInfo(TEST_COMPONENT, "not panel", hasPanel = false))
+        `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+
+        createStartable(enabled = true).start()
+
+        verify(controlsController, never()).setPreferredSelection(any())
+    }
+
+    @Test
+    fun testExistingSelection_noNewSelection() {
+        context.orCreateTestableResources.addOverride(
+            R.array.config_controlsPreferredPackages,
+            arrayOf(TEST_PACKAGE_PANEL)
+        )
+        `when`(controlsController.getPreferredSelection())
+            .thenReturn(mock<SelectedItem.PanelItem>())
+        val listings = listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true))
+        `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+
+        createStartable(enabled = true).start()
+
+        verify(controlsController, never()).setPreferredSelection(any())
+    }
+
+    @Test
+    fun testPanelAdded() {
+        context.orCreateTestableResources.addOverride(
+            R.array.config_controlsPreferredPackages,
+            arrayOf(TEST_PACKAGE_PANEL)
+        )
+        `when`(controlsController.getPreferredSelection()).thenReturn(SelectedItem.EMPTY_SELECTION)
+        val listings = listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true))
+        `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+
+        createStartable(enabled = true).start()
+
+        verify(controlsController).setPreferredSelection(listings[0].toPanelItem())
+    }
+
+    @Test
+    fun testMultiplePreferredOnlyOnePanel_panelAdded() {
+        context.orCreateTestableResources.addOverride(
+            R.array.config_controlsPreferredPackages,
+            arrayOf("other_package", TEST_PACKAGE_PANEL)
+        )
+        `when`(controlsController.getPreferredSelection()).thenReturn(SelectedItem.EMPTY_SELECTION)
+        val listings =
+            listOf(
+                ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true),
+                ControlsServiceInfo(ComponentName("other_package", "cls"), "non panel", false)
+            )
+        `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+
+        createStartable(enabled = true).start()
+
+        verify(controlsController).setPreferredSelection(listings[0].toPanelItem())
+    }
+
+    @Test
+    fun testMultiplePreferredMultiplePanels_firstPreferredAdded() {
+        context.orCreateTestableResources.addOverride(
+            R.array.config_controlsPreferredPackages,
+            arrayOf(TEST_PACKAGE_PANEL, "other_package")
+        )
+        `when`(controlsController.getPreferredSelection()).thenReturn(SelectedItem.EMPTY_SELECTION)
+        val listings =
+            listOf(
+                ControlsServiceInfo(ComponentName("other_package", "cls"), "panel", true),
+                ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true)
+            )
+        `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+
+        createStartable(enabled = true).start()
+
+        verify(controlsController).setPreferredSelection(listings[1].toPanelItem())
+    }
+
+    @Test
+    fun testPreferredSelectionIsPanel_bindOnStart() {
+        val listings = listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true))
+        `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+        `when`(controlsController.getPreferredSelection()).thenReturn(listings[0].toPanelItem())
+
+        createStartable(enabled = true).start()
+
+        verify(controlsController).bindComponentForPanel(TEST_COMPONENT_PANEL)
+    }
+
+    @Test
+    fun testPreferredSelectionPanel_listingNoPanel_notBind() {
+        val listings = listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = false))
+        `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+        `when`(controlsController.getPreferredSelection())
+            .thenReturn(SelectedItem.PanelItem("panel", TEST_COMPONENT_PANEL))
+
+        createStartable(enabled = true).start()
+
+        verify(controlsController, never()).bindComponentForPanel(any())
+    }
+
+    @Test
+    fun testNotPanelSelection_noBind() {
+        val listings = listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = false))
+        `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+        `when`(controlsController.getPreferredSelection()).thenReturn(SelectedItem.EMPTY_SELECTION)
+
+        createStartable(enabled = true).start()
+
+        verify(controlsController, never()).bindComponentForPanel(any())
+    }
+
+    private fun createStartable(enabled: Boolean): ControlsStartable {
+        val component: ControlsComponent =
+            mock() {
+                `when`(isEnabled()).thenReturn(enabled)
+                if (enabled) {
+                    `when`(getControlsController()).thenReturn(Optional.of(controlsController))
+                    `when`(getControlsListingController())
+                        .thenReturn(Optional.of(controlsListingController))
+                } else {
+                    `when`(getControlsController()).thenReturn(Optional.empty())
+                    `when`(getControlsListingController()).thenReturn(Optional.empty())
+                }
+            }
+        return ControlsStartable(context.resources, fakeExecutor, component, userTracker)
+    }
+
+    private fun ControlsServiceInfo(
+        componentName: ComponentName,
+        label: CharSequence,
+        hasPanel: Boolean
+    ): ControlsServiceInfo {
+        val serviceInfo =
+            ServiceInfo().apply {
+                applicationInfo = ApplicationInfo()
+                packageName = componentName.packageName
+                name = componentName.className
+            }
+        return FakeControlsServiceInfo(context, serviceInfo, label, hasPanel)
+    }
+
+    private class FakeControlsServiceInfo(
+        context: Context,
+        serviceInfo: ServiceInfo,
+        private val label: CharSequence,
+        hasPanel: Boolean
+    ) : ControlsServiceInfo(context, serviceInfo) {
+
+        init {
+            if (hasPanel) {
+                panelActivity = serviceInfo.componentName
+            }
+        }
+
+        override fun loadLabel(): CharSequence {
+            return label
+        }
+    }
+
+    companion object {
+        private fun ControlsServiceInfo.toPanelItem(): SelectedItem.PanelItem {
+            if (panelActivity == null) {
+                throw IllegalArgumentException("$this is not a panel")
+            }
+            return SelectedItem.PanelItem(loadLabel(), componentName)
+        }
+
+        private const val TEST_PACKAGE = "pkg"
+        private val TEST_COMPONENT = ComponentName(TEST_PACKAGE, "service")
+        private const val TEST_PACKAGE_PANEL = "pkg.panel"
+        private val TEST_COMPONENT_PANEL = ComponentName(TEST_PACKAGE_PANEL, "service")
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
index edc6882..aa90e2a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
@@ -20,6 +20,7 @@
 import android.content.ComponentName
 import android.content.Context
 import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
 import android.content.pm.ServiceInfo
 import android.os.UserHandle
 import android.service.controls.ControlsProviderService
@@ -28,6 +29,7 @@
 import android.util.AttributeSet
 import android.view.LayoutInflater
 import android.view.View
+import android.view.ViewGroup
 import android.widget.FrameLayout
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
@@ -39,8 +41,10 @@
 import com.android.systemui.controls.controller.StructureInfo
 import com.android.systemui.controls.management.ControlsListingController
 import com.android.systemui.controls.management.ControlsProviderSelectorActivity
+import com.android.systemui.controls.panels.AuthorizedPanelsRepository
 import com.android.systemui.controls.settings.FakeControlsSettingsRepository
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.settings.UserTracker
@@ -91,6 +95,9 @@
     @Mock lateinit var userTracker: UserTracker
     @Mock lateinit var taskViewFactory: TaskViewFactory
     @Mock lateinit var dumpManager: DumpManager
+    @Mock lateinit var authorizedPanelsRepository: AuthorizedPanelsRepository
+    @Mock lateinit var featureFlags: FeatureFlags
+    @Mock lateinit var packageManager: PackageManager
     val sharedPreferences = FakeSharedPreferences()
     lateinit var controlsSettingsRepository: FakeControlsSettingsRepository
 
@@ -120,6 +127,7 @@
             ControlsUiControllerImpl(
                 Lazy { controlsController },
                 context,
+                packageManager,
                 uiExecutor,
                 bgExecutor,
                 Lazy { controlsListingController },
@@ -132,6 +140,8 @@
                 userTracker,
                 Optional.of(taskViewFactory),
                 controlsSettingsRepository,
+                authorizedPanelsRepository,
+                featureFlags,
                 dumpManager
             )
         `when`(
@@ -240,7 +250,9 @@
     @Test
     fun testPanelCallsTaskViewFactoryCreate() {
         mockLayoutInflater()
-        val panel = SelectedItem.PanelItem("App name", ComponentName("pkg", "cls"))
+        val packageName = "pkg"
+        `when`(authorizedPanelsRepository.getAuthorizedPanels()).thenReturn(setOf(packageName))
+        val panel = SelectedItem.PanelItem("App name", ComponentName(packageName, "cls"))
         val serviceInfo = setUpPanel(panel)
 
         underTest.show(parent, {}, context)
@@ -258,9 +270,11 @@
     @Test
     fun testPanelControllerStartActivityWithCorrectArguments() {
         mockLayoutInflater()
+        val packageName = "pkg"
+        `when`(authorizedPanelsRepository.getAuthorizedPanels()).thenReturn(setOf(packageName))
         controlsSettingsRepository.setAllowActionOnTrivialControlsInLockscreen(true)
 
-        val panel = SelectedItem.PanelItem("App name", ComponentName("pkg", "cls"))
+        val panel = SelectedItem.PanelItem("App name", ComponentName(packageName, "cls"))
         val serviceInfo = setUpPanel(panel)
 
         underTest.show(parent, {}, context)
@@ -290,9 +304,11 @@
     @Test
     fun testPendingIntentExtrasAreModified() {
         mockLayoutInflater()
+        val packageName = "pkg"
+        `when`(authorizedPanelsRepository.getAuthorizedPanels()).thenReturn(setOf(packageName))
         controlsSettingsRepository.setAllowActionOnTrivialControlsInLockscreen(true)
 
-        val panel = SelectedItem.PanelItem("App name", ComponentName("pkg", "cls"))
+        val panel = SelectedItem.PanelItem("App name", ComponentName(packageName, "cls"))
         val serviceInfo = setUpPanel(panel)
 
         underTest.show(parent, {}, context)
@@ -313,7 +329,7 @@
             )
             .isTrue()
 
-        underTest.hide()
+        underTest.hide(parent)
 
         clearInvocations(controlsListingController, taskViewFactory)
         controlsSettingsRepository.setAllowActionOnTrivialControlsInLockscreen(false)
@@ -372,6 +388,28 @@
         assertThat(underTest.resolveActivity()).isEqualTo(ControlsActivity::class.java)
     }
 
+    @Test
+    fun testRemoveViewsOnlyForParentPassedInHide() {
+        underTest.show(parent, {}, context)
+        parent.addView(View(context))
+
+        val mockParent: ViewGroup = mock()
+
+        underTest.hide(mockParent)
+
+        verify(mockParent).removeAllViews()
+        assertThat(parent.childCount).isGreaterThan(0)
+    }
+
+    @Test
+    fun testHideDifferentParentDoesntCancelListeners() {
+        underTest.show(parent, {}, context)
+        underTest.hide(mock())
+
+        verify(controlsController, never()).unsubscribe()
+        verify(controlsListingController, never()).removeCallback(any())
+    }
+
     private fun setUpPanel(panel: SelectedItem.PanelItem): ControlsServiceInfo {
         val activity = ComponentName("pkg", "activity")
         sharedPreferences
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt
new file mode 100644
index 0000000..dbaf94f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.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.controls.ui
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class OverflowMenuAdapterTest : SysuiTestCase() {
+
+    @Test
+    fun testGetItemId() {
+        val ids = listOf(27L, 73L)
+        val labels = listOf("first", "second")
+        val adapter =
+            OverflowMenuAdapter(
+                context,
+                layoutId = 0,
+                labels.zip(ids).map { OverflowMenuAdapter.MenuItem(it.first, it.second) }
+            ) { true }
+
+        ids.forEachIndexed { index, id -> assertThat(adapter.getItemId(index)).isEqualTo(id) }
+    }
+
+    @Test
+    fun testCheckEnabled() {
+        val ids = listOf(27L, 73L)
+        val labels = listOf("first", "second")
+        val adapter =
+            OverflowMenuAdapter(
+                context,
+                layoutId = 0,
+                labels.zip(ids).map { OverflowMenuAdapter.MenuItem(it.first, it.second) }
+            ) { position -> position == 0 }
+
+        assertThat(adapter.isEnabled(0)).isTrue()
+        assertThat(adapter.isEnabled(1)).isFalse()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/coroutines/FlowTest.kt b/packages/SystemUI/tests/src/com/android/systemui/coroutines/FlowTest.kt
new file mode 100644
index 0000000..1e4753e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/coroutines/FlowTest.kt
@@ -0,0 +1,24 @@
+package com.android.systemui.coroutines
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class FlowTest : SysuiTestCase() {
+
+    @Test
+    fun collectLastValue() = runTest {
+        val flow = flowOf(0, 1, 2)
+        val lastValue by collectLastValue(flow)
+        assertThat(lastValue).isEqualTo(2)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
index ff883eb..6b095ff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
@@ -36,11 +36,10 @@
 import com.android.keyguard.BouncerPanelExpansionCalculator;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dreams.complication.ComplicationHostViewController;
+import com.android.systemui.dreams.touch.scrim.BouncerlessScrimController;
 import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor;
 import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
 import com.android.systemui.statusbar.BlurUtils;
-import com.android.systemui.statusbar.phone.KeyguardBouncer;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -81,12 +80,6 @@
     BlurUtils mBlurUtils;
 
     @Mock
-    StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
-
-    @Mock
-    KeyguardBouncer mBouncer;
-
-    @Mock
     ViewRootImpl mViewRoot;
 
     @Mock
@@ -96,6 +89,9 @@
     DreamOverlayAnimationsController mAnimationsController;
 
     @Mock
+    BouncerlessScrimController mBouncerlessScrimController;
+
+    @Mock
     DreamOverlayStateController mStateController;
 
     DreamOverlayContainerViewController mController;
@@ -106,7 +102,6 @@
 
         when(mDreamOverlayContainerView.getResources()).thenReturn(mResources);
         when(mDreamOverlayContainerView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
-        when(mStatusBarKeyguardViewManager.getPrimaryBouncer()).thenReturn(mBouncer);
         when(mDreamOverlayContainerView.getViewRootImpl()).thenReturn(mViewRoot);
 
         mController = new DreamOverlayContainerViewController(
@@ -114,7 +109,6 @@
                 mComplicationHostViewController,
                 mDreamOverlayContentView,
                 mDreamOverlayStatusBarViewController,
-                mStatusBarKeyguardViewManager,
                 mBlurUtils,
                 mHandler,
                 mResources,
@@ -123,7 +117,8 @@
                 MILLIS_UNTIL_FULL_JITTER,
                 mPrimaryBouncerCallbackInteractor,
                 mAnimationsController,
-                mStateController);
+                mStateController,
+                mBouncerlessScrimController);
     }
 
     @Test
@@ -170,7 +165,8 @@
         final ArgumentCaptor<PrimaryBouncerExpansionCallback> bouncerExpansionCaptor =
                 ArgumentCaptor.forClass(PrimaryBouncerExpansionCallback.class);
         mController.onViewAttached();
-        verify(mBouncer).addBouncerExpansionCallback(bouncerExpansionCaptor.capture());
+        verify(mPrimaryBouncerCallbackInteractor).addBouncerExpansionCallback(
+                bouncerExpansionCaptor.capture());
 
         bouncerExpansionCaptor.getValue().onExpansionChanged(0.5f);
         verify(mBlurUtils, never()).applyBlur(eq(mViewRoot), anyInt(), eq(false));
@@ -181,7 +177,8 @@
         final ArgumentCaptor<PrimaryBouncerExpansionCallback> bouncerExpansionCaptor =
                 ArgumentCaptor.forClass(PrimaryBouncerExpansionCallback.class);
         mController.onViewAttached();
-        verify(mBouncer).addBouncerExpansionCallback(bouncerExpansionCaptor.capture());
+        verify(mPrimaryBouncerCallbackInteractor).addBouncerExpansionCallback(
+                bouncerExpansionCaptor.capture());
 
         final float blurRadius = 1337f;
         when(mBlurUtils.blurRadiusOfRatio(anyFloat())).thenReturn(blurRadius);
@@ -217,6 +214,27 @@
     }
 
     @Test
+    public void testSkipEntryAnimationsWhenExitingLowLight() {
+        ArgumentCaptor<DreamOverlayStateController.Callback> callbackCaptor =
+                ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class);
+        when(mStateController.isLowLightActive()).thenReturn(false);
+
+        // Call onInit so that the callback is added.
+        mController.onInit();
+        verify(mStateController).addCallback(callbackCaptor.capture());
+
+        // Send the signal that low light is exiting
+        callbackCaptor.getValue().onExitLowLight();
+
+        // View is attached to trigger animations.
+        mController.onViewAttached();
+
+        // Entry animations should be started then immediately ended to skip to the end.
+        verify(mAnimationsController).startEntryAnimations();
+        verify(mAnimationsController).endAnimations();
+    }
+
+    @Test
     public void testCancelDreamEntryAnimationsOnDetached() {
         mController.onViewAttached();
         mController.onViewDetached();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
index 3b8bb80..dfb4d5b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
@@ -31,6 +31,8 @@
 import android.os.RemoteException;
 import android.service.dreams.IDreamOverlay;
 import android.service.dreams.IDreamOverlayCallback;
+import android.service.dreams.IDreamOverlayClient;
+import android.service.dreams.IDreamOverlayClientCallback;
 import android.testing.AndroidTestingRunner;
 import android.view.View;
 import android.view.ViewGroup;
@@ -58,6 +60,7 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
@@ -148,13 +151,25 @@
                 mDreamOverlayCallbackController);
     }
 
-    @Test
-    public void testOnStartMetricsLogged() throws Exception {
+    public IDreamOverlayClient getClient() throws RemoteException {
         final IBinder proxy = mService.onBind(new Intent());
         final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+        final IDreamOverlayClientCallback callback =
+                Mockito.mock(IDreamOverlayClientCallback.class);
+        overlay.getClient(callback);
+        final ArgumentCaptor<IDreamOverlayClient> clientCaptor =
+                ArgumentCaptor.forClass(IDreamOverlayClient.class);
+        verify(callback).onDreamOverlayClient(clientCaptor.capture());
+
+        return clientCaptor.getValue();
+    }
+
+    @Test
+    public void testOnStartMetricsLogged() throws Exception {
+        final IDreamOverlayClient client = getClient();
 
         // Inform the overlay service of dream starting.
-        overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
+        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
                 false /*shouldShowComplication*/);
         mMainExecutor.runAllReady();
 
@@ -165,11 +180,10 @@
 
     @Test
     public void testOverlayContainerViewAddedToWindow() throws Exception {
-        final IBinder proxy = mService.onBind(new Intent());
-        final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+        final IDreamOverlayClient client = getClient();
 
         // Inform the overlay service of dream starting.
-        overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
+        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
                 false /*shouldShowComplication*/);
         mMainExecutor.runAllReady();
 
@@ -178,11 +192,10 @@
 
     @Test
     public void testDreamOverlayContainerViewControllerInitialized() throws Exception {
-        final IBinder proxy = mService.onBind(new Intent());
-        final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+        final IDreamOverlayClient client = getClient();
 
         // Inform the overlay service of dream starting.
-        overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
+        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
                 false /*shouldShowComplication*/);
         mMainExecutor.runAllReady();
 
@@ -196,11 +209,10 @@
                 .thenReturn(mDreamOverlayContainerViewParent)
                 .thenReturn(null);
 
-        final IBinder proxy = mService.onBind(new Intent());
-        final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+        final IDreamOverlayClient client = getClient();
 
         // Inform the overlay service of dream starting.
-        overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
+        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
                 false /*shouldShowComplication*/);
         mMainExecutor.runAllReady();
 
@@ -209,11 +221,10 @@
 
     @Test
     public void testShouldShowComplicationsSetByStartDream() throws RemoteException {
-        final IBinder proxy = mService.onBind(new Intent());
-        final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+        final IDreamOverlayClient client = getClient();
 
         // Inform the overlay service of dream starting.
-        overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
+        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
                 true /*shouldShowComplication*/);
 
         assertThat(mService.shouldShowComplications()).isTrue();
@@ -221,11 +232,10 @@
 
     @Test
     public void testLowLightSetByStartDream() throws RemoteException {
-        final IBinder proxy = mService.onBind(new Intent());
-        final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+        final IDreamOverlayClient client = getClient();
 
         // Inform the overlay service of dream starting.
-        overlay.startDream(mWindowParams, mDreamOverlayCallback,
+        client.startDream(mWindowParams, mDreamOverlayCallback,
                 LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/);
         mMainExecutor.runAllReady();
 
@@ -235,11 +245,10 @@
 
     @Test
     public void testOnEndDream() throws RemoteException {
-        final IBinder proxy = mService.onBind(new Intent());
-        final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+        final IDreamOverlayClient client = getClient();
 
         // Inform the overlay service of dream starting.
-        overlay.startDream(mWindowParams, mDreamOverlayCallback,
+        client.startDream(mWindowParams, mDreamOverlayCallback,
                 LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/);
         mMainExecutor.runAllReady();
 
@@ -261,11 +270,10 @@
 
     @Test
     public void testDestroy() throws RemoteException {
-        final IBinder proxy = mService.onBind(new Intent());
-        final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+        final IDreamOverlayClient client = getClient();
 
         // Inform the overlay service of dream starting.
-        overlay.startDream(mWindowParams, mDreamOverlayCallback,
+        client.startDream(mWindowParams, mDreamOverlayCallback,
                 LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/);
         mMainExecutor.runAllReady();
 
@@ -305,15 +313,14 @@
 
     @Test
     public void testDecorViewNotAddedToWindowAfterDestroy() throws Exception {
-        final IBinder proxy = mService.onBind(new Intent());
-        final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+        final IDreamOverlayClient client = getClient();
 
         // Destroy the service.
         mService.onDestroy();
         mMainExecutor.runAllReady();
 
         // Inform the overlay service of dream starting.
-        overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
+        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
                 false /*shouldShowComplication*/);
         mMainExecutor.runAllReady();
 
@@ -331,11 +338,10 @@
 
     @Test
     public void testResetCurrentOverlayWhenConnectedToNewDream() throws RemoteException {
-        final IBinder proxy = mService.onBind(new Intent());
-        final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+        final IDreamOverlayClient client = getClient();
 
         // Inform the overlay service of dream starting. Do not show dream complications.
-        overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
+        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
                 false /*shouldShowComplication*/);
         mMainExecutor.runAllReady();
 
@@ -352,7 +358,7 @@
         // New dream starting with dream complications showing. Note that when a new dream is
         // binding to the dream overlay service, it receives the same instance of IBinder as the
         // first one.
-        overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
+        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
                 true /*shouldShowComplication*/);
         mMainExecutor.runAllReady();
 
@@ -371,11 +377,10 @@
 
     @Test
     public void testWakeUp() throws RemoteException {
-        final IBinder proxy = mService.onBind(new Intent());
-        final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+        final IDreamOverlayClient client = getClient();
 
         // Inform the overlay service of dream starting.
-        overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
+        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
                 true /*shouldShowComplication*/);
         mMainExecutor.runAllReady();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
index ee989d1..b7d0f29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
@@ -251,6 +251,30 @@
     }
 
     @Test
+    public void testNotifyLowLightExit() {
+        final DreamOverlayStateController stateController =
+                new DreamOverlayStateController(mExecutor, true);
+
+        stateController.addCallback(mCallback);
+        mExecutor.runAllReady();
+        assertThat(stateController.isLowLightActive()).isFalse();
+
+        // Turn low light on then off to trigger the exiting callback.
+        stateController.setLowLightActive(true);
+        stateController.setLowLightActive(false);
+
+        // Callback was only called once, when
+        mExecutor.runAllReady();
+        verify(mCallback, times(1)).onExitLowLight();
+        assertThat(stateController.isLowLightActive()).isFalse();
+
+        // Set with false again, which should not cause the callback to trigger again.
+        stateController.setLowLightActive(false);
+        mExecutor.runAllReady();
+        verify(mCallback, times(1)).onExitLowLight();
+    }
+
+    @Test
     public void testNotifyEntryAnimationsFinishedChanged() {
         final DreamOverlayStateController stateController =
                 new DreamOverlayStateController(mExecutor, true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java
index 9f4a7c8..b3329eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java
@@ -32,7 +32,9 @@
 
 import com.android.settingslib.dream.DreamBackend;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.condition.SelfExecutingMonitor;
 import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.shared.condition.Monitor;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.settings.SecureSettings;
 import com.android.systemui.util.time.FakeSystemClock;
@@ -66,13 +68,16 @@
 
     private ComplicationTypesUpdater mController;
 
+    private Monitor mMonitor;
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         when(mDreamBackend.getEnabledComplications()).thenReturn(new HashSet<>());
 
+        mMonitor = SelfExecutingMonitor.createInstance();
         mController = new ComplicationTypesUpdater(mDreamBackend, mExecutor,
-                mSecureSettings, mDreamOverlayStateController);
+                mSecureSettings, mDreamOverlayStateController, mMonitor);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java
index ec448f9..f6662d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java
@@ -29,7 +29,9 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.condition.SelfExecutingMonitor;
 import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.shared.condition.Monitor;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -69,10 +71,13 @@
     @Mock
     private ComplicationLayoutParams mLayoutParams;
 
+    private Monitor mMonitor;
+
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
         when(mDreamClockTimeViewHolderProvider.get()).thenReturn(mDreamClockTimeViewHolder);
+        mMonitor = SelfExecutingMonitor.createInstance();
     }
 
     /**
@@ -83,7 +88,8 @@
         final DreamClockTimeComplication.Registrant registrant =
                 new DreamClockTimeComplication.Registrant(
                         mDreamOverlayStateController,
-                        mComplication);
+                        mComplication,
+                        mMonitor);
         registrant.start();
         verify(mDreamOverlayStateController).addComplication(eq(mComplication));
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
index 89c7280..3312c43 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
@@ -31,13 +31,14 @@
 import android.content.Context;
 import android.testing.AndroidTestingRunner;
 import android.view.View;
-import android.widget.ImageView;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.view.LaunchableImageView;
+import com.android.systemui.condition.SelfExecutingMonitor;
 import com.android.systemui.controls.ControlsServiceInfo;
 import com.android.systemui.controls.controller.ControlsController;
 import com.android.systemui.controls.controller.StructureInfo;
@@ -46,6 +47,7 @@
 import com.android.systemui.dreams.DreamOverlayStateController;
 import com.android.systemui.dreams.complication.dagger.DreamHomeControlsComplicationComponent;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.shared.condition.Monitor;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -90,7 +92,7 @@
     private View mView;
 
     @Mock
-    private ImageView mHomeControlsView;
+    private LaunchableImageView mHomeControlsView;
 
     @Mock
     private ActivityStarter mActivityStarter;
@@ -101,6 +103,8 @@
     @Captor
     private ArgumentCaptor<DreamOverlayStateController.Callback> mStateCallbackCaptor;
 
+    private Monitor mMonitor;
+
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
@@ -112,6 +116,8 @@
                 Optional.of(mControlsListingController));
         when(mControlsComponent.getVisibility()).thenReturn(AVAILABLE);
         when(mView.findViewById(R.id.home_controls_chip)).thenReturn(mHomeControlsView);
+
+        mMonitor = SelfExecutingMonitor.createInstance();
     }
 
     @Test
@@ -126,7 +132,7 @@
     public void complicationAvailability_serviceNotAvailable_noFavorites_doNotAddComplication() {
         final DreamHomeControlsComplication.Registrant registrant =
                 new DreamHomeControlsComplication.Registrant(mComplication,
-                        mDreamOverlayStateController, mControlsComponent);
+                        mDreamOverlayStateController, mControlsComponent, mMonitor);
         registrant.start();
 
         setHaveFavorites(false);
@@ -139,7 +145,7 @@
     public void complicationAvailability_serviceAvailable_noFavorites_doNotAddComplication() {
         final DreamHomeControlsComplication.Registrant registrant =
                 new DreamHomeControlsComplication.Registrant(mComplication,
-                        mDreamOverlayStateController, mControlsComponent);
+                        mDreamOverlayStateController, mControlsComponent, mMonitor);
         registrant.start();
 
         setHaveFavorites(false);
@@ -152,7 +158,7 @@
     public void complicationAvailability_serviceAvailable_noFavorites_panel_addComplication() {
         final DreamHomeControlsComplication.Registrant registrant =
                 new DreamHomeControlsComplication.Registrant(mComplication,
-                        mDreamOverlayStateController, mControlsComponent);
+                        mDreamOverlayStateController, mControlsComponent, mMonitor);
         registrant.start();
 
         setHaveFavorites(false);
@@ -165,7 +171,7 @@
     public void complicationAvailability_serviceNotAvailable_haveFavorites_doNotAddComplication() {
         final DreamHomeControlsComplication.Registrant registrant =
                 new DreamHomeControlsComplication.Registrant(mComplication,
-                        mDreamOverlayStateController, mControlsComponent);
+                        mDreamOverlayStateController, mControlsComponent, mMonitor);
         registrant.start();
 
         setHaveFavorites(true);
@@ -178,7 +184,7 @@
     public void complicationAvailability_serviceAvailable_haveFavorites_addComplication() {
         final DreamHomeControlsComplication.Registrant registrant =
                 new DreamHomeControlsComplication.Registrant(mComplication,
-                        mDreamOverlayStateController, mControlsComponent);
+                        mDreamOverlayStateController, mControlsComponent, mMonitor);
         registrant.start();
 
         setHaveFavorites(true);
@@ -191,7 +197,7 @@
     public void complicationAvailability_checkAvailabilityWhenDreamOverlayBecomesActive() {
         final DreamHomeControlsComplication.Registrant registrant =
                 new DreamHomeControlsComplication.Registrant(mComplication,
-                        mDreamOverlayStateController, mControlsComponent);
+                        mDreamOverlayStateController, mControlsComponent, mMonitor);
         registrant.start();
 
         setServiceAvailable(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java
index c8b2b25..ef62abf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java
@@ -30,9 +30,12 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.condition.SelfExecutingMonitor;
 import com.android.systemui.dreams.DreamOverlayStateController;
 import com.android.systemui.dreams.smartspace.DreamSmartspaceController;
 import com.android.systemui.plugins.BcSmartspaceDataPlugin;
+import com.android.systemui.shared.condition.Condition;
+import com.android.systemui.shared.condition.Monitor;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -43,6 +46,8 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -60,9 +65,14 @@
     @Mock
     private View mBcSmartspaceView;
 
+    private Monitor mMonitor;
+
+    private final Set<Condition> mPreconditions = new HashSet<>();
+
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
+        mMonitor = SelfExecutingMonitor.createInstance();
     }
 
     /**
@@ -79,7 +89,8 @@
         return new SmartSpaceComplication.Registrant(
                 mDreamOverlayStateController,
                 mComplication,
-                mSmartspaceController);
+                mSmartspaceController,
+                mMonitor);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java
new file mode 100644
index 0000000..19347c7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.dreams.conditions;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.shared.condition.Condition;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DreamConditionTest extends SysuiTestCase {
+    @Mock
+    Context mContext;
+
+    @Mock
+    Condition.Callback mCallback;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    /**
+     * Ensure a dreaming state immediately triggers the condition.
+     */
+    @Test
+    public void testInitialState() {
+        final Intent intent = new Intent(Intent.ACTION_DREAMING_STARTED);
+        when(mContext.registerReceiver(any(), any())).thenReturn(intent);
+        final DreamCondition condition = new DreamCondition(mContext);
+        condition.addCallback(mCallback);
+        condition.start();
+
+        verify(mCallback).onConditionChanged(eq(condition));
+        assertThat(condition.isConditionMet()).isTrue();
+    }
+
+    /**
+     * Ensure that changing dream state triggers condition.
+     */
+    @Test
+    public void testChange() {
+        final Intent intent = new Intent(Intent.ACTION_DREAMING_STARTED);
+        final ArgumentCaptor<BroadcastReceiver> receiverCaptor =
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
+        when(mContext.registerReceiver(receiverCaptor.capture(), any())).thenReturn(intent);
+        final DreamCondition condition = new DreamCondition(mContext);
+        condition.addCallback(mCallback);
+        condition.start();
+        clearInvocations(mCallback);
+        receiverCaptor.getValue().onReceive(mContext, new Intent(Intent.ACTION_DREAMING_STOPPED));
+        verify(mCallback).onConditionChanged(eq(condition));
+        assertThat(condition.isConditionMet()).isFalse();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
index f64179d..3a168d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
@@ -41,12 +41,13 @@
 
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dreams.touch.scrim.ScrimController;
+import com.android.systemui.dreams.touch.scrim.ScrimManager;
 import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants;
 import com.android.systemui.shade.ShadeExpansionChangeEvent;
 import com.android.systemui.shared.system.InputChannelCompat;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.wm.shell.animation.FlingAnimationUtils;
 
 import org.junit.Before;
@@ -63,10 +64,13 @@
 @RunWith(AndroidTestingRunner.class)
 public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
     @Mock
-    StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+    CentralSurfaces mCentralSurfaces;
 
     @Mock
-    CentralSurfaces mCentralSurfaces;
+    ScrimManager mScrimManager;
+
+    @Mock
+    ScrimController mScrimController;
 
     @Mock
     NotificationShadeWindowController mNotificationShadeWindowController;
@@ -111,7 +115,7 @@
         MockitoAnnotations.initMocks(this);
         mTouchHandler = new BouncerSwipeTouchHandler(
                 mDisplayMetrics,
-                mStatusBarKeyguardViewManager,
+                mScrimManager,
                 Optional.of(mCentralSurfaces),
                 mNotificationShadeWindowController,
                 mValueAnimatorCreator,
@@ -121,6 +125,7 @@
                 TOUCH_REGION,
                 mUiEventLogger);
 
+        when(mScrimManager.getCurrentController()).thenReturn(mScrimController);
         when(mCentralSurfaces.isBouncerShowing()).thenReturn(false);
         when(mCentralSurfaces.getDisplayHeight()).thenReturn((float) SCREEN_HEIGHT_PX);
         when(mValueAnimatorCreator.create(anyFloat(), anyFloat())).thenReturn(mValueAnimator);
@@ -193,7 +198,7 @@
         assertThat(gestureListener.onScroll(event1, event2, 0, distanceY))
                 .isTrue();
 
-        verify(mStatusBarKeyguardViewManager, never()).onPanelExpansionChanged(any());
+        verify(mScrimController, never()).expand(any());
     }
 
     /**
@@ -220,7 +225,7 @@
         assertThat(gestureListener.onScroll(event1, event2, 0, distanceY))
                 .isTrue();
 
-        verify(mStatusBarKeyguardViewManager, never()).onPanelExpansionChanged(any());
+        verify(mScrimController, never()).expand(any());
     }
 
     /**
@@ -274,12 +279,12 @@
         final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
                 0, direction == Direction.UP ? SCREEN_HEIGHT_PX - distanceY : distanceY, 0);
 
-        reset(mStatusBarKeyguardViewManager);
+        reset(mScrimController);
         assertThat(gestureListener.onScroll(event1, event2, 0, distanceY))
                 .isTrue();
 
         // Ensure only called once
-        verify(mStatusBarKeyguardViewManager).onPanelExpansionChanged(any());
+        verify(mScrimController).expand(any());
 
         final float expansion = isBouncerInitiallyShowing ? percent : 1 - percent;
         final float dragDownAmount = event2.getY() - event1.getY();
@@ -288,7 +293,7 @@
         ShadeExpansionChangeEvent event =
                 new ShadeExpansionChangeEvent(
                         expansion, /* expanded= */ false, /* tracking= */ true, dragDownAmount);
-        verify(mStatusBarKeyguardViewManager).onPanelExpansionChanged(event);
+        verify(mScrimController).expand(event);
     }
 
     /**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
index a807407..178b9cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
@@ -424,6 +424,32 @@
         verify(gestureListener2).onDown(eq(followupEvent));
     }
 
+    @Test
+    public void testOnRemovedCallbackOnStopMonitoring() {
+        final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
+        final DreamTouchHandler.TouchSession.Callback callback =
+                Mockito.mock(DreamTouchHandler.TouchSession.Callback.class);
+
+        final Environment environment = new Environment(Stream.of(touchHandler)
+                .collect(Collectors.toCollection(HashSet::new)));
+
+        final InputEvent initialEvent = Mockito.mock(InputEvent.class);
+        environment.publishInputEvent(initialEvent);
+
+        final DreamTouchHandler.TouchSession session = captureSession(touchHandler);
+        session.registerCallback(callback);
+
+        environment.executeAll();
+
+        environment.updateLifecycle(observerOwnerPair -> {
+            observerOwnerPair.first.onPause(observerOwnerPair.second);
+        });
+
+        environment.executeAll();
+
+        verify(callback).onRemoved();
+    }
+
     public GestureDetector.OnGestureListener registerGestureListener(DreamTouchHandler handler) {
         final GestureDetector.OnGestureListener gestureListener = Mockito.mock(
                 GestureDetector.OnGestureListener.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java
new file mode 100644
index 0000000..79c535a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.dreams.touch.scrim;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.os.PowerManager;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.shade.ShadeExpansionChangeEvent;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class BouncerlessScrimControllerTest extends SysuiTestCase {
+    @Mock
+    BouncerlessScrimController.Callback mCallback;
+
+    @Mock
+    PowerManager mPowerManager;
+
+    private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testWakeupOnSwipeOpen() {
+        final BouncerlessScrimController scrimController =
+                new BouncerlessScrimController(mExecutor, mPowerManager);
+        scrimController.addCallback(mCallback);
+        scrimController.expand(new ShadeExpansionChangeEvent(.5f, true, false, 0.0f));
+        mExecutor.runAllReady();
+        verify(mPowerManager).wakeUp(anyLong(), eq(PowerManager.WAKE_REASON_GESTURE), any());
+        verify(mCallback).onWakeup();
+    }
+
+    @Test
+    public void testExpansionPropagation() {
+        final BouncerlessScrimController scrimController =
+                new BouncerlessScrimController(mExecutor, mPowerManager);
+        scrimController.addCallback(mCallback);
+        final ShadeExpansionChangeEvent expansionEvent =
+                new ShadeExpansionChangeEvent(0.5f, false, false, 0.0f);
+        scrimController.expand(expansionEvent);
+        mExecutor.runAllReady();
+        verify(mCallback).onExpansion(eq(expansionEvent));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java
new file mode 100644
index 0000000..ac9822d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.dreams.touch.scrim;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class ScrimManagerTest extends SysuiTestCase {
+    @Mock
+    ScrimController mBouncerlessScrimController;
+
+    @Mock
+    ScrimController mBouncerScrimController;
+
+    @Mock
+    KeyguardStateController mKeyguardStateController;
+
+    @Mock
+    ScrimManager.Callback mCallback;
+
+    private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testControllerSelection() {
+        when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false);
+        ArgumentCaptor<KeyguardStateController.Callback> callbackCaptor =
+                ArgumentCaptor.forClass(KeyguardStateController.Callback.class);
+        final ScrimManager manager = new ScrimManager(mExecutor, mBouncerScrimController,
+                mBouncerlessScrimController, mKeyguardStateController);
+        verify(mKeyguardStateController).addCallback(callbackCaptor.capture());
+
+        assertThat(manager.getCurrentController()).isEqualTo(mBouncerScrimController);
+        when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
+        callbackCaptor.getValue().onKeyguardShowingChanged();
+        mExecutor.runAllReady();
+        assertThat(manager.getCurrentController()).isEqualTo(mBouncerlessScrimController);
+    }
+
+    @Test
+    public void testCallback() {
+        when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false);
+        ArgumentCaptor<KeyguardStateController.Callback> callbackCaptor =
+                ArgumentCaptor.forClass(KeyguardStateController.Callback.class);
+        final ScrimManager manager = new ScrimManager(mExecutor, mBouncerScrimController,
+                mBouncerlessScrimController, mKeyguardStateController);
+        verify(mKeyguardStateController).addCallback(callbackCaptor.capture());
+
+        manager.addCallback(mCallback);
+        when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
+        callbackCaptor.getValue().onKeyguardShowingChanged();
+        mExecutor.runAllReady();
+        verify(mCallback).onScrimControllerChanged(eq(mBouncerlessScrimController));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
index 170a70f..35f0f6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
@@ -125,7 +125,7 @@
         flags.set(unreleasedFlag, false)
         flags.set(unreleasedFlag, false)
 
-        listener.verifyInOrder(unreleasedFlag.id, unreleasedFlag.id)
+        listener.verifyInOrder(unreleasedFlag.name, unreleasedFlag.name)
     }
 
     @Test
@@ -137,7 +137,7 @@
         flags.set(stringFlag, "Test")
         flags.set(stringFlag, "Test")
 
-        listener.verifyInOrder(stringFlag.id)
+        listener.verifyInOrder(stringFlag.name)
     }
 
     @Test
@@ -149,7 +149,7 @@
         flags.removeListener(listener)
         flags.set(unreleasedFlag, false)
 
-        listener.verifyInOrder(unreleasedFlag.id)
+        listener.verifyInOrder(unreleasedFlag.name)
     }
 
     @Test
@@ -162,7 +162,7 @@
         flags.removeListener(listener)
         flags.set(stringFlag, "Other")
 
-        listener.verifyInOrder(stringFlag.id)
+        listener.verifyInOrder(stringFlag.name)
     }
 
     @Test
@@ -175,7 +175,7 @@
         flags.set(releasedFlag, true)
         flags.set(unreleasedFlag, true)
 
-        listener.verifyInOrder(releasedFlag.id, unreleasedFlag.id)
+        listener.verifyInOrder(releasedFlag.name, unreleasedFlag.name)
     }
 
     @Test
@@ -191,7 +191,7 @@
         flags.set(releasedFlag, false)
         flags.set(unreleasedFlag, false)
 
-        listener.verifyInOrder(releasedFlag.id, unreleasedFlag.id)
+        listener.verifyInOrder(releasedFlag.name, unreleasedFlag.name)
     }
 
     @Test
@@ -204,8 +204,8 @@
 
         flags.set(releasedFlag, true)
 
-        listener1.verifyInOrder(releasedFlag.id)
-        listener2.verifyInOrder(releasedFlag.id)
+        listener1.verifyInOrder(releasedFlag.name)
+        listener2.verifyInOrder(releasedFlag.name)
     }
 
     @Test
@@ -220,18 +220,18 @@
         flags.removeListener(listener2)
         flags.set(releasedFlag, false)
 
-        listener1.verifyInOrder(releasedFlag.id, releasedFlag.id)
-        listener2.verifyInOrder(releasedFlag.id)
+        listener1.verifyInOrder(releasedFlag.name, releasedFlag.name)
+        listener2.verifyInOrder(releasedFlag.name)
     }
 
     class VerifyingListener : FlagListenable.Listener {
-        var flagEventIds = mutableListOf<Int>()
+        var flagEventNames = mutableListOf<String>()
         override fun onFlagChanged(event: FlagListenable.FlagEvent) {
-            flagEventIds.add(event.flagId)
+            flagEventNames.add(event.flagName)
         }
 
-        fun verifyInOrder(vararg eventIds: Int) {
-            assertThat(flagEventIds).containsExactlyElementsIn(eventIds.asList())
+        fun verifyInOrder(vararg eventNames: String) {
+            assertThat(flagEventNames).containsExactlyElementsIn(eventNames.asList())
         }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugRestarterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugRestarterTest.kt
index ed16721..686782f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugRestarterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugRestarterTest.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP
 import com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE
+import com.android.systemui.util.mockito.any
 import org.junit.Before
 import org.junit.Test
 import org.mockito.ArgumentCaptor
@@ -48,22 +49,22 @@
     @Test
     fun testRestart_ImmediateWhenAsleep() {
         whenever(wakefulnessLifecycle.wakefulness).thenReturn(WAKEFULNESS_ASLEEP)
-        restarter.restartSystemUI()
-        verify(systemExitRestarter).restartSystemUI()
+        restarter.restartSystemUI("Restart for test")
+        verify(systemExitRestarter).restartSystemUI(any())
     }
 
     @Test
     fun testRestart_WaitsForSceenOff() {
         whenever(wakefulnessLifecycle.wakefulness).thenReturn(WAKEFULNESS_AWAKE)
 
-        restarter.restartSystemUI()
-        verify(systemExitRestarter, never()).restartSystemUI()
+        restarter.restartSystemUI("Restart for test")
+        verify(systemExitRestarter, never()).restartSystemUI(any())
 
         val captor = ArgumentCaptor.forClass(WakefulnessLifecycle.Observer::class.java)
         verify(wakefulnessLifecycle).addObserver(captor.capture())
 
         captor.value.onFinishedGoingToSleep()
 
-        verify(systemExitRestarter).restartSystemUI()
+        verify(systemExitRestarter).restartSystemUI(any())
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
index 7592cc5..2bcd75b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
@@ -23,13 +23,11 @@
 import android.content.res.Resources.NotFoundException
 import android.test.suitebuilder.annotation.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.commandline.CommandRegistry
-import com.android.systemui.util.DeviceConfigProxyFake
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.nullable
 import com.android.systemui.util.mockito.withArgCaptor
-import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.settings.GlobalSettings
 import com.google.common.truth.Truth.assertThat
 import org.junit.Assert
 import org.junit.Before
@@ -62,21 +60,18 @@
     @Mock
     private lateinit var mockContext: Context
     @Mock
-    private lateinit var secureSettings: SecureSettings
+    private lateinit var globalSettings: GlobalSettings
     @Mock
     private lateinit var systemProperties: SystemPropertiesHelper
     @Mock
     private lateinit var resources: Resources
     @Mock
-    private lateinit var commandRegistry: CommandRegistry
-    @Mock
     private lateinit var restarter: Restarter
-    private val flagMap = mutableMapOf<Int, Flag<*>>()
+    private val flagMap = mutableMapOf<String, Flag<*>>()
     private lateinit var broadcastReceiver: BroadcastReceiver
-    private lateinit var clearCacheAction: Consumer<Int>
+    private lateinit var clearCacheAction: Consumer<String>
     private val serverFlagReader = ServerFlagReaderFake()
 
-    private val deviceConfig = DeviceConfigProxyFake()
     private val teamfoodableFlagA = UnreleasedFlag(
         500, name = "a", namespace = "test", teamfood = true
     )
@@ -87,12 +82,13 @@
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
-        flagMap.put(teamfoodableFlagA.id, teamfoodableFlagA)
-        flagMap.put(teamfoodableFlagB.id, teamfoodableFlagB)
+        flagMap.put(Flags.TEAMFOOD.name, Flags.TEAMFOOD)
+        flagMap.put(teamfoodableFlagA.name, teamfoodableFlagA)
+        flagMap.put(teamfoodableFlagB.name, teamfoodableFlagB)
         mFeatureFlagsDebug = FeatureFlagsDebug(
             flagManager,
             mockContext,
-            secureSettings,
+            globalSettings,
             systemProperties,
             resources,
             serverFlagReader,
@@ -110,14 +106,14 @@
         clearCacheAction = withArgCaptor {
             verify(flagManager).clearCacheAction = capture()
         }
-        whenever(flagManager.idToSettingsKey(any())).thenAnswer { "key-${it.arguments[0]}" }
+        whenever(flagManager.nameToSettingsKey(any())).thenAnswer { "key-${it.arguments[0]}" }
     }
 
     @Test
     fun readBooleanFlag() {
         // Remember that the TEAMFOOD flag is id#1 and has special behavior.
-        whenever(flagManager.readFlagValue<Boolean>(eq(3), any())).thenReturn(true)
-        whenever(flagManager.readFlagValue<Boolean>(eq(4), any())).thenReturn(false)
+        whenever(flagManager.readFlagValue<Boolean>(eq("3"), any())).thenReturn(true)
+        whenever(flagManager.readFlagValue<Boolean>(eq("4"), any())).thenReturn(false)
 
         assertThat(
             mFeatureFlagsDebug.isEnabled(
@@ -141,7 +137,7 @@
             mFeatureFlagsDebug.isEnabled(
                 ReleasedFlag(
                     4,
-                    name = "3",
+                    name = "4",
                     namespace = "test"
                 )
             )
@@ -150,7 +146,7 @@
             mFeatureFlagsDebug.isEnabled(
                 UnreleasedFlag(
                     5,
-                    name = "4",
+                    name = "5",
                     namespace = "test"
                 )
             )
@@ -159,7 +155,8 @@
 
     @Test
     fun teamFoodFlag_False() {
-        whenever(flagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(false)
+        whenever(flagManager.readFlagValue<Boolean>(
+            eq(Flags.TEAMFOOD.name), any())).thenReturn(false)
         assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagA)).isFalse()
         assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagB)).isTrue()
 
@@ -170,7 +167,8 @@
 
     @Test
     fun teamFoodFlag_True() {
-        whenever(flagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(true)
+        whenever(flagManager.readFlagValue<Boolean>(
+            eq(Flags.TEAMFOOD.name), any())).thenReturn(true)
         assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagA)).isTrue()
         assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagB)).isTrue()
 
@@ -181,11 +179,12 @@
 
     @Test
     fun teamFoodFlag_Overridden() {
-        whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagA.id), any()))
+        whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagA.name), any()))
             .thenReturn(true)
-        whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagB.id), any()))
+        whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagB.name), any()))
             .thenReturn(false)
-        whenever(flagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(true)
+        whenever(flagManager.readFlagValue<Boolean>(
+            eq(Flags.TEAMFOOD.name), any())).thenReturn(true)
         assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagA)).isTrue()
         assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagB)).isFalse()
 
@@ -202,8 +201,8 @@
         whenever(resources.getBoolean(1004)).thenAnswer { throw NameNotFoundException() }
         whenever(resources.getBoolean(1005)).thenAnswer { throw NameNotFoundException() }
 
-        whenever(flagManager.readFlagValue<Boolean>(eq(3), any())).thenReturn(true)
-        whenever(flagManager.readFlagValue<Boolean>(eq(5), any())).thenReturn(false)
+        whenever(flagManager.readFlagValue<Boolean>(eq("3"), any())).thenReturn(true)
+        whenever(flagManager.readFlagValue<Boolean>(eq("5"), any())).thenReturn(false)
 
         assertThat(
             mFeatureFlagsDebug.isEnabled(
@@ -255,8 +254,8 @@
 
     @Test
     fun readStringFlag() {
-        whenever(flagManager.readFlagValue<String>(eq(3), any())).thenReturn("foo")
-        whenever(flagManager.readFlagValue<String>(eq(4), any())).thenReturn("bar")
+        whenever(flagManager.readFlagValue<String>(eq("3"), any())).thenReturn("foo")
+        whenever(flagManager.readFlagValue<String>(eq("4"), any())).thenReturn("bar")
         assertThat(mFeatureFlagsDebug.getString(StringFlag(1, "1", "test", "biz"))).isEqualTo("biz")
         assertThat(mFeatureFlagsDebug.getString(StringFlag(2, "2", "test", "baz"))).isEqualTo("baz")
         assertThat(mFeatureFlagsDebug.getString(StringFlag(3, "3", "test", "buz"))).isEqualTo("foo")
@@ -272,9 +271,9 @@
         whenever(resources.getString(1005)).thenAnswer { throw NameNotFoundException() }
         whenever(resources.getString(1006)).thenAnswer { throw NameNotFoundException() }
 
-        whenever(flagManager.readFlagValue<String>(eq(3), any())).thenReturn("override3")
-        whenever(flagManager.readFlagValue<String>(eq(4), any())).thenReturn("override4")
-        whenever(flagManager.readFlagValue<String>(eq(6), any())).thenReturn("override6")
+        whenever(flagManager.readFlagValue<String>(eq("3"), any())).thenReturn("override3")
+        whenever(flagManager.readFlagValue<String>(eq("4"), any())).thenReturn("override4")
+        whenever(flagManager.readFlagValue<String>(eq("6"), any())).thenReturn("override6")
 
         assertThat(
             mFeatureFlagsDebug.getString(
@@ -322,8 +321,8 @@
 
     @Test
     fun readIntFlag() {
-        whenever(flagManager.readFlagValue<Int>(eq(3), any())).thenReturn(22)
-        whenever(flagManager.readFlagValue<Int>(eq(4), any())).thenReturn(48)
+        whenever(flagManager.readFlagValue<Int>(eq("3"), any())).thenReturn(22)
+        whenever(flagManager.readFlagValue<Int>(eq("4"), any())).thenReturn(48)
         assertThat(mFeatureFlagsDebug.getInt(IntFlag(1, "1", "test", 12))).isEqualTo(12)
         assertThat(mFeatureFlagsDebug.getInt(IntFlag(2, "2", "test", 93))).isEqualTo(93)
         assertThat(mFeatureFlagsDebug.getInt(IntFlag(3, "3", "test", 8))).isEqualTo(22)
@@ -368,12 +367,12 @@
         broadcastReceiver.onReceive(mockContext, Intent())
         broadcastReceiver.onReceive(mockContext, Intent("invalid action"))
         broadcastReceiver.onReceive(mockContext, Intent(FlagManager.ACTION_SET_FLAG))
-        setByBroadcast(0, false) // unknown id does nothing
-        setByBroadcast(1, "string") // wrong type does nothing
-        setByBroadcast(2, 123) // wrong type does nothing
-        setByBroadcast(3, false) // wrong type does nothing
-        setByBroadcast(4, 123) // wrong type does nothing
-        verifyNoMoreInteractions(flagManager, secureSettings)
+        setByBroadcast("0", false) // unknown id does nothing
+        setByBroadcast("1", "string") // wrong type does nothing
+        setByBroadcast("2", 123) // wrong type does nothing
+        setByBroadcast("3", false) // wrong type does nothing
+        setByBroadcast("4", 123) // wrong type does nothing
+        verifyNoMoreInteractions(flagManager, globalSettings)
     }
 
     @Test
@@ -383,16 +382,16 @@
         // trying to erase an id not in the map does nothing
         broadcastReceiver.onReceive(
             mockContext,
-            Intent(FlagManager.ACTION_SET_FLAG).putExtra(FlagManager.EXTRA_ID, 0)
+            Intent(FlagManager.ACTION_SET_FLAG).putExtra(FlagManager.EXTRA_NAME, "")
         )
-        verifyNoMoreInteractions(flagManager, secureSettings)
+        verifyNoMoreInteractions(flagManager, globalSettings)
 
         // valid id with no value puts empty string in the setting
         broadcastReceiver.onReceive(
             mockContext,
-            Intent(FlagManager.ACTION_SET_FLAG).putExtra(FlagManager.EXTRA_ID, 1)
+            Intent(FlagManager.ACTION_SET_FLAG).putExtra(FlagManager.EXTRA_NAME, "1")
         )
-        verifyPutData(1, "", numReads = 0)
+        verifyPutData("1", "", numReads = 0)
     }
 
     @Test
@@ -402,51 +401,51 @@
         addFlag(ResourceBooleanFlag(3, "3", "test", 1003))
         addFlag(ResourceBooleanFlag(4, "4", "test", 1004))
 
-        setByBroadcast(1, false)
-        verifyPutData(1, "{\"type\":\"boolean\",\"value\":false}")
+        setByBroadcast("1", false)
+        verifyPutData("1", "{\"type\":\"boolean\",\"value\":false}")
 
-        setByBroadcast(2, true)
-        verifyPutData(2, "{\"type\":\"boolean\",\"value\":true}")
+        setByBroadcast("2", true)
+        verifyPutData("2", "{\"type\":\"boolean\",\"value\":true}")
 
-        setByBroadcast(3, false)
-        verifyPutData(3, "{\"type\":\"boolean\",\"value\":false}")
+        setByBroadcast("3", false)
+        verifyPutData("3", "{\"type\":\"boolean\",\"value\":false}")
 
-        setByBroadcast(4, true)
-        verifyPutData(4, "{\"type\":\"boolean\",\"value\":true}")
+        setByBroadcast("4", true)
+        verifyPutData("4", "{\"type\":\"boolean\",\"value\":true}")
     }
 
     @Test
     fun setStringFlag() {
-        addFlag(StringFlag(1, "flag1", "1", "test"))
+        addFlag(StringFlag(1, "1", "1", "test"))
         addFlag(ResourceStringFlag(2, "2", "test", 1002))
 
-        setByBroadcast(1, "override1")
-        verifyPutData(1, "{\"type\":\"string\",\"value\":\"override1\"}")
+        setByBroadcast("1", "override1")
+        verifyPutData("1", "{\"type\":\"string\",\"value\":\"override1\"}")
 
-        setByBroadcast(2, "override2")
-        verifyPutData(2, "{\"type\":\"string\",\"value\":\"override2\"}")
+        setByBroadcast("2", "override2")
+        verifyPutData("2", "{\"type\":\"string\",\"value\":\"override2\"}")
     }
 
     @Test
     fun setFlag_ClearsCache() {
         val flag1 = addFlag(StringFlag(1, "1", "test", "flag1"))
-        whenever(flagManager.readFlagValue<String>(eq(1), any())).thenReturn("original")
+        whenever(flagManager.readFlagValue<String>(eq("1"), any())).thenReturn("original")
 
         // gets the flag & cache it
         assertThat(mFeatureFlagsDebug.getString(flag1)).isEqualTo("original")
-        verify(flagManager).readFlagValue(eq(1), eq(StringFlagSerializer))
+        verify(flagManager, times(1)).readFlagValue(eq("1"), eq(StringFlagSerializer))
 
         // hit the cache
         assertThat(mFeatureFlagsDebug.getString(flag1)).isEqualTo("original")
         verifyNoMoreInteractions(flagManager)
 
         // set the flag
-        setByBroadcast(1, "new")
-        verifyPutData(1, "{\"type\":\"string\",\"value\":\"new\"}", numReads = 2)
-        whenever(flagManager.readFlagValue<String>(eq(1), any())).thenReturn("new")
+        setByBroadcast("1", "new")
+        verifyPutData("1", "{\"type\":\"string\",\"value\":\"new\"}", numReads = 2)
+        whenever(flagManager.readFlagValue<String>(eq("1"), any())).thenReturn("new")
 
         assertThat(mFeatureFlagsDebug.getString(flag1)).isEqualTo("new")
-        verify(flagManager, times(3)).readFlagValue(eq(1), eq(StringFlagSerializer))
+        verify(flagManager, times(3)).readFlagValue(eq("1"), eq(StringFlagSerializer))
     }
 
     @Test
@@ -463,7 +462,6 @@
         val flag = UnreleasedFlag(100, name = "100", namespace = "test")
 
         serverFlagReader.setFlagValue(flag.namespace, flag.name, true)
-
         assertThat(mFeatureFlagsDebug.isEnabled(flag)).isTrue()
     }
 
@@ -503,26 +501,26 @@
         assertThat(dump).contains(" sysui_flag_7: [length=9] \"override7\"\n")
     }
 
-    private fun verifyPutData(id: Int, data: String, numReads: Int = 1) {
-        inOrder(flagManager, secureSettings).apply {
-            verify(flagManager, times(numReads)).readFlagValue(eq(id), any<FlagSerializer<*>>())
-            verify(flagManager).idToSettingsKey(eq(id))
-            verify(secureSettings).putStringForUser(eq("key-$id"), eq(data), anyInt())
-            verify(flagManager).dispatchListenersAndMaybeRestart(eq(id), any())
+    private fun verifyPutData(name: String, data: String, numReads: Int = 1) {
+        inOrder(flagManager, globalSettings).apply {
+            verify(flagManager, times(numReads)).readFlagValue(eq(name), any<FlagSerializer<*>>())
+            verify(flagManager).nameToSettingsKey(eq(name))
+            verify(globalSettings).putStringForUser(eq("key-$name"), eq(data), anyInt())
+            verify(flagManager).dispatchListenersAndMaybeRestart(eq(name), any())
         }.verifyNoMoreInteractions()
-        verifyNoMoreInteractions(flagManager, secureSettings)
+        verifyNoMoreInteractions(flagManager, globalSettings)
     }
 
-    private fun setByBroadcast(id: Int, value: Serializable?) {
+    private fun setByBroadcast(name: String, value: Serializable?) {
         val intent = Intent(FlagManager.ACTION_SET_FLAG)
-        intent.putExtra(FlagManager.EXTRA_ID, id)
+        intent.putExtra(FlagManager.EXTRA_NAME, name)
         intent.putExtra(FlagManager.EXTRA_VALUE, value)
         broadcastReceiver.onReceive(mockContext, intent)
     }
 
     private fun <F : Flag<*>> addFlag(flag: F): F {
-        val old = flagMap.put(flag.id, flag)
-        check(old == null) { "Flag ${flag.id} already registered" }
+        val old = flagMap.put(flag.name, flag)
+        check(old == null) { "Flag ${flag.name} already registered" }
         return flag
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseRestarterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseRestarterTest.kt
index 7d807e2..6060afe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseRestarterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseRestarterTest.kt
@@ -22,6 +22,7 @@
 import com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE
 import com.android.systemui.statusbar.policy.BatteryController
 import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
@@ -63,7 +64,7 @@
         whenever(batteryController.isPluggedIn).thenReturn(true)
 
         assertThat(executor.numPending()).isEqualTo(0)
-        restarter.restartSystemUI()
+        restarter.restartSystemUI("Restart for test")
         assertThat(executor.numPending()).isEqualTo(1)
     }
 
@@ -72,11 +73,11 @@
         whenever(wakefulnessLifecycle.wakefulness).thenReturn(WAKEFULNESS_ASLEEP)
         whenever(batteryController.isPluggedIn).thenReturn(true)
 
-        restarter.restartSystemUI()
-        verify(systemExitRestarter, never()).restartSystemUI()
+        restarter.restartSystemUI("Restart for test")
+        verify(systemExitRestarter, never()).restartSystemUI("Restart for test")
         executor.advanceClockToLast()
         executor.runAllReady()
-        verify(systemExitRestarter).restartSystemUI()
+        verify(systemExitRestarter).restartSystemUI(any())
     }
 
     @Test
@@ -85,7 +86,7 @@
         whenever(batteryController.isPluggedIn).thenReturn(true)
 
         assertThat(executor.numPending()).isEqualTo(0)
-        restarter.restartSystemUI()
+        restarter.restartSystemUI("Restart for test")
         assertThat(executor.numPending()).isEqualTo(0)
     }
 
@@ -95,7 +96,7 @@
         whenever(batteryController.isPluggedIn).thenReturn(false)
 
         assertThat(executor.numPending()).isEqualTo(0)
-        restarter.restartSystemUI()
+        restarter.restartSystemUI("Restart for test")
         assertThat(executor.numPending()).isEqualTo(0)
     }
 
@@ -105,8 +106,8 @@
         whenever(batteryController.isPluggedIn).thenReturn(true)
 
         assertThat(executor.numPending()).isEqualTo(0)
-        restarter.restartSystemUI()
-        restarter.restartSystemUI()
+        restarter.restartSystemUI("Restart for test")
+        restarter.restartSystemUI("Restart for test")
         assertThat(executor.numPending()).isEqualTo(1)
     }
 
@@ -115,7 +116,7 @@
         whenever(wakefulnessLifecycle.wakefulness).thenReturn(WAKEFULNESS_AWAKE)
         whenever(batteryController.isPluggedIn).thenReturn(true)
         assertThat(executor.numPending()).isEqualTo(0)
-        restarter.restartSystemUI()
+        restarter.restartSystemUI("Restart for test")
 
         val captor = ArgumentCaptor.forClass(WakefulnessLifecycle.Observer::class.java)
         verify(wakefulnessLifecycle).addObserver(captor.capture())
@@ -131,7 +132,7 @@
         whenever(wakefulnessLifecycle.wakefulness).thenReturn(WAKEFULNESS_ASLEEP)
         whenever(batteryController.isPluggedIn).thenReturn(false)
         assertThat(executor.numPending()).isEqualTo(0)
-        restarter.restartSystemUI()
+        restarter.restartSystemUI("Restart for test")
 
         val captor =
             ArgumentCaptor.forClass(BatteryController.BatteryStateChangeCallback::class.java)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
index d5b5a4a..4c6028c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
@@ -19,7 +19,6 @@
 import android.content.res.Resources
 import android.test.suitebuilder.annotation.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.DeviceConfigProxyFake
 import com.google.common.truth.Truth.assertThat
 import org.junit.Assert.assertThrows
 import org.junit.Before
@@ -39,9 +38,8 @@
     @Mock private lateinit var mResources: Resources
     @Mock private lateinit var mSystemProperties: SystemPropertiesHelper
     @Mock private lateinit var restarter: Restarter
-    private val flagMap = mutableMapOf<Int, Flag<*>>()
+    private val flagMap = mutableMapOf<String, Flag<*>>()
     private val serverFlagReader = ServerFlagReaderFake()
-    private val deviceConfig = DeviceConfigProxyFake()
 
     @Before
     fun setup() {
@@ -49,7 +47,6 @@
         mFeatureFlagsRelease = FeatureFlagsRelease(
             mResources,
             mSystemProperties,
-            deviceConfig,
             serverFlagReader,
             flagMap,
             restarter)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagCommandTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagCommandTest.kt
index fea91c5..28131b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagCommandTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagCommandTest.kt
@@ -32,7 +32,7 @@
 
     @Mock private lateinit var featureFlags: FeatureFlagsDebug
     @Mock private lateinit var pw: PrintWriter
-    private val flagMap = mutableMapOf<Int, Flag<*>>()
+    private val flagMap = mutableMapOf<String, Flag<*>>()
     private val flagA = UnreleasedFlag(500, "500", "test")
     private val flagB = ReleasedFlag(501, "501", "test")
     private val stringFlag = StringFlag(502, "502", "test", "abracadabra")
@@ -53,59 +53,59 @@
             (invocation.getArgument(0) as IntFlag).default
         }
 
-        flagMap.put(flagA.id, flagA)
-        flagMap.put(flagB.id, flagB)
-        flagMap.put(stringFlag.id, stringFlag)
-        flagMap.put(intFlag.id, intFlag)
+        flagMap.put(flagA.name, flagA)
+        flagMap.put(flagB.name, flagB)
+        flagMap.put(stringFlag.name, stringFlag)
+        flagMap.put(intFlag.name, intFlag)
 
         cmd = FlagCommand(featureFlags, flagMap)
     }
 
     @Test
     fun readBooleanFlagCommand() {
-        cmd.execute(pw, listOf(flagA.id.toString()))
+        cmd.execute(pw, listOf(flagA.name))
         Mockito.verify(featureFlags).isEnabled(flagA)
     }
 
     @Test
     fun readStringFlagCommand() {
-        cmd.execute(pw, listOf(stringFlag.id.toString()))
+        cmd.execute(pw, listOf(stringFlag.name))
         Mockito.verify(featureFlags).getString(stringFlag)
     }
 
     @Test
     fun readIntFlag() {
-        cmd.execute(pw, listOf(intFlag.id.toString()))
+        cmd.execute(pw, listOf(intFlag.name))
         Mockito.verify(featureFlags).getInt(intFlag)
     }
 
     @Test
     fun setBooleanFlagCommand() {
-        cmd.execute(pw, listOf(flagB.id.toString(), "on"))
+        cmd.execute(pw, listOf(flagB.name, "on"))
         Mockito.verify(featureFlags).setBooleanFlagInternal(flagB, true)
     }
 
     @Test
     fun setStringFlagCommand() {
-        cmd.execute(pw, listOf(stringFlag.id.toString(), "set", "foobar"))
+        cmd.execute(pw, listOf(stringFlag.name, "set", "foobar"))
         Mockito.verify(featureFlags).setStringFlagInternal(stringFlag, "foobar")
     }
 
     @Test
     fun setIntFlag() {
-        cmd.execute(pw, listOf(intFlag.id.toString(), "put", "123"))
+        cmd.execute(pw, listOf(intFlag.name, "put", "123"))
         Mockito.verify(featureFlags).setIntFlagInternal(intFlag, 123)
     }
 
     @Test
     fun toggleBooleanFlagCommand() {
-        cmd.execute(pw, listOf(flagB.id.toString(), "toggle"))
+        cmd.execute(pw, listOf(flagB.name, "toggle"))
         Mockito.verify(featureFlags).setBooleanFlagInternal(flagB, false)
     }
 
     @Test
     fun eraseFlagCommand() {
-        cmd.execute(pw, listOf(flagA.id.toString(), "erase"))
+        cmd.execute(pw, listOf(flagA.name, "erase"))
         Mockito.verify(featureFlags).eraseFlag(flagA)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
index fca7e96..e679d47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
@@ -87,14 +87,14 @@
     @Test
     fun testObserverClearsCache() {
         val listener = mock<FlagListenable.Listener>()
-        val clearCacheAction = mock<Consumer<Int>>()
+        val clearCacheAction = mock<Consumer<String>>()
         mFlagManager.clearCacheAction = clearCacheAction
         mFlagManager.addListener(ReleasedFlag(1, "1", "test"), listener)
         val observer = withArgCaptor<ContentObserver> {
             verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture())
         }
         observer.onChange(false, flagUri(1))
-        verify(clearCacheAction).accept(eq(1))
+        verify(clearCacheAction).accept(eq("1"))
     }
 
     @Test
@@ -110,14 +110,14 @@
         val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> {
             verify(listener1).onFlagChanged(capture())
         }
-        assertThat(flagEvent1.flagId).isEqualTo(1)
+        assertThat(flagEvent1.flagName).isEqualTo("1")
         verifyNoMoreInteractions(listener1, listener10)
 
         observer.onChange(false, flagUri(10))
         val flagEvent10 = withArgCaptor<FlagListenable.FlagEvent> {
             verify(listener10).onFlagChanged(capture())
         }
-        assertThat(flagEvent10.flagId).isEqualTo(10)
+        assertThat(flagEvent10.flagName).isEqualTo("10")
         verifyNoMoreInteractions(listener1, listener10)
     }
 
@@ -130,18 +130,18 @@
         mFlagManager.addListener(ReleasedFlag(1, "1", "test"), listener1)
         mFlagManager.addListener(ReleasedFlag(10, "10", "test"), listener10)
 
-        mFlagManager.dispatchListenersAndMaybeRestart(1, null)
+        mFlagManager.dispatchListenersAndMaybeRestart("1", null)
         val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> {
             verify(listener1).onFlagChanged(capture())
         }
-        assertThat(flagEvent1.flagId).isEqualTo(1)
+        assertThat(flagEvent1.flagName).isEqualTo("1")
         verifyNoMoreInteractions(listener1, listener10)
 
-        mFlagManager.dispatchListenersAndMaybeRestart(10, null)
+        mFlagManager.dispatchListenersAndMaybeRestart("10", null)
         val flagEvent10 = withArgCaptor<FlagListenable.FlagEvent> {
             verify(listener10).onFlagChanged(capture())
         }
-        assertThat(flagEvent10.flagId).isEqualTo(10)
+        assertThat(flagEvent10.flagName).isEqualTo("10")
         verifyNoMoreInteractions(listener1, listener10)
     }
 
@@ -151,25 +151,25 @@
         mFlagManager.addListener(ReleasedFlag(1, "1", "test"), listener)
         mFlagManager.addListener(ReleasedFlag(10, "10", "test"), listener)
 
-        mFlagManager.dispatchListenersAndMaybeRestart(1, null)
+        mFlagManager.dispatchListenersAndMaybeRestart("1", null)
         val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> {
             verify(listener).onFlagChanged(capture())
         }
-        assertThat(flagEvent1.flagId).isEqualTo(1)
+        assertThat(flagEvent1.flagName).isEqualTo("1")
         verifyNoMoreInteractions(listener)
 
-        mFlagManager.dispatchListenersAndMaybeRestart(10, null)
+        mFlagManager.dispatchListenersAndMaybeRestart("10", null)
         val flagEvent10 = withArgCaptor<FlagListenable.FlagEvent> {
             verify(listener, times(2)).onFlagChanged(capture())
         }
-        assertThat(flagEvent10.flagId).isEqualTo(10)
+        assertThat(flagEvent10.flagName).isEqualTo("10")
         verifyNoMoreInteractions(listener)
     }
 
     @Test
     fun testRestartWithNoListeners() {
         val restartAction = mock<Consumer<Boolean>>()
-        mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
+        mFlagManager.dispatchListenersAndMaybeRestart("1", restartAction)
         verify(restartAction).accept(eq(false))
         verifyNoMoreInteractions(restartAction)
     }
@@ -180,7 +180,7 @@
         mFlagManager.addListener(ReleasedFlag(1, "1", "test")) { event ->
             event.requestNoRestart()
         }
-        mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
+        mFlagManager.dispatchListenersAndMaybeRestart("1", restartAction)
         verify(restartAction).accept(eq(true))
         verifyNoMoreInteractions(restartAction)
     }
@@ -191,7 +191,7 @@
         mFlagManager.addListener(ReleasedFlag(10, "10", "test")) { event ->
             event.requestNoRestart()
         }
-        mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
+        mFlagManager.dispatchListenersAndMaybeRestart("1", restartAction)
         verify(restartAction).accept(eq(false))
         verifyNoMoreInteractions(restartAction)
     }
@@ -205,7 +205,7 @@
         mFlagManager.addListener(ReleasedFlag(10, "10", "test")) {
             // do not request
         }
-        mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
+        mFlagManager.dispatchListenersAndMaybeRestart("1", restartAction)
         verify(restartAction).accept(eq(false))
         verifyNoMoreInteractions(restartAction)
     }
@@ -214,31 +214,31 @@
     fun testReadBooleanFlag() {
         // test that null string returns null
         whenever(mFlagSettingsHelper.getString(any())).thenReturn(null)
-        assertThat(mFlagManager.readFlagValue(1, BooleanFlagSerializer)).isNull()
+        assertThat(mFlagManager.readFlagValue("1", BooleanFlagSerializer)).isNull()
 
         // test that empty string returns null
         whenever(mFlagSettingsHelper.getString(any())).thenReturn("")
-        assertThat(mFlagManager.readFlagValue(1, BooleanFlagSerializer)).isNull()
+        assertThat(mFlagManager.readFlagValue("1", BooleanFlagSerializer)).isNull()
 
         // test false
         whenever(mFlagSettingsHelper.getString(any()))
             .thenReturn("{\"type\":\"boolean\",\"value\":false}")
-        assertThat(mFlagManager.readFlagValue(1, BooleanFlagSerializer)).isFalse()
+        assertThat(mFlagManager.readFlagValue("1", BooleanFlagSerializer)).isFalse()
 
         // test true
         whenever(mFlagSettingsHelper.getString(any()))
             .thenReturn("{\"type\":\"boolean\",\"value\":true}")
-        assertThat(mFlagManager.readFlagValue(1, BooleanFlagSerializer)).isTrue()
+        assertThat(mFlagManager.readFlagValue("1", BooleanFlagSerializer)).isTrue()
 
         // Reading a value of a different type should just return null
         whenever(mFlagSettingsHelper.getString(any()))
             .thenReturn("{\"type\":\"string\",\"value\":\"foo\"}")
-        assertThat(mFlagManager.readFlagValue(1, BooleanFlagSerializer)).isNull()
+        assertThat(mFlagManager.readFlagValue("1", BooleanFlagSerializer)).isNull()
 
         // Reading a value that isn't json should throw an exception
         assertThrows(InvalidFlagStorageException::class.java) {
             whenever(mFlagSettingsHelper.getString(any())).thenReturn("1")
-            mFlagManager.readFlagValue(1, BooleanFlagSerializer)
+            mFlagManager.readFlagValue("1", BooleanFlagSerializer)
         }
     }
 
@@ -257,31 +257,31 @@
     fun testReadStringFlag() {
         // test that null string returns null
         whenever(mFlagSettingsHelper.getString(any())).thenReturn(null)
-        assertThat(mFlagManager.readFlagValue(1, StringFlagSerializer)).isNull()
+        assertThat(mFlagManager.readFlagValue("1", StringFlagSerializer)).isNull()
 
         // test that empty string returns null
         whenever(mFlagSettingsHelper.getString(any())).thenReturn("")
-        assertThat(mFlagManager.readFlagValue(1, StringFlagSerializer)).isNull()
+        assertThat(mFlagManager.readFlagValue("1", StringFlagSerializer)).isNull()
 
         // test json with the empty string value returns empty string
         whenever(mFlagSettingsHelper.getString(any()))
             .thenReturn("{\"type\":\"string\",\"value\":\"\"}")
-        assertThat(mFlagManager.readFlagValue(1, StringFlagSerializer)).isEqualTo("")
+        assertThat(mFlagManager.readFlagValue("1", StringFlagSerializer)).isEqualTo("")
 
         // test string with value is returned
         whenever(mFlagSettingsHelper.getString(any()))
             .thenReturn("{\"type\":\"string\",\"value\":\"foo\"}")
-        assertThat(mFlagManager.readFlagValue(1, StringFlagSerializer)).isEqualTo("foo")
+        assertThat(mFlagManager.readFlagValue("1", StringFlagSerializer)).isEqualTo("foo")
 
         // Reading a value of a different type should just return null
         whenever(mFlagSettingsHelper.getString(any()))
             .thenReturn("{\"type\":\"boolean\",\"value\":false}")
-        assertThat(mFlagManager.readFlagValue(1, StringFlagSerializer)).isNull()
+        assertThat(mFlagManager.readFlagValue("1", StringFlagSerializer)).isNull()
 
         // Reading a value that isn't json should throw an exception
         assertThrows(InvalidFlagStorageException::class.java) {
             whenever(mFlagSettingsHelper.getString(any())).thenReturn("1")
-            mFlagManager.readFlagValue(1, StringFlagSerializer)
+            mFlagManager.readFlagValue("1", StringFlagSerializer)
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/RestartDozeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/RestartDozeListenerTest.kt
new file mode 100644
index 0000000..de0e511
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/RestartDozeListenerTest.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.flags
+
+import android.os.PowerManager
+import android.test.suitebuilder.annotation.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.mockito.ArgumentCaptor
+import org.mockito.Mock
+import org.mockito.Mockito.anyLong
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+class RestartDozeListenerTest : SysuiTestCase() {
+
+    lateinit var restartDozeListener: RestartDozeListener
+
+    val settings = FakeSettings()
+    @Mock lateinit var statusBarStateController: StatusBarStateController
+    @Mock lateinit var powerManager: PowerManager
+    val clock = FakeSystemClock()
+    lateinit var listener: StatusBarStateController.StateListener
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        restartDozeListener =
+            RestartDozeListener(settings, statusBarStateController, powerManager, clock)
+
+        val captor = ArgumentCaptor.forClass(StatusBarStateController.StateListener::class.java)
+        restartDozeListener.init()
+        verify(statusBarStateController).addCallback(captor.capture())
+        listener = captor.value
+    }
+
+    @Test
+    fun testStoreDreamState_onDreamingStarted() {
+        listener.onDreamingChanged(true)
+        assertThat(settings.getBool(RestartDozeListener.RESTART_NAP_KEY)).isTrue()
+    }
+
+    @Test
+    fun testStoreDreamState_onDreamingStopped() {
+        listener.onDreamingChanged(false)
+        assertThat(settings.getBool(RestartDozeListener.RESTART_NAP_KEY)).isFalse()
+    }
+
+    @Test
+    fun testRestoreDreamState_dreamingShouldStart() {
+        settings.putBool(RestartDozeListener.RESTART_NAP_KEY, true)
+        restartDozeListener.maybeRestartSleep()
+        verify(powerManager).wakeUp(clock.uptimeMillis())
+        verify(powerManager).goToSleep(clock.uptimeMillis())
+    }
+
+    @Test
+    fun testRestoreDreamState_dreamingShouldNot() {
+        settings.putBool(RestartDozeListener.RESTART_NAP_KEY, false)
+        restartDozeListener.maybeRestartSleep()
+        verify(powerManager, never()).wakeUp(anyLong())
+        verify(powerManager, never()).goToSleep(anyLong())
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt
index 1633912..2e98006 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt
@@ -26,6 +26,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
+import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
@@ -45,17 +46,29 @@
     fun setup() {
         MockitoAnnotations.initMocks(this)
 
-        serverFlagReader = ServerFlagReaderImpl(NAMESPACE, deviceConfig, executor)
+        serverFlagReader = ServerFlagReaderImpl(NAMESPACE, deviceConfig, executor, false)
     }
 
     @Test
     fun testChange_alertsListener() {
+        val flag = ReleasedFlag(1, "flag_1", "test")
+        serverFlagReader.listenForChanges(listOf(flag), changeListener)
+
+        deviceConfig.setProperty(NAMESPACE, "flag_1", "1", false)
+        executor.runAllReady()
+
+        verify(changeListener).onChange(flag)
+    }
+
+    @Test
+    fun testChange_ignoresListenersDuringTest() {
+        val serverFlagReader = ServerFlagReaderImpl(NAMESPACE, deviceConfig, executor, true)
         val flag = ReleasedFlag(1, "1", "test")
         serverFlagReader.listenForChanges(listOf(flag), changeListener)
 
         deviceConfig.setProperty(NAMESPACE, "flag_override_1", "1", false)
         executor.runAllReady()
 
-        verify(changeListener).onChange()
+        verify(changeListener, never()).onChange(flag)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/fragments/FragmentServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/fragments/FragmentServiceTest.kt
index 77c837b..a2dc1eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/fragments/FragmentServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/fragments/FragmentServiceTest.kt
@@ -14,6 +14,7 @@
 @SmallTest
 class FragmentServiceTest : SysuiTestCase() {
     private val fragmentCreator = TestFragmentCreator()
+    private val fragmenetHostManagerFactory: FragmentHostManager.Factory = mock()
     private val fragmentCreatorFactory = FragmentService.FragmentCreator.Factory { fragmentCreator }
 
     private lateinit var fragmentService: FragmentService
@@ -24,7 +25,13 @@
             Looper.prepare()
         }
 
-        fragmentService = FragmentService(fragmentCreatorFactory, mock(), DumpManager())
+        fragmentService =
+            FragmentService(
+                fragmentCreatorFactory,
+                fragmenetHostManagerFactory,
+                mock(),
+                DumpManager()
+            )
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index c0af0cb..a4e5bca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -17,6 +17,7 @@
 
 package com.android.systemui.keyguard
 
+import android.app.admin.DevicePolicyManager
 import android.content.ContentValues
 import android.content.pm.PackageManager
 import android.content.pm.ProviderInfo
@@ -39,6 +40,7 @@
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
+import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -60,8 +62,8 @@
 import com.android.systemui.util.settings.FakeSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
@@ -88,6 +90,7 @@
     @Mock private lateinit var previewSurfacePackage: SurfaceControlViewHost.SurfacePackage
     @Mock private lateinit var launchAnimator: DialogLaunchAnimator
     @Mock private lateinit var commandQueue: CommandQueue
+    @Mock private lateinit var devicePolicyManager: DevicePolicyManager
 
     private lateinit var underTest: CustomizationProvider
     private lateinit var testScope: TestScope
@@ -100,7 +103,7 @@
         whenever(backgroundHandler.looper).thenReturn(TestableLooper.get(this).looper)
 
         underTest = CustomizationProvider()
-        val testDispatcher = StandardTestDispatcher()
+        val testDispatcher = UnconfinedTestDispatcher()
         testScope = TestScope(testDispatcher)
         val localUserSelectionManager =
             KeyguardQuickAffordanceLocalUserSelectionManager(
@@ -156,27 +159,33 @@
                 dumpManager = mock(),
                 userHandle = UserHandle.SYSTEM,
             )
+        val featureFlags =
+            FakeFeatureFlags().apply {
+                set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, true)
+                set(Flags.LOCKSCREEN_CUSTOM_CLOCKS, true)
+                set(Flags.REVAMPED_WALLPAPER_UI, true)
+                set(Flags.WALLPAPER_FULLSCREEN_PREVIEW, true)
+                set(Flags.FACE_AUTH_REFACTOR, true)
+            }
         underTest.interactor =
             KeyguardQuickAffordanceInteractor(
                 keyguardInteractor =
                     KeyguardInteractor(
                         repository = FakeKeyguardRepository(),
                         commandQueue = commandQueue,
+                        featureFlags = featureFlags,
+                        bouncerRepository = FakeKeyguardBouncerRepository(),
                     ),
                 registry = mock(),
                 lockPatternUtils = lockPatternUtils,
                 keyguardStateController = keyguardStateController,
                 userTracker = userTracker,
                 activityStarter = activityStarter,
-                featureFlags =
-                    FakeFeatureFlags().apply {
-                        set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, true)
-                        set(Flags.LOCKSCREEN_CUSTOM_CLOCKS, true)
-                        set(Flags.REVAMPED_WALLPAPER_UI, true)
-                        set(Flags.WALLPAPER_FULLSCREEN_PREVIEW, true)
-                    },
+                featureFlags = featureFlags,
                 repository = { quickAffordanceRepository },
                 launchAnimator = launchAnimator,
+                devicePolicyManager = devicePolicyManager,
+                backgroundDispatcher = testDispatcher,
             )
         underTest.previewManager =
             KeyguardRemotePreviewManager(
@@ -184,6 +193,7 @@
                 mainDispatcher = testDispatcher,
                 backgroundHandler = backgroundHandler,
             )
+        underTest.mainDispatcher = UnconfinedTestDispatcher()
 
         underTest.attachInfoForTesting(
             context,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java
index 2290676..c3b0e5226 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java
@@ -38,6 +38,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.keyguard.logging.KeyguardLogger;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
@@ -66,6 +67,8 @@
     private KeyguardIndicationTextView mView;
     @Mock
     private StatusBarStateController mStatusBarStateController;
+    @Mock
+    private KeyguardLogger mLogger;
     @Captor
     private ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor;
 
@@ -77,7 +80,7 @@
         MockitoAnnotations.initMocks(this);
         when(mView.getTextColors()).thenReturn(ColorStateList.valueOf(Color.WHITE));
         mController = new KeyguardIndicationRotateTextViewController(mView, mExecutor,
-                mStatusBarStateController);
+                mStatusBarStateController, mLogger);
         mController.onViewAttached();
 
         verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
index 8da4eae..5bb8367 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
@@ -18,44 +18,58 @@
 package com.android.systemui.keyguard.data.quickaffordance
 
 import android.app.StatusBarManager
+import android.app.admin.DevicePolicyManager
 import android.content.Context
+import android.content.pm.PackageManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.camera.CameraGestureHelper
+import com.android.systemui.settings.UserTracker
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
 import org.junit.Assert.assertEquals
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
-import org.mockito.Mockito.anyInt
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class CameraQuickAffordanceConfigTest : SysuiTestCase() {
 
     @Mock private lateinit var cameraGestureHelper: CameraGestureHelper
     @Mock private lateinit var context: Context
+    @Mock private lateinit var packageManager: PackageManager
+    @Mock private lateinit var userTracker: UserTracker
+    @Mock private lateinit var devicePolicyManager: DevicePolicyManager
 
     private lateinit var underTest: CameraQuickAffordanceConfig
+    private lateinit var testScope: TestScope
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+        setLaunchable()
 
+        val testDispatcher = StandardTestDispatcher()
+        testScope = TestScope(testDispatcher)
         underTest =
             CameraQuickAffordanceConfig(
                 context,
-            ) {
-                cameraGestureHelper
-            }
+                packageManager,
+                { cameraGestureHelper },
+                userTracker,
+                devicePolicyManager,
+                testDispatcher,
+            )
     }
 
     @Test
@@ -70,22 +84,57 @@
     }
 
     @Test
-    fun `getPickerScreenState - default when launchable`() = runTest {
-        setLaunchable(true)
+    fun `getPickerScreenState - default when launchable`() =
+        testScope.runTest {
+            setLaunchable(true)
 
-        Truth.assertThat(underTest.getPickerScreenState())
-            .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Default::class.java)
-    }
+            Truth.assertThat(underTest.getPickerScreenState())
+                .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Default::class.java)
+        }
 
     @Test
-    fun `getPickerScreenState - unavailable when not launchable`() = runTest {
-        setLaunchable(false)
+    fun `getPickerScreenState - unavailable when camera app not installed`() =
+        testScope.runTest {
+            setLaunchable(isCameraAppInstalled = false)
 
-        Truth.assertThat(underTest.getPickerScreenState())
-            .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
-    }
+            Truth.assertThat(underTest.getPickerScreenState())
+                .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
+        }
 
-    private fun setLaunchable(isLaunchable: Boolean) {
-        whenever(cameraGestureHelper.canCameraGestureBeLaunched(anyInt())).thenReturn(isLaunchable)
+    @Test
+    fun `getPickerScreenState - unavailable when camera disabled by admin`() =
+        testScope.runTest {
+            setLaunchable(isCameraDisabledByDeviceAdmin = true)
+
+            Truth.assertThat(underTest.getPickerScreenState())
+                .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
+        }
+
+    @Test
+    fun `getPickerScreenState - unavailable when secure camera disabled by admin`() =
+        testScope.runTest {
+            setLaunchable(isSecureCameraDisabledByDeviceAdmin = true)
+
+            Truth.assertThat(underTest.getPickerScreenState())
+                .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
+        }
+
+    private fun setLaunchable(
+        isCameraAppInstalled: Boolean = true,
+        isCameraDisabledByDeviceAdmin: Boolean = false,
+        isSecureCameraDisabledByDeviceAdmin: Boolean = false,
+    ) {
+        whenever(packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY))
+            .thenReturn(isCameraAppInstalled)
+        whenever(devicePolicyManager.getCameraDisabled(null, userTracker.userId))
+            .thenReturn(isCameraDisabledByDeviceAdmin)
+        whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId))
+            .thenReturn(
+                if (isSecureCameraDisabledByDeviceAdmin) {
+                    DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA
+                } else {
+                    0
+                }
+            )
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
index 15b85de..64839e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
@@ -22,6 +22,7 @@
 import android.provider.Settings.Global.ZEN_MODE_OFF
 import android.provider.Settings.Secure.ZEN_DURATION_FOREVER
 import android.provider.Settings.Secure.ZEN_DURATION_PROMPT
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.settingslib.notification.EnableZenModeDialog
 import com.android.systemui.R
@@ -51,7 +52,6 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.ArgumentCaptor
 import org.mockito.Captor
 import org.mockito.Mock
@@ -60,7 +60,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() {
 
     @Mock private lateinit var zenModeController: ZenModeController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
index 9fa7db1..31391ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
@@ -18,6 +18,7 @@
 package com.android.systemui.keyguard.data.quickaffordance
 
 import android.content.Context
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
 import com.android.systemui.common.shared.model.Icon
@@ -35,13 +36,12 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() {
 
     @Mock private lateinit var context: Context
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
index 659c1e5..2c1c04c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
@@ -17,6 +17,7 @@
 
 package com.android.systemui.keyguard.data.quickaffordance
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
@@ -34,13 +35,12 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class HomeControlsKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
 
     @Mock private lateinit var component: ControlsComponent
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
index 3b0169d..3bae7f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
@@ -20,6 +20,7 @@
 import android.content.Context
 import android.content.res.Resources
 import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
@@ -40,7 +41,6 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.anyString
 import org.mockito.Mock
@@ -48,7 +48,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class KeyguardQuickAffordanceLegacySettingSyncerTest : SysuiTestCase() {
 
     @Mock private lateinit var sharedPrefs: FakeSharedPreferences
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
index 3d65713..1259b47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
@@ -20,6 +20,7 @@
 import android.content.Intent
 import android.content.SharedPreferences
 import android.content.pm.UserInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
@@ -40,7 +41,6 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.anyString
 import org.mockito.Mock
@@ -51,7 +51,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class KeyguardQuickAffordanceLocalUserSelectionManagerTest : SysuiTestCase() {
 
     @Mock private lateinit var userFileManager: UserFileManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
index b21cec9..c08ef42 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
@@ -19,6 +19,7 @@
 
 import android.content.pm.UserInfo
 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.settings.FakeUserTracker
@@ -37,13 +38,12 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class KeyguardQuickAffordanceRemoteUserSelectionManagerTest : SysuiTestCase() {
 
     @Mock private lateinit var userHandle: UserHandle
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
new file mode 100644
index 0000000..925c06f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.quickaffordance
+
+import android.content.Context
+import android.media.AudioManager
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.UserFileManager
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.RingerModeTracker
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.whenever
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+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.Assert.assertEquals
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class MuteQuickAffordanceConfigTest : SysuiTestCase() {
+
+    private lateinit var underTest: MuteQuickAffordanceConfig
+    @Mock
+    private lateinit var ringerModeTracker: RingerModeTracker
+    @Mock
+    private lateinit var audioManager: AudioManager
+    @Mock
+    private lateinit var userTracker: UserTracker
+    @Mock
+    private lateinit var userFileManager: UserFileManager
+
+    private lateinit var testDispatcher: TestDispatcher
+    private lateinit var testScope: TestScope
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        testDispatcher = StandardTestDispatcher()
+        testScope = TestScope(testDispatcher)
+
+        whenever(userTracker.userContext).thenReturn(context)
+        whenever(userFileManager.getSharedPreferences(any(), any(), any()))
+                .thenReturn(context.getSharedPreferences("mutequickaffordancetest", Context.MODE_PRIVATE))
+
+        underTest = MuteQuickAffordanceConfig(
+                context,
+                userTracker,
+                userFileManager,
+                ringerModeTracker,
+                audioManager,
+                testScope.backgroundScope,
+                testDispatcher,
+                testDispatcher,
+        )
+    }
+
+    @Test
+    fun `picker state - volume fixed - not available`() = testScope.runTest {
+        //given
+        whenever(audioManager.isVolumeFixed).thenReturn(true)
+
+        //when
+        val result = underTest.getPickerScreenState()
+
+        //then
+        assertEquals(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice, result)
+    }
+
+    @Test
+    fun `picker state - volume not fixed - available`() = testScope.runTest {
+        //given
+        whenever(audioManager.isVolumeFixed).thenReturn(false)
+
+        //when
+        val result = underTest.getPickerScreenState()
+
+        //then
+        assertEquals(KeyguardQuickAffordanceConfig.PickerScreenState.Default(), result)
+    }
+
+    @Test
+    fun `triggered - state was previously NORMAL - currently SILENT - move to previous state`() = testScope.runTest {
+        //given
+        val ringerModeCapture = argumentCaptor<Int>()
+        whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_NORMAL)
+        underTest.onTriggered(null)
+        whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_SILENT)
+
+        //when
+        val result = underTest.onTriggered(null)
+        runCurrent()
+        verify(audioManager, times(2)).ringerModeInternal = ringerModeCapture.capture()
+
+        //then
+        assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+        assertEquals(AudioManager.RINGER_MODE_NORMAL, ringerModeCapture.value)
+    }
+
+    @Test
+    fun `triggered - state is not SILENT - move to SILENT ringer`() = testScope.runTest {
+        //given
+        val ringerModeCapture = argumentCaptor<Int>()
+        whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_NORMAL)
+
+        //when
+        val result = underTest.onTriggered(null)
+        runCurrent()
+        verify(audioManager).ringerModeInternal = ringerModeCapture.capture()
+
+        //then
+        assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+        assertEquals(AudioManager.RINGER_MODE_SILENT, ringerModeCapture.value)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartableTest.kt
new file mode 100644
index 0000000..facc747
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartableTest.kt
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.quickaffordance
+
+import android.content.Context
+import android.media.AudioManager
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.Observer
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
+import com.android.systemui.settings.UserFileManager
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.RingerModeTracker
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.cancelChildren
+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.Assert.assertEquals
+
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class MuteQuickAffordanceCoreStartableTest : SysuiTestCase() {
+
+    @Mock
+    private lateinit var featureFlags: FeatureFlags
+    @Mock
+    private lateinit var userTracker: UserTracker
+    @Mock
+    private lateinit var ringerModeTracker: RingerModeTracker
+    @Mock
+    private lateinit var userFileManager: UserFileManager
+    @Mock
+    private lateinit var keyguardQuickAffordanceRepository: KeyguardQuickAffordanceRepository
+
+    private lateinit var testDispatcher: TestDispatcher
+    private lateinit var testScope: TestScope
+
+    private lateinit var underTest: MuteQuickAffordanceCoreStartable
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        whenever(featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES)).thenReturn(true)
+
+        val config: KeyguardQuickAffordanceConfig = mock()
+        whenever(config.key).thenReturn(BuiltInKeyguardQuickAffordanceKeys.MUTE)
+
+        val emission = MutableStateFlow(mapOf("testQuickAffordanceKey" to listOf(config)))
+        whenever(keyguardQuickAffordanceRepository.selections).thenReturn(emission)
+
+        testDispatcher = StandardTestDispatcher()
+        testScope = TestScope(testDispatcher)
+
+        underTest = MuteQuickAffordanceCoreStartable(
+            featureFlags,
+            userTracker,
+            ringerModeTracker,
+            userFileManager,
+            keyguardQuickAffordanceRepository,
+            testScope.backgroundScope,
+            testDispatcher,
+        )
+    }
+
+    @Test
+    fun `feature flag is OFF - do nothing with keyguardQuickAffordanceRepository`() = testScope.runTest {
+        //given
+        whenever(featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES)).thenReturn(false)
+
+        //when
+        underTest.start()
+
+        //then
+        verifyZeroInteractions(keyguardQuickAffordanceRepository)
+        coroutineContext.cancelChildren()
+    }
+
+    @Test
+    fun `feature flag is ON - call to keyguardQuickAffordanceRepository`() = testScope.runTest {
+        //given
+        val ringerModeInternal = mock<MutableLiveData<Int>>()
+        whenever(ringerModeTracker.ringerModeInternal).thenReturn(ringerModeInternal)
+
+        //when
+        underTest.start()
+        runCurrent()
+
+        //then
+        verify(keyguardQuickAffordanceRepository).selections
+        coroutineContext.cancelChildren()
+    }
+
+    @Test
+    fun `ringer mode is changed to SILENT - do not save to shared preferences`() = testScope.runTest {
+        //given
+        val ringerModeInternal = mock<MutableLiveData<Int>>()
+        val observerCaptor = argumentCaptor<Observer<Int>>()
+        whenever(ringerModeTracker.ringerModeInternal).thenReturn(ringerModeInternal)
+
+        //when
+        underTest.start()
+        runCurrent()
+        verify(ringerModeInternal).observeForever(observerCaptor.capture())
+        observerCaptor.value.onChanged(AudioManager.RINGER_MODE_SILENT)
+
+        //then
+        verifyZeroInteractions(userFileManager)
+        coroutineContext.cancelChildren()
+    }
+
+    @Test
+    fun `ringerModeInternal changes to something not SILENT - is set in sharedpreferences`() = testScope.runTest {
+        //given
+        val newRingerMode = 99
+        val observerCaptor = argumentCaptor<Observer<Int>>()
+        val ringerModeInternal = mock<MutableLiveData<Int>>()
+        val sharedPrefs = context.getSharedPreferences("quick_affordance_mute_ringer_mode_cache_test", Context.MODE_PRIVATE)
+        whenever(ringerModeTracker.ringerModeInternal).thenReturn(ringerModeInternal)
+        whenever(
+            userFileManager.getSharedPreferences(eq("quick_affordance_mute_ringer_mode_cache"), any(), any())
+        ).thenReturn(sharedPrefs)
+
+        //when
+        underTest.start()
+        runCurrent()
+        verify(ringerModeInternal).observeForever(observerCaptor.capture())
+        observerCaptor.value.onChanged(newRingerMode)
+        runCurrent()
+        val result = sharedPrefs.getInt("key_last_non_silent_ringer_mode", -1)
+
+        //then
+        assertEquals(newRingerMode, result)
+        coroutineContext.cancelChildren()
+    }
+
+    @Test
+    fun `MUTE is in selections - observe ringerModeInternal`() = testScope.runTest {
+        //given
+        val ringerModeInternal = mock<MutableLiveData<Int>>()
+        whenever(ringerModeTracker.ringerModeInternal).thenReturn(ringerModeInternal)
+
+        //when
+        underTest.start()
+        runCurrent()
+
+        //then
+        verify(ringerModeInternal).observeForever(any())
+        coroutineContext.cancelChildren()
+    }
+
+    @Test
+    fun `MUTE is in selections 2x - observe ringerModeInternal`() = testScope.runTest {
+        //given
+        val config: KeyguardQuickAffordanceConfig = mock()
+        whenever(config.key).thenReturn(BuiltInKeyguardQuickAffordanceKeys.MUTE)
+        val emission = MutableStateFlow(mapOf("testKey" to listOf(config), "testkey2" to listOf(config)))
+        whenever(keyguardQuickAffordanceRepository.selections).thenReturn(emission)
+        val ringerModeInternal = mock<MutableLiveData<Int>>()
+        whenever(ringerModeTracker.ringerModeInternal).thenReturn(ringerModeInternal)
+
+        //when
+        underTest.start()
+        runCurrent()
+
+        //then
+        verify(ringerModeInternal).observeForever(any())
+        coroutineContext.cancelChildren()
+    }
+
+    @Test
+    fun `MUTE is not in selections - stop observing ringerModeInternal`() = testScope.runTest {
+        //given
+        val config: KeyguardQuickAffordanceConfig = mock()
+        whenever(config.key).thenReturn("notmutequickaffordance")
+        val emission = MutableStateFlow(mapOf("testKey" to listOf(config)))
+        whenever(keyguardQuickAffordanceRepository.selections).thenReturn(emission)
+        val ringerModeInternal = mock<MutableLiveData<Int>>()
+        whenever(ringerModeTracker.ringerModeInternal).thenReturn(ringerModeInternal)
+
+        //when
+        underTest.start()
+        runCurrent()
+
+        //then
+        verify(ringerModeInternal).removeObserver(any())
+        coroutineContext.cancelChildren()
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
index 9d2ddff..1adf808 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
@@ -18,6 +18,7 @@
 package com.android.systemui.keyguard.data.quickaffordance
 
 import android.content.Intent
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnTriggeredResult
@@ -33,13 +34,12 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class QrCodeScannerKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
 
     @Mock private lateinit var controller: QRCodeScannerController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
index 8f56b95..752963f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
@@ -20,6 +20,7 @@
 import android.graphics.drawable.Drawable
 import android.service.quickaccesswallet.GetWalletCardsResponse
 import android.service.quickaccesswallet.QuickAccessWalletClient
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
@@ -41,14 +42,13 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
 
     @Mock private lateinit var walletController: QuickAccessWalletController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
index 805dcec..f1b9c5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
@@ -17,22 +17,26 @@
 
 package com.android.systemui.keyguard.data.quickaffordance
 
+import android.app.admin.DevicePolicyManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.ActivityIntentHelper
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.camera.CameraIntentsWrapper
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.settings.UserTracker
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mock
@@ -40,63 +44,98 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class VideoCameraQuickAffordanceConfigTest : SysuiTestCase() {
 
     @Mock private lateinit var activityIntentHelper: ActivityIntentHelper
+    @Mock private lateinit var devicePolicyManager: DevicePolicyManager
 
     private lateinit var underTest: VideoCameraQuickAffordanceConfig
+    private lateinit var userTracker: UserTracker
+    private lateinit var testScope: TestScope
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
+        val testDispatcher = StandardTestDispatcher()
+        testScope = TestScope(testDispatcher)
+        userTracker = FakeUserTracker()
         underTest =
             VideoCameraQuickAffordanceConfig(
                 context = context,
                 cameraIntents = CameraIntentsWrapper(context),
                 activityIntentHelper = activityIntentHelper,
-                userTracker = FakeUserTracker(),
+                userTracker = userTracker,
+                devicePolicyManager = devicePolicyManager,
+                backgroundDispatcher = testDispatcher,
             )
     }
 
     @Test
-    fun `lockScreenState - visible when launchable`() = runTest {
-        setLaunchable(true)
+    fun `lockScreenState - visible when launchable`() =
+        testScope.runTest {
+            setLaunchable()
 
-        val lockScreenState = collectLastValue(underTest.lockScreenState)
+            val lockScreenState = collectLastValue(underTest.lockScreenState)
 
-        assertThat(lockScreenState())
-            .isInstanceOf(KeyguardQuickAffordanceConfig.LockScreenState.Visible::class.java)
-    }
+            assertThat(lockScreenState())
+                .isInstanceOf(KeyguardQuickAffordanceConfig.LockScreenState.Visible::class.java)
+        }
 
     @Test
-    fun `lockScreenState - hidden when not launchable`() = runTest {
-        setLaunchable(false)
+    fun `lockScreenState - hidden when app not installed on device`() =
+        testScope.runTest {
+            setLaunchable(isVideoCameraAppInstalled = false)
 
-        val lockScreenState = collectLastValue(underTest.lockScreenState)
+            val lockScreenState = collectLastValue(underTest.lockScreenState)
 
-        assertThat(lockScreenState())
-            .isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
-    }
+            assertThat(lockScreenState())
+                .isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
+        }
 
     @Test
-    fun `getPickerScreenState - default when launchable`() = runTest {
-        setLaunchable(true)
+    fun `lockScreenState - hidden when camera disabled by admin`() =
+        testScope.runTest {
+            setLaunchable(isCameraDisabledByAdmin = true)
 
-        assertThat(underTest.getPickerScreenState())
-            .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Default::class.java)
-    }
+            val lockScreenState = collectLastValue(underTest.lockScreenState)
+
+            assertThat(lockScreenState())
+                .isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
+        }
 
     @Test
-    fun `getPickerScreenState - unavailable when not launchable`() = runTest {
-        setLaunchable(false)
+    fun `getPickerScreenState - default when launchable`() =
+        testScope.runTest {
+            setLaunchable()
 
-        assertThat(underTest.getPickerScreenState())
-            .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
-    }
+            assertThat(underTest.getPickerScreenState())
+                .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Default::class.java)
+        }
 
-    private fun setLaunchable(isLaunchable: Boolean) {
+    @Test
+    fun `getPickerScreenState - unavailable when app not installed on device`() =
+        testScope.runTest {
+            setLaunchable(isVideoCameraAppInstalled = false)
+
+            assertThat(underTest.getPickerScreenState())
+                .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
+        }
+
+    @Test
+    fun `getPickerScreenState - unavailable when camera disabled by admin`() =
+        testScope.runTest {
+            setLaunchable(isCameraDisabledByAdmin = true)
+
+            assertThat(underTest.getPickerScreenState())
+                .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
+        }
+
+    private fun setLaunchable(
+        isVideoCameraAppInstalled: Boolean = true,
+        isCameraDisabledByAdmin: Boolean = false,
+    ) {
         whenever(
                 activityIntentHelper.getTargetActivityInfo(
                     any(),
@@ -105,11 +144,13 @@
                 )
             )
             .thenReturn(
-                if (isLaunchable) {
+                if (isVideoCameraAppInstalled) {
                     mock()
                 } else {
                     null
                 }
             )
+        whenever(devicePolicyManager.getCameraDisabled(null, userTracker.userId))
+            .thenReturn(isCameraDisabledByAdmin)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricRepositoryTest.kt
deleted file mode 100644
index a92dd3b..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricRepositoryTest.kt
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.systemui.keyguard.data.repository
-
-import android.app.admin.DevicePolicyManager
-import android.content.Intent
-import android.content.pm.UserInfo
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import androidx.test.filters.SmallTest
-import com.android.internal.widget.LockPatternUtils
-import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED
-import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.biometrics.AuthController
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.util.mockito.argumentCaptor
-import com.android.systemui.util.mockito.whenever
-import com.google.common.truth.Truth.assertThat
-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.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.Mock
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-@RunWith(AndroidTestingRunner::class)
-class BiometricRepositoryTest : SysuiTestCase() {
-    private lateinit var underTest: BiometricRepository
-
-    @Mock private lateinit var authController: AuthController
-    @Mock private lateinit var lockPatternUtils: LockPatternUtils
-    @Mock private lateinit var devicePolicyManager: DevicePolicyManager
-    private lateinit var userRepository: FakeUserRepository
-
-    private lateinit var testDispatcher: TestDispatcher
-    private lateinit var testScope: TestScope
-    private var testableLooper: TestableLooper? = null
-
-    @Before
-    fun setUp() {
-        MockitoAnnotations.initMocks(this)
-        testableLooper = TestableLooper.get(this)
-        testDispatcher = StandardTestDispatcher()
-        testScope = TestScope(testDispatcher)
-        userRepository = FakeUserRepository()
-    }
-
-    private suspend fun createBiometricRepository() {
-        userRepository.setUserInfos(listOf(PRIMARY_USER))
-        userRepository.setSelectedUserInfo(PRIMARY_USER)
-        underTest =
-            BiometricRepositoryImpl(
-                context = context,
-                lockPatternUtils = lockPatternUtils,
-                broadcastDispatcher = fakeBroadcastDispatcher,
-                authController = authController,
-                userRepository = userRepository,
-                devicePolicyManager = devicePolicyManager,
-                scope = testScope.backgroundScope,
-                backgroundDispatcher = testDispatcher,
-                looper = testableLooper!!.looper,
-            )
-    }
-
-    @Test
-    fun fingerprintEnrollmentChange() =
-        testScope.runTest {
-            createBiometricRepository()
-            val fingerprintEnabledByDevicePolicy = collectLastValue(underTest.isFingerprintEnrolled)
-            runCurrent()
-
-            val captor = argumentCaptor<AuthController.Callback>()
-            verify(authController).addCallback(captor.capture())
-            whenever(authController.isFingerprintEnrolled(anyInt())).thenReturn(true)
-            captor.value.onEnrollmentsChanged(
-                BiometricType.UNDER_DISPLAY_FINGERPRINT,
-                PRIMARY_USER_ID,
-                true
-            )
-            assertThat(fingerprintEnabledByDevicePolicy()).isTrue()
-
-            whenever(authController.isFingerprintEnrolled(anyInt())).thenReturn(false)
-            captor.value.onEnrollmentsChanged(
-                BiometricType.UNDER_DISPLAY_FINGERPRINT,
-                PRIMARY_USER_ID,
-                false
-            )
-            assertThat(fingerprintEnabledByDevicePolicy()).isFalse()
-        }
-
-    @Test
-    fun strongBiometricAllowedChange() =
-        testScope.runTest {
-            createBiometricRepository()
-            val strongBiometricAllowed = collectLastValue(underTest.isStrongBiometricAllowed)
-            runCurrent()
-
-            val captor = argumentCaptor<LockPatternUtils.StrongAuthTracker>()
-            verify(lockPatternUtils).registerStrongAuthTracker(captor.capture())
-
-            captor.value
-                .getStub()
-                .onStrongAuthRequiredChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID)
-            testableLooper?.processAllMessages() // StrongAuthTracker uses the TestableLooper
-            assertThat(strongBiometricAllowed()).isTrue()
-
-            captor.value
-                .getStub()
-                .onStrongAuthRequiredChanged(STRONG_AUTH_REQUIRED_AFTER_BOOT, PRIMARY_USER_ID)
-            testableLooper?.processAllMessages() // StrongAuthTracker uses the TestableLooper
-            assertThat(strongBiometricAllowed()).isFalse()
-        }
-
-    @Test
-    fun fingerprintDisabledByDpmChange() =
-        testScope.runTest {
-            createBiometricRepository()
-            val fingerprintEnabledByDevicePolicy =
-                collectLastValue(underTest.isFingerprintEnabledByDevicePolicy)
-            runCurrent()
-
-            whenever(devicePolicyManager.getKeyguardDisabledFeatures(any(), anyInt()))
-                .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT)
-            broadcastDPMStateChange()
-            assertThat(fingerprintEnabledByDevicePolicy()).isFalse()
-
-            whenever(devicePolicyManager.getKeyguardDisabledFeatures(any(), anyInt())).thenReturn(0)
-            broadcastDPMStateChange()
-            assertThat(fingerprintEnabledByDevicePolicy()).isTrue()
-        }
-
-    private fun broadcastDPMStateChange() {
-        fakeBroadcastDispatcher.registeredReceivers.forEach { receiver ->
-            receiver.onReceive(
-                context,
-                Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED)
-            )
-        }
-    }
-
-    companion object {
-        private const val PRIMARY_USER_ID = 0
-        private val PRIMARY_USER =
-            UserInfo(
-                /* id= */ PRIMARY_USER_ID,
-                /* name= */ "primary user",
-                /* flags= */ UserInfo.FLAG_PRIMARY
-            )
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
new file mode 100644
index 0000000..21ad5e2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import android.app.admin.DevicePolicyManager
+import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FACE
+import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT
+import android.content.Intent
+import android.content.pm.UserInfo
+import android.hardware.biometrics.BiometricManager
+import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.widget.LockPatternUtils
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.AuthController
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.data.repository.BiometricType.FACE
+import com.android.systemui.keyguard.data.repository.BiometricType.REAR_FINGERPRINT
+import com.android.systemui.keyguard.data.repository.BiometricType.SIDE_FINGERPRINT
+import com.android.systemui.keyguard.data.repository.BiometricType.UNDER_DISPLAY_FINGERPRINT
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+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.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.isNull
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@RunWith(AndroidTestingRunner::class)
+class BiometricSettingsRepositoryTest : SysuiTestCase() {
+    private lateinit var underTest: BiometricSettingsRepository
+
+    @Mock private lateinit var authController: AuthController
+    @Mock private lateinit var lockPatternUtils: LockPatternUtils
+    @Mock private lateinit var devicePolicyManager: DevicePolicyManager
+    @Mock private lateinit var dumpManager: DumpManager
+    @Mock private lateinit var biometricManager: BiometricManager
+    @Captor private lateinit var authControllerCallback: ArgumentCaptor<AuthController.Callback>
+    @Captor
+    private lateinit var biometricManagerCallback:
+        ArgumentCaptor<IBiometricEnabledOnKeyguardCallback.Stub>
+    private lateinit var userRepository: FakeUserRepository
+
+    private lateinit var testDispatcher: TestDispatcher
+    private lateinit var testScope: TestScope
+    private var testableLooper: TestableLooper? = null
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        testableLooper = TestableLooper.get(this)
+        testDispatcher = StandardTestDispatcher()
+        testScope = TestScope(testDispatcher)
+        userRepository = FakeUserRepository()
+    }
+
+    private suspend fun createBiometricSettingsRepository() {
+        userRepository.setUserInfos(listOf(PRIMARY_USER, ANOTHER_USER))
+        userRepository.setSelectedUserInfo(PRIMARY_USER)
+        underTest =
+            BiometricSettingsRepositoryImpl(
+                context = context,
+                lockPatternUtils = lockPatternUtils,
+                broadcastDispatcher = fakeBroadcastDispatcher,
+                authController = authController,
+                userRepository = userRepository,
+                devicePolicyManager = devicePolicyManager,
+                scope = testScope.backgroundScope,
+                backgroundDispatcher = testDispatcher,
+                looper = testableLooper!!.looper,
+                dumpManager = dumpManager,
+                biometricManager = biometricManager,
+            )
+        testScope.runCurrent()
+    }
+
+    @Test
+    fun fingerprintEnrollmentChange() =
+        testScope.runTest {
+            createBiometricSettingsRepository()
+            val fingerprintEnrolled = collectLastValue(underTest.isFingerprintEnrolled)
+            runCurrent()
+
+            verify(authController).addCallback(authControllerCallback.capture())
+            whenever(authController.isFingerprintEnrolled(anyInt())).thenReturn(true)
+            enrollmentChange(UNDER_DISPLAY_FINGERPRINT, PRIMARY_USER_ID, true)
+            assertThat(fingerprintEnrolled()).isTrue()
+
+            whenever(authController.isFingerprintEnrolled(anyInt())).thenReturn(false)
+            enrollmentChange(UNDER_DISPLAY_FINGERPRINT, ANOTHER_USER_ID, false)
+            assertThat(fingerprintEnrolled()).isTrue()
+
+            enrollmentChange(UNDER_DISPLAY_FINGERPRINT, PRIMARY_USER_ID, false)
+            assertThat(fingerprintEnrolled()).isFalse()
+        }
+
+    @Test
+    fun strongBiometricAllowedChange() =
+        testScope.runTest {
+            createBiometricSettingsRepository()
+            val strongBiometricAllowed = collectLastValue(underTest.isStrongBiometricAllowed)
+            runCurrent()
+
+            val captor = argumentCaptor<LockPatternUtils.StrongAuthTracker>()
+            verify(lockPatternUtils).registerStrongAuthTracker(captor.capture())
+
+            captor.value.stub.onStrongAuthRequiredChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID)
+            testableLooper?.processAllMessages() // StrongAuthTracker uses the TestableLooper
+            assertThat(strongBiometricAllowed()).isTrue()
+
+            captor.value.stub.onStrongAuthRequiredChanged(
+                STRONG_AUTH_REQUIRED_AFTER_BOOT,
+                PRIMARY_USER_ID
+            )
+            testableLooper?.processAllMessages() // StrongAuthTracker uses the TestableLooper
+            assertThat(strongBiometricAllowed()).isFalse()
+        }
+
+    @Test
+    fun fingerprintDisabledByDpmChange() =
+        testScope.runTest {
+            createBiometricSettingsRepository()
+            val fingerprintEnabledByDevicePolicy =
+                collectLastValue(underTest.isFingerprintEnabledByDevicePolicy)
+            runCurrent()
+
+            whenever(devicePolicyManager.getKeyguardDisabledFeatures(any(), anyInt()))
+                .thenReturn(KEYGUARD_DISABLE_FINGERPRINT)
+            broadcastDPMStateChange()
+            assertThat(fingerprintEnabledByDevicePolicy()).isFalse()
+
+            whenever(devicePolicyManager.getKeyguardDisabledFeatures(any(), anyInt())).thenReturn(0)
+            broadcastDPMStateChange()
+            assertThat(fingerprintEnabledByDevicePolicy()).isTrue()
+        }
+
+    @Test
+    fun faceEnrollmentChangeIsPropagatedForTheCurrentUser() =
+        testScope.runTest {
+            createBiometricSettingsRepository()
+            runCurrent()
+            clearInvocations(authController)
+
+            whenever(authController.isFaceAuthEnrolled(PRIMARY_USER_ID)).thenReturn(false)
+            val faceEnrolled = collectLastValue(underTest.isFaceEnrolled)
+
+            assertThat(faceEnrolled()).isFalse()
+            verify(authController).addCallback(authControllerCallback.capture())
+            enrollmentChange(REAR_FINGERPRINT, PRIMARY_USER_ID, true)
+
+            assertThat(faceEnrolled()).isFalse()
+
+            enrollmentChange(SIDE_FINGERPRINT, PRIMARY_USER_ID, true)
+
+            assertThat(faceEnrolled()).isFalse()
+
+            enrollmentChange(UNDER_DISPLAY_FINGERPRINT, PRIMARY_USER_ID, true)
+
+            assertThat(faceEnrolled()).isFalse()
+
+            enrollmentChange(FACE, ANOTHER_USER_ID, true)
+
+            assertThat(faceEnrolled()).isFalse()
+
+            enrollmentChange(FACE, PRIMARY_USER_ID, true)
+
+            assertThat(faceEnrolled()).isTrue()
+        }
+
+    @Test
+    fun faceEnrollmentStatusOfNewUserUponUserSwitch() =
+        testScope.runTest {
+            createBiometricSettingsRepository()
+            runCurrent()
+            clearInvocations(authController)
+
+            whenever(authController.isFaceAuthEnrolled(PRIMARY_USER_ID)).thenReturn(false)
+            whenever(authController.isFaceAuthEnrolled(ANOTHER_USER_ID)).thenReturn(true)
+            val faceEnrolled = collectLastValue(underTest.isFaceEnrolled)
+
+            assertThat(faceEnrolled()).isFalse()
+        }
+
+    @Test
+    fun faceEnrollmentChangesArePropagatedAfterUserSwitch() =
+        testScope.runTest {
+            createBiometricSettingsRepository()
+
+            userRepository.setSelectedUserInfo(ANOTHER_USER)
+            runCurrent()
+            clearInvocations(authController)
+
+            val faceEnrolled = collectLastValue(underTest.isFaceEnrolled)
+            runCurrent()
+
+            verify(authController).addCallback(authControllerCallback.capture())
+
+            enrollmentChange(FACE, ANOTHER_USER_ID, true)
+
+            assertThat(faceEnrolled()).isTrue()
+        }
+
+    @Test
+    fun devicePolicyControlsFaceAuthenticationEnabledState() =
+        testScope.runTest {
+            createBiometricSettingsRepository()
+            verify(biometricManager)
+                .registerEnabledOnKeyguardCallback(biometricManagerCallback.capture())
+
+            whenever(devicePolicyManager.getKeyguardDisabledFeatures(isNull(), eq(PRIMARY_USER_ID)))
+                .thenReturn(KEYGUARD_DISABLE_FINGERPRINT or KEYGUARD_DISABLE_FACE)
+
+            val isFaceAuthEnabled = collectLastValue(underTest.isFaceAuthenticationEnabled)
+            runCurrent()
+
+            broadcastDPMStateChange()
+
+            assertThat(isFaceAuthEnabled()).isFalse()
+
+            biometricManagerCallback.value.onChanged(true, PRIMARY_USER_ID)
+            runCurrent()
+            assertThat(isFaceAuthEnabled()).isFalse()
+
+            whenever(devicePolicyManager.getKeyguardDisabledFeatures(isNull(), eq(PRIMARY_USER_ID)))
+                .thenReturn(KEYGUARD_DISABLE_FINGERPRINT)
+            broadcastDPMStateChange()
+
+            assertThat(isFaceAuthEnabled()).isTrue()
+        }
+
+    @Test
+    fun biometricManagerControlsFaceAuthenticationEnabledStatus() =
+        testScope.runTest {
+            createBiometricSettingsRepository()
+            verify(biometricManager)
+                .registerEnabledOnKeyguardCallback(biometricManagerCallback.capture())
+
+            whenever(devicePolicyManager.getKeyguardDisabledFeatures(isNull(), eq(PRIMARY_USER_ID)))
+                .thenReturn(0)
+            broadcastDPMStateChange()
+
+            biometricManagerCallback.value.onChanged(true, PRIMARY_USER_ID)
+            val isFaceAuthEnabled = collectLastValue(underTest.isFaceAuthenticationEnabled)
+
+            assertThat(isFaceAuthEnabled()).isTrue()
+
+            biometricManagerCallback.value.onChanged(false, PRIMARY_USER_ID)
+
+            assertThat(isFaceAuthEnabled()).isFalse()
+        }
+
+    @Test
+    fun biometricManagerCallbackIsRegisteredOnlyOnce() =
+        testScope.runTest {
+            createBiometricSettingsRepository()
+
+            collectLastValue(underTest.isFaceAuthenticationEnabled)()
+            collectLastValue(underTest.isFaceAuthenticationEnabled)()
+            collectLastValue(underTest.isFaceAuthenticationEnabled)()
+
+            verify(biometricManager, times(1)).registerEnabledOnKeyguardCallback(any())
+        }
+
+    private fun enrollmentChange(biometricType: BiometricType, userId: Int, enabled: Boolean) {
+        authControllerCallback.value.onEnrollmentsChanged(biometricType, userId, enabled)
+    }
+
+    private fun broadcastDPMStateChange() {
+        fakeBroadcastDispatcher.registeredReceivers.forEach { receiver ->
+            receiver.onReceive(
+                context,
+                Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED)
+            )
+        }
+    }
+
+    companion object {
+        private const val PRIMARY_USER_ID = 0
+        private val PRIMARY_USER =
+            UserInfo(
+                /* id= */ PRIMARY_USER_ID,
+                /* name= */ "primary user",
+                /* flags= */ UserInfo.FLAG_PRIMARY
+            )
+
+        private const val ANOTHER_USER_ID = 1
+        private val ANOTHER_USER =
+            UserInfo(
+                /* id= */ ANOTHER_USER_ID,
+                /* name= */ "another user",
+                /* flags= */ UserInfo.FLAG_PRIMARY
+            )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
index c4ae2db..0519a44 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
@@ -22,6 +22,7 @@
 import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.dump.DumpManager
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -44,6 +45,7 @@
 @RunWith(JUnit4::class)
 class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() {
     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+    @Mock private lateinit var dumpManager: DumpManager
     @Captor private lateinit var callbackCaptor: ArgumentCaptor<KeyguardUpdateMonitorCallback>
 
     private lateinit var testScope: TestScope
@@ -55,7 +57,12 @@
         MockitoAnnotations.initMocks(this)
         testScope = TestScope()
 
-        underTest = DeviceEntryFingerprintAuthRepositoryImpl(keyguardUpdateMonitor)
+        underTest =
+            DeviceEntryFingerprintAuthRepositoryImpl(
+                keyguardUpdateMonitor,
+                testScope.backgroundScope,
+                dumpManager,
+            )
     }
 
     @After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepositoryTest.kt
index 969537d2..ff22f1e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepositoryTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.data.repository
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.keyguard.ViewMediatorCallback
 import com.android.systemui.SysuiTestCase
@@ -26,13 +27,12 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class KeyguardBouncerRepositoryTest : SysuiTestCase() {
 
     @Mock private lateinit var systemClock: SystemClock
@@ -45,7 +45,7 @@
         MockitoAnnotations.initMocks(this)
         val testCoroutineScope = TestCoroutineScope()
         underTest =
-            KeyguardBouncerRepository(
+            KeyguardBouncerRepositoryImpl(
                 viewMediatorCallback,
                 systemClock,
                 testCoroutineScope,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManagerTest.kt
new file mode 100644
index 0000000..7c604f7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManagerTest.kt
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import android.app.StatusBarManager.SESSION_KEYGUARD
+import android.content.pm.UserInfo
+import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_CANCELED
+import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT
+import android.hardware.biometrics.ComponentInfoInternal
+import android.hardware.face.FaceManager
+import android.hardware.face.FaceSensorProperties
+import android.hardware.face.FaceSensorPropertiesInternal
+import android.os.CancellationSignal
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.InstanceId.fakeInstanceId
+import com.android.internal.logging.UiEventLogger
+import com.android.keyguard.FaceAuthUiEvent
+import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
+import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.FlowValue
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.keyguard.shared.model.AuthenticationStatus
+import com.android.systemui.keyguard.shared.model.DetectionStatus
+import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.HelpAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus
+import com.android.systemui.log.FaceAuthenticationLogger
+import com.android.systemui.log.SessionTracker
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.StringWriter
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.isNull
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class KeyguardFaceAuthManagerTest : SysuiTestCase() {
+    private lateinit var underTest: KeyguardFaceAuthManagerImpl
+
+    @Mock private lateinit var faceManager: FaceManager
+    @Mock private lateinit var bypassController: KeyguardBypassController
+    @Mock private lateinit var sessionTracker: SessionTracker
+    @Mock private lateinit var uiEventLogger: UiEventLogger
+    @Mock private lateinit var dumpManager: DumpManager
+
+    @Captor
+    private lateinit var authenticationCallback: ArgumentCaptor<FaceManager.AuthenticationCallback>
+    @Captor
+    private lateinit var detectionCallback: ArgumentCaptor<FaceManager.FaceDetectionCallback>
+    @Captor private lateinit var cancellationSignal: ArgumentCaptor<CancellationSignal>
+    @Captor
+    private lateinit var faceLockoutResetCallback: ArgumentCaptor<FaceManager.LockoutResetCallback>
+    private lateinit var testDispatcher: TestDispatcher
+
+    private lateinit var testScope: TestScope
+    private lateinit var fakeUserRepository: FakeUserRepository
+    private lateinit var authStatus: FlowValue<AuthenticationStatus?>
+    private lateinit var detectStatus: FlowValue<DetectionStatus?>
+    private lateinit var authRunning: FlowValue<Boolean?>
+    private lateinit var lockedOut: FlowValue<Boolean?>
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        fakeUserRepository = FakeUserRepository()
+        fakeUserRepository.setUserInfos(listOf(currentUser))
+        testDispatcher = StandardTestDispatcher()
+        testScope = TestScope(testDispatcher)
+        whenever(sessionTracker.getSessionId(SESSION_KEYGUARD)).thenReturn(keyguardSessionId)
+        whenever(bypassController.bypassEnabled).thenReturn(true)
+        underTest = createFaceAuthManagerImpl(faceManager)
+    }
+
+    private fun createFaceAuthManagerImpl(
+        fmOverride: FaceManager? = faceManager,
+        bypassControllerOverride: KeyguardBypassController? = bypassController
+    ) =
+        KeyguardFaceAuthManagerImpl(
+            mContext,
+            fmOverride,
+            fakeUserRepository,
+            bypassControllerOverride,
+            testScope.backgroundScope,
+            testDispatcher,
+            sessionTracker,
+            uiEventLogger,
+            FaceAuthenticationLogger(logcatLogBuffer("KeyguardFaceAuthManagerLog")),
+            dumpManager,
+        )
+
+    @Test
+    fun faceAuthRunsAndProvidesAuthStatusUpdates() =
+        testScope.runTest {
+            testSetup(this)
+
+            FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER.extraInfo = 10
+            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            faceAuthenticateIsCalled()
+            uiEventIsLogged(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+
+            assertThat(authRunning()).isTrue()
+
+            val successResult = successResult()
+            authenticationCallback.value.onAuthenticationSucceeded(successResult)
+
+            assertThat(authStatus()).isEqualTo(SuccessAuthenticationStatus(successResult))
+
+            assertThat(authRunning()).isFalse()
+        }
+
+    private fun uiEventIsLogged(faceAuthUiEvent: FaceAuthUiEvent) {
+        verify(uiEventLogger)
+            .logWithInstanceIdAndPosition(
+                faceAuthUiEvent,
+                0,
+                null,
+                keyguardSessionId,
+                faceAuthUiEvent.extraInfo
+            )
+    }
+
+    @Test
+    fun faceAuthDoesNotRunWhileItIsAlreadyRunning() =
+        testScope.runTest {
+            testSetup(this)
+
+            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            faceAuthenticateIsCalled()
+            clearInvocations(faceManager)
+            clearInvocations(uiEventLogger)
+
+            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            verifyNoMoreInteractions(faceManager)
+            verifyNoMoreInteractions(uiEventLogger)
+        }
+
+    @Test
+    fun faceLockoutStatusIsPropagated() =
+        testScope.runTest {
+            testSetup(this)
+            verify(faceManager).addLockoutResetCallback(faceLockoutResetCallback.capture())
+
+            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            faceAuthenticateIsCalled()
+
+            authenticationCallback.value.onAuthenticationError(
+                FACE_ERROR_LOCKOUT_PERMANENT,
+                "face locked out"
+            )
+
+            assertThat(lockedOut()).isTrue()
+
+            faceLockoutResetCallback.value.onLockoutReset(0)
+            assertThat(lockedOut()).isFalse()
+        }
+
+    @Test
+    fun faceDetectionSupportIsTheCorrectValue() =
+        testScope.runTest {
+            assertThat(createFaceAuthManagerImpl(fmOverride = null).isDetectionSupported).isFalse()
+
+            whenever(faceManager.sensorPropertiesInternal).thenReturn(null)
+            assertThat(createFaceAuthManagerImpl().isDetectionSupported).isFalse()
+
+            whenever(faceManager.sensorPropertiesInternal).thenReturn(listOf())
+            assertThat(createFaceAuthManagerImpl().isDetectionSupported).isFalse()
+
+            whenever(faceManager.sensorPropertiesInternal)
+                .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = false)))
+            assertThat(createFaceAuthManagerImpl().isDetectionSupported).isFalse()
+
+            whenever(faceManager.sensorPropertiesInternal)
+                .thenReturn(
+                    listOf(
+                        createFaceSensorProperties(supportsFaceDetection = false),
+                        createFaceSensorProperties(supportsFaceDetection = true)
+                    )
+                )
+            assertThat(createFaceAuthManagerImpl().isDetectionSupported).isFalse()
+
+            whenever(faceManager.sensorPropertiesInternal)
+                .thenReturn(
+                    listOf(
+                        createFaceSensorProperties(supportsFaceDetection = true),
+                        createFaceSensorProperties(supportsFaceDetection = false)
+                    )
+                )
+            assertThat(createFaceAuthManagerImpl().isDetectionSupported).isTrue()
+        }
+
+    @Test
+    fun cancelStopsFaceAuthentication() =
+        testScope.runTest {
+            testSetup(this)
+
+            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            faceAuthenticateIsCalled()
+
+            var wasAuthCancelled = false
+            cancellationSignal.value.setOnCancelListener { wasAuthCancelled = true }
+
+            underTest.cancel()
+            assertThat(wasAuthCancelled).isTrue()
+            assertThat(authRunning()).isFalse()
+        }
+
+    @Test
+    fun cancelInvokedWithoutFaceAuthRunningIsANoop() = testScope.runTest { underTest.cancel() }
+
+    @Test
+    fun faceDetectionRunsAndPropagatesDetectionStatus() =
+        testScope.runTest {
+            whenever(faceManager.sensorPropertiesInternal)
+                .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = true)))
+            underTest = createFaceAuthManagerImpl()
+            testSetup(this)
+
+            underTest.detect()
+            faceDetectIsCalled()
+
+            detectionCallback.value.onFaceDetected(1, 1, true)
+
+            assertThat(detectStatus()).isEqualTo(DetectionStatus(1, 1, true))
+        }
+
+    @Test
+    fun faceDetectDoesNotRunIfDetectionIsNotSupported() =
+        testScope.runTest {
+            whenever(faceManager.sensorPropertiesInternal)
+                .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = false)))
+            underTest = createFaceAuthManagerImpl()
+            testSetup(this)
+            clearInvocations(faceManager)
+
+            underTest.detect()
+
+            verify(faceManager, never()).detectFace(any(), any(), anyInt())
+        }
+
+    @Test
+    fun faceAuthShouldWaitAndRunIfTriggeredWhileCancelling() =
+        testScope.runTest {
+            testSetup(this)
+
+            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            faceAuthenticateIsCalled()
+
+            // Enter cancelling state
+            underTest.cancel()
+            clearInvocations(faceManager)
+
+            // Auth is while cancelling.
+            underTest.authenticate(FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN)
+            // Auth is not started
+            verifyNoMoreInteractions(faceManager)
+
+            // Auth is done cancelling.
+            authenticationCallback.value.onAuthenticationError(
+                FACE_ERROR_CANCELED,
+                "First auth attempt cancellation completed"
+            )
+            assertThat(authStatus())
+                .isEqualTo(
+                    ErrorAuthenticationStatus(
+                        FACE_ERROR_CANCELED,
+                        "First auth attempt cancellation completed"
+                    )
+                )
+
+            faceAuthenticateIsCalled()
+            uiEventIsLogged(FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN)
+        }
+
+    @Test
+    fun faceAuthAutoCancelsAfterDefaultCancellationTimeout() =
+        testScope.runTest {
+            testSetup(this)
+
+            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            faceAuthenticateIsCalled()
+
+            clearInvocations(faceManager)
+            underTest.cancel()
+            advanceTimeBy(KeyguardFaceAuthManagerImpl.DEFAULT_CANCEL_SIGNAL_TIMEOUT + 1)
+
+            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            faceAuthenticateIsCalled()
+        }
+
+    @Test
+    fun faceHelpMessagesAreIgnoredBasedOnConfig() =
+        testScope.runTest {
+            overrideResource(
+                R.array.config_face_acquire_device_entry_ignorelist,
+                intArrayOf(10, 11)
+            )
+            underTest = createFaceAuthManagerImpl()
+            testSetup(this)
+
+            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            faceAuthenticateIsCalled()
+
+            authenticationCallback.value.onAuthenticationHelp(9, "help msg")
+            authenticationCallback.value.onAuthenticationHelp(10, "Ignored help msg")
+            authenticationCallback.value.onAuthenticationHelp(11, "Ignored help msg")
+
+            assertThat(authStatus()).isEqualTo(HelpAuthenticationStatus(9, "help msg"))
+        }
+
+    @Test
+    fun dumpDoesNotErrorOutWhenFaceManagerOrBypassControllerIsNull() =
+        testScope.runTest {
+            fakeUserRepository.setSelectedUserInfo(currentUser)
+            underTest.dump(PrintWriter(StringWriter()), emptyArray())
+
+            underTest =
+                createFaceAuthManagerImpl(fmOverride = null, bypassControllerOverride = null)
+            fakeUserRepository.setSelectedUserInfo(currentUser)
+
+            underTest.dump(PrintWriter(StringWriter()), emptyArray())
+        }
+
+    private suspend fun testSetup(testScope: TestScope) {
+        with(testScope) {
+            authStatus = collectLastValue(underTest.authenticationStatus)
+            detectStatus = collectLastValue(underTest.detectionStatus)
+            authRunning = collectLastValue(underTest.isAuthRunning)
+            lockedOut = collectLastValue(underTest.isLockedOut)
+            fakeUserRepository.setSelectedUserInfo(currentUser)
+        }
+    }
+
+    private fun successResult() = FaceManager.AuthenticationResult(null, null, currentUserId, false)
+
+    private fun faceDetectIsCalled() {
+        verify(faceManager)
+            .detectFace(
+                cancellationSignal.capture(),
+                detectionCallback.capture(),
+                eq(currentUserId)
+            )
+    }
+
+    private fun faceAuthenticateIsCalled() {
+        verify(faceManager)
+            .authenticate(
+                isNull(),
+                cancellationSignal.capture(),
+                authenticationCallback.capture(),
+                isNull(),
+                eq(currentUserId),
+                eq(true)
+            )
+    }
+
+    private fun createFaceSensorProperties(
+        supportsFaceDetection: Boolean
+    ): FaceSensorPropertiesInternal {
+        val componentInfo =
+            listOf(
+                ComponentInfoInternal(
+                    "faceSensor" /* componentId */,
+                    "vendor/model/revision" /* hardwareVersion */,
+                    "1.01" /* firmwareVersion */,
+                    "00000001" /* serialNumber */,
+                    "" /* softwareVersion */
+                )
+            )
+        return FaceSensorPropertiesInternal(
+            0 /* id */,
+            FaceSensorProperties.STRENGTH_STRONG,
+            1 /* maxTemplatesAllowed */,
+            componentInfo,
+            FaceSensorProperties.TYPE_UNKNOWN,
+            supportsFaceDetection /* supportsFaceDetection */,
+            true /* supportsSelfIllumination */,
+            false /* resetLockoutRequiresChallenge */
+        )
+    }
+
+    companion object {
+        const val currentUserId = 1
+        val keyguardSessionId = fakeInstanceId(10)!!
+        val currentUser = UserInfo(currentUserId, "test user", 0)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
index b071a02..86e8c9a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
@@ -19,9 +19,11 @@
 
 import android.content.pm.UserInfo
 import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
 import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
@@ -39,23 +41,19 @@
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.settings.FakeSettings
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.yield
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.anyString
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() {
 
     private lateinit var underTest: KeyguardQuickAffordanceRepository
@@ -65,12 +63,14 @@
     private lateinit var userTracker: FakeUserTracker
     private lateinit var client1: FakeCustomizationProviderClient
     private lateinit var client2: FakeCustomizationProviderClient
+    private lateinit var testScope: TestScope
 
     @Before
     fun setUp() {
         config1 = FakeKeyguardQuickAffordanceConfig(FakeCustomizationProviderClient.AFFORDANCE_1)
         config2 = FakeKeyguardQuickAffordanceConfig(FakeCustomizationProviderClient.AFFORDANCE_2)
-        val scope = CoroutineScope(IMMEDIATE)
+        val testDispatcher = StandardTestDispatcher()
+        testScope = TestScope(testDispatcher)
         userTracker = FakeUserTracker()
         val localUserSelectionManager =
             KeyguardQuickAffordanceLocalUserSelectionManager(
@@ -93,7 +93,7 @@
         client2 = FakeCustomizationProviderClient()
         val remoteUserSelectionManager =
             KeyguardQuickAffordanceRemoteUserSelectionManager(
-                scope = scope,
+                scope = testScope.backgroundScope,
                 userTracker = userTracker,
                 clientFactory =
                     FakeKeyguardQuickAffordanceProviderClientFactory(
@@ -116,14 +116,14 @@
         underTest =
             KeyguardQuickAffordanceRepository(
                 appContext = context,
-                scope = scope,
+                scope = testScope.backgroundScope,
                 localUserSelectionManager = localUserSelectionManager,
                 remoteUserSelectionManager = remoteUserSelectionManager,
                 userTracker = userTracker,
                 legacySettingSyncer =
                     KeyguardQuickAffordanceLegacySettingSyncer(
-                        scope = scope,
-                        backgroundDispatcher = IMMEDIATE,
+                        scope = testScope.backgroundScope,
+                        backgroundDispatcher = testDispatcher,
                         secureSettings = FakeSettings(),
                         selectionsManager = localUserSelectionManager,
                     ),
@@ -135,15 +135,14 @@
 
     @Test
     fun setSelections() =
-        runBlocking(IMMEDIATE) {
-            var configsBySlotId: Map<String, List<KeyguardQuickAffordanceConfig>>? = null
-            val job = underTest.selections.onEach { configsBySlotId = it }.launchIn(this)
+        testScope.runTest {
+            val configsBySlotId = collectLastValue(underTest.selections)
             val slotId1 = "slot1"
             val slotId2 = "slot2"
 
             underTest.setSelections(slotId1, listOf(config1.key))
             assertSelections(
-                configsBySlotId,
+                configsBySlotId(),
                 mapOf(
                     slotId1 to listOf(config1),
                 ),
@@ -151,7 +150,7 @@
 
             underTest.setSelections(slotId2, listOf(config2.key))
             assertSelections(
-                configsBySlotId,
+                configsBySlotId(),
                 mapOf(
                     slotId1 to listOf(config1),
                     slotId2 to listOf(config2),
@@ -161,19 +160,17 @@
             underTest.setSelections(slotId1, emptyList())
             underTest.setSelections(slotId2, listOf(config1.key))
             assertSelections(
-                configsBySlotId,
+                configsBySlotId(),
                 mapOf(
                     slotId1 to emptyList(),
                     slotId2 to listOf(config1),
                 ),
             )
-
-            job.cancel()
         }
 
     @Test
     fun getAffordancePickerRepresentations() =
-        runBlocking(IMMEDIATE) {
+        testScope.runTest {
             assertThat(underTest.getAffordancePickerRepresentations())
                 .isEqualTo(
                     listOf(
@@ -226,7 +223,7 @@
 
     @Test
     fun `selections for secondary user`() =
-        runBlocking(IMMEDIATE) {
+        testScope.runTest {
             userTracker.set(
                 userInfos =
                     listOf(
@@ -252,12 +249,10 @@
                 slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
                 affordanceId = FakeCustomizationProviderClient.AFFORDANCE_2,
             )
-            val observed = mutableListOf<Map<String, List<KeyguardQuickAffordanceConfig>>>()
-            val job = underTest.selections.onEach { observed.add(it) }.launchIn(this)
-            yield()
+            val observed = collectLastValue(underTest.selections)
 
             assertSelections(
-                observed = observed.last(),
+                observed = observed(),
                 expected =
                     mapOf(
                         KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to
@@ -266,8 +261,6 @@
                             ),
                     )
             )
-
-            job.cancel()
         }
 
     private fun assertSelections(
@@ -275,15 +268,14 @@
         expected: Map<String, List<KeyguardQuickAffordanceConfig>>,
     ) {
         assertThat(observed).isEqualTo(expected)
-        assertThat(underTest.getSelections())
+        assertThat(underTest.getCurrentSelections())
             .isEqualTo(expected.mapValues { (_, configs) -> configs.map { it.key } })
         expected.forEach { (slotId, configs) ->
-            assertThat(underTest.getSelections(slotId)).isEqualTo(configs)
+            assertThat(underTest.getCurrentSelections(slotId)).isEqualTo(configs)
         }
     }
 
     companion object {
-        private val IMMEDIATE = Dispatchers.Main.immediate
         private const val SECONDARY_USER_1 = UserHandle.MIN_SECONDARY_USER_ID + 1
         private const val SECONDARY_USER_2 = UserHandle.MIN_SECONDARY_USER_ID + 2
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index f997d18..0469e77 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -18,6 +18,7 @@
 
 import android.graphics.Point
 import android.hardware.biometrics.BiometricSourceType
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
@@ -54,13 +55,12 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class KeyguardRepositoryImplTest : SysuiTestCase() {
 
     @Mock private lateinit var statusBarStateController: StatusBarStateController
@@ -324,29 +324,6 @@
         }
 
     @Test
-    fun isBouncerShowing() =
-        runTest(UnconfinedTestDispatcher()) {
-            whenever(keyguardStateController.isBouncerShowing).thenReturn(false)
-            var latest: Boolean? = null
-            val job = underTest.isBouncerShowing.onEach { latest = it }.launchIn(this)
-
-            assertThat(latest).isFalse()
-
-            val captor = argumentCaptor<KeyguardStateController.Callback>()
-            verify(keyguardStateController).addCallback(captor.capture())
-
-            whenever(keyguardStateController.isBouncerShowing).thenReturn(true)
-            captor.value.onBouncerShowingChanged()
-            assertThat(latest).isTrue()
-
-            whenever(keyguardStateController.isBouncerShowing).thenReturn(false)
-            captor.value.onBouncerShowingChanged()
-            assertThat(latest).isFalse()
-
-            job.cancel()
-        }
-
-    @Test
     fun isKeyguardGoingAway() =
         runTest(UnconfinedTestDispatcher()) {
             whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
index 32cec09..ae227b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
@@ -20,6 +20,7 @@
 import android.util.Log
 import android.util.Log.TerribleFailure
 import android.util.Log.TerribleFailureHandler
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.Interpolators
@@ -43,10 +44,9 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class KeyguardTransitionRepositoryTest : SysuiTestCase() {
 
     private lateinit var underTest: KeyguardTransitionRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt
index 4b06905..a181137 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt
@@ -18,6 +18,7 @@
 
 import android.app.trust.TrustManager
 import android.content.pm.UserInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.keyguard.logging.TrustRepositoryLogger
 import com.android.systemui.SysuiTestCase
@@ -33,7 +34,6 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.ArgumentCaptor
 import org.mockito.Captor
 import org.mockito.Mock
@@ -43,7 +43,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class TrustRepositoryTest : SysuiTestCase() {
     @Mock private lateinit var trustManager: TrustManager
     @Captor private lateinit var listenerCaptor: ArgumentCaptor<TrustManager.TrustListener>
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt
index 1da7241..18e80ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt
@@ -16,14 +16,17 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.ViewMediatorCallback
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.data.repository.FakeBiometricRepository
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
+import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepositoryImpl
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.util.time.FakeSystemClock
 import com.android.systemui.util.time.SystemClock
@@ -34,18 +37,19 @@
 import org.junit.Before
 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.MockitoAnnotations
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class AlternateBouncerInteractorTest : SysuiTestCase() {
     private lateinit var underTest: AlternateBouncerInteractor
     private lateinit var bouncerRepository: KeyguardBouncerRepository
-    private lateinit var biometricRepository: FakeBiometricRepository
+    private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
+    private lateinit var deviceEntryFingerprintAuthRepository:
+        FakeDeviceEntryFingerprintAuthRepository
     @Mock private lateinit var systemClock: SystemClock
     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
     @Mock private lateinit var bouncerLogger: TableLogBuffer
@@ -55,18 +59,20 @@
     fun setup() {
         MockitoAnnotations.initMocks(this)
         bouncerRepository =
-            KeyguardBouncerRepository(
+            KeyguardBouncerRepositoryImpl(
                 mock(ViewMediatorCallback::class.java),
                 FakeSystemClock(),
                 TestCoroutineScope(),
                 bouncerLogger,
             )
-        biometricRepository = FakeBiometricRepository()
+        biometricSettingsRepository = FakeBiometricSettingsRepository()
+        deviceEntryFingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository()
         featureFlags = FakeFeatureFlags().apply { this.set(Flags.MODERN_ALTERNATE_BOUNCER, true) }
         underTest =
             AlternateBouncerInteractor(
                 bouncerRepository,
-                biometricRepository,
+                biometricSettingsRepository,
+                deviceEntryFingerprintAuthRepository,
                 systemClock,
                 keyguardUpdateMonitor,
                 featureFlags,
@@ -90,7 +96,7 @@
     @Test
     fun canShowAlternateBouncerForFingerprint_noFingerprintsEnrolled() {
         givenCanShowAlternateBouncer()
-        biometricRepository.setFingerprintEnrolled(false)
+        biometricSettingsRepository.setFingerprintEnrolled(false)
 
         assertFalse(underTest.canShowAlternateBouncerForFingerprint())
     }
@@ -98,7 +104,7 @@
     @Test
     fun canShowAlternateBouncerForFingerprint_strongBiometricNotAllowed() {
         givenCanShowAlternateBouncer()
-        biometricRepository.setStrongBiometricAllowed(false)
+        biometricSettingsRepository.setStrongBiometricAllowed(false)
 
         assertFalse(underTest.canShowAlternateBouncerForFingerprint())
     }
@@ -106,7 +112,15 @@
     @Test
     fun canShowAlternateBouncerForFingerprint_devicePolicyDoesNotAllowFingerprint() {
         givenCanShowAlternateBouncer()
-        biometricRepository.setFingerprintEnabledByDevicePolicy(false)
+        biometricSettingsRepository.setFingerprintEnabledByDevicePolicy(false)
+
+        assertFalse(underTest.canShowAlternateBouncerForFingerprint())
+    }
+
+    @Test
+    fun canShowAlternateBouncerForFingerprint_fingerprintLockedOut() {
+        givenCanShowAlternateBouncer()
+        deviceEntryFingerprintAuthRepository.setLockedOut(true)
 
         assertFalse(underTest.canShowAlternateBouncerForFingerprint())
     }
@@ -116,7 +130,7 @@
         givenCanShowAlternateBouncer()
 
         assertTrue(underTest.show())
-        assertTrue(bouncerRepository.isAlternateBouncerVisible.value)
+        assertTrue(bouncerRepository.alternateBouncerVisible.value)
     }
 
     @Test
@@ -124,7 +138,7 @@
         givenCannotShowAlternateBouncer()
 
         assertFalse(underTest.show())
-        assertFalse(bouncerRepository.isAlternateBouncerVisible.value)
+        assertFalse(bouncerRepository.alternateBouncerVisible.value)
     }
 
     @Test
@@ -132,7 +146,7 @@
         bouncerRepository.setAlternateVisible(true)
 
         assertTrue(underTest.hide())
-        assertFalse(bouncerRepository.isAlternateBouncerVisible.value)
+        assertFalse(bouncerRepository.alternateBouncerVisible.value)
     }
 
     @Test
@@ -140,17 +154,18 @@
         bouncerRepository.setAlternateVisible(false)
 
         assertFalse(underTest.hide())
-        assertFalse(bouncerRepository.isAlternateBouncerVisible.value)
+        assertFalse(bouncerRepository.alternateBouncerVisible.value)
     }
 
     private fun givenCanShowAlternateBouncer() {
         bouncerRepository.setAlternateBouncerUIAvailable(true)
-        biometricRepository.setFingerprintEnrolled(true)
-        biometricRepository.setStrongBiometricAllowed(true)
-        biometricRepository.setFingerprintEnabledByDevicePolicy(true)
+        biometricSettingsRepository.setFingerprintEnrolled(true)
+        biometricSettingsRepository.setStrongBiometricAllowed(true)
+        biometricSettingsRepository.setFingerprintEnabledByDevicePolicy(true)
+        deviceEntryFingerprintAuthRepository.setLockedOut(false)
     }
 
     private fun givenCannotShowAlternateBouncer() {
-        biometricRepository.setFingerprintEnrolled(false)
+        biometricSettingsRepository.setFingerprintEnrolled(false)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index 68d13d3..153439e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -18,69 +18,178 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import android.app.StatusBarManager
+import android.content.Context
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
+import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel
+import com.android.systemui.settings.DisplayTracker
 import com.android.systemui.statusbar.CommandQueue
-import com.android.systemui.statusbar.CommandQueue.Callbacks
-import com.android.systemui.util.mockito.argumentCaptor
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.onCompletion
+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.junit.runners.JUnit4
-import org.mockito.Mock
-import org.mockito.Mockito.verify
+import org.mockito.Mockito.mock
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class KeyguardInteractorTest : SysuiTestCase() {
-    @Mock private lateinit var commandQueue: CommandQueue
+    private lateinit var commandQueue: FakeCommandQueue
+    private lateinit var featureFlags: FakeFeatureFlags
+    private lateinit var testScope: TestScope
 
     private lateinit var underTest: KeyguardInteractor
     private lateinit var repository: FakeKeyguardRepository
+    private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-
+        featureFlags = FakeFeatureFlags().apply { set(FACE_AUTH_REFACTOR, true) }
+        commandQueue = FakeCommandQueue(mock(Context::class.java), mock(DisplayTracker::class.java))
+        testScope = TestScope()
         repository = FakeKeyguardRepository()
-        underTest = KeyguardInteractor(repository, commandQueue)
+        bouncerRepository = FakeKeyguardBouncerRepository()
+        underTest =
+            KeyguardInteractor(
+                repository,
+                commandQueue,
+                featureFlags,
+                bouncerRepository,
+            )
     }
 
     @Test
-    fun onCameraLaunchDetected() = runTest {
-        val flow = underTest.onCameraLaunchDetected
-        var cameraLaunchSource = collectLastValue(flow)
-        runCurrent()
+    fun onCameraLaunchDetected() =
+        testScope.runTest {
+            val flow = underTest.onCameraLaunchDetected
+            var cameraLaunchSource = collectLastValue(flow)
+            runCurrent()
 
-        val captor = argumentCaptor<CommandQueue.Callbacks>()
-        verify(commandQueue).addCallback(captor.capture())
+            commandQueue.doForEachCallback {
+                it.onCameraLaunchGestureDetected(StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE)
+            }
+            assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.WIGGLE)
 
-        captor.value.onCameraLaunchGestureDetected(StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE)
-        assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.WIGGLE)
+            commandQueue.doForEachCallback {
+                it.onCameraLaunchGestureDetected(
+                    StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP
+                )
+            }
+            assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.POWER_DOUBLE_TAP)
 
-        captor.value.onCameraLaunchGestureDetected(
-            StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP
-        )
-        assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.POWER_DOUBLE_TAP)
+            commandQueue.doForEachCallback {
+                it.onCameraLaunchGestureDetected(StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER)
+            }
+            assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.LIFT_TRIGGER)
 
-        captor.value.onCameraLaunchGestureDetected(
-            StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER
-        )
-        assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.LIFT_TRIGGER)
+            commandQueue.doForEachCallback {
+                it.onCameraLaunchGestureDetected(
+                    StatusBarManager.CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE
+                )
+            }
+            assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.QUICK_AFFORDANCE)
 
-        captor.value.onCameraLaunchGestureDetected(
-            StatusBarManager.CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE
-        )
-        assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.QUICK_AFFORDANCE)
+            flow.onCompletion { assertThat(commandQueue.callbackCount()).isEqualTo(0) }
+        }
 
-        flow.onCompletion { verify(commandQueue).removeCallback(captor.value) }
+    @Test
+    fun testKeyguardGuardVisibilityStopsSecureCamera() =
+        testScope.runTest {
+            val secureCameraActive = collectLastValue(underTest.isSecureCameraActive)
+            runCurrent()
+
+            commandQueue.doForEachCallback {
+                it.onCameraLaunchGestureDetected(
+                    StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP
+                )
+            }
+
+            assertThat(secureCameraActive()).isTrue()
+
+            // Keyguard is showing but occluded
+            repository.setKeyguardShowing(true)
+            repository.setKeyguardOccluded(true)
+            assertThat(secureCameraActive()).isTrue()
+
+            // Keyguard is showing and not occluded
+            repository.setKeyguardOccluded(false)
+            assertThat(secureCameraActive()).isFalse()
+        }
+
+    @Test
+    fun testBouncerShowingResetsSecureCameraState() =
+        testScope.runTest {
+            val secureCameraActive = collectLastValue(underTest.isSecureCameraActive)
+            runCurrent()
+
+            commandQueue.doForEachCallback {
+                it.onCameraLaunchGestureDetected(
+                    StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP
+                )
+            }
+            assertThat(secureCameraActive()).isTrue()
+
+            // Keyguard is showing and not occluded
+            repository.setKeyguardShowing(true)
+            repository.setKeyguardOccluded(true)
+            assertThat(secureCameraActive()).isTrue()
+
+            bouncerRepository.setPrimaryVisible(true)
+            assertThat(secureCameraActive()).isFalse()
+        }
+
+    @Test
+    fun keyguardVisibilityIsDefinedAsKeyguardShowingButNotOccluded() = runTest {
+        var isVisible = collectLastValue(underTest.isKeyguardVisible)
+        repository.setKeyguardShowing(true)
+        repository.setKeyguardOccluded(false)
+
+        assertThat(isVisible()).isTrue()
+
+        repository.setKeyguardOccluded(true)
+        assertThat(isVisible()).isFalse()
+
+        repository.setKeyguardShowing(false)
+        repository.setKeyguardOccluded(true)
+        assertThat(isVisible()).isFalse()
     }
+
+    @Test
+    fun secureCameraIsNotActiveWhenNoCameraLaunchEventHasBeenFiredYet() =
+        testScope.runTest {
+            val secureCameraActive = collectLastValue(underTest.isSecureCameraActive)
+            runCurrent()
+
+            assertThat(secureCameraActive()).isFalse()
+        }
+}
+
+class FakeCommandQueue(val context: Context, val displayTracker: DisplayTracker) :
+    CommandQueue(context, displayTracker) {
+    private val callbacks = mutableListOf<Callbacks>()
+
+    override fun addCallback(callback: Callbacks) {
+        callbacks.add(callback)
+    }
+
+    override fun removeCallback(callback: Callbacks) {
+        callbacks.remove(callback)
+    }
+
+    fun doForEachCallback(func: (callback: Callbacks) -> Unit) {
+        callbacks.forEach { func(it) }
+    }
+
+    fun callbackCount(): Int = callbacks.size
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
new file mode 100644
index 0000000..51988ef
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.content.Intent
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.util.mockito.any
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.runBlocking
+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.ArgumentMatchers.anyBoolean
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class KeyguardLongPressInteractorTest : SysuiTestCase() {
+
+    @Mock private lateinit var activityStarter: ActivityStarter
+    @Mock private lateinit var logger: UiEventLogger
+
+    private lateinit var underTest: KeyguardLongPressInteractor
+
+    private lateinit var testScope: TestScope
+    private lateinit var keyguardRepository: FakeKeyguardRepository
+    private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        runBlocking { createUnderTest() }
+    }
+
+    @Test
+    fun isEnabled() =
+        testScope.runTest {
+            val isEnabled = collectLastValue(underTest.isLongPressHandlingEnabled)
+            KeyguardState.values().forEach { keyguardState ->
+                setUpState(
+                    keyguardState = keyguardState,
+                )
+
+                if (keyguardState == KeyguardState.LOCKSCREEN) {
+                    assertThat(isEnabled()).isTrue()
+                } else {
+                    assertThat(isEnabled()).isFalse()
+                }
+            }
+        }
+
+    @Test
+    fun `isEnabled - always false when quick settings are visible`() =
+        testScope.runTest {
+            val isEnabled = collectLastValue(underTest.isLongPressHandlingEnabled)
+            KeyguardState.values().forEach { keyguardState ->
+                setUpState(
+                    keyguardState = keyguardState,
+                    isQuickSettingsVisible = true,
+                )
+
+                assertThat(isEnabled()).isFalse()
+            }
+        }
+
+    @Test
+    fun `long-pressed - pop-up clicked - starts activity`() =
+        testScope.runTest {
+            val menu = collectLastValue(underTest.menu)
+            runCurrent()
+
+            val x = 100
+            val y = 123
+            underTest.onLongPress(x, y)
+            assertThat(menu()).isNotNull()
+            assertThat(menu()?.position?.x).isEqualTo(x)
+            assertThat(menu()?.position?.y).isEqualTo(y)
+
+            menu()?.onClicked?.invoke()
+
+            assertThat(menu()).isNull()
+            verify(activityStarter).dismissKeyguardThenExecute(any(), any(), anyBoolean())
+        }
+
+    @Test
+    fun `long-pressed - pop-up dismissed - never starts activity`() =
+        testScope.runTest {
+            val menu = collectLastValue(underTest.menu)
+            runCurrent()
+
+            menu()?.onDismissed?.invoke()
+
+            assertThat(menu()).isNull()
+            verify(activityStarter, never()).dismissKeyguardThenExecute(any(), any(), anyBoolean())
+        }
+
+    @Suppress("DEPRECATION") // We're okay using ACTION_CLOSE_SYSTEM_DIALOGS on system UI.
+    @Test
+    fun `long pressed - close dialogs broadcast received - popup dismissed`() =
+        testScope.runTest {
+            val menu = collectLastValue(underTest.menu)
+            runCurrent()
+
+            underTest.onLongPress(123, 456)
+            assertThat(menu()).isNotNull()
+
+            fakeBroadcastDispatcher.registeredReceivers.forEach { broadcastReceiver ->
+                broadcastReceiver.onReceive(context, Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
+            }
+
+            assertThat(menu()).isNull()
+        }
+
+    @Test
+    fun `logs when menu is shown`() =
+        testScope.runTest {
+            collectLastValue(underTest.menu)
+            runCurrent()
+
+            underTest.onLongPress(100, 123)
+
+            verify(logger)
+                .log(KeyguardLongPressInteractor.LogEvents.LOCK_SCREEN_LONG_PRESS_POPUP_SHOWN)
+        }
+
+    @Test
+    fun `logs when menu is clicked`() =
+        testScope.runTest {
+            val menu = collectLastValue(underTest.menu)
+            runCurrent()
+
+            underTest.onLongPress(100, 123)
+            menu()?.onClicked?.invoke()
+
+            verify(logger)
+                .log(KeyguardLongPressInteractor.LogEvents.LOCK_SCREEN_LONG_PRESS_POPUP_CLICKED)
+        }
+
+    private suspend fun createUnderTest(
+        isLongPressFeatureEnabled: Boolean = true,
+        isRevampedWppFeatureEnabled: Boolean = true,
+    ) {
+        testScope = TestScope()
+        keyguardRepository = FakeKeyguardRepository()
+        keyguardTransitionRepository = FakeKeyguardTransitionRepository()
+
+        underTest =
+            KeyguardLongPressInteractor(
+                unsafeContext = context,
+                scope = testScope.backgroundScope,
+                transitionInteractor =
+                    KeyguardTransitionInteractor(
+                        repository = keyguardTransitionRepository,
+                    ),
+                repository = keyguardRepository,
+                activityStarter = activityStarter,
+                logger = logger,
+                featureFlags =
+                    FakeFeatureFlags().apply {
+                        set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, isLongPressFeatureEnabled)
+                        set(Flags.REVAMPED_WALLPAPER_UI, isRevampedWppFeatureEnabled)
+                    },
+                broadcastDispatcher = fakeBroadcastDispatcher,
+            )
+        setUpState()
+    }
+
+    private suspend fun setUpState(
+        keyguardState: KeyguardState = KeyguardState.LOCKSCREEN,
+        isQuickSettingsVisible: Boolean = false,
+    ) {
+        keyguardTransitionRepository.sendTransitionStep(
+            TransitionStep(
+                to = keyguardState,
+            ),
+        )
+        keyguardRepository.setQuickSettingsVisible(isVisible = isQuickSettingsVisible)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index 43287b0..84ec125 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -17,6 +17,7 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import android.app.admin.DevicePolicyManager
 import android.content.Intent
 import android.os.UserHandle
 import androidx.test.filters.SmallTest
@@ -36,6 +37,7 @@
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
+import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
 import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
@@ -53,7 +55,10 @@
 import com.android.systemui.util.settings.FakeSettings
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.test.runBlockingTest
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -69,6 +74,7 @@
 import org.mockito.Mockito.verifyZeroInteractions
 import org.mockito.MockitoAnnotations
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(Parameterized::class)
 class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
@@ -218,8 +224,10 @@
     @Mock private lateinit var expandable: Expandable
     @Mock private lateinit var launchAnimator: DialogLaunchAnimator
     @Mock private lateinit var commandQueue: CommandQueue
+    @Mock private lateinit var devicePolicyManager: DevicePolicyManager
 
     private lateinit var underTest: KeyguardQuickAffordanceInteractor
+    private lateinit var testScope: TestScope
 
     @JvmField @Parameter(0) var needStrongAuthAfterBoot: Boolean = false
     @JvmField @Parameter(1) var canShowWhileLocked: Boolean = false
@@ -286,12 +294,21 @@
                 dumpManager = mock(),
                 userHandle = UserHandle.SYSTEM,
             )
+        val featureFlags =
+            FakeFeatureFlags().apply {
+                set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
+                set(Flags.FACE_AUTH_REFACTOR, true)
+            }
+        val testDispatcher = StandardTestDispatcher()
+        testScope = TestScope(testDispatcher)
         underTest =
             KeyguardQuickAffordanceInteractor(
                 keyguardInteractor =
                     KeyguardInteractor(
                         repository = FakeKeyguardRepository(),
-                        commandQueue = commandQueue
+                        commandQueue = commandQueue,
+                        featureFlags = featureFlags,
+                        bouncerRepository = FakeKeyguardBouncerRepository(),
                     ),
                 registry =
                     FakeKeyguardQuickAffordanceRegistry(
@@ -311,64 +328,64 @@
                 keyguardStateController = keyguardStateController,
                 userTracker = userTracker,
                 activityStarter = activityStarter,
-                featureFlags =
-                    FakeFeatureFlags().apply {
-                        set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
-                    },
+                featureFlags = featureFlags,
                 repository = { quickAffordanceRepository },
                 launchAnimator = launchAnimator,
+                devicePolicyManager = devicePolicyManager,
+                backgroundDispatcher = testDispatcher,
             )
     }
 
     @Test
-    fun onQuickAffordanceTriggered() = runBlockingTest {
-        setUpMocks(
-            needStrongAuthAfterBoot = needStrongAuthAfterBoot,
-            keyguardIsUnlocked = keyguardIsUnlocked,
-        )
+    fun onQuickAffordanceTriggered() =
+        testScope.runTest {
+            setUpMocks(
+                needStrongAuthAfterBoot = needStrongAuthAfterBoot,
+                keyguardIsUnlocked = keyguardIsUnlocked,
+            )
 
-        homeControls.setState(
-            lockScreenState =
-                KeyguardQuickAffordanceConfig.LockScreenState.Visible(
-                    icon = DRAWABLE,
-                )
-        )
-        homeControls.onTriggeredResult =
+            homeControls.setState(
+                lockScreenState =
+                    KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+                        icon = DRAWABLE,
+                    )
+            )
+            homeControls.onTriggeredResult =
+                if (startActivity) {
+                    KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity(
+                        intent = INTENT,
+                        canShowWhileLocked = canShowWhileLocked,
+                    )
+                } else {
+                    KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+                }
+
+            underTest.onQuickAffordanceTriggered(
+                configKey = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS,
+                expandable = expandable,
+            )
+
             if (startActivity) {
-                KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity(
-                    intent = INTENT,
-                    canShowWhileLocked = canShowWhileLocked,
-                )
+                if (needsToUnlockFirst) {
+                    verify(activityStarter)
+                        .postStartActivityDismissingKeyguard(
+                            any(),
+                            /* delay= */ eq(0),
+                            same(animationController),
+                        )
+                } else {
+                    verify(activityStarter)
+                        .startActivity(
+                            any(),
+                            /* dismissShade= */ eq(true),
+                            same(animationController),
+                            /* showOverLockscreenWhenLocked= */ eq(true),
+                        )
+                }
             } else {
-                KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+                verifyZeroInteractions(activityStarter)
             }
-
-        underTest.onQuickAffordanceTriggered(
-            configKey = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS,
-            expandable = expandable,
-        )
-
-        if (startActivity) {
-            if (needsToUnlockFirst) {
-                verify(activityStarter)
-                    .postStartActivityDismissingKeyguard(
-                        any(),
-                        /* delay= */ eq(0),
-                        same(animationController),
-                    )
-            } else {
-                verify(activityStarter)
-                    .startActivity(
-                        any(),
-                        /* dismissShade= */ eq(true),
-                        same(animationController),
-                        /* showOverLockscreenWhenLocked= */ eq(true),
-                    )
-            }
-        } else {
-            verifyZeroInteractions(activityStarter)
         }
-    }
 
     private fun setUpMocks(
         needStrongAuthAfterBoot: Boolean = true,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index b75a15d..62c9e5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -17,7 +17,9 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import android.app.admin.DevicePolicyManager
 import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.R
@@ -35,6 +37,7 @@
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
+import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
 import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
@@ -60,7 +63,6 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.anyString
 import org.mockito.Mock
@@ -68,7 +70,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
 
     @Mock private lateinit var lockPatternUtils: LockPatternUtils
@@ -77,6 +79,7 @@
     @Mock private lateinit var activityStarter: ActivityStarter
     @Mock private lateinit var launchAnimator: DialogLaunchAnimator
     @Mock private lateinit var commandQueue: CommandQueue
+    @Mock private lateinit var devicePolicyManager: DevicePolicyManager
 
     private lateinit var underTest: KeyguardQuickAffordanceInteractor
 
@@ -150,12 +153,18 @@
         featureFlags =
             FakeFeatureFlags().apply {
                 set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
+                set(Flags.FACE_AUTH_REFACTOR, true)
             }
 
         underTest =
             KeyguardQuickAffordanceInteractor(
                 keyguardInteractor =
-                    KeyguardInteractor(repository = repository, commandQueue = commandQueue),
+                    KeyguardInteractor(
+                        repository = repository,
+                        commandQueue = commandQueue,
+                        featureFlags = featureFlags,
+                        bouncerRepository = FakeKeyguardBouncerRepository(),
+                    ),
                 registry =
                     FakeKeyguardQuickAffordanceRegistry(
                         mapOf(
@@ -177,6 +186,8 @@
                 featureFlags = featureFlags,
                 repository = { quickAffordanceRepository },
                 launchAnimator = launchAnimator,
+                devicePolicyManager = devicePolicyManager,
+                backgroundDispatcher = testDispatcher,
             )
     }
 
@@ -232,6 +243,44 @@
         }
 
     @Test
+    fun `quickAffordance - hidden when all features are disabled by device policy`() =
+        testScope.runTest {
+            whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId))
+                .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL)
+            quickAccessWallet.setState(
+                KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+                    icon = ICON,
+                )
+            )
+
+            val collectedValue by
+                collectLastValue(
+                    underTest.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_END)
+                )
+
+            assertThat(collectedValue).isInstanceOf(KeyguardQuickAffordanceModel.Hidden::class.java)
+        }
+
+    @Test
+    fun `quickAffordance - hidden when shortcuts feature is disabled by device policy`() =
+        testScope.runTest {
+            whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId))
+                .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL)
+            quickAccessWallet.setState(
+                KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+                    icon = ICON,
+                )
+            )
+
+            val collectedValue by
+                collectLastValue(
+                    underTest.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_END)
+                )
+
+            assertThat(collectedValue).isInstanceOf(KeyguardQuickAffordanceModel.Hidden::class.java)
+        }
+
+    @Test
     fun `quickAffordance - bottom start affordance hidden while dozing`() =
         testScope.runTest {
             repository.setDozing(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index 6333b24..3d13d80 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -17,6 +17,7 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
@@ -29,17 +30,17 @@
 import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
+@kotlinx.coroutines.ExperimentalCoroutinesApi
 class KeyguardTransitionInteractorTest : SysuiTestCase() {
 
     private lateinit var underTest: KeyguardTransitionInteractor
@@ -53,7 +54,7 @@
 
     @Test
     fun `transition collectors receives only appropriate events`() =
-        runBlocking(IMMEDIATE) {
+        runTest(UnconfinedTestDispatcher()) {
             var lockscreenToAodSteps = mutableListOf<TransitionStep>()
             val job1 =
                 underTest.lockscreenToAodTransition
@@ -87,10 +88,9 @@
 
     @Test
     fun dozeAmountTransitionTest() =
-        runBlocking(IMMEDIATE) {
+        runTest(UnconfinedTestDispatcher()) {
             var dozeAmountSteps = mutableListOf<TransitionStep>()
-            val job =
-                underTest.dozeAmountTransition.onEach { dozeAmountSteps.add(it) }.launchIn(this)
+            val job = underTest.dozeAmountTransition.onEach { dozeAmountSteps.add(it) }.launchIn(this)
 
             val steps = mutableListOf<TransitionStep>()
 
@@ -119,10 +119,9 @@
 
     @Test
     fun keyguardStateTests() =
-        runBlocking(IMMEDIATE) {
+        runTest(UnconfinedTestDispatcher()) {
             var finishedSteps = mutableListOf<KeyguardState>()
-            val job =
-                underTest.finishedKeyguardState.onEach { finishedSteps.add(it) }.launchIn(this)
+            val job = underTest.finishedKeyguardState.onEach { finishedSteps.add(it) }.launchIn(this)
 
             val steps = mutableListOf<TransitionStep>()
 
@@ -143,12 +142,10 @@
 
     @Test
     fun finishedKeyguardTransitionStepTests() =
-        runBlocking(IMMEDIATE) {
+        runTest(UnconfinedTestDispatcher()) {
             var finishedSteps = mutableListOf<TransitionStep>()
             val job =
-                underTest.finishedKeyguardTransitionStep
-                    .onEach { finishedSteps.add(it) }
-                    .launchIn(this)
+                underTest.finishedKeyguardTransitionStep.onEach { finishedSteps.add(it) }.launchIn(this)
 
             val steps = mutableListOf<TransitionStep>()
 
@@ -169,12 +166,10 @@
 
     @Test
     fun startedKeyguardTransitionStepTests() =
-        runBlocking(IMMEDIATE) {
+        runTest(UnconfinedTestDispatcher()) {
             var startedSteps = mutableListOf<TransitionStep>()
             val job =
-                underTest.startedKeyguardTransitionStep
-                    .onEach { startedSteps.add(it) }
-                    .launchIn(this)
+                underTest.startedKeyguardTransitionStep.onEach { startedSteps.add(it) }.launchIn(this)
 
             val steps = mutableListOf<TransitionStep>()
 
@@ -192,8 +187,4 @@
 
             job.cancel()
         }
-
-    companion object {
-        private val IMMEDIATE = Dispatchers.Main.immediate
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index 5a7a3d4..ae7a928 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -21,9 +21,14 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.Interpolators
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepositoryImpl
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
 import com.android.systemui.keyguard.shared.model.DozeStateModel
 import com.android.systemui.keyguard.shared.model.DozeTransitionModel
 import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -62,6 +67,7 @@
     private lateinit var testScope: TestScope
 
     private lateinit var keyguardRepository: FakeKeyguardRepository
+    private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
     private lateinit var shadeRepository: ShadeRepository
 
     // Used to issue real transition steps for test input
@@ -78,6 +84,10 @@
     private lateinit var fromOccludedTransitionInteractor: FromOccludedTransitionInteractor
     private lateinit var fromGoneTransitionInteractor: FromGoneTransitionInteractor
     private lateinit var fromAodTransitionInteractor: FromAodTransitionInteractor
+    private lateinit var fromAlternateBouncerTransitionInteractor:
+        FromAlternateBouncerTransitionInteractor
+    private lateinit var fromPrimaryBouncerTransitionInteractor:
+        FromPrimaryBouncerTransitionInteractor
 
     @Before
     fun setUp() {
@@ -85,16 +95,18 @@
         testScope = TestScope()
 
         keyguardRepository = FakeKeyguardRepository()
+        bouncerRepository = FakeKeyguardBouncerRepository()
         shadeRepository = FakeShadeRepository()
 
         /* Used to issue full transition steps, to better simulate a real device */
         transitionRepository = KeyguardTransitionRepositoryImpl()
         runner = KeyguardTransitionRunner(transitionRepository)
 
+        val featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, true) }
         fromLockscreenTransitionInteractor =
             FromLockscreenTransitionInteractor(
                 scope = testScope,
-                keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
+                keyguardInteractor = createKeyguardInteractor(featureFlags),
                 shadeRepository = shadeRepository,
                 keyguardTransitionRepository = mockTransitionRepository,
                 keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
@@ -104,7 +116,7 @@
         fromDreamingTransitionInteractor =
             FromDreamingTransitionInteractor(
                 scope = testScope,
-                keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
+                keyguardInteractor = createKeyguardInteractor(featureFlags),
                 keyguardTransitionRepository = mockTransitionRepository,
                 keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
             )
@@ -113,7 +125,7 @@
         fromAodTransitionInteractor =
             FromAodTransitionInteractor(
                 scope = testScope,
-                keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
+                keyguardInteractor = createKeyguardInteractor(featureFlags),
                 keyguardTransitionRepository = mockTransitionRepository,
                 keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
             )
@@ -122,7 +134,7 @@
         fromGoneTransitionInteractor =
             FromGoneTransitionInteractor(
                 scope = testScope,
-                keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
+                keyguardInteractor = createKeyguardInteractor(featureFlags),
                 keyguardTransitionRepository = mockTransitionRepository,
                 keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
             )
@@ -131,7 +143,7 @@
         fromDozingTransitionInteractor =
             FromDozingTransitionInteractor(
                 scope = testScope,
-                keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
+                keyguardInteractor = createKeyguardInteractor(featureFlags),
                 keyguardTransitionRepository = mockTransitionRepository,
                 keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
             )
@@ -140,15 +152,33 @@
         fromOccludedTransitionInteractor =
             FromOccludedTransitionInteractor(
                 scope = testScope,
-                keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
+                keyguardInteractor = createKeyguardInteractor(featureFlags),
                 keyguardTransitionRepository = mockTransitionRepository,
                 keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
             )
         fromOccludedTransitionInteractor.start()
+
+        fromAlternateBouncerTransitionInteractor =
+            FromAlternateBouncerTransitionInteractor(
+                scope = testScope,
+                keyguardInteractor = createKeyguardInteractor(featureFlags),
+                keyguardTransitionRepository = mockTransitionRepository,
+                keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
+            )
+        fromAlternateBouncerTransitionInteractor.start()
+
+        fromPrimaryBouncerTransitionInteractor =
+            FromPrimaryBouncerTransitionInteractor(
+                scope = testScope,
+                keyguardInteractor = createKeyguardInteractor(featureFlags),
+                keyguardTransitionRepository = mockTransitionRepository,
+                keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
+            )
+        fromPrimaryBouncerTransitionInteractor.start()
     }
 
     @Test
-    fun `DREAMING to LOCKSCREEN`() =
+    fun `DREAMING to LOCKSCREEN - dreaming state changes first`() =
         testScope.runTest {
             // GIVEN a device is dreaming and occluded
             keyguardRepository.setDreamingWithOverlay(true)
@@ -178,7 +208,8 @@
             )
             // AND dreaming has stopped
             keyguardRepository.setDreamingWithOverlay(false)
-            // AND occluded has stopped
+            advanceUntilIdle()
+            // AND then occluded has stopped
             keyguardRepository.setKeyguardOccluded(false)
             advanceUntilIdle()
 
@@ -196,7 +227,56 @@
         }
 
     @Test
-    fun `LOCKSCREEN to BOUNCER via bouncer showing call`() =
+    fun `DREAMING to LOCKSCREEN - occluded state changes first`() =
+        testScope.runTest {
+            // GIVEN a device is dreaming and occluded
+            keyguardRepository.setDreamingWithOverlay(true)
+            keyguardRepository.setKeyguardOccluded(true)
+            runCurrent()
+
+            // GIVEN a prior transition has run to DREAMING
+            runner.startTransition(
+                testScope,
+                TransitionInfo(
+                    ownerName = "",
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.DREAMING,
+                    animator =
+                        ValueAnimator().apply {
+                            duration = 10
+                            interpolator = Interpolators.LINEAR
+                        },
+                )
+            )
+            runCurrent()
+            reset(mockTransitionRepository)
+
+            // WHEN doze is complete
+            keyguardRepository.setDozeTransitionModel(
+                DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
+            )
+            // AND occluded has stopped
+            keyguardRepository.setKeyguardOccluded(false)
+            advanceUntilIdle()
+            // AND then dreaming has stopped
+            keyguardRepository.setDreamingWithOverlay(false)
+            advanceUntilIdle()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(mockTransitionRepository).startTransition(capture())
+                }
+            // THEN a transition to BOUNCER should occur
+            assertThat(info.ownerName).isEqualTo("FromDreamingTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.DREAMING)
+            assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun `LOCKSCREEN to PRIMARY_BOUNCER via bouncer showing call`() =
         testScope.runTest {
             // GIVEN a device that has at least woken up
             keyguardRepository.setWakefulnessModel(startingToWake())
@@ -218,18 +298,18 @@
             )
             runCurrent()
 
-            // WHEN the bouncer is set to show
-            keyguardRepository.setBouncerShowing(true)
+            // WHEN the primary bouncer is set to show
+            bouncerRepository.setPrimaryVisible(true)
             runCurrent()
 
             val info =
                 withArgCaptor<TransitionInfo> {
                     verify(mockTransitionRepository).startTransition(capture())
                 }
-            // THEN a transition to BOUNCER should occur
+            // THEN a transition to PRIMARY_BOUNCER should occur
             assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
             assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
-            assertThat(info.to).isEqualTo(KeyguardState.BOUNCER)
+            assertThat(info.to).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
             assertThat(info.animator).isNotNull()
 
             coroutineContext.cancelChildren()
@@ -437,6 +517,43 @@
         }
 
     @Test
+    fun `DOZING to GONE`() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to DOZING
+            runner.startTransition(
+                testScope,
+                TransitionInfo(
+                    ownerName = "",
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.DOZING,
+                    animator =
+                        ValueAnimator().apply {
+                            duration = 10
+                            interpolator = Interpolators.LINEAR
+                        },
+                )
+            )
+            runCurrent()
+            reset(mockTransitionRepository)
+
+            // WHEN biometrics succeeds with wake and unlock mode
+            keyguardRepository.setBiometricUnlockState(BiometricUnlockModel.WAKE_AND_UNLOCK)
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(mockTransitionRepository).startTransition(capture())
+                }
+            // THEN a transition to DOZING should occur
+            assertThat(info.ownerName).isEqualTo("FromDozingTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.DOZING)
+            assertThat(info.to).isEqualTo(KeyguardState.GONE)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
     fun `GONE to DOZING`() =
         testScope.runTest {
             // GIVEN a device with AOD not available
@@ -519,6 +636,43 @@
         }
 
     @Test
+    fun `GONE to LOCKSREEN`() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to GONE
+            runner.startTransition(
+                testScope,
+                TransitionInfo(
+                    ownerName = "",
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.GONE,
+                    animator =
+                        ValueAnimator().apply {
+                            duration = 10
+                            interpolator = Interpolators.LINEAR
+                        },
+                )
+            )
+            runCurrent()
+            reset(mockTransitionRepository)
+
+            // WHEN the keyguard starts to show
+            keyguardRepository.setKeyguardShowing(true)
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(mockTransitionRepository).startTransition(capture())
+                }
+            // THEN a transition to AOD should occur
+            assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.GONE)
+            assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
     fun `GONE to DREAMING`() =
         testScope.runTest {
             // GIVEN a device that is not dreaming or dozing
@@ -561,6 +715,297 @@
             coroutineContext.cancelChildren()
         }
 
+    @Test
+    fun `ALTERNATE_BOUNCER to PRIMARY_BOUNCER`() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to ALTERNATE_BOUNCER
+            runner.startTransition(
+                testScope,
+                TransitionInfo(
+                    ownerName = "",
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.ALTERNATE_BOUNCER,
+                    animator =
+                        ValueAnimator().apply {
+                            duration = 10
+                            interpolator = Interpolators.LINEAR
+                        },
+                )
+            )
+            runCurrent()
+            reset(mockTransitionRepository)
+
+            // WHEN the alternateBouncer stops showing and then the primary bouncer shows
+            bouncerRepository.setPrimaryVisible(true)
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(mockTransitionRepository).startTransition(capture())
+                }
+            // THEN a transition to PRIMARY_BOUNCER should occur
+            assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.ALTERNATE_BOUNCER)
+            assertThat(info.to).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun `ALTERNATE_BOUNCER to AOD`() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to ALTERNATE_BOUNCER
+            bouncerRepository.setAlternateVisible(true)
+            runner.startTransition(
+                testScope,
+                TransitionInfo(
+                    ownerName = "",
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.ALTERNATE_BOUNCER,
+                    animator =
+                        ValueAnimator().apply {
+                            duration = 10
+                            interpolator = Interpolators.LINEAR
+                        },
+                )
+            )
+            runCurrent()
+            reset(mockTransitionRepository)
+
+            // GIVEN the primary bouncer isn't showing, aod available and starting to sleep
+            bouncerRepository.setPrimaryVisible(false)
+            keyguardRepository.setAodAvailable(true)
+            keyguardRepository.setWakefulnessModel(startingToSleep())
+
+            // WHEN the alternateBouncer stops showing
+            bouncerRepository.setAlternateVisible(false)
+            advanceUntilIdle()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(mockTransitionRepository).startTransition(capture())
+                }
+            // THEN a transition to AOD should occur
+            assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.ALTERNATE_BOUNCER)
+            assertThat(info.to).isEqualTo(KeyguardState.AOD)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun `ALTERNATE_BOUNCER to DOZING`() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to ALTERNATE_BOUNCER
+            bouncerRepository.setAlternateVisible(true)
+            runner.startTransition(
+                testScope,
+                TransitionInfo(
+                    ownerName = "",
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.ALTERNATE_BOUNCER,
+                    animator =
+                        ValueAnimator().apply {
+                            duration = 10
+                            interpolator = Interpolators.LINEAR
+                        },
+                )
+            )
+            runCurrent()
+            reset(mockTransitionRepository)
+
+            // GIVEN the primary bouncer isn't showing, aod not available and starting to sleep
+            // to sleep
+            bouncerRepository.setPrimaryVisible(false)
+            keyguardRepository.setAodAvailable(false)
+            keyguardRepository.setWakefulnessModel(startingToSleep())
+
+            // WHEN the alternateBouncer stops showing
+            bouncerRepository.setAlternateVisible(false)
+            advanceUntilIdle()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(mockTransitionRepository).startTransition(capture())
+                }
+            // THEN a transition to DOZING should occur
+            assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.ALTERNATE_BOUNCER)
+            assertThat(info.to).isEqualTo(KeyguardState.DOZING)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun `ALTERNATE_BOUNCER to LOCKSCREEN`() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to ALTERNATE_BOUNCER
+            bouncerRepository.setAlternateVisible(true)
+            runner.startTransition(
+                testScope,
+                TransitionInfo(
+                    ownerName = "",
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.ALTERNATE_BOUNCER,
+                    animator =
+                        ValueAnimator().apply {
+                            duration = 10
+                            interpolator = Interpolators.LINEAR
+                        },
+                )
+            )
+            runCurrent()
+            reset(mockTransitionRepository)
+
+            // GIVEN the primary bouncer isn't showing and device not sleeping
+            bouncerRepository.setPrimaryVisible(false)
+            keyguardRepository.setWakefulnessModel(startingToWake())
+
+            // WHEN the alternateBouncer stops showing
+            bouncerRepository.setAlternateVisible(false)
+            advanceUntilIdle()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(mockTransitionRepository).startTransition(capture())
+                }
+            // THEN a transition to LOCKSCREEN should occur
+            assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.ALTERNATE_BOUNCER)
+            assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun `PRIMARY_BOUNCER to AOD`() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to PRIMARY_BOUNCER
+            bouncerRepository.setPrimaryVisible(true)
+            runner.startTransition(
+                testScope,
+                TransitionInfo(
+                    ownerName = "",
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.PRIMARY_BOUNCER,
+                    animator =
+                        ValueAnimator().apply {
+                            duration = 10
+                            interpolator = Interpolators.LINEAR
+                        },
+                )
+            )
+            runCurrent()
+            reset(mockTransitionRepository)
+
+            // GIVEN aod available and starting to sleep
+            keyguardRepository.setAodAvailable(true)
+            keyguardRepository.setWakefulnessModel(startingToSleep())
+
+            // WHEN the primaryBouncer stops showing
+            bouncerRepository.setPrimaryVisible(false)
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(mockTransitionRepository).startTransition(capture())
+                }
+            // THEN a transition to AOD should occur
+            assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
+            assertThat(info.to).isEqualTo(KeyguardState.AOD)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun `PRIMARY_BOUNCER to DOZING`() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to PRIMARY_BOUNCER
+            bouncerRepository.setPrimaryVisible(true)
+            runner.startTransition(
+                testScope,
+                TransitionInfo(
+                    ownerName = "",
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.PRIMARY_BOUNCER,
+                    animator =
+                        ValueAnimator().apply {
+                            duration = 10
+                            interpolator = Interpolators.LINEAR
+                        },
+                )
+            )
+            runCurrent()
+            reset(mockTransitionRepository)
+
+            // GIVEN aod not available and starting to sleep to sleep
+            keyguardRepository.setAodAvailable(false)
+            keyguardRepository.setWakefulnessModel(startingToSleep())
+
+            // WHEN the primaryBouncer stops showing
+            bouncerRepository.setPrimaryVisible(false)
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(mockTransitionRepository).startTransition(capture())
+                }
+            // THEN a transition to DOZING should occur
+            assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
+            assertThat(info.to).isEqualTo(KeyguardState.DOZING)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun `PRIMARY_BOUNCER to LOCKSCREEN`() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to PRIMARY_BOUNCER
+            bouncerRepository.setPrimaryVisible(true)
+            runner.startTransition(
+                testScope,
+                TransitionInfo(
+                    ownerName = "",
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.PRIMARY_BOUNCER,
+                    animator =
+                        ValueAnimator().apply {
+                            duration = 10
+                            interpolator = Interpolators.LINEAR
+                        },
+                )
+            )
+            runCurrent()
+            reset(mockTransitionRepository)
+
+            // GIVEN device not sleeping
+            keyguardRepository.setWakefulnessModel(startingToWake())
+
+            // WHEN the alternateBouncer stops showing
+            bouncerRepository.setPrimaryVisible(false)
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(mockTransitionRepository).startTransition(capture())
+                }
+            // THEN a transition to LOCKSCREEN should occur
+            assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
+            assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
     private fun startingToWake() =
         WakefulnessModel(
             WakefulnessState.STARTING_TO_WAKE,
@@ -576,4 +1021,13 @@
             WakeSleepReason.OTHER,
             WakeSleepReason.OTHER
         )
+
+    private fun createKeyguardInteractor(featureFlags: FeatureFlags): KeyguardInteractor {
+        return KeyguardInteractor(
+            keyguardRepository,
+            commandQueue,
+            featureFlags,
+            bouncerRepository,
+        )
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
index 3166214..6236616 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
@@ -33,11 +34,10 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class LightRevealScrimInteractorTest : SysuiTestCase() {
     private val fakeKeyguardTransitionRepository = FakeKeyguardTransitionRepository()
     private val fakeLightRevealScrimRepository = FakeLightRevealScrimRepository()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt
index fbfeca9..f86ac79 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt
@@ -17,18 +17,18 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class PrimaryBouncerCallbackInteractorTest : SysuiTestCase() {
     private val mPrimaryBouncerCallbackInteractor = PrimaryBouncerCallbackInteractor()
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt
index 7f48ea1..46ed829 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt
@@ -68,13 +68,13 @@
     @Mock private lateinit var keyguardBypassController: KeyguardBypassController
     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
     private val mainHandler = FakeHandler(Looper.getMainLooper())
-    private lateinit var mPrimaryBouncerInteractor: PrimaryBouncerInteractor
+    private lateinit var underTest: PrimaryBouncerInteractor
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         DejankUtils.setImmediate(true)
-        mPrimaryBouncerInteractor =
+        underTest =
             PrimaryBouncerInteractor(
                 repository,
                 bouncerView,
@@ -94,7 +94,7 @@
 
     @Test
     fun testShow_isScrimmed() {
-        mPrimaryBouncerInteractor.show(true)
+        underTest.show(true)
         verify(repository).setOnScreenTurnedOff(false)
         verify(repository).setKeyguardAuthenticated(null)
         verify(repository).setPrimaryHide(false)
@@ -102,7 +102,7 @@
         verify(repository).setPrimaryScrimmed(true)
         verify(repository).setPanelExpansion(EXPANSION_VISIBLE)
         verify(repository).setPrimaryShowingSoon(true)
-        verify(keyguardStateController).notifyBouncerShowing(true)
+        verify(keyguardStateController).notifyPrimaryBouncerShowing(true)
         verify(mPrimaryBouncerCallbackInteractor).dispatchStartingToShow()
         verify(repository).setPrimaryVisible(true)
         verify(repository).setPrimaryShow(any(KeyguardBouncerModel::class.java))
@@ -118,15 +118,15 @@
     @Test
     fun testShow_keyguardIsDone() {
         `when`(bouncerView.delegate?.showNextSecurityScreenOrFinish()).thenReturn(true)
-        verify(keyguardStateController, never()).notifyBouncerShowing(true)
+        verify(keyguardStateController, never()).notifyPrimaryBouncerShowing(true)
         verify(mPrimaryBouncerCallbackInteractor, never()).dispatchStartingToShow()
     }
 
     @Test
     fun testHide() {
-        mPrimaryBouncerInteractor.hide()
+        underTest.hide()
         verify(falsingCollector).onBouncerHidden()
-        verify(keyguardStateController).notifyBouncerShowing(false)
+        verify(keyguardStateController).notifyPrimaryBouncerShowing(false)
         verify(repository).setPrimaryShowingSoon(false)
         verify(repository).setPrimaryVisible(false)
         verify(repository).setPrimaryHide(true)
@@ -137,7 +137,7 @@
     @Test
     fun testExpansion() {
         `when`(repository.panelExpansionAmount.value).thenReturn(0.5f)
-        mPrimaryBouncerInteractor.setPanelExpansion(0.6f)
+        underTest.setPanelExpansion(0.6f)
         verify(repository).setPanelExpansion(0.6f)
         verify(mPrimaryBouncerCallbackInteractor).dispatchExpansionChanged(0.6f)
     }
@@ -146,7 +146,7 @@
     fun testExpansion_fullyShown() {
         `when`(repository.panelExpansionAmount.value).thenReturn(0.5f)
         `when`(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null)
-        mPrimaryBouncerInteractor.setPanelExpansion(EXPANSION_VISIBLE)
+        underTest.setPanelExpansion(EXPANSION_VISIBLE)
         verify(falsingCollector).onBouncerShown()
         verify(mPrimaryBouncerCallbackInteractor).dispatchFullyShown()
     }
@@ -155,7 +155,7 @@
     fun testExpansion_fullyHidden() {
         `when`(repository.panelExpansionAmount.value).thenReturn(0.5f)
         `when`(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null)
-        mPrimaryBouncerInteractor.setPanelExpansion(EXPANSION_HIDDEN)
+        underTest.setPanelExpansion(EXPANSION_HIDDEN)
         verify(repository).setPrimaryVisible(false)
         verify(repository).setPrimaryShow(null)
         verify(repository).setPrimaryHide(true)
@@ -167,7 +167,7 @@
     @Test
     fun testExpansion_startingToHide() {
         `when`(repository.panelExpansionAmount.value).thenReturn(EXPANSION_VISIBLE)
-        mPrimaryBouncerInteractor.setPanelExpansion(0.1f)
+        underTest.setPanelExpansion(0.1f)
         verify(repository).setPrimaryStartingToHide(true)
         verify(mPrimaryBouncerCallbackInteractor).dispatchStartingToHide()
     }
@@ -175,7 +175,7 @@
     @Test
     fun testShowMessage() {
         val argCaptor = ArgumentCaptor.forClass(BouncerShowMessageModel::class.java)
-        mPrimaryBouncerInteractor.showMessage("abc", null)
+        underTest.showMessage("abc", null)
         verify(repository).setShowMessage(argCaptor.capture())
         assertThat(argCaptor.value.message).isEqualTo("abc")
     }
@@ -184,62 +184,62 @@
     fun testDismissAction() {
         val onDismissAction = mock(ActivityStarter.OnDismissAction::class.java)
         val cancelAction = mock(Runnable::class.java)
-        mPrimaryBouncerInteractor.setDismissAction(onDismissAction, cancelAction)
+        underTest.setDismissAction(onDismissAction, cancelAction)
         verify(bouncerViewDelegate).setDismissAction(onDismissAction, cancelAction)
     }
 
     @Test
     fun testUpdateResources() {
-        mPrimaryBouncerInteractor.updateResources()
+        underTest.updateResources()
         verify(repository).setResourceUpdateRequests(true)
     }
 
     @Test
     fun testNotifyKeyguardAuthenticated() {
-        mPrimaryBouncerInteractor.notifyKeyguardAuthenticated(true)
+        underTest.notifyKeyguardAuthenticated(true)
         verify(repository).setKeyguardAuthenticated(true)
     }
 
     @Test
     fun testNotifyShowedMessage() {
-        mPrimaryBouncerInteractor.onMessageShown()
+        underTest.onMessageShown()
         verify(repository).setShowMessage(null)
     }
 
     @Test
     fun testOnScreenTurnedOff() {
-        mPrimaryBouncerInteractor.onScreenTurnedOff()
+        underTest.onScreenTurnedOff()
         verify(repository).setOnScreenTurnedOff(true)
     }
 
     @Test
     fun testSetKeyguardPosition() {
-        mPrimaryBouncerInteractor.setKeyguardPosition(0f)
+        underTest.setKeyguardPosition(0f)
         verify(repository).setKeyguardPosition(0f)
     }
 
     @Test
     fun testNotifyKeyguardAuthenticatedHandled() {
-        mPrimaryBouncerInteractor.notifyKeyguardAuthenticatedHandled()
+        underTest.notifyKeyguardAuthenticatedHandled()
         verify(repository).setKeyguardAuthenticated(null)
     }
 
     @Test
     fun testNotifyUpdatedResources() {
-        mPrimaryBouncerInteractor.notifyUpdatedResources()
+        underTest.notifyUpdatedResources()
         verify(repository).setResourceUpdateRequests(false)
     }
 
     @Test
     fun testSetBackButtonEnabled() {
-        mPrimaryBouncerInteractor.setBackButtonEnabled(true)
+        underTest.setBackButtonEnabled(true)
         verify(repository).setIsBackButtonEnabled(true)
     }
 
     @Test
     fun testStartDisappearAnimation() {
         val runnable = mock(Runnable::class.java)
-        mPrimaryBouncerInteractor.startDisappearAnimation(runnable)
+        underTest.startDisappearAnimation(runnable)
         verify(repository).setPrimaryStartDisappearAnimation(any(Runnable::class.java))
     }
 
@@ -248,42 +248,42 @@
         `when`(repository.primaryBouncerVisible.value).thenReturn(true)
         `when`(repository.panelExpansionAmount.value).thenReturn(EXPANSION_VISIBLE)
         `when`(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null)
-        assertThat(mPrimaryBouncerInteractor.isFullyShowing()).isTrue()
+        assertThat(underTest.isFullyShowing()).isTrue()
         `when`(repository.primaryBouncerVisible.value).thenReturn(false)
-        assertThat(mPrimaryBouncerInteractor.isFullyShowing()).isFalse()
+        assertThat(underTest.isFullyShowing()).isFalse()
     }
 
     @Test
     fun testIsScrimmed() {
         `when`(repository.primaryBouncerScrimmed.value).thenReturn(true)
-        assertThat(mPrimaryBouncerInteractor.isScrimmed()).isTrue()
+        assertThat(underTest.isScrimmed()).isTrue()
         `when`(repository.primaryBouncerScrimmed.value).thenReturn(false)
-        assertThat(mPrimaryBouncerInteractor.isScrimmed()).isFalse()
+        assertThat(underTest.isScrimmed()).isFalse()
     }
 
     @Test
     fun testIsInTransit() {
         `when`(repository.primaryBouncerShowingSoon.value).thenReturn(true)
-        assertThat(mPrimaryBouncerInteractor.isInTransit()).isTrue()
+        assertThat(underTest.isInTransit()).isTrue()
         `when`(repository.primaryBouncerShowingSoon.value).thenReturn(false)
-        assertThat(mPrimaryBouncerInteractor.isInTransit()).isFalse()
+        assertThat(underTest.isInTransit()).isFalse()
         `when`(repository.panelExpansionAmount.value).thenReturn(0.5f)
-        assertThat(mPrimaryBouncerInteractor.isInTransit()).isTrue()
+        assertThat(underTest.isInTransit()).isTrue()
     }
 
     @Test
     fun testIsAnimatingAway() {
         `when`(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(Runnable {})
-        assertThat(mPrimaryBouncerInteractor.isAnimatingAway()).isTrue()
+        assertThat(underTest.isAnimatingAway()).isTrue()
         `when`(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null)
-        assertThat(mPrimaryBouncerInteractor.isAnimatingAway()).isFalse()
+        assertThat(underTest.isAnimatingAway()).isFalse()
     }
 
     @Test
     fun testWillDismissWithAction() {
         `when`(bouncerViewDelegate.willDismissWithActions()).thenReturn(true)
-        assertThat(mPrimaryBouncerInteractor.willDismissWithAction()).isTrue()
+        assertThat(underTest.willDismissWithAction()).isTrue()
         `when`(bouncerViewDelegate.willDismissWithActions()).thenReturn(false)
-        assertThat(mPrimaryBouncerInteractor.willDismissWithAction()).isFalse()
+        assertThat(underTest.willDismissWithAction()).isFalse()
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
new file mode 100644
index 0000000..75b74b0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.os.Looper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.DismissCallbackRegistry
+import com.android.systemui.keyguard.data.BouncerView
+import com.android.systemui.keyguard.data.BouncerViewDelegate
+import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.utils.os.FakeHandler
+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 PrimaryBouncerInteractorWithCoroutinesTest : SysuiTestCase() {
+    private lateinit var repository: FakeKeyguardBouncerRepository
+    @Mock private lateinit var bouncerView: BouncerView
+    @Mock private lateinit var bouncerViewDelegate: BouncerViewDelegate
+    @Mock private lateinit var keyguardStateController: KeyguardStateController
+    @Mock private lateinit var keyguardSecurityModel: KeyguardSecurityModel
+    @Mock private lateinit var primaryBouncerCallbackInteractor: PrimaryBouncerCallbackInteractor
+    @Mock private lateinit var falsingCollector: FalsingCollector
+    @Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry
+    @Mock private lateinit var keyguardBypassController: KeyguardBypassController
+    @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+    private val mainHandler = FakeHandler(Looper.getMainLooper())
+    private lateinit var underTest: PrimaryBouncerInteractor
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        repository = FakeKeyguardBouncerRepository()
+        underTest =
+            PrimaryBouncerInteractor(
+                repository,
+                bouncerView,
+                mainHandler,
+                keyguardStateController,
+                keyguardSecurityModel,
+                primaryBouncerCallbackInteractor,
+                falsingCollector,
+                dismissCallbackRegistry,
+                keyguardBypassController,
+                keyguardUpdateMonitor,
+            )
+    }
+
+    @Test
+    fun notInteractableWhenExpansionIsBelow90Percent() = runTest {
+        val isInteractable = collectLastValue(underTest.isInteractable)
+
+        repository.setPrimaryVisible(true)
+        repository.setPanelExpansion(0.15f)
+
+        assertThat(isInteractable()).isFalse()
+    }
+
+    @Test
+    fun notInteractableWhenExpansionAbove90PercentButNotVisible() = runTest {
+        val isInteractable = collectLastValue(underTest.isInteractable)
+
+        repository.setPrimaryVisible(false)
+        repository.setPanelExpansion(0.05f)
+
+        assertThat(isInteractable()).isFalse()
+    }
+
+    @Test
+    fun isInteractableWhenExpansionAbove90PercentAndVisible() = runTest {
+        var isInteractable = collectLastValue(underTest.isInteractable)
+
+        repository.setPrimaryVisible(true)
+        repository.setPanelExpansion(0.09f)
+
+        assertThat(isInteractable()).isTrue()
+
+        repository.setPanelExpansion(0.12f)
+        assertThat(isInteractable()).isFalse()
+
+        repository.setPanelExpansion(0f)
+        assertThat(isInteractable()).isTrue()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
new file mode 100644
index 0000000..a5b78b74
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.google.common.truth.Truth.assertThat
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class KeyguardTransitionAnimationFlowTest : SysuiTestCase() {
+    private lateinit var underTest: KeyguardTransitionAnimationFlow
+    private lateinit var repository: FakeKeyguardTransitionRepository
+
+    @Before
+    fun setUp() {
+        repository = FakeKeyguardTransitionRepository()
+        underTest =
+            KeyguardTransitionAnimationFlow(
+                1000.milliseconds,
+                repository.transitions,
+            )
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun zeroDurationThrowsException() = runTest {
+        val flow = underTest.createFlow(duration = 0.milliseconds, onStep = { it })
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun startTimePlusDurationGreaterThanTransitionDurationThrowsException() = runTest {
+        val flow =
+            underTest.createFlow(
+                startTime = 300.milliseconds,
+                duration = 800.milliseconds,
+                onStep = { it }
+            )
+    }
+
+    @Test
+    fun onFinishRunsWhenSpecified() = runTest {
+        val flow =
+            underTest.createFlow(
+                duration = 100.milliseconds,
+                onStep = { it },
+                onFinish = { 10f },
+            )
+        var animationValues = collectLastValue(flow)
+        repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+        assertThat(animationValues()).isEqualTo(10f)
+    }
+
+    @Test
+    fun onCancelRunsWhenSpecified() = runTest {
+        val flow =
+            underTest.createFlow(
+                duration = 100.milliseconds,
+                onStep = { it },
+                onCancel = { 100f },
+            )
+        var animationValues = collectLastValue(flow)
+        repository.sendTransitionStep(step(0.5f, TransitionState.CANCELED))
+        assertThat(animationValues()).isEqualTo(100f)
+    }
+
+    @Test
+    fun usesStartTime() = runTest {
+        val flow =
+            underTest.createFlow(
+                startTime = 500.milliseconds,
+                duration = 500.milliseconds,
+                onStep = { it },
+            )
+        var animationValues = collectLastValue(flow)
+        repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+        assertThat(animationValues()).isEqualTo(0f)
+
+        // Should not emit a value
+        repository.sendTransitionStep(step(0.1f, TransitionState.RUNNING))
+
+        repository.sendTransitionStep(step(0.5f, TransitionState.RUNNING))
+        assertFloat(animationValues(), 0f)
+        repository.sendTransitionStep(step(0.6f, TransitionState.RUNNING))
+        assertFloat(animationValues(), 0.2f)
+        repository.sendTransitionStep(step(0.8f, TransitionState.RUNNING))
+        assertFloat(animationValues(), 0.6f)
+        repository.sendTransitionStep(step(1f, TransitionState.RUNNING))
+        assertFloat(animationValues(), 1f)
+    }
+
+    @Test
+    fun usesInterpolator() = runTest {
+        val flow =
+            underTest.createFlow(
+                duration = 1000.milliseconds,
+                interpolator = EMPHASIZED_ACCELERATE,
+                onStep = { it },
+            )
+        var animationValues = collectLastValue(flow)
+        repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+        assertFloat(animationValues(), EMPHASIZED_ACCELERATE.getInterpolation(0f))
+        repository.sendTransitionStep(step(0.5f, TransitionState.RUNNING))
+        assertFloat(animationValues(), EMPHASIZED_ACCELERATE.getInterpolation(0.5f))
+        repository.sendTransitionStep(step(0.6f, TransitionState.RUNNING))
+        assertFloat(animationValues(), EMPHASIZED_ACCELERATE.getInterpolation(0.6f))
+        repository.sendTransitionStep(step(0.8f, TransitionState.RUNNING))
+        assertFloat(animationValues(), EMPHASIZED_ACCELERATE.getInterpolation(0.8f))
+        repository.sendTransitionStep(step(1f, TransitionState.RUNNING))
+        assertFloat(animationValues(), EMPHASIZED_ACCELERATE.getInterpolation(1f))
+    }
+
+    @Test
+    fun usesOnStepToDoubleValue() = runTest {
+        val flow =
+            underTest.createFlow(
+                duration = 1000.milliseconds,
+                onStep = { it * 2 },
+            )
+        var animationValues = collectLastValue(flow)
+        repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+        assertFloat(animationValues(), 0f)
+        repository.sendTransitionStep(step(0.3f, TransitionState.RUNNING))
+        assertFloat(animationValues(), 0.6f)
+        repository.sendTransitionStep(step(0.6f, TransitionState.RUNNING))
+        assertFloat(animationValues(), 1.2f)
+        repository.sendTransitionStep(step(0.8f, TransitionState.RUNNING))
+        assertFloat(animationValues(), 1.6f)
+        repository.sendTransitionStep(step(1f, TransitionState.RUNNING))
+        assertFloat(animationValues(), 2f)
+    }
+
+    private fun assertFloat(actual: Float?, expected: Float) {
+        assertThat(actual!!).isWithin(0.01f).of(expected)
+    }
+
+    private fun step(
+        value: Float,
+        state: TransitionState = TransitionState.RUNNING
+    ): TransitionStep {
+        return TransitionStep(
+            from = KeyguardState.GONE,
+            to = KeyguardState.DREAMING,
+            value = value,
+            transitionState = state,
+            ownerName = "GoneToDreamingTransitionViewModelTest"
+        )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
index 5571663..706154e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
@@ -16,21 +16,16 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
-import com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.AnimationParams
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel.Companion.DREAM_OVERLAY_ALPHA
-import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel.Companion.DREAM_OVERLAY_TRANSLATION_Y
-import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel.Companion.LOCKSCREEN_ALPHA
-import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel.Companion.LOCKSCREEN_TRANSLATION_Y
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
@@ -39,13 +34,13 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() {
     private lateinit var underTest: DreamingToLockscreenTransitionViewModel
     private lateinit var repository: FakeKeyguardTransitionRepository
+    private lateinit var transitionAnimation: KeyguardTransitionAnimationFlow
 
     @Before
     fun setUp() {
@@ -63,32 +58,18 @@
             val job =
                 underTest.dreamOverlayTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
 
+            // Should start running here...
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
             repository.sendTransitionStep(step(0f))
             repository.sendTransitionStep(step(0.3f))
             repository.sendTransitionStep(step(0.5f))
+            repository.sendTransitionStep(step(0.6f))
+            // ...up to here
+            repository.sendTransitionStep(step(0.8f))
             repository.sendTransitionStep(step(1f))
 
-            // Only 3 values should be present, since the dream overlay runs for a small fraction
-            // of the overall animation time
-            assertThat(values.size).isEqualTo(3)
-            assertThat(values[0])
-                .isEqualTo(
-                    EMPHASIZED_ACCELERATE.getInterpolation(
-                        animValue(0f, DREAM_OVERLAY_TRANSLATION_Y)
-                    ) * pixels
-                )
-            assertThat(values[1])
-                .isEqualTo(
-                    EMPHASIZED_ACCELERATE.getInterpolation(
-                        animValue(0.3f, DREAM_OVERLAY_TRANSLATION_Y)
-                    ) * pixels
-                )
-            assertThat(values[2])
-                .isEqualTo(
-                    EMPHASIZED_ACCELERATE.getInterpolation(
-                        animValue(0.5f, DREAM_OVERLAY_TRANSLATION_Y)
-                    ) * pixels
-                )
+            assertThat(values.size).isEqualTo(5)
+            values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) }
 
             job.cancel()
         }
@@ -100,16 +81,18 @@
 
             val job = underTest.dreamOverlayAlpha.onEach { values.add(it) }.launchIn(this)
 
+            // Should start running here...
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
             repository.sendTransitionStep(step(0f))
             repository.sendTransitionStep(step(0.1f))
             repository.sendTransitionStep(step(0.5f))
+            // ...up to here
             repository.sendTransitionStep(step(1f))
 
             // Only two values should be present, since the dream overlay runs for a small fraction
             // of the overall animation time
-            assertThat(values.size).isEqualTo(2)
-            assertThat(values[0]).isEqualTo(1f - animValue(0f, DREAM_OVERLAY_ALPHA))
-            assertThat(values[1]).isEqualTo(1f - animValue(0.1f, DREAM_OVERLAY_ALPHA))
+            assertThat(values.size).isEqualTo(4)
+            values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
 
             job.cancel()
         }
@@ -121,19 +104,15 @@
 
             val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this)
 
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
             repository.sendTransitionStep(step(0f))
             repository.sendTransitionStep(step(0.1f))
-            // Should start running here...
             repository.sendTransitionStep(step(0.2f))
             repository.sendTransitionStep(step(0.3f))
-            // ...up to here
             repository.sendTransitionStep(step(1f))
 
-            // Only two values should be present, since the dream overlay runs for a small fraction
-            // of the overall animation time
-            assertThat(values.size).isEqualTo(2)
-            assertThat(values[0]).isEqualTo(animValue(0.2f, LOCKSCREEN_ALPHA))
-            assertThat(values[1]).isEqualTo(animValue(0.3f, LOCKSCREEN_ALPHA))
+            assertThat(values.size).isEqualTo(4)
+            values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
 
             job.cancel()
         }
@@ -147,58 +126,27 @@
             val job =
                 underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
 
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
             repository.sendTransitionStep(step(0f))
             repository.sendTransitionStep(step(0.3f))
             repository.sendTransitionStep(step(0.5f))
             repository.sendTransitionStep(step(1f))
 
-            assertThat(values.size).isEqualTo(4)
-            assertThat(values[0])
-                .isEqualTo(
-                    -pixels +
-                        EMPHASIZED_DECELERATE.getInterpolation(
-                            animValue(0f, LOCKSCREEN_TRANSLATION_Y)
-                        ) * pixels
-                )
-            assertThat(values[1])
-                .isEqualTo(
-                    -pixels +
-                        EMPHASIZED_DECELERATE.getInterpolation(
-                            animValue(0.3f, LOCKSCREEN_TRANSLATION_Y)
-                        ) * pixels
-                )
-            assertThat(values[2])
-                .isEqualTo(
-                    -pixels +
-                        EMPHASIZED_DECELERATE.getInterpolation(
-                            animValue(0.5f, LOCKSCREEN_TRANSLATION_Y)
-                        ) * pixels
-                )
-            assertThat(values[3])
-                .isEqualTo(
-                    -pixels +
-                        EMPHASIZED_DECELERATE.getInterpolation(
-                            animValue(1f, LOCKSCREEN_TRANSLATION_Y)
-                        ) * pixels
-                )
+            assertThat(values.size).isEqualTo(5)
+            values.forEach { assertThat(it).isIn(Range.closed(-100f, 0f)) }
 
             job.cancel()
         }
 
-    private fun animValue(stepValue: Float, params: AnimationParams): Float {
-        val totalDuration = TO_LOCKSCREEN_DURATION
-        val startValue = (params.startTime / totalDuration).toFloat()
-
-        val multiplier = (totalDuration / params.duration).toFloat()
-        return (stepValue - startValue) * multiplier
-    }
-
-    private fun step(value: Float): TransitionStep {
+    private fun step(
+        value: Float,
+        state: TransitionState = TransitionState.RUNNING
+    ): TransitionStep {
         return TransitionStep(
             from = KeyguardState.DREAMING,
             to = KeyguardState.LOCKSCREEN,
             value = value,
-            transitionState = TransitionState.RUNNING,
+            transitionState = state,
             ownerName = "DreamingToLockscreenTransitionViewModelTest"
         )
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
index 7fa204b..b15ce10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
@@ -16,18 +16,15 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DREAMING_DURATION
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.AnimationParams
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel.Companion.LOCKSCREEN_ALPHA
-import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel.Companion.LOCKSCREEN_TRANSLATION_Y
+import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
@@ -36,10 +33,9 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class GoneToDreamingTransitionViewModelTest : SysuiTestCase() {
     private lateinit var underTest: GoneToDreamingTransitionViewModel
     private lateinit var repository: FakeKeyguardTransitionRepository
@@ -59,20 +55,18 @@
             val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this)
 
             // Should start running here...
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
             repository.sendTransitionStep(step(0f))
             repository.sendTransitionStep(step(0.1f))
             repository.sendTransitionStep(step(0.2f))
-            // ...up to here
             repository.sendTransitionStep(step(0.3f))
+            // ...up to here
             repository.sendTransitionStep(step(1f))
 
             // Only three values should be present, since the dream overlay runs for a small
-            // fraction
-            // of the overall animation time
-            assertThat(values.size).isEqualTo(3)
-            assertThat(values[0]).isEqualTo(1f - animValue(0f, LOCKSCREEN_ALPHA))
-            assertThat(values[1]).isEqualTo(1f - animValue(0.1f, LOCKSCREEN_ALPHA))
-            assertThat(values[2]).isEqualTo(1f - animValue(0.2f, LOCKSCREEN_ALPHA))
+            // fraction of the overall animation time
+            assertThat(values.size).isEqualTo(5)
+            values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
 
             job.cancel()
         }
@@ -87,45 +81,19 @@
                 underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
 
             // Should start running here...
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
             repository.sendTransitionStep(step(0f))
             repository.sendTransitionStep(step(0.3f))
             repository.sendTransitionStep(step(0.5f))
-            // ...up to here
-            repository.sendTransitionStep(step(1f))
             // And a final reset event on CANCEL
             repository.sendTransitionStep(step(0.8f, TransitionState.CANCELED))
 
-            assertThat(values.size).isEqualTo(4)
-            assertThat(values[0])
-                .isEqualTo(
-                    EMPHASIZED_ACCELERATE.getInterpolation(
-                        animValue(0f, LOCKSCREEN_TRANSLATION_Y)
-                    ) * pixels
-                )
-            assertThat(values[1])
-                .isEqualTo(
-                    EMPHASIZED_ACCELERATE.getInterpolation(
-                        animValue(0.3f, LOCKSCREEN_TRANSLATION_Y)
-                    ) * pixels
-                )
-            assertThat(values[2])
-                .isEqualTo(
-                    EMPHASIZED_ACCELERATE.getInterpolation(
-                        animValue(0.5f, LOCKSCREEN_TRANSLATION_Y)
-                    ) * pixels
-                )
-            assertThat(values[3]).isEqualTo(0f)
+            assertThat(values.size).isEqualTo(5)
+            values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) }
+
             job.cancel()
         }
 
-    private fun animValue(stepValue: Float, params: AnimationParams): Float {
-        val totalDuration = TO_DREAMING_DURATION
-        val startValue = (params.startTime / totalDuration).toFloat()
-
-        val multiplier = (totalDuration / params.duration).toFloat()
-        return (stepValue - startValue) * multiplier
-    }
-
     private fun step(
         value: Float,
         state: TransitionState = TransitionState.RUNNING
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 022afdd..8bd8be5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import android.app.admin.DevicePolicyManager
 import android.content.Intent
 import android.os.UserHandle
 import androidx.test.filters.SmallTest
@@ -35,6 +36,7 @@
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
+import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
@@ -86,6 +88,7 @@
     @Mock private lateinit var activityStarter: ActivityStarter
     @Mock private lateinit var launchAnimator: DialogLaunchAnimator
     @Mock private lateinit var commandQueue: CommandQueue
+    @Mock private lateinit var devicePolicyManager: DevicePolicyManager
 
     private lateinit var underTest: KeyguardBottomAreaViewModel
 
@@ -125,10 +128,21 @@
                 ),
             )
         repository = FakeKeyguardRepository()
+        val featureFlags =
+            FakeFeatureFlags().apply {
+                set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
+                set(Flags.FACE_AUTH_REFACTOR, true)
+            }
 
         val keyguardInteractor =
-            KeyguardInteractor(repository = repository, commandQueue = commandQueue)
+            KeyguardInteractor(
+                repository = repository,
+                commandQueue = commandQueue,
+                featureFlags = featureFlags,
+                bouncerRepository = FakeKeyguardBouncerRepository(),
+            )
         whenever(userTracker.userHandle).thenReturn(mock())
+        whenever(userTracker.userId).thenReturn(10)
         whenever(lockPatternUtils.getStrongAuthForUser(anyInt()))
             .thenReturn(LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED)
         val testDispatcher = StandardTestDispatcher()
@@ -191,12 +205,11 @@
                         keyguardStateController = keyguardStateController,
                         userTracker = userTracker,
                         activityStarter = activityStarter,
-                        featureFlags =
-                            FakeFeatureFlags().apply {
-                                set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
-                            },
+                        featureFlags = featureFlags,
                         repository = { quickAffordanceRepository },
                         launchAnimator = launchAnimator,
+                        devicePolicyManager = devicePolicyManager,
+                        backgroundDispatcher = testDispatcher,
                     ),
                 bottomAreaInteractor = KeyguardBottomAreaInteractor(repository = repository),
                 burnInHelperWrapper = burnInHelperWrapper,
@@ -232,9 +245,45 @@
         }
 
     @Test
+    fun `startButton - hidden when device policy disables all keyguard features`() =
+        testScope.runTest {
+            whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId))
+                .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL)
+            repository.setKeyguardShowing(true)
+            val latest by collectLastValue(underTest.startButton)
+
+            val testConfig =
+                TestConfig(
+                    isVisible = true,
+                    isClickable = true,
+                    isActivated = true,
+                    icon = mock(),
+                    canShowWhileLocked = false,
+                    intent = Intent("action"),
+                )
+            val configKey =
+                setUpQuickAffordanceModel(
+                    position = KeyguardQuickAffordancePosition.BOTTOM_START,
+                    testConfig = testConfig,
+                )
+
+            assertQuickAffordanceViewModel(
+                viewModel = latest,
+                testConfig =
+                    TestConfig(
+                        isVisible = false,
+                    ),
+                configKey = configKey,
+            )
+        }
+
+    @Test
     fun `startButton - in preview mode - visible even when keyguard not showing`() =
         testScope.runTest {
-            underTest.enablePreviewMode(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START)
+            underTest.enablePreviewMode(
+                initiallySelectedSlotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+                shouldHighlightSelectedAffordance = true,
+            )
             repository.setKeyguardShowing(false)
             val latest = collectLastValue(underTest.startButton)
 
@@ -263,6 +312,7 @@
                         icon = icon,
                         canShowWhileLocked = false,
                         intent = Intent("action"),
+                        isSelected = true,
                     ),
                 configKey = configKey,
             )
@@ -270,6 +320,60 @@
         }
 
     @Test
+    fun `endButton - in higlighted preview mode - dimmed when other is selected`() =
+        testScope.runTest {
+            underTest.enablePreviewMode(
+                initiallySelectedSlotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+                shouldHighlightSelectedAffordance = true,
+            )
+            repository.setKeyguardShowing(false)
+            val startButton = collectLastValue(underTest.startButton)
+            val endButton = collectLastValue(underTest.endButton)
+
+            val icon: Icon = mock()
+            setUpQuickAffordanceModel(
+                position = KeyguardQuickAffordancePosition.BOTTOM_START,
+                testConfig =
+                    TestConfig(
+                        isVisible = true,
+                        isClickable = true,
+                        isActivated = true,
+                        icon = icon,
+                        canShowWhileLocked = false,
+                        intent = Intent("action"),
+                    ),
+            )
+            val configKey =
+                setUpQuickAffordanceModel(
+                    position = KeyguardQuickAffordancePosition.BOTTOM_END,
+                    testConfig =
+                        TestConfig(
+                            isVisible = true,
+                            isClickable = true,
+                            isActivated = true,
+                            icon = icon,
+                            canShowWhileLocked = false,
+                            intent = Intent("action"),
+                        ),
+                )
+
+            assertQuickAffordanceViewModel(
+                viewModel = endButton(),
+                testConfig =
+                    TestConfig(
+                        isVisible = true,
+                        isClickable = false,
+                        isActivated = true,
+                        icon = icon,
+                        canShowWhileLocked = false,
+                        intent = Intent("action"),
+                        isDimmed = true,
+                    ),
+                configKey = configKey,
+            )
+        }
+
+    @Test
     fun `endButton - present - visible model - do nothing on click`() =
         testScope.runTest {
             repository.setKeyguardShowing(true)
@@ -377,7 +481,10 @@
     @Test
     fun `alpha - in preview mode - does not change`() =
         testScope.runTest {
-            underTest.enablePreviewMode(null)
+            underTest.enablePreviewMode(
+                initiallySelectedSlotId = null,
+                shouldHighlightSelectedAffordance = false,
+            )
             val value = collectLastValue(underTest.alpha)
 
             assertThat(value()).isEqualTo(1f)
@@ -639,6 +746,8 @@
         assertThat(viewModel.isVisible).isEqualTo(testConfig.isVisible)
         assertThat(viewModel.isClickable).isEqualTo(testConfig.isClickable)
         assertThat(viewModel.isActivated).isEqualTo(testConfig.isActivated)
+        assertThat(viewModel.isSelected).isEqualTo(testConfig.isSelected)
+        assertThat(viewModel.isDimmed).isEqualTo(testConfig.isDimmed)
         if (testConfig.isVisible) {
             assertThat(viewModel.icon).isEqualTo(testConfig.icon)
             viewModel.onClicked.invoke(
@@ -664,6 +773,8 @@
         val icon: Icon? = null,
         val canShowWhileLocked: Boolean = false,
         val intent: Intent? = null,
+        val isSelected: Boolean = false,
+        val isDimmed: Boolean = false,
     ) {
         init {
             check(!isVisible || icon != null) { "Must supply non-null icon if visible!" }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModelTest.kt
index 3727134..586af62 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModelTest.kt
@@ -16,28 +16,29 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyguard.data.BouncerView
 import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.Mockito
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
+@kotlinx.coroutines.ExperimentalCoroutinesApi
 class KeyguardBouncerViewModelTest : SysuiTestCase() {
     lateinit var underTest: KeyguardBouncerViewModel
     @Mock lateinit var bouncerView: BouncerView
@@ -51,7 +52,7 @@
 
     @Test
     fun setMessage() =
-        runBlocking(Dispatchers.Main.immediate) {
+        runTest {
             val flow = MutableStateFlow<BouncerShowMessageModel?>(null)
             var message: BouncerShowMessageModel? = null
             Mockito.`when`(bouncerInteractor.showMessage)
@@ -62,6 +63,8 @@
             flow.value = BouncerShowMessageModel(message = "abc", colorStateList = null)
 
             val job = underTest.bouncerShowMessage.onEach { message = it }.launchIn(this)
+            // Run the tasks that are pending at this point of virtual time.
+            runCurrent()
             assertThat(message?.message).isEqualTo("abc")
             job.cancel()
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
index 539fc2c..d94c108 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
@@ -16,18 +16,15 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_DURATION
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.AnimationParams
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel.Companion.LOCKSCREEN_ALPHA
-import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel.Companion.LOCKSCREEN_TRANSLATION_Y
+import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
@@ -36,10 +33,9 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() {
     private lateinit var underTest: LockscreenToDreamingTransitionViewModel
     private lateinit var repository: FakeKeyguardTransitionRepository
@@ -59,19 +55,18 @@
             val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this)
 
             // Should start running here...
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
             repository.sendTransitionStep(step(0f))
             repository.sendTransitionStep(step(0.1f))
             repository.sendTransitionStep(step(0.2f))
-            // ...up to here
             repository.sendTransitionStep(step(0.3f))
+            // ...up to here
             repository.sendTransitionStep(step(1f))
 
             // Only three values should be present, since the dream overlay runs for a small
             // fraction of the overall animation time
-            assertThat(values.size).isEqualTo(3)
-            assertThat(values[0]).isEqualTo(1f - animValue(0f, LOCKSCREEN_ALPHA))
-            assertThat(values[1]).isEqualTo(1f - animValue(0.1f, LOCKSCREEN_ALPHA))
-            assertThat(values[2]).isEqualTo(1f - animValue(0.2f, LOCKSCREEN_ALPHA))
+            assertThat(values.size).isEqualTo(5)
+            values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
 
             job.cancel()
         }
@@ -85,47 +80,22 @@
             val job =
                 underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
 
-            // Should start running here...
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
             repository.sendTransitionStep(step(0f))
             repository.sendTransitionStep(step(0.3f))
             repository.sendTransitionStep(step(0.5f))
-            // ...up to here
             repository.sendTransitionStep(step(1f))
             // And a final reset event on FINISHED
             repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
 
-            assertThat(values.size).isEqualTo(4)
-            assertThat(values[0])
-                .isEqualTo(
-                    EMPHASIZED_ACCELERATE.getInterpolation(
-                        animValue(0f, LOCKSCREEN_TRANSLATION_Y)
-                    ) * pixels
-                )
-            assertThat(values[1])
-                .isEqualTo(
-                    EMPHASIZED_ACCELERATE.getInterpolation(
-                        animValue(0.3f, LOCKSCREEN_TRANSLATION_Y)
-                    ) * pixels
-                )
-            assertThat(values[2])
-                .isEqualTo(
-                    EMPHASIZED_ACCELERATE.getInterpolation(
-                        animValue(0.5f, LOCKSCREEN_TRANSLATION_Y)
-                    ) * pixels
-                )
-            assertThat(values[3]).isEqualTo(0f)
+            assertThat(values.size).isEqualTo(6)
+            values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) }
+            // Validate finished value
+            assertThat(values[5]).isEqualTo(0f)
 
             job.cancel()
         }
 
-    private fun animValue(stepValue: Float, params: AnimationParams): Float {
-        val totalDuration = TO_DREAMING_DURATION
-        val startValue = (params.startTime / totalDuration).toFloat()
-
-        val multiplier = (totalDuration / params.duration).toFloat()
-        return (stepValue - startValue) * multiplier
-    }
-
     private fun step(
         value: Float,
         state: TransitionState = TransitionState.RUNNING
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
index 759345f..12ec24d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
@@ -16,18 +16,15 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_OCCLUDED_DURATION
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.AnimationParams
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel.Companion.LOCKSCREEN_ALPHA
-import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel.Companion.LOCKSCREEN_TRANSLATION_Y
+import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
@@ -36,10 +33,9 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class LockscreenToOccludedTransitionViewModelTest : SysuiTestCase() {
     private lateinit var underTest: LockscreenToOccludedTransitionViewModel
     private lateinit var repository: FakeKeyguardTransitionRepository
@@ -59,19 +55,18 @@
             val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this)
 
             // Should start running here...
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
             repository.sendTransitionStep(step(0f))
             repository.sendTransitionStep(step(0.1f))
             repository.sendTransitionStep(step(0.4f))
-            // ...up to here
             repository.sendTransitionStep(step(0.7f))
+            // ...up to here
             repository.sendTransitionStep(step(1f))
 
             // Only 3 values should be present, since the dream overlay runs for a small fraction
             // of the overall animation time
-            assertThat(values.size).isEqualTo(3)
-            assertThat(values[0]).isEqualTo(1f - animValue(0f, LOCKSCREEN_ALPHA))
-            assertThat(values[1]).isEqualTo(1f - animValue(0.1f, LOCKSCREEN_ALPHA))
-            assertThat(values[2]).isEqualTo(1f - animValue(0.4f, LOCKSCREEN_ALPHA))
+            assertThat(values.size).isEqualTo(5)
+            values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
 
             job.cancel()
         }
@@ -86,54 +81,51 @@
                 underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
 
             // Should start running here...
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
             repository.sendTransitionStep(step(0f))
             repository.sendTransitionStep(step(0.3f))
             repository.sendTransitionStep(step(0.5f))
             repository.sendTransitionStep(step(1f))
             // ...up to here
 
-            assertThat(values.size).isEqualTo(4)
-            assertThat(values[0])
-                .isEqualTo(
-                    EMPHASIZED_ACCELERATE.getInterpolation(
-                        animValue(0f, LOCKSCREEN_TRANSLATION_Y)
-                    ) * pixels
-                )
-            assertThat(values[1])
-                .isEqualTo(
-                    EMPHASIZED_ACCELERATE.getInterpolation(
-                        animValue(0.3f, LOCKSCREEN_TRANSLATION_Y)
-                    ) * pixels
-                )
-            assertThat(values[2])
-                .isEqualTo(
-                    EMPHASIZED_ACCELERATE.getInterpolation(
-                        animValue(0.5f, LOCKSCREEN_TRANSLATION_Y)
-                    ) * pixels
-                )
-            assertThat(values[3])
-                .isEqualTo(
-                    EMPHASIZED_ACCELERATE.getInterpolation(
-                        animValue(1f, LOCKSCREEN_TRANSLATION_Y)
-                    ) * pixels
-                )
+            assertThat(values.size).isEqualTo(5)
+            values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) }
+
             job.cancel()
         }
 
-    private fun animValue(stepValue: Float, params: AnimationParams): Float {
-        val totalDuration = TO_OCCLUDED_DURATION
-        val startValue = (params.startTime / totalDuration).toFloat()
+    @Test
+    fun lockscreenTranslationYIsCanceled() =
+        runTest(UnconfinedTestDispatcher()) {
+            val values = mutableListOf<Float>()
 
-        val multiplier = (totalDuration / params.duration).toFloat()
-        return (stepValue - startValue) * multiplier
-    }
+            val pixels = 100
+            val job =
+                underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
 
-    private fun step(value: Float): TransitionStep {
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            repository.sendTransitionStep(step(0f))
+            repository.sendTransitionStep(step(0.3f))
+            repository.sendTransitionStep(step(0.3f, TransitionState.CANCELED))
+
+            assertThat(values.size).isEqualTo(4)
+            values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) }
+
+            // Cancel will reset the translation
+            assertThat(values[3]).isEqualTo(0)
+
+            job.cancel()
+        }
+
+    private fun step(
+        value: Float,
+        state: TransitionState = TransitionState.RUNNING,
+    ): TransitionStep {
         return TransitionStep(
             from = KeyguardState.LOCKSCREEN,
             to = KeyguardState.OCCLUDED,
             value = value,
-            transitionState = TransitionState.RUNNING,
+            transitionState = state,
             ownerName = "LockscreenToOccludedTransitionViewModelTest"
         )
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
index 98d292d..0c4e845 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
@@ -16,18 +16,15 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.AnimationParams
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel.Companion.LOCKSCREEN_ALPHA
-import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel.Companion.LOCKSCREEN_TRANSLATION_Y
+import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
@@ -36,10 +33,9 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class OccludedToLockscreenTransitionViewModelTest : SysuiTestCase() {
     private lateinit var underTest: OccludedToLockscreenTransitionViewModel
     private lateinit var repository: FakeKeyguardTransitionRepository
@@ -58,21 +54,19 @@
 
             val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this)
 
-            repository.sendTransitionStep(step(0f))
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            repository.sendTransitionStep(step(0.1f))
             // Should start running here...
             repository.sendTransitionStep(step(0.3f))
             repository.sendTransitionStep(step(0.4f))
             repository.sendTransitionStep(step(0.5f))
-            // ...up to here
             repository.sendTransitionStep(step(0.6f))
+            // ...up to here
+            repository.sendTransitionStep(step(0.8f))
             repository.sendTransitionStep(step(1f))
 
-            // Only two values should be present, since the dream overlay runs for a small fraction
-            // of the overall animation time
-            assertThat(values.size).isEqualTo(3)
-            assertThat(values[0]).isEqualTo(animValue(0.3f, LOCKSCREEN_ALPHA))
-            assertThat(values[1]).isEqualTo(animValue(0.4f, LOCKSCREEN_ALPHA))
-            assertThat(values[2]).isEqualTo(animValue(0.5f, LOCKSCREEN_ALPHA))
+            assertThat(values.size).isEqualTo(5)
+            values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
 
             job.cancel()
         }
@@ -86,58 +80,27 @@
             val job =
                 underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
 
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
             repository.sendTransitionStep(step(0f))
             repository.sendTransitionStep(step(0.3f))
             repository.sendTransitionStep(step(0.5f))
             repository.sendTransitionStep(step(1f))
 
-            assertThat(values.size).isEqualTo(4)
-            assertThat(values[0])
-                .isEqualTo(
-                    -pixels +
-                        EMPHASIZED_DECELERATE.getInterpolation(
-                            animValue(0f, LOCKSCREEN_TRANSLATION_Y)
-                        ) * pixels
-                )
-            assertThat(values[1])
-                .isEqualTo(
-                    -pixels +
-                        EMPHASIZED_DECELERATE.getInterpolation(
-                            animValue(0.3f, LOCKSCREEN_TRANSLATION_Y)
-                        ) * pixels
-                )
-            assertThat(values[2])
-                .isEqualTo(
-                    -pixels +
-                        EMPHASIZED_DECELERATE.getInterpolation(
-                            animValue(0.5f, LOCKSCREEN_TRANSLATION_Y)
-                        ) * pixels
-                )
-            assertThat(values[3])
-                .isEqualTo(
-                    -pixels +
-                        EMPHASIZED_DECELERATE.getInterpolation(
-                            animValue(1f, LOCKSCREEN_TRANSLATION_Y)
-                        ) * pixels
-                )
+            assertThat(values.size).isEqualTo(5)
+            values.forEach { assertThat(it).isIn(Range.closed(-100f, 0f)) }
 
             job.cancel()
         }
 
-    private fun animValue(stepValue: Float, params: AnimationParams): Float {
-        val totalDuration = TO_LOCKSCREEN_DURATION
-        val startValue = (params.startTime / totalDuration).toFloat()
-
-        val multiplier = (totalDuration / params.duration).toFloat()
-        return (stepValue - startValue) * multiplier
-    }
-
-    private fun step(value: Float): TransitionStep {
+    private fun step(
+        value: Float,
+        state: TransitionState = TransitionState.RUNNING
+    ): TransitionStep {
         return TransitionStep(
             from = KeyguardState.OCCLUDED,
             to = KeyguardState.LOCKSCREEN,
             value = value,
-            transitionState = TransitionState.RUNNING,
+            transitionState = state,
             ownerName = "OccludedToLockscreenTransitionViewModelTest"
         )
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt
index c88f84a..54fc493 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt
@@ -71,7 +71,7 @@
         waitUntilComplete(info.animator!!)
     }
 
-    suspend private fun waitUntilComplete(animator: ValueAnimator) {
+    private suspend fun waitUntilComplete(animator: ValueAnimator) {
         withContext(Dispatchers.Main) {
             val startTime = System.currentTimeMillis()
             while (!isTerminated && animator.isRunning()) {
@@ -96,6 +96,6 @@
     override fun setFrameDelay(delay: Long) {}
 
     companion object {
-        private const val MAX_TEST_DURATION = 100L
+        private const val MAX_TEST_DURATION = 200L
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/LogDiffsForTableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/LogDiffsForTableTest.kt
index 3b5e6b9..d1744c6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/table/LogDiffsForTableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/LogDiffsForTableTest.kt
@@ -276,6 +276,52 @@
         }
 
     @Test
+    fun intNullable_logsNull() =
+        testScope.runTest {
+            systemClock.setCurrentTimeMillis(100L)
+            val flow = flow {
+                for (int in listOf(null, 6, null, 8)) {
+                    systemClock.advanceTime(100L)
+                    emit(int)
+                }
+            }
+
+            val flowWithLogging =
+                flow.logDiffsForTable(
+                    tableLogBuffer,
+                    COLUMN_PREFIX,
+                    COLUMN_NAME,
+                    initialValue = 1234,
+                )
+
+            val job = launch { flowWithLogging.collect() }
+
+            val logs = dumpLog()
+            assertThat(logs)
+                .contains(
+                    TABLE_LOG_DATE_FORMAT.format(100L) + SEPARATOR + FULL_NAME + SEPARATOR + "1234"
+                )
+            assertThat(logs)
+                .contains(
+                    TABLE_LOG_DATE_FORMAT.format(200L) + SEPARATOR + FULL_NAME + SEPARATOR + "null"
+                )
+            assertThat(logs)
+                .contains(
+                    TABLE_LOG_DATE_FORMAT.format(300L) + SEPARATOR + FULL_NAME + SEPARATOR + "6"
+                )
+            assertThat(logs)
+                .contains(
+                    TABLE_LOG_DATE_FORMAT.format(400L) + SEPARATOR + FULL_NAME + SEPARATOR + "null"
+                )
+            assertThat(logs)
+                .contains(
+                    TABLE_LOG_DATE_FORMAT.format(500L) + SEPARATOR + FULL_NAME + SEPARATOR + "8"
+                )
+
+            job.cancel()
+        }
+
+    @Test
     fun int_logsUpdates() =
         testScope.runTest {
             systemClock.setCurrentTimeMillis(100L)
@@ -1030,6 +1076,246 @@
             job.cancel()
         }
 
+    // ---- Flow<List<T>> tests ----
+
+    @Test
+    fun list_doesNotLogWhenNotCollected() {
+        val flow = flowOf(listOf(5), listOf(6), listOf(7))
+
+        flow.logDiffsForTable(
+            tableLogBuffer,
+            COLUMN_PREFIX,
+            COLUMN_NAME,
+            initialValue = listOf(1234),
+        )
+
+        val logs = dumpLog()
+        assertThat(logs).doesNotContain(COLUMN_PREFIX)
+        assertThat(logs).doesNotContain(COLUMN_NAME)
+        assertThat(logs).doesNotContain("1234")
+    }
+
+    @Test
+    fun list_logsInitialWhenCollected() =
+        testScope.runTest {
+            val flow = flowOf(listOf(5), listOf(6), listOf(7))
+
+            val flowWithLogging =
+                flow.logDiffsForTable(
+                    tableLogBuffer,
+                    COLUMN_PREFIX,
+                    COLUMN_NAME,
+                    initialValue = listOf(1234),
+                )
+
+            systemClock.setCurrentTimeMillis(3000L)
+            val job = launch { flowWithLogging.collect() }
+
+            val logs = dumpLog()
+            assertThat(logs)
+                .contains(
+                    TABLE_LOG_DATE_FORMAT.format(3000L) +
+                        SEPARATOR +
+                        FULL_NAME +
+                        SEPARATOR +
+                        listOf(1234).toString()
+                )
+
+            job.cancel()
+        }
+
+    @Test
+    fun list_logsUpdates() =
+        testScope.runTest {
+            systemClock.setCurrentTimeMillis(100L)
+
+            val listItems =
+                listOf(listOf("val1", "val2"), listOf("val3"), listOf("val4", "val5", "val6"))
+            val flow = flow {
+                for (list in listItems) {
+                    systemClock.advanceTime(100L)
+                    emit(list)
+                }
+            }
+
+            val flowWithLogging =
+                flow.logDiffsForTable(
+                    tableLogBuffer,
+                    COLUMN_PREFIX,
+                    COLUMN_NAME,
+                    initialValue = listOf("val0", "val00"),
+                )
+
+            val job = launch { flowWithLogging.collect() }
+
+            val logs = dumpLog()
+            assertThat(logs)
+                .contains(
+                    TABLE_LOG_DATE_FORMAT.format(100L) +
+                        SEPARATOR +
+                        FULL_NAME +
+                        SEPARATOR +
+                        listOf("val0", "val00").toString()
+                )
+            assertThat(logs)
+                .contains(
+                    TABLE_LOG_DATE_FORMAT.format(200L) +
+                        SEPARATOR +
+                        FULL_NAME +
+                        SEPARATOR +
+                        listOf("val1", "val2").toString()
+                )
+            assertThat(logs)
+                .contains(
+                    TABLE_LOG_DATE_FORMAT.format(300L) +
+                        SEPARATOR +
+                        FULL_NAME +
+                        SEPARATOR +
+                        listOf("val3").toString()
+                )
+            assertThat(logs)
+                .contains(
+                    TABLE_LOG_DATE_FORMAT.format(400L) +
+                        SEPARATOR +
+                        FULL_NAME +
+                        SEPARATOR +
+                        listOf("val4", "val5", "val6").toString()
+                )
+
+            job.cancel()
+        }
+
+    @Test
+    fun list_doesNotLogIfSameValue() =
+        testScope.runTest {
+            systemClock.setCurrentTimeMillis(100L)
+
+            val listItems =
+                listOf(
+                    listOf("val0", "val00"),
+                    listOf("val1"),
+                    listOf("val1"),
+                    listOf("val1", "val2"),
+                )
+            val flow = flow {
+                for (bool in listItems) {
+                    systemClock.advanceTime(100L)
+                    emit(bool)
+                }
+            }
+
+            val flowWithLogging =
+                flow.logDiffsForTable(
+                    tableLogBuffer,
+                    COLUMN_PREFIX,
+                    COLUMN_NAME,
+                    initialValue = listOf("val0", "val00"),
+                )
+
+            val job = launch { flowWithLogging.collect() }
+
+            val logs = dumpLog()
+
+            val expected1 =
+                TABLE_LOG_DATE_FORMAT.format(100L) +
+                    SEPARATOR +
+                    FULL_NAME +
+                    SEPARATOR +
+                    listOf("val0", "val00").toString()
+            val expected3 =
+                TABLE_LOG_DATE_FORMAT.format(300L) +
+                    SEPARATOR +
+                    FULL_NAME +
+                    SEPARATOR +
+                    listOf("val1").toString()
+            val expected5 =
+                TABLE_LOG_DATE_FORMAT.format(500L) +
+                    SEPARATOR +
+                    FULL_NAME +
+                    SEPARATOR +
+                    listOf("val1", "val2").toString()
+            assertThat(logs).contains(expected1)
+            assertThat(logs).contains(expected3)
+            assertThat(logs).contains(expected5)
+
+            val unexpected2 =
+                TABLE_LOG_DATE_FORMAT.format(200L) +
+                    SEPARATOR +
+                    FULL_NAME +
+                    SEPARATOR +
+                    listOf("val0", "val00")
+            val unexpected4 =
+                TABLE_LOG_DATE_FORMAT.format(400L) +
+                    SEPARATOR +
+                    FULL_NAME +
+                    SEPARATOR +
+                    listOf("val1")
+            assertThat(logs).doesNotContain(unexpected2)
+            assertThat(logs).doesNotContain(unexpected4)
+            job.cancel()
+        }
+
+    @Test
+    fun list_worksForStateFlows() =
+        testScope.runTest {
+            val flow = MutableStateFlow(listOf(1111))
+
+            val flowWithLogging =
+                flow.logDiffsForTable(
+                    tableLogBuffer,
+                    COLUMN_PREFIX,
+                    COLUMN_NAME,
+                    initialValue = listOf(1111),
+                )
+
+            systemClock.setCurrentTimeMillis(50L)
+            val job = launch { flowWithLogging.collect() }
+            assertThat(dumpLog())
+                .contains(
+                    TABLE_LOG_DATE_FORMAT.format(50L) +
+                        SEPARATOR +
+                        FULL_NAME +
+                        SEPARATOR +
+                        listOf(1111).toString()
+                )
+
+            systemClock.setCurrentTimeMillis(100L)
+            flow.emit(listOf(2222, 3333))
+            assertThat(dumpLog())
+                .contains(
+                    TABLE_LOG_DATE_FORMAT.format(100L) +
+                        SEPARATOR +
+                        FULL_NAME +
+                        SEPARATOR +
+                        listOf(2222, 3333).toString()
+                )
+
+            systemClock.setCurrentTimeMillis(200L)
+            flow.emit(listOf(3333, 4444))
+            assertThat(dumpLog())
+                .contains(
+                    TABLE_LOG_DATE_FORMAT.format(200L) +
+                        SEPARATOR +
+                        FULL_NAME +
+                        SEPARATOR +
+                        listOf(3333, 4444).toString()
+                )
+
+            // Doesn't log duplicates
+            systemClock.setCurrentTimeMillis(300L)
+            flow.emit(listOf(3333, 4444))
+            assertThat(dumpLog())
+                .doesNotContain(
+                    TABLE_LOG_DATE_FORMAT.format(300L) +
+                        SEPARATOR +
+                        FULL_NAME +
+                        SEPARATOR +
+                        listOf(3333, 4444).toString()
+                )
+
+            job.cancel()
+        }
+
     private fun dumpLog(): String {
         val outputWriter = StringWriter()
         tableLogBuffer.dump(PrintWriter(outputWriter), arrayOf())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt
index 432764a..c7f3fa0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt
@@ -36,6 +36,17 @@
     }
 
     @Test
+    fun setString_null() {
+        val underTest = TableChange()
+
+        underTest.reset(timestamp = 100, columnPrefix = "", columnName = "fakeName")
+        underTest.set(null as String?)
+
+        assertThat(underTest.hasData()).isTrue()
+        assertThat(underTest.getVal()).isEqualTo("null")
+    }
+
+    @Test
     fun setBoolean_isBoolean() {
         val underTest = TableChange()
 
@@ -58,6 +69,17 @@
     }
 
     @Test
+    fun setInt_null() {
+        val underTest = TableChange()
+
+        underTest.reset(timestamp = 100, columnPrefix = "", columnName = "fakeName")
+        underTest.set(null as Int?)
+
+        assertThat(underTest.hasData()).isTrue()
+        assertThat(underTest.getVal()).isEqualTo("null")
+    }
+
+    @Test
     fun setThenReset_isEmpty() {
         val underTest = TableChange()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataTest.kt
index 1d6e980..670f117 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataTest.kt
@@ -113,5 +113,6 @@
         recommendations = emptyList(),
         dismissIntent = null,
         headphoneConnectionTimeMillis = 0,
-        instanceId = InstanceId.fakeInstanceId(-1)
+        instanceId = InstanceId.fakeInstanceId(-1),
+        expiryTimeMs = 0,
     )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataCombineLatestTest.java
index c0639f3..0a5b124 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataCombineLatestTest.java
@@ -79,7 +79,7 @@
                 USER_ID, true, APP, null, ARTIST, TITLE, null,
                 new ArrayList<>(), new ArrayList<>(), null, PACKAGE, null, null, null, true, null,
                 MediaData.PLAYBACK_LOCAL, false, KEY, false, false, false, 0L,
-                InstanceId.fakeInstanceId(-1), -1, false);
+                InstanceId.fakeInstanceId(-1), -1, false, null);
         mDeviceData = new MediaDeviceData(true, null, DEVICE_NAME, null, false);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataFilterTest.kt
index 9d33e6f..eb6235c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataFilterTest.kt
@@ -27,11 +27,13 @@
 import com.android.systemui.media.controls.models.player.MediaData
 import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
 import com.android.systemui.media.controls.ui.MediaPlayerData
+import com.android.systemui.media.controls.util.MediaFlags
 import com.android.systemui.media.controls.util.MediaUiEventLogger
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import java.util.concurrent.Executor
@@ -40,11 +42,11 @@
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyLong
 import org.mockito.Mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
 
 private const val KEY = "TEST_KEY"
@@ -72,6 +74,7 @@
     @Mock private lateinit var smartspaceData: SmartspaceMediaData
     @Mock private lateinit var smartspaceMediaRecommendationItem: SmartspaceAction
     @Mock private lateinit var logger: MediaUiEventLogger
+    @Mock private lateinit var mediaFlags: MediaFlags
 
     private lateinit var mediaDataFilter: MediaDataFilter
     private lateinit var dataMain: MediaData
@@ -82,6 +85,7 @@
     fun setup() {
         MockitoAnnotations.initMocks(this)
         MediaPlayerData.clear()
+        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(false)
         mediaDataFilter =
             MediaDataFilter(
                 context,
@@ -90,7 +94,8 @@
                 lockscreenUserManager,
                 executor,
                 clock,
-                logger
+                logger,
+                mediaFlags
             )
         mediaDataFilter.mediaDataManager = mediaDataManager
         mediaDataFilter.addListener(listener)
@@ -108,19 +113,20 @@
             )
         dataGuest = dataMain.copy(userId = USER_GUEST)
 
-        `when`(smartspaceData.targetId).thenReturn(SMARTSPACE_KEY)
-        `when`(smartspaceData.isActive).thenReturn(true)
-        `when`(smartspaceData.isValid()).thenReturn(true)
-        `when`(smartspaceData.packageName).thenReturn(SMARTSPACE_PACKAGE)
-        `when`(smartspaceData.recommendations).thenReturn(listOf(smartspaceMediaRecommendationItem))
-        `when`(smartspaceData.headphoneConnectionTimeMillis)
+        whenever(smartspaceData.targetId).thenReturn(SMARTSPACE_KEY)
+        whenever(smartspaceData.isActive).thenReturn(true)
+        whenever(smartspaceData.isValid()).thenReturn(true)
+        whenever(smartspaceData.packageName).thenReturn(SMARTSPACE_PACKAGE)
+        whenever(smartspaceData.recommendations)
+            .thenReturn(listOf(smartspaceMediaRecommendationItem))
+        whenever(smartspaceData.headphoneConnectionTimeMillis)
             .thenReturn(clock.currentTimeMillis() - 100)
-        `when`(smartspaceData.instanceId).thenReturn(SMARTSPACE_INSTANCE_ID)
+        whenever(smartspaceData.instanceId).thenReturn(SMARTSPACE_INSTANCE_ID)
     }
 
     private fun setUser(id: Int) {
-        `when`(lockscreenUserManager.isCurrentProfile(anyInt())).thenReturn(false)
-        `when`(lockscreenUserManager.isCurrentProfile(eq(id))).thenReturn(true)
+        whenever(lockscreenUserManager.isCurrentProfile(anyInt())).thenReturn(false)
+        whenever(lockscreenUserManager.isCurrentProfile(eq(id))).thenReturn(true)
         mediaDataFilter.handleUserSwitched(id)
     }
 
@@ -277,7 +283,7 @@
 
     @Test
     fun hasActiveMediaOrRecommendation_inactiveRecommendationSet_returnsFalse() {
-        `when`(smartspaceData.isActive).thenReturn(false)
+        whenever(smartspaceData.isActive).thenReturn(false)
         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
 
         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
@@ -285,7 +291,7 @@
 
     @Test
     fun hasActiveMediaOrRecommendation_invalidRecommendationSet_returnsFalse() {
-        `when`(smartspaceData.isValid()).thenReturn(false)
+        whenever(smartspaceData.isValid()).thenReturn(false)
         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
 
         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
@@ -293,8 +299,8 @@
 
     @Test
     fun hasActiveMediaOrRecommendation_activeAndValidRecommendationSet_returnsTrue() {
-        `when`(smartspaceData.isActive).thenReturn(true)
-        `when`(smartspaceData.isValid()).thenReturn(true)
+        whenever(smartspaceData.isActive).thenReturn(true)
+        whenever(smartspaceData.isValid()).thenReturn(true)
         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
 
         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue()
@@ -349,7 +355,7 @@
 
     @Test
     fun testOnSmartspaceMediaDataLoaded_noMedia_inactiveRec_showsNothing() {
-        `when`(smartspaceData.isActive).thenReturn(false)
+        whenever(smartspaceData.isActive).thenReturn(false)
 
         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
 
@@ -379,7 +385,7 @@
 
     @Test
     fun testOnSmartspaceMediaDataLoaded_noRecentMedia_inactiveRec_showsNothing() {
-        `when`(smartspaceData.isActive).thenReturn(false)
+        whenever(smartspaceData.isActive).thenReturn(false)
 
         val dataOld = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
         mediaDataFilter.onMediaDataLoaded(KEY, null, dataOld)
@@ -395,7 +401,7 @@
 
     @Test
     fun testOnSmartspaceMediaDataLoaded_hasRecentMedia_inactiveRec_showsNothing() {
-        `when`(smartspaceData.isActive).thenReturn(false)
+        whenever(smartspaceData.isActive).thenReturn(false)
 
         // WHEN we have media that was recently played, but not currently active
         val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
@@ -418,7 +424,7 @@
 
     @Test
     fun testOnSmartspaceMediaDataLoaded_hasRecentMedia_activeInvalidRec_usesMedia() {
-        `when`(smartspaceData.isValid()).thenReturn(false)
+        whenever(smartspaceData.isValid()).thenReturn(false)
 
         // WHEN we have media that was recently played, but not currently active
         val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
@@ -513,4 +519,59 @@
         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
         assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
     }
+
+    @Test
+    fun testOnSmartspaceLoaded_persistentEnabled_isInactive_notifiesListeners() {
+        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
+        whenever(smartspaceData.isActive).thenReturn(false)
+
+        mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
+
+        verify(listener)
+            .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
+        assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
+        assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isTrue()
+    }
+
+    @Test
+    fun testOnSmartspaceLoaded_persistentEnabled_inactive_hasRecentMedia_staysInactive() {
+        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
+        whenever(smartspaceData.isActive).thenReturn(false)
+
+        // If there is media that was recently played but inactive
+        val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
+        mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
+        verify(listener)
+            .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
+
+        // And an inactive recommendation is loaded
+        mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
+
+        // Smartspace is loaded but the media stays inactive
+        verify(listener)
+            .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
+        verify(listener, never())
+            .onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyInt(), anyBoolean())
+        assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
+        assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isTrue()
+    }
+
+    @Test
+    fun testOnSwipeToDismiss_persistentEnabled_recommendationSetInactive() {
+        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
+
+        val data =
+            EMPTY_SMARTSPACE_MEDIA_DATA.copy(
+                targetId = SMARTSPACE_KEY,
+                isActive = true,
+                packageName = SMARTSPACE_PACKAGE,
+                recommendations = listOf(smartspaceMediaRecommendationItem),
+            )
+        mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, data)
+        mediaDataFilter.onSwipeToDismiss()
+
+        verify(mediaDataManager).setRecommendationInactive(eq(SMARTSPACE_KEY))
+        verify(mediaDataManager, never())
+            .dismissSmartspaceRecommendation(eq(SMARTSPACE_KEY), anyLong())
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
index 1ac6695..a07a714 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
@@ -40,15 +40,19 @@
 import androidx.media.utils.MediaConstants
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.InstanceId
+import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.InstanceIdSequenceFake
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.media.controls.models.player.MediaData
+import com.android.systemui.media.controls.models.recommendation.EXTRA_KEY_TRIGGER_SOURCE
+import com.android.systemui.media.controls.models.recommendation.EXTRA_VALUE_TRIGGER_PERIODIC
 import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
 import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaDataProvider
 import com.android.systemui.media.controls.resume.MediaResumeListener
+import com.android.systemui.media.controls.resume.ResumeMediaBrowser
 import com.android.systemui.media.controls.util.MediaControllerFactory
 import com.android.systemui.media.controls.util.MediaFlags
 import com.android.systemui.media.controls.util.MediaUiEventLogger
@@ -82,6 +86,8 @@
 private const val KEY = "KEY"
 private const val KEY_2 = "KEY_2"
 private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID"
+private const val SMARTSPACE_CREATION_TIME = 1234L
+private const val SMARTSPACE_EXPIRY_TIME = 5678L
 private const val PACKAGE_NAME = "com.example.app"
 private const val SYSTEM_PACKAGE_NAME = "com.android.systemui"
 private const val APP_NAME = "SystemUI"
@@ -121,6 +127,7 @@
     @Mock lateinit var pendingIntent: PendingIntent
     @Mock lateinit var activityStarter: ActivityStarter
     @Mock lateinit var smartspaceManager: SmartspaceManager
+    @Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
     lateinit var smartspaceMediaDataProvider: SmartspaceMediaDataProvider
     @Mock lateinit var mediaSmartspaceTarget: SmartspaceTarget
     @Mock private lateinit var mediaRecommendationItem: SmartspaceAction
@@ -182,6 +189,7 @@
                 mediaFlags = mediaFlags,
                 logger = logger,
                 smartspaceManager = smartspaceManager,
+                keyguardUpdateMonitor = keyguardUpdateMonitor
             )
         verify(tunerService)
             .addTunable(capture(tunableCaptor), eq(Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION))
@@ -230,11 +238,14 @@
         whenever(mediaSmartspaceTarget.smartspaceTargetId).thenReturn(KEY_MEDIA_SMARTSPACE)
         whenever(mediaSmartspaceTarget.featureType).thenReturn(SmartspaceTarget.FEATURE_MEDIA)
         whenever(mediaSmartspaceTarget.iconGrid).thenReturn(validRecommendationList)
-        whenever(mediaSmartspaceTarget.creationTimeMillis).thenReturn(1234L)
+        whenever(mediaSmartspaceTarget.creationTimeMillis).thenReturn(SMARTSPACE_CREATION_TIME)
+        whenever(mediaSmartspaceTarget.expiryTimeMillis).thenReturn(SMARTSPACE_EXPIRY_TIME)
         whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(false)
         whenever(mediaFlags.isExplicitIndicatorEnabled()).thenReturn(true)
         whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(false)
+        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(false)
         whenever(logger.getNewInstanceId()).thenReturn(instanceIdSequence.newInstanceId())
+        whenever(keyguardUpdateMonitor.isUserInLockdown(any())).thenReturn(false)
     }
 
     @After
@@ -636,6 +647,59 @@
     }
 
     @Test
+    fun testOnNotificationRemoved_withResumption_tooManyPlayers() {
+        // Given the maximum number of resume controls already
+        val desc =
+            MediaDescription.Builder().run {
+                setTitle(SESSION_TITLE)
+                build()
+            }
+        for (i in 0..ResumeMediaBrowser.MAX_RESUMPTION_CONTROLS) {
+            addResumeControlAndLoad(desc, "$i:$PACKAGE_NAME")
+            clock.advanceTime(1000)
+        }
+
+        // And an active, resumable notification
+        whenever(controller.metadata).thenReturn(metadataBuilder.build())
+        addNotificationAndLoad()
+        val data = mediaDataCaptor.value
+        assertThat(data.resumption).isFalse()
+        mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
+
+        // When the notification is removed
+        mediaDataManager.onNotificationRemoved(KEY)
+
+        // Then it is converted to resumption
+        verify(listener)
+            .onMediaDataLoaded(
+                eq(PACKAGE_NAME),
+                eq(KEY),
+                capture(mediaDataCaptor),
+                eq(true),
+                eq(0),
+                eq(false)
+            )
+        assertThat(mediaDataCaptor.value.resumption).isTrue()
+        assertThat(mediaDataCaptor.value.isPlaying).isFalse()
+
+        // And the oldest resume control was removed
+        verify(listener).onMediaDataRemoved(eq("0:$PACKAGE_NAME"))
+    }
+
+    fun testOnNotificationRemoved_lockDownMode() {
+        whenever(keyguardUpdateMonitor.isUserInLockdown(any())).thenReturn(true)
+
+        addNotificationAndLoad()
+        val data = mediaDataCaptor.value
+        mediaDataManager.onNotificationRemoved(KEY)
+
+        verify(listener, never()).onMediaDataRemoved(eq(KEY))
+        verify(logger, never())
+            .logActiveConvertedToResume(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
+        verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
+    }
+
+    @Test
     fun testAddResumptionControls() {
         // WHEN resumption controls are added
         val desc =
@@ -644,27 +708,8 @@
                 build()
             }
         val currentTime = clock.elapsedRealtime()
-        mediaDataManager.addResumptionControls(
-            USER_ID,
-            desc,
-            Runnable {},
-            session.sessionToken,
-            APP_NAME,
-            pendingIntent,
-            PACKAGE_NAME
-        )
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
-        // THEN the media data indicates that it is for resumption
-        verify(listener)
-            .onMediaDataLoaded(
-                eq(PACKAGE_NAME),
-                eq(null),
-                capture(mediaDataCaptor),
-                eq(true),
-                eq(0),
-                eq(false)
-            )
+        addResumeControlAndLoad(desc)
+
         val data = mediaDataCaptor.value
         assertThat(data.resumption).isTrue()
         assertThat(data.song).isEqualTo(SESSION_TITLE)
@@ -690,27 +735,8 @@
                 build()
             }
         val currentTime = clock.elapsedRealtime()
-        mediaDataManager.addResumptionControls(
-            USER_ID,
-            desc,
-            Runnable {},
-            session.sessionToken,
-            APP_NAME,
-            pendingIntent,
-            PACKAGE_NAME
-        )
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
-        // THEN the media data indicates that it is for resumption
-        verify(listener)
-            .onMediaDataLoaded(
-                eq(PACKAGE_NAME),
-                eq(null),
-                capture(mediaDataCaptor),
-                eq(true),
-                eq(0),
-                eq(false)
-            )
+        addResumeControlAndLoad(desc)
+
         val data = mediaDataCaptor.value
         assertThat(data.resumption).isTrue()
         assertThat(data.song).isEqualTo(SESSION_TITLE)
@@ -723,6 +749,102 @@
     }
 
     @Test
+    fun testAddResumptionControls_hasPartialProgress() {
+        whenever(mediaFlags.isResumeProgressEnabled()).thenReturn(true)
+
+        // WHEN resumption controls are added with partial progress
+        val progress = 0.5
+        val extras =
+            Bundle().apply {
+                putInt(
+                    MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS,
+                    MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED
+                )
+                putDouble(MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, progress)
+            }
+        val desc =
+            MediaDescription.Builder().run {
+                setTitle(SESSION_TITLE)
+                setExtras(extras)
+                build()
+            }
+        addResumeControlAndLoad(desc)
+
+        val data = mediaDataCaptor.value
+        assertThat(data.resumption).isTrue()
+        assertThat(data.resumeProgress).isEqualTo(progress)
+    }
+
+    @Test
+    fun testAddResumptionControls_hasNotPlayedProgress() {
+        whenever(mediaFlags.isResumeProgressEnabled()).thenReturn(true)
+
+        // WHEN resumption controls are added that have not been played
+        val extras =
+            Bundle().apply {
+                putInt(
+                    MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS,
+                    MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_NOT_PLAYED
+                )
+            }
+        val desc =
+            MediaDescription.Builder().run {
+                setTitle(SESSION_TITLE)
+                setExtras(extras)
+                build()
+            }
+        addResumeControlAndLoad(desc)
+
+        val data = mediaDataCaptor.value
+        assertThat(data.resumption).isTrue()
+        assertThat(data.resumeProgress).isEqualTo(0)
+    }
+
+    @Test
+    fun testAddResumptionControls_hasFullProgress() {
+        whenever(mediaFlags.isResumeProgressEnabled()).thenReturn(true)
+
+        // WHEN resumption controls are added with progress info
+        val extras =
+            Bundle().apply {
+                putInt(
+                    MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS,
+                    MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_FULLY_PLAYED
+                )
+            }
+        val desc =
+            MediaDescription.Builder().run {
+                setTitle(SESSION_TITLE)
+                setExtras(extras)
+                build()
+            }
+        addResumeControlAndLoad(desc)
+
+        // THEN the media data includes the progress
+        val data = mediaDataCaptor.value
+        assertThat(data.resumption).isTrue()
+        assertThat(data.resumeProgress).isEqualTo(1)
+    }
+
+    @Test
+    fun testAddResumptionControls_hasNoExtras() {
+        whenever(mediaFlags.isResumeProgressEnabled()).thenReturn(true)
+
+        // WHEN resumption controls are added that do not have any extras
+        val desc =
+            MediaDescription.Builder().run {
+                setTitle(SESSION_TITLE)
+                build()
+            }
+        addResumeControlAndLoad(desc)
+
+        // Resume progress is null
+        val data = mediaDataCaptor.value
+        assertThat(data.resumption).isTrue()
+        assertThat(data.resumeProgress).isEqualTo(null)
+    }
+
+    @Test
     fun testResumptionDisabled_dismissesResumeControls() {
         // WHEN there are resume controls and resumption is switched off
         val desc =
@@ -730,26 +852,8 @@
                 setTitle(SESSION_TITLE)
                 build()
             }
-        mediaDataManager.addResumptionControls(
-            USER_ID,
-            desc,
-            Runnable {},
-            session.sessionToken,
-            APP_NAME,
-            pendingIntent,
-            PACKAGE_NAME
-        )
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
-        verify(listener)
-            .onMediaDataLoaded(
-                eq(PACKAGE_NAME),
-                eq(null),
-                capture(mediaDataCaptor),
-                eq(true),
-                eq(0),
-                eq(false)
-            )
+        addResumeControlAndLoad(desc)
+
         val data = mediaDataCaptor.value
         mediaDataManager.setMediaResumptionEnabled(false)
 
@@ -825,8 +929,9 @@
                         cardAction = mediaSmartspaceBaseAction,
                         recommendations = validRecommendationList,
                         dismissIntent = DISMISS_INTENT,
-                        headphoneConnectionTimeMillis = 1234L,
-                        instanceId = InstanceId.fakeInstanceId(instanceId)
+                        headphoneConnectionTimeMillis = SMARTSPACE_CREATION_TIME,
+                        instanceId = InstanceId.fakeInstanceId(instanceId),
+                        expiryTimeMs = SMARTSPACE_EXPIRY_TIME,
                     )
                 ),
                 eq(false)
@@ -848,8 +953,9 @@
                         targetId = KEY_MEDIA_SMARTSPACE,
                         isActive = true,
                         dismissIntent = DISMISS_INTENT,
-                        headphoneConnectionTimeMillis = 1234L,
-                        instanceId = InstanceId.fakeInstanceId(instanceId)
+                        headphoneConnectionTimeMillis = SMARTSPACE_CREATION_TIME,
+                        instanceId = InstanceId.fakeInstanceId(instanceId),
+                        expiryTimeMs = SMARTSPACE_EXPIRY_TIME,
                     )
                 ),
                 eq(false)
@@ -879,8 +985,9 @@
                         targetId = KEY_MEDIA_SMARTSPACE,
                         isActive = true,
                         dismissIntent = null,
-                        headphoneConnectionTimeMillis = 1234L,
-                        instanceId = InstanceId.fakeInstanceId(instanceId)
+                        headphoneConnectionTimeMillis = SMARTSPACE_CREATION_TIME,
+                        instanceId = InstanceId.fakeInstanceId(instanceId),
+                        expiryTimeMs = SMARTSPACE_EXPIRY_TIME,
                     )
                 ),
                 eq(false)
@@ -909,6 +1016,129 @@
     }
 
     @Test
+    fun testOnSmartspaceMediaDataLoaded_persistentEnabled_headphoneTrigger_isActive() {
+        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
+        smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
+        val instanceId = instanceIdSequence.lastInstanceId
+
+        verify(listener)
+            .onSmartspaceMediaDataLoaded(
+                eq(KEY_MEDIA_SMARTSPACE),
+                eq(
+                    SmartspaceMediaData(
+                        targetId = KEY_MEDIA_SMARTSPACE,
+                        isActive = true,
+                        packageName = PACKAGE_NAME,
+                        cardAction = mediaSmartspaceBaseAction,
+                        recommendations = validRecommendationList,
+                        dismissIntent = DISMISS_INTENT,
+                        headphoneConnectionTimeMillis = SMARTSPACE_CREATION_TIME,
+                        instanceId = InstanceId.fakeInstanceId(instanceId),
+                        expiryTimeMs = SMARTSPACE_EXPIRY_TIME,
+                    )
+                ),
+                eq(false)
+            )
+    }
+
+    @Test
+    fun testOnSmartspaceMediaDataLoaded_persistentEnabled_periodicTrigger_notActive() {
+        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
+        val extras =
+            Bundle().apply {
+                putString("package_name", PACKAGE_NAME)
+                putParcelable("dismiss_intent", DISMISS_INTENT)
+                putString(EXTRA_KEY_TRIGGER_SOURCE, EXTRA_VALUE_TRIGGER_PERIODIC)
+            }
+        whenever(mediaSmartspaceBaseAction.extras).thenReturn(extras)
+
+        smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
+        val instanceId = instanceIdSequence.lastInstanceId
+
+        verify(listener)
+            .onSmartspaceMediaDataLoaded(
+                eq(KEY_MEDIA_SMARTSPACE),
+                eq(
+                    SmartspaceMediaData(
+                        targetId = KEY_MEDIA_SMARTSPACE,
+                        isActive = false,
+                        packageName = PACKAGE_NAME,
+                        cardAction = mediaSmartspaceBaseAction,
+                        recommendations = validRecommendationList,
+                        dismissIntent = DISMISS_INTENT,
+                        headphoneConnectionTimeMillis = SMARTSPACE_CREATION_TIME,
+                        instanceId = InstanceId.fakeInstanceId(instanceId),
+                        expiryTimeMs = SMARTSPACE_EXPIRY_TIME,
+                    )
+                ),
+                eq(false)
+            )
+    }
+
+    @Test
+    fun testOnSmartspaceMediaDataLoaded_persistentEnabled_noTargets_inactive() {
+        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
+
+        smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
+        val instanceId = instanceIdSequence.lastInstanceId
+
+        smartspaceMediaDataProvider.onTargetsAvailable(listOf())
+        uiExecutor.advanceClockToLast()
+        uiExecutor.runAllReady()
+
+        verify(listener)
+            .onSmartspaceMediaDataLoaded(
+                eq(KEY_MEDIA_SMARTSPACE),
+                eq(
+                    SmartspaceMediaData(
+                        targetId = KEY_MEDIA_SMARTSPACE,
+                        isActive = false,
+                        packageName = PACKAGE_NAME,
+                        cardAction = mediaSmartspaceBaseAction,
+                        recommendations = validRecommendationList,
+                        dismissIntent = DISMISS_INTENT,
+                        headphoneConnectionTimeMillis = SMARTSPACE_CREATION_TIME,
+                        instanceId = InstanceId.fakeInstanceId(instanceId),
+                        expiryTimeMs = SMARTSPACE_EXPIRY_TIME,
+                    )
+                ),
+                eq(false)
+            )
+        verify(listener, never()).onSmartspaceMediaDataRemoved(eq(KEY_MEDIA_SMARTSPACE), eq(false))
+    }
+
+    @Test
+    fun testSetRecommendationInactive_notifiesListeners() {
+        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
+
+        smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
+        val instanceId = instanceIdSequence.lastInstanceId
+
+        mediaDataManager.setRecommendationInactive(KEY_MEDIA_SMARTSPACE)
+        uiExecutor.advanceClockToLast()
+        uiExecutor.runAllReady()
+
+        verify(listener)
+            .onSmartspaceMediaDataLoaded(
+                eq(KEY_MEDIA_SMARTSPACE),
+                eq(
+                    SmartspaceMediaData(
+                        targetId = KEY_MEDIA_SMARTSPACE,
+                        isActive = false,
+                        packageName = PACKAGE_NAME,
+                        cardAction = mediaSmartspaceBaseAction,
+                        recommendations = validRecommendationList,
+                        dismissIntent = DISMISS_INTENT,
+                        headphoneConnectionTimeMillis = SMARTSPACE_CREATION_TIME,
+                        instanceId = InstanceId.fakeInstanceId(instanceId),
+                        expiryTimeMs = SMARTSPACE_EXPIRY_TIME,
+                    )
+                ),
+                eq(false)
+            )
+    }
+
+    @Test
     fun testOnSmartspaceMediaDataLoaded_settingDisabled_doesNothing() {
         // WHEN media recommendation setting is off
         Settings.Secure.putInt(
@@ -1667,6 +1897,20 @@
             .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
     }
 
+    @Test
+    fun testSessionDestroyed_noNotificationKey_stillRemoved() {
+        whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
+        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
+
+        // When a notiifcation is added and then removed before it is fully processed
+        mediaDataManager.onNotificationAdded(KEY, mediaNotification)
+        backgroundExecutor.runAllReady()
+        mediaDataManager.onNotificationRemoved(KEY)
+
+        // We still make sure to remove it
+        verify(listener).onMediaDataRemoved(eq(KEY))
+    }
+
     /** Helper function to add a media notification and capture the resulting MediaData */
     private fun addNotificationAndLoad() {
         mediaDataManager.onNotificationAdded(KEY, mediaNotification)
@@ -1690,4 +1934,32 @@
         stateBuilder.setState(PlaybackState.STATE_PAUSED, 0, 1.0f)
         whenever(controller.playbackState).thenReturn(stateBuilder.build())
     }
+
+    /** Helper function to add a resumption control and capture the resulting MediaData */
+    private fun addResumeControlAndLoad(
+        desc: MediaDescription,
+        packageName: String = PACKAGE_NAME
+    ) {
+        mediaDataManager.addResumptionControls(
+            USER_ID,
+            desc,
+            Runnable {},
+            session.sessionToken,
+            APP_NAME,
+            pendingIntent,
+            packageName
+        )
+        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
+        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+
+        verify(listener)
+            .onMediaDataLoaded(
+                eq(packageName),
+                eq(null),
+                capture(mediaDataCaptor),
+                eq(true),
+                eq(0),
+                eq(false)
+            )
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListenerTest.kt
index 92bf84c..8baa06a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListenerTest.kt
@@ -25,13 +25,16 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.media.controls.MediaTestUtils
 import com.android.systemui.media.controls.models.player.MediaData
+import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
 import com.android.systemui.media.controls.util.MediaControllerFactory
+import com.android.systemui.media.controls.util.MediaFlags
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
@@ -48,7 +51,6 @@
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
 import org.mockito.junit.MockitoJUnit
 
 private const val KEY = "KEY"
@@ -56,6 +58,7 @@
 private const val SESSION_KEY = "SESSION_KEY"
 private const val SESSION_ARTIST = "SESSION_ARTIST"
 private const val SESSION_TITLE = "SESSION_TITLE"
+private const val SMARTSPACE_KEY = "SMARTSPACE_KEY"
 
 private fun <T> anyObject(): T {
     return Mockito.anyObject<T>()
@@ -85,10 +88,13 @@
     private lateinit var resumeData: MediaData
     private lateinit var mediaTimeoutListener: MediaTimeoutListener
     private var clock = FakeSystemClock()
+    @Mock private lateinit var mediaFlags: MediaFlags
+    @Mock private lateinit var smartspaceData: SmartspaceMediaData
 
     @Before
     fun setup() {
-        `when`(mediaControllerFactory.create(any())).thenReturn(mediaController)
+        whenever(mediaControllerFactory.create(any())).thenReturn(mediaController)
+        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(false)
         executor = FakeExecutor(clock)
         mediaTimeoutListener =
             MediaTimeoutListener(
@@ -96,7 +102,8 @@
                 executor,
                 logger,
                 statusBarStateController,
-                clock
+                clock,
+                mediaFlags,
             )
         mediaTimeoutListener.timeoutCallback = timeoutCallback
         mediaTimeoutListener.stateCallback = stateCallback
@@ -133,9 +140,9 @@
     @Test
     fun testOnMediaDataLoaded_registersPlaybackListener() {
         val playingState = mock(android.media.session.PlaybackState::class.java)
-        `when`(playingState.state).thenReturn(PlaybackState.STATE_PLAYING)
+        whenever(playingState.state).thenReturn(PlaybackState.STATE_PLAYING)
 
-        `when`(mediaController.playbackState).thenReturn(playingState)
+        whenever(mediaController.playbackState).thenReturn(playingState)
         mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
         verify(mediaController).registerCallback(capture(mediaCallbackCaptor))
         verify(logger).logPlaybackState(eq(KEY), eq(playingState))
@@ -188,8 +195,8 @@
 
         // To playing
         val playingState = mock(android.media.session.PlaybackState::class.java)
-        `when`(playingState.state).thenReturn(PlaybackState.STATE_PLAYING)
-        `when`(mediaController.playbackState).thenReturn(playingState)
+        whenever(playingState.state).thenReturn(PlaybackState.STATE_PLAYING)
+        whenever(mediaController.playbackState).thenReturn(playingState)
         mediaTimeoutListener.onMediaDataLoaded(newKey, KEY, mediaData)
         verify(mediaController).unregisterCallback(anyObject())
         verify(mediaController).registerCallback(anyObject())
@@ -208,8 +215,8 @@
 
         // Migrate, still not playing
         val playingState = mock(android.media.session.PlaybackState::class.java)
-        `when`(playingState.state).thenReturn(PlaybackState.STATE_PAUSED)
-        `when`(mediaController.playbackState).thenReturn(playingState)
+        whenever(playingState.state).thenReturn(PlaybackState.STATE_PAUSED)
+        whenever(mediaController.playbackState).thenReturn(playingState)
         mediaTimeoutListener.onMediaDataLoaded(newKey, KEY, mediaData)
 
         // The number of queued timeout tasks remains the same. The timeout task isn't cancelled nor
@@ -296,8 +303,8 @@
 
         // WHEN we get an update with media playing
         val playingState = mock(android.media.session.PlaybackState::class.java)
-        `when`(playingState.state).thenReturn(PlaybackState.STATE_PLAYING)
-        `when`(mediaController.playbackState).thenReturn(playingState)
+        whenever(playingState.state).thenReturn(PlaybackState.STATE_PLAYING)
+        whenever(mediaController.playbackState).thenReturn(playingState)
         val mediaPlaying = mediaData.copy(isPlaying = true)
         mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaPlaying)
 
@@ -347,7 +354,7 @@
         // WHEN regular media is paused
         val pausedState =
             PlaybackState.Builder().setState(PlaybackState.STATE_PAUSED, 0L, 0f).build()
-        `when`(mediaController.playbackState).thenReturn(pausedState)
+        whenever(mediaController.playbackState).thenReturn(pausedState)
         mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
         assertThat(executor.numPending()).isEqualTo(1)
 
@@ -379,7 +386,7 @@
         // AND that media is resumed
         val playingState =
             PlaybackState.Builder().setState(PlaybackState.STATE_PAUSED, 0L, 0f).build()
-        `when`(mediaController.playbackState).thenReturn(playingState)
+        whenever(mediaController.playbackState).thenReturn(playingState)
         mediaTimeoutListener.onMediaDataLoaded(KEY, PACKAGE, mediaData)
 
         // THEN the timeout length is changed to a regular media control
@@ -593,8 +600,91 @@
         assertThat(executor.numPending()).isEqualTo(1)
     }
 
+    @Test
+    fun testSmartspaceDataLoaded_schedulesTimeout() {
+        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
+        val duration = 60_000
+        val createTime = 1234L
+        val expireTime = createTime + duration
+        whenever(smartspaceData.headphoneConnectionTimeMillis).thenReturn(createTime)
+        whenever(smartspaceData.expiryTimeMs).thenReturn(expireTime)
+
+        mediaTimeoutListener.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
+        assertThat(executor.numPending()).isEqualTo(1)
+        assertThat(executor.advanceClockToNext()).isEqualTo(duration)
+    }
+
+    @Test
+    fun testSmartspaceMediaData_timesOut_invokesCallback() {
+        // Given a pending timeout
+        testSmartspaceDataLoaded_schedulesTimeout()
+
+        executor.runAllReady()
+        verify(timeoutCallback).invoke(eq(SMARTSPACE_KEY), eq(true))
+    }
+
+    @Test
+    fun testSmartspaceDataLoaded_alreadyExists_updatesTimeout() {
+        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
+        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
+        val duration = 100
+        val createTime = 1234L
+        val expireTime = createTime + duration
+        whenever(smartspaceData.headphoneConnectionTimeMillis).thenReturn(createTime)
+        whenever(smartspaceData.expiryTimeMs).thenReturn(expireTime)
+
+        mediaTimeoutListener.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
+        assertThat(executor.numPending()).isEqualTo(1)
+
+        val expiryLonger = expireTime + duration
+        whenever(smartspaceData.expiryTimeMs).thenReturn(expiryLonger)
+        mediaTimeoutListener.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
+
+        assertThat(executor.numPending()).isEqualTo(1)
+        assertThat(executor.advanceClockToNext()).isEqualTo(duration * 2)
+    }
+
+    @Test
+    fun testSmartspaceDataRemoved_cancelTimeout() {
+        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
+
+        mediaTimeoutListener.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
+        assertThat(executor.numPending()).isEqualTo(1)
+
+        mediaTimeoutListener.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
+        assertThat(executor.numPending()).isEqualTo(0)
+    }
+
+    @Test
+    fun testSmartspaceData_dozedPastTimeout_invokedOnWakeup() {
+        // Given a pending timeout
+        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
+        verify(statusBarStateController).addCallback(capture(dozingCallbackCaptor))
+        val duration = 60_000
+        val createTime = 1234L
+        val expireTime = createTime + duration
+        whenever(smartspaceData.headphoneConnectionTimeMillis).thenReturn(createTime)
+        whenever(smartspaceData.expiryTimeMs).thenReturn(expireTime)
+
+        mediaTimeoutListener.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
+        assertThat(executor.numPending()).isEqualTo(1)
+
+        // And we doze past the scheduled timeout
+        val time = clock.currentTimeMillis()
+        clock.setElapsedRealtime(time + duration * 2)
+        assertThat(executor.numPending()).isEqualTo(1)
+
+        // Then when no longer dozing, the timeout runs immediately
+        dozingCallbackCaptor.value.onDozingChanged(false)
+        verify(timeoutCallback).invoke(eq(SMARTSPACE_KEY), eq(true))
+        verify(logger).logTimeout(eq(SMARTSPACE_KEY))
+
+        // and cancel any later scheduled timeout
+        assertThat(executor.numPending()).isEqualTo(0)
+    }
+
     private fun loadMediaDataWithPlaybackState(state: PlaybackState) {
-        `when`(mediaController.playbackState).thenReturn(state)
+        whenever(mediaController.playbackState).thenReturn(state)
         mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
         verify(mediaController).registerCallback(capture(mediaCallbackCaptor))
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
index e4e95e5..a72634b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
@@ -21,18 +21,27 @@
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.util.MathUtils.abs
+import android.view.View
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.InstanceId
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.media.controls.MediaTestUtils
 import com.android.systemui.media.controls.models.player.MediaData
 import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
 import com.android.systemui.media.controls.pipeline.EMPTY_SMARTSPACE_MEDIA_DATA
 import com.android.systemui.media.controls.pipeline.MediaDataManager
 import com.android.systemui.media.controls.ui.MediaHierarchyManager.Companion.LOCATION_QS
+import com.android.systemui.media.controls.util.MediaFlags
 import com.android.systemui.media.controls.util.MediaUiEventLogger
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.FalsingManager
@@ -46,9 +55,12 @@
 import com.android.systemui.util.time.FakeSystemClock
 import javax.inject.Provider
 import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertFalse
 import junit.framework.Assert.assertTrue
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
@@ -56,6 +68,8 @@
 import org.mockito.Mock
 import org.mockito.Mockito.floatThat
 import org.mockito.Mockito.mock
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
@@ -87,11 +101,15 @@
     @Mock lateinit var smartspaceMediaData: SmartspaceMediaData
     @Mock lateinit var mediaCarousel: MediaScrollView
     @Mock lateinit var pageIndicator: PageIndicator
+    @Mock lateinit var mediaFlags: MediaFlags
+    @Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+    @Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
+    private lateinit var transitionRepository: FakeKeyguardTransitionRepository
     @Captor lateinit var listener: ArgumentCaptor<MediaDataManager.Listener>
     @Captor
     lateinit var configListener: ArgumentCaptor<ConfigurationController.ConfigurationListener>
-    @Captor lateinit var newConfig: ArgumentCaptor<Configuration>
     @Captor lateinit var visualStabilityCallback: ArgumentCaptor<OnReorderingAllowedListener>
+    @Captor lateinit var keyguardCallback: ArgumentCaptor<KeyguardUpdateMonitorCallback>
 
     private val clock = FakeSystemClock()
     private lateinit var mediaCarouselController: MediaCarouselController
@@ -99,6 +117,7 @@
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
+        transitionRepository = FakeKeyguardTransitionRepository()
         mediaCarouselController =
             MediaCarouselController(
                 context,
@@ -114,19 +133,23 @@
                 falsingManager,
                 dumpManager,
                 logger,
-                debugLogger
+                debugLogger,
+                mediaFlags,
+                keyguardUpdateMonitor,
+                KeyguardTransitionInteractor(repository = transitionRepository),
             )
         verify(configurationController).addCallback(capture(configListener))
         verify(mediaDataManager).addListener(capture(listener))
         verify(visualStabilityProvider)
             .addPersistentReorderingAllowedListener(capture(visualStabilityCallback))
+        verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCallback))
         whenever(mediaControlPanelFactory.get()).thenReturn(panel)
         whenever(panel.mediaViewController).thenReturn(mediaViewController)
         whenever(mediaDataManager.smartspaceMediaData).thenReturn(smartspaceMediaData)
+        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(false)
         MediaPlayerData.clear()
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testPlayerOrdering() {
         // Test values: key, data, last active time
@@ -303,7 +326,6 @@
         }
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testOrderWithSmartspace_prioritized() {
         testPlayerOrdering()
@@ -311,7 +333,7 @@
         // If smartspace is prioritized
         MediaPlayerData.addMediaRecommendation(
             SMARTSPACE_KEY,
-            EMPTY_SMARTSPACE_MEDIA_DATA,
+            EMPTY_SMARTSPACE_MEDIA_DATA.copy(isActive = true),
             panel,
             true,
             clock
@@ -321,7 +343,6 @@
         assertTrue(MediaPlayerData.playerKeys().elementAt(2).isSsMediaRec)
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testOrderWithSmartspace_prioritized_updatingVisibleMediaPlayers() {
         testPlayerOrdering()
@@ -338,7 +359,6 @@
         assertTrue(MediaPlayerData.visiblePlayerKeys().elementAt(2).isSsMediaRec)
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testOrderWithSmartspace_notPrioritized() {
         testPlayerOrdering()
@@ -346,7 +366,7 @@
         // If smartspace is not prioritized
         MediaPlayerData.addMediaRecommendation(
             SMARTSPACE_KEY,
-            EMPTY_SMARTSPACE_MEDIA_DATA,
+            EMPTY_SMARTSPACE_MEDIA_DATA.copy(isActive = true),
             panel,
             false,
             clock
@@ -357,7 +377,6 @@
         assertTrue(MediaPlayerData.playerKeys().elementAt(idx).isSsMediaRec)
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testPlayingExistingMediaPlayerFromCarousel_visibleMediaPlayersNotUpdated() {
         testPlayerOrdering()
@@ -395,7 +414,6 @@
         )
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testSwipeDismiss_logged() {
         mediaCarouselController.mediaCarouselScrollHandler.dismissCallback.invoke()
@@ -403,7 +421,6 @@
         verify(logger).logSwipeDismiss()
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testSettingsButton_logged() {
         mediaCarouselController.settingsButton.callOnClick()
@@ -411,18 +428,16 @@
         verify(logger).logCarouselSettings()
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testLocationChangeQs_logged() {
         mediaCarouselController.onDesiredLocationChanged(
-            MediaHierarchyManager.LOCATION_QS,
+            LOCATION_QS,
             mediaHostState,
             animate = false
         )
-        verify(logger).logCarouselPosition(MediaHierarchyManager.LOCATION_QS)
+        verify(logger).logCarouselPosition(LOCATION_QS)
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testLocationChangeQqs_logged() {
         mediaCarouselController.onDesiredLocationChanged(
@@ -433,7 +448,6 @@
         verify(logger).logCarouselPosition(MediaHierarchyManager.LOCATION_QQS)
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testLocationChangeLockscreen_logged() {
         mediaCarouselController.onDesiredLocationChanged(
@@ -444,7 +458,6 @@
         verify(logger).logCarouselPosition(MediaHierarchyManager.LOCATION_LOCKSCREEN)
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testLocationChangeDream_logged() {
         mediaCarouselController.onDesiredLocationChanged(
@@ -455,7 +468,6 @@
         verify(logger).logCarouselPosition(MediaHierarchyManager.LOCATION_DREAM_OVERLAY)
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testRecommendationRemoved_logged() {
         val packageName = "smartspace package"
@@ -469,7 +481,6 @@
         verify(logger).logRecommendationRemoved(eq(packageName), eq(instanceId!!))
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testMediaLoaded_ScrollToActivePlayer() {
         listener.value.onMediaDataLoaded(
@@ -527,7 +538,6 @@
         )
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testMediaLoadedFromRecommendationCard_ScrollToActivePlayer() {
         listener.value.onSmartspaceMediaDataLoaded(
@@ -571,7 +581,6 @@
         assertEquals(playerIndex, 0)
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testRecommendationRemovedWhileNotVisible_updateHostVisibility() {
         var result = false
@@ -583,7 +592,6 @@
         assertEquals(true, result)
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testRecommendationRemovedWhileVisible_thenReorders_updateHostVisibility() {
         var result = false
@@ -597,7 +605,6 @@
         assertEquals(true, result)
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testGetCurrentVisibleMediaContentIntent() {
         val clickIntent1 = mock(PendingIntent::class.java)
@@ -644,7 +651,6 @@
         assertEquals(mediaCarouselController.getCurrentVisibleMediaContentIntent(), clickIntent2)
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testSetCurrentState_UpdatePageIndicatorAlphaWhenSquish() {
         val delta = 0.0001F
@@ -666,7 +672,6 @@
         verify(pageIndicator).alpha = floatThat { abs(it - 1.0F) < delta }
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testOnConfigChanged_playersAreAddedBack() {
         listener.value.onMediaDataLoaded(
@@ -692,7 +697,7 @@
 
         val playersSize = MediaPlayerData.players().size
 
-        configListener.value.onConfigChanged(capture(newConfig))
+        configListener.value.onConfigChanged(Configuration())
 
         assertEquals(playersSize, MediaPlayerData.players().size)
         assertEquals(
@@ -700,4 +705,131 @@
             mediaCarouselController.mediaCarouselScrollHandler.visibleMediaIndex
         )
     }
+
+    @Test
+    fun testRecommendation_persistentEnabled_newSmartspaceLoaded_updatesSort() {
+        testRecommendation_persistentEnabled_inactiveSmartspaceDataLoaded_isAdded()
+
+        // When an update to existing smartspace data is loaded
+        listener.value.onSmartspaceMediaDataLoaded(
+            SMARTSPACE_KEY,
+            EMPTY_SMARTSPACE_MEDIA_DATA.copy(isActive = true),
+            true
+        )
+
+        // Then the carousel is updated
+        assertTrue(MediaPlayerData.playerKeys().elementAt(0).data.active)
+        assertTrue(MediaPlayerData.visiblePlayerKeys().elementAt(0).data.active)
+    }
+
+    @Test
+    fun testRecommendation_persistentEnabled_inactiveSmartspaceDataLoaded_isAdded() {
+        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
+
+        // When inactive smartspace data is loaded
+        listener.value.onSmartspaceMediaDataLoaded(
+            SMARTSPACE_KEY,
+            EMPTY_SMARTSPACE_MEDIA_DATA,
+            false
+        )
+
+        // Then it is added to the carousel with correct state
+        assertTrue(MediaPlayerData.playerKeys().elementAt(0).isSsMediaRec)
+        assertFalse(MediaPlayerData.playerKeys().elementAt(0).data.active)
+
+        assertTrue(MediaPlayerData.visiblePlayerKeys().elementAt(0).isSsMediaRec)
+        assertFalse(MediaPlayerData.visiblePlayerKeys().elementAt(0).data.active)
+    }
+
+    @Test
+    fun testOnLockDownMode_hideMediaCarousel() {
+        whenever(keyguardUpdateMonitor.isUserInLockdown(context.userId)).thenReturn(true)
+        mediaCarouselController.mediaCarousel = mediaCarousel
+
+        keyguardCallback.value.onStrongAuthStateChanged(context.userId)
+
+        verify(mediaCarousel).visibility = View.GONE
+    }
+
+    @Test
+    fun testLockDownModeOff_showMediaCarousel() {
+        whenever(keyguardUpdateMonitor.isUserInLockdown(context.userId)).thenReturn(false)
+        whenever(keyguardUpdateMonitor.isUserUnlocked(context.userId)).thenReturn(true)
+        mediaCarouselController.mediaCarousel = mediaCarousel
+
+        keyguardCallback.value.onStrongAuthStateChanged(context.userId)
+
+        verify(mediaCarousel).visibility = View.VISIBLE
+    }
+
+    @ExperimentalCoroutinesApi
+    @Test
+    fun testKeyguardGone_showMediaCarousel() =
+        runTest(UnconfinedTestDispatcher()) {
+            mediaCarouselController.mediaCarousel = mediaCarousel
+
+            val job = mediaCarouselController.listenForAnyStateToGoneKeyguardTransition(this)
+            transitionRepository.sendTransitionStep(
+                TransitionStep(to = KeyguardState.GONE, transitionState = TransitionState.FINISHED)
+            )
+
+            verify(mediaCarousel).visibility = View.VISIBLE
+
+            job.cancel()
+        }
+
+    @Test
+    fun testInvisibleToUserAndExpanded_playersNotListening() {
+        // Add players to carousel.
+        testPlayerOrdering()
+
+        // Make the carousel visible to user in expanded layout.
+        mediaCarouselController.currentlyExpanded = true
+        mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = true
+
+        // panel is the player for each MediaPlayerData.
+        // Verify that seekbar listening attribute in media control panel is set to true.
+        verify(panel, times(MediaPlayerData.players().size)).listening = true
+
+        // Make the carousel invisible to user.
+        mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = false
+
+        // panel is the player for each MediaPlayerData.
+        // Verify that seekbar listening attribute in media control panel is set to false.
+        verify(panel, times(MediaPlayerData.players().size)).listening = false
+    }
+
+    @Test
+    fun testVisibleToUserAndExpanded_playersListening() {
+        // Add players to carousel.
+        testPlayerOrdering()
+
+        // Make the carousel visible to user in expanded layout.
+        mediaCarouselController.currentlyExpanded = true
+        mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = true
+
+        // panel is the player for each MediaPlayerData.
+        // Verify that seekbar listening attribute in media control panel is set to true.
+        verify(panel, times(MediaPlayerData.players().size)).listening = true
+    }
+
+    @Test
+    fun testUMOCollapsed_playersNotListening() {
+        // Add players to carousel.
+        testPlayerOrdering()
+
+        // Make the carousel in collapsed layout.
+        mediaCarouselController.currentlyExpanded = false
+
+        // panel is the player for each MediaPlayerData.
+        // Verify that seekbar listening attribute in media control panel is set to false.
+        verify(panel, times(MediaPlayerData.players().size)).listening = false
+
+        // Make the carousel visible to user.
+        reset(panel)
+        mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = true
+
+        // Verify that seekbar listening attribute in media control panel is set to false.
+        verify(panel, times(MediaPlayerData.players().size)).listening = false
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
index b35dd26..55a33b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
@@ -52,6 +52,7 @@
 import androidx.constraintlayout.widget.Barrier
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.lifecycle.LiveData
+import androidx.media.utils.MediaConstants
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.InstanceId
 import com.android.internal.widget.CachingIconView
@@ -204,6 +205,15 @@
     @Mock private lateinit var coverContainer1: ViewGroup
     @Mock private lateinit var coverContainer2: ViewGroup
     @Mock private lateinit var coverContainer3: ViewGroup
+    @Mock private lateinit var recAppIconItem: CachingIconView
+    @Mock private lateinit var recCardTitle: TextView
+    @Mock private lateinit var recProgressBar1: SeekBar
+    @Mock private lateinit var recProgressBar2: SeekBar
+    @Mock private lateinit var recProgressBar3: SeekBar
+    @Mock private lateinit var recSubtitleMock1: TextView
+    @Mock private lateinit var recSubtitleMock2: TextView
+    @Mock private lateinit var recSubtitleMock3: TextView
+    @Mock private lateinit var coverItem: ImageView
     private lateinit var coverItem1: ImageView
     private lateinit var coverItem2: ImageView
     private lateinit var coverItem3: ImageView
@@ -220,6 +230,7 @@
             this.set(Flags.UMO_TURBULENCE_NOISE, false)
             this.set(Flags.MEDIA_FALSING_PENALTY, true)
             this.set(Flags.MEDIA_EXPLICIT_INDICATOR, true)
+            this.set(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE, false)
         }
 
     @JvmField @Rule val mockito = MockitoJUnit.rule()
@@ -902,6 +913,17 @@
     }
 
     @Test
+    fun bind_resumeState_withProgress() {
+        val progress = 0.5
+        val state = mediaData.copy(resumption = true, resumeProgress = progress)
+
+        player.attachPlayer(viewHolder)
+        player.bindPlayer(state, PACKAGE)
+
+        verify(seekBarViewModel).updateStaticProgress(progress)
+    }
+
+    @Test
     fun bindNotificationActions() {
         val icon = context.getDrawable(android.R.drawable.ic_media_play)
         val bg = context.getDrawable(R.drawable.qs_media_round_button_background)
@@ -2059,6 +2081,114 @@
     }
 
     @Test
+    fun bindRecommendation_setAfterExecutors() {
+        fakeFeatureFlag.set(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE, true)
+        whenever(recommendationViewHolder.mediaAppIcons)
+            .thenReturn(listOf(recAppIconItem, recAppIconItem, recAppIconItem))
+        whenever(recommendationViewHolder.cardTitle).thenReturn(recCardTitle)
+        whenever(recommendationViewHolder.mediaCoverItems)
+            .thenReturn(listOf(coverItem, coverItem, coverItem))
+        whenever(recommendationViewHolder.mediaProgressBars)
+            .thenReturn(listOf(recProgressBar1, recProgressBar2, recProgressBar3))
+        whenever(recommendationViewHolder.mediaSubtitles)
+            .thenReturn(listOf(recSubtitleMock1, recSubtitleMock2, recSubtitleMock3))
+
+        val bmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888)
+        val canvas = Canvas(bmp)
+        canvas.drawColor(Color.RED)
+        val albumArt = Icon.createWithBitmap(bmp)
+        val data =
+            smartspaceData.copy(
+                recommendations =
+                    listOf(
+                        SmartspaceAction.Builder("id1", "title1")
+                            .setSubtitle("subtitle1")
+                            .setIcon(albumArt)
+                            .setExtras(Bundle.EMPTY)
+                            .build(),
+                        SmartspaceAction.Builder("id2", "title2")
+                            .setSubtitle("subtitle1")
+                            .setIcon(albumArt)
+                            .setExtras(Bundle.EMPTY)
+                            .build(),
+                        SmartspaceAction.Builder("id3", "title3")
+                            .setSubtitle("subtitle1")
+                            .setIcon(albumArt)
+                            .setExtras(Bundle.EMPTY)
+                            .build()
+                    )
+            )
+
+        player.attachRecommendation(recommendationViewHolder)
+        player.bindRecommendation(data)
+        bgExecutor.runAllReady()
+        mainExecutor.runAllReady()
+
+        verify(recCardTitle).setTextColor(any<Int>())
+        verify(recAppIconItem, times(3)).setImageDrawable(any(Drawable::class.java))
+        verify(coverItem, times(3)).setImageDrawable(any(Drawable::class.java))
+    }
+
+    @Test
+    fun bindRecommendationWithProgressBars() {
+        fakeFeatureFlag.set(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE, true)
+        whenever(recommendationViewHolder.mediaAppIcons)
+            .thenReturn(listOf(recAppIconItem, recAppIconItem, recAppIconItem))
+        whenever(recommendationViewHolder.cardTitle).thenReturn(recCardTitle)
+        whenever(recommendationViewHolder.mediaCoverItems)
+            .thenReturn(listOf(coverItem, coverItem, coverItem))
+        whenever(recommendationViewHolder.mediaProgressBars)
+            .thenReturn(listOf(recProgressBar1, recProgressBar2, recProgressBar3))
+        whenever(recommendationViewHolder.mediaSubtitles)
+            .thenReturn(listOf(recSubtitleMock1, recSubtitleMock2, recSubtitleMock3))
+
+        val bmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888)
+        val canvas = Canvas(bmp)
+        canvas.drawColor(Color.RED)
+        val albumArt = Icon.createWithBitmap(bmp)
+        val bundle =
+            Bundle().apply {
+                putInt(
+                    MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS,
+                    MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED
+                )
+                putDouble(MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, 0.5)
+            }
+        val data =
+            smartspaceData.copy(
+                recommendations =
+                    listOf(
+                        SmartspaceAction.Builder("id1", "title1")
+                            .setSubtitle("subtitle1")
+                            .setIcon(albumArt)
+                            .setExtras(bundle)
+                            .build(),
+                        SmartspaceAction.Builder("id2", "title2")
+                            .setSubtitle("subtitle1")
+                            .setIcon(albumArt)
+                            .setExtras(Bundle.EMPTY)
+                            .build(),
+                        SmartspaceAction.Builder("id3", "title3")
+                            .setSubtitle("subtitle1")
+                            .setIcon(albumArt)
+                            .setExtras(Bundle.EMPTY)
+                            .build()
+                    )
+            )
+
+        player.attachRecommendation(recommendationViewHolder)
+        player.bindRecommendation(data)
+
+        verify(recProgressBar1).setProgress(50)
+        verify(recProgressBar1).visibility = View.VISIBLE
+        verify(recProgressBar2).visibility = View.GONE
+        verify(recProgressBar3).visibility = View.GONE
+        verify(recSubtitleMock1).visibility = View.GONE
+        verify(recSubtitleMock2).visibility = View.VISIBLE
+        verify(recSubtitleMock3).visibility = View.VISIBLE
+    }
+
+    @Test
     fun onButtonClick_touchRippleFlagEnabled_playsTouchRipple() {
         fakeFeatureFlag.set(Flags.UMO_SURFACE_RIPPLE, true)
         val semanticActions =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt
index 4ed6d7c..af91cdb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt
@@ -22,6 +22,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.media.controls.util.MediaFlags
 import com.android.systemui.util.animation.MeasurementInput
 import com.android.systemui.util.animation.TransitionLayout
 import com.android.systemui.util.animation.TransitionViewState
@@ -32,6 +33,7 @@
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.floatThat
 import org.mockito.Mock
+import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
@@ -55,6 +57,7 @@
     @Mock private lateinit var mediaTitleWidgetState: WidgetState
     @Mock private lateinit var mediaSubTitleWidgetState: WidgetState
     @Mock private lateinit var mediaContainerWidgetState: WidgetState
+    @Mock private lateinit var mediaFlags: MediaFlags
 
     val delta = 0.1F
 
@@ -64,7 +67,13 @@
     fun setup() {
         MockitoAnnotations.initMocks(this)
         mediaViewController =
-            MediaViewController(context, configurationController, mediaHostStatesManager, logger)
+            MediaViewController(
+                context,
+                configurationController,
+                mediaHostStatesManager,
+                logger,
+                mediaFlags,
+            )
     }
 
     @Test
@@ -131,14 +140,12 @@
         whenever(controlWidgetState.y).thenReturn(150F)
         whenever(controlWidgetState.height).thenReturn(20)
         // in current beizer, when the progress reach 0.38, the result will be 0.5
-        mediaViewController.squishViewState(mockViewState, 119F / 200F)
-        verify(detailWidgetState).alpha = floatThat { kotlin.math.abs(it - 0.5F) < delta }
-        mediaViewController.squishViewState(mockViewState, 150F / 200F)
-        verify(detailWidgetState).alpha = floatThat { kotlin.math.abs(it - 1.0F) < delta }
         mediaViewController.squishViewState(mockViewState, 181.4F / 200F)
         verify(controlWidgetState).alpha = floatThat { kotlin.math.abs(it - 0.5F) < delta }
+        verify(detailWidgetState).alpha = floatThat { kotlin.math.abs(it - 1.0F) < delta }
         mediaViewController.squishViewState(mockViewState, 200F / 200F)
         verify(controlWidgetState).alpha = floatThat { kotlin.math.abs(it - 1.0F) < delta }
+        verify(detailWidgetState, times(2)).alpha = floatThat { kotlin.math.abs(it - 1.0F) < delta }
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 7c3c9d2..8fd15c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -29,6 +29,7 @@
 import android.testing.AndroidTestingRunner;
 import android.view.View;
 import android.widget.LinearLayout;
+import android.widget.SeekBar;
 
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.test.filters.SmallTest;
@@ -43,6 +44,8 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -57,6 +60,7 @@
     private static final String TEST_DEVICE_ID_1 = "test_device_id_1";
     private static final String TEST_DEVICE_ID_2 = "test_device_id_2";
     private static final String TEST_SESSION_NAME = "test_session_name";
+
     private static final int TEST_MAX_VOLUME = 20;
     private static final int TEST_CURRENT_VOLUME = 10;
 
@@ -69,6 +73,8 @@
     private IconCompat mIconCompat = mock(IconCompat.class);
     private View mDialogLaunchView = mock(View.class);
 
+    @Captor
+    private ArgumentCaptor<SeekBar.OnSeekBarChangeListener> mOnSeekBarChangeListenerCaptor;
     private MediaOutputAdapter mMediaOutputAdapter;
     private MediaOutputAdapter.MediaDeviceViewHolder mViewHolder;
     private List<MediaDevice> mMediaDevices = new ArrayList<>();
@@ -78,6 +84,7 @@
     @Before
     public void setUp() {
         when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(false);
+        when(mMediaOutputController.isSubStatusSupported()).thenReturn(false);
         when(mMediaOutputController.getMediaItemList()).thenReturn(mMediaItems);
         when(mMediaOutputController.getMediaDevices()).thenReturn(mMediaDevices);
         when(mMediaOutputController.hasAdjustVolumeUserRestriction()).thenReturn(false);
@@ -348,6 +355,24 @@
     }
 
     @Test
+    public void onBindViewHolder_dragSeekbar_setsVolume() {
+        mOnSeekBarChangeListenerCaptor = ArgumentCaptor.forClass(
+                SeekBar.OnSeekBarChangeListener.class);
+        MediaOutputSeekbar mSpySeekbar = spy(mViewHolder.mSeekBar);
+        mViewHolder.mSeekBar = mSpySeekbar;
+        when(mMediaDevice1.getMaxVolume()).thenReturn(TEST_MAX_VOLUME);
+        when(mMediaDevice1.getCurrentVolume()).thenReturn(TEST_MAX_VOLUME);
+        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+        verify(mViewHolder.mSeekBar).setOnSeekBarChangeListener(
+                mOnSeekBarChangeListenerCaptor.capture());
+
+        mOnSeekBarChangeListenerCaptor.getValue().onStopTrackingTouch(mViewHolder.mSeekBar);
+        assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+        verify(mMediaOutputController).logInteractionAdjustVolume(mMediaDevice1);
+    }
+
+    @Test
     public void onBindViewHolder_bindSelectableDevice_verifyView() {
         List<MediaDevice> selectableDevices = new ArrayList<>();
         selectableDevices.add(mMediaDevice2);
@@ -404,6 +429,24 @@
     }
 
     @Test
+    public void subStatusSupported_onBindViewHolder_bindFailedStateDevice_verifyView() {
+        String deviceStatus = "";
+        when(mMediaOutputController.isSubStatusSupported()).thenReturn(true);
+        when(mMediaDevice2.hasDisabledReason()).thenReturn(true);
+        when(mMediaDevice2.getDisableReason()).thenReturn(-1);
+        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
+
+        assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mSubTitleText.getText()).isEqualTo(deviceStatus);
+        assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_2);
+    }
+
+    @Test
     public void onBindViewHolder_inTransferring_bindTransferringDevice_verifyView() {
         when(mMediaOutputController.isAnyDeviceTransferring()).thenReturn(true);
         when(mMediaDevice1.getState()).thenReturn(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index 117751c..7c36e46 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -93,6 +93,11 @@
     private static final String TEST_SONG = "test_song";
     private static final String TEST_SESSION_ID = "test_session_id";
     private static final String TEST_SESSION_NAME = "test_session_name";
+    private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
+    private final ActivityLaunchAnimator.Controller mActivityLaunchAnimatorController = mock(
+            ActivityLaunchAnimator.Controller.class);
+    private final NearbyMediaDevicesManager mNearbyMediaDevicesManager = mock(
+            NearbyMediaDevicesManager.class);
     // Mock
     private MediaController mMediaController = mock(MediaController.class);
     private MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class);
@@ -111,12 +116,7 @@
     private KeyguardManager mKeyguardManager = mock(KeyguardManager.class);
     private PowerExemptionManager mPowerExemptionManager = mock(PowerExemptionManager.class);
     private CommonNotifCollection mNotifCollection = mock(CommonNotifCollection.class);
-    private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
     private FeatureFlags mFlags = mock(FeatureFlags.class);
-    private final ActivityLaunchAnimator.Controller mActivityLaunchAnimatorController = mock(
-            ActivityLaunchAnimator.Controller.class);
-    private final NearbyMediaDevicesManager mNearbyMediaDevicesManager = mock(
-            NearbyMediaDevicesManager.class);
     private View mDialogLaunchView = mock(View.class);
     private MediaOutputController.Callback mCallback = mock(MediaOutputController.Callback.class);
 
@@ -522,6 +522,17 @@
     }
 
     @Test
+    public void logInteractionAdjustVolume_triggersFromMetricLogger() {
+        MediaOutputMetricLogger spyMediaOutputMetricLogger = spy(
+                mMediaOutputController.mMetricLogger);
+        mMediaOutputController.mMetricLogger = spyMediaOutputMetricLogger;
+
+        mMediaOutputController.logInteractionAdjustVolume(mMediaDevice1);
+
+        verify(spyMediaOutputMetricLogger).logInteractionAdjustVolume(mMediaDevice1);
+    }
+
+    @Test
     public void getSessionVolumeMax_triggersFromLocalMediaManager() {
         mMediaOutputController.getSessionVolumeMax();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtilsTest.kt
similarity index 70%
copy from packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
copy to packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtilsTest.kt
index 0e7bf8d..8da1c64 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtilsTest.kt
@@ -22,7 +22,6 @@
 import com.android.systemui.log.LogBufferFactory
 import com.android.systemui.plugins.log.LogBuffer
 import com.android.systemui.plugins.log.LogcatEchoTracker
-import com.android.systemui.temporarydisplay.TemporaryViewInfo
 import com.google.common.truth.Truth.assertThat
 import java.io.PrintWriter
 import java.io.StringWriter
@@ -31,16 +30,15 @@
 import org.mockito.Mockito.mock
 
 @SmallTest
-class MediaTttLoggerTest : SysuiTestCase() {
+class MediaTttLoggerUtilsTest : SysuiTestCase() {
 
     private lateinit var buffer: LogBuffer
-    private lateinit var logger: MediaTttLogger<TemporaryViewInfo>
 
     @Before
-    fun setUp () {
-        buffer = LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
-            .create("buffer", 10)
-        logger = MediaTttLogger(DEVICE_TYPE_TAG, buffer)
+    fun setUp() {
+        buffer =
+            LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
+                .create("buffer", 10)
     }
 
     @Test
@@ -49,35 +47,33 @@
         val id = "test id"
         val packageName = "this.is.a.package"
 
-        logger.logStateChange(stateName, id, packageName)
+        MediaTttLoggerUtils.logStateChange(buffer, TAG, stateName, id, packageName)
 
         val actualString = getStringFromBuffer()
-        assertThat(actualString).contains(DEVICE_TYPE_TAG)
+        assertThat(actualString).contains(TAG)
         assertThat(actualString).contains(stateName)
         assertThat(actualString).contains(id)
         assertThat(actualString).contains(packageName)
     }
 
     @Test
-    fun logPackageNotFound_bufferHasPackageName() {
-        val packageName = "this.is.a.package"
-
-        logger.logPackageNotFound(packageName)
+    fun logStateChangeError_hasState() {
+        MediaTttLoggerUtils.logStateChangeError(buffer, TAG, 3456)
 
         val actualString = getStringFromBuffer()
-        assertThat(actualString).contains(packageName)
+        assertThat(actualString).contains(TAG)
+        assertThat(actualString).contains("3456")
     }
 
     @Test
-    fun logRemovalBypass_bufferHasReasons() {
-        val removalReason = "fakeRemovalReason"
-        val bypassReason = "fakeBypassReason"
+    fun logPackageNotFound_bufferHasPackageName() {
+        val packageName = "this.is.a.package"
 
-        logger.logRemovalBypass(removalReason, bypassReason)
+        MediaTttLoggerUtils.logPackageNotFound(buffer, TAG, packageName)
 
         val actualString = getStringFromBuffer()
-        assertThat(actualString).contains(removalReason)
-        assertThat(actualString).contains(bypassReason)
+        assertThat(actualString).contains(TAG)
+        assertThat(actualString).contains(packageName)
     }
 
     private fun getStringFromBuffer(): String {
@@ -87,4 +83,4 @@
     }
 }
 
-private const val DEVICE_TYPE_TAG = "TEST TYPE"
+private const val TAG = "TEST TAG"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt
index 561867f..4fc9ca7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt
@@ -25,7 +25,6 @@
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.temporarydisplay.TemporaryViewInfo
 import com.android.systemui.util.mockito.any
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
@@ -41,7 +40,6 @@
     private lateinit var appIconFromPackageName: Drawable
     @Mock private lateinit var packageManager: PackageManager
     @Mock private lateinit var applicationInfo: ApplicationInfo
-    @Mock private lateinit var logger: MediaTttLogger<TemporaryViewInfo>
 
     @Before
     fun setUp() {
@@ -68,7 +66,12 @@
     @Test
     fun getIconInfoFromPackageName_nullPackageName_returnsDefault() {
         val iconInfo =
-            MediaTttUtils.getIconInfoFromPackageName(context, appPackageName = null, logger)
+            MediaTttUtils.getIconInfoFromPackageName(
+                context,
+                appPackageName = null,
+                isReceiver = false,
+            ) {
+            }
 
         assertThat(iconInfo.isAppIcon).isFalse()
         assertThat(iconInfo.contentDescription.loadContentDescription(context))
@@ -77,8 +80,47 @@
     }
 
     @Test
+    fun getIconInfoFromPackageName_nullPackageName_isReceiver_returnsDefault() {
+        val iconInfo =
+            MediaTttUtils.getIconInfoFromPackageName(
+                context,
+                appPackageName = null,
+                isReceiver = true,
+            ) {
+            }
+
+        assertThat(iconInfo.isAppIcon).isFalse()
+        assertThat(iconInfo.contentDescription.loadContentDescription(context))
+            .isEqualTo(
+                context.getString(R.string.media_transfer_receiver_content_description_unknown_app)
+            )
+        assertThat(iconInfo.icon).isEqualTo(MediaTttIcon.Resource(R.drawable.ic_cast))
+    }
+
+    @Test
+    fun getIconInfoFromPackageName_nullPackageName_exceptionFnNotTriggered() {
+        var exceptionTriggered = false
+
+        MediaTttUtils.getIconInfoFromPackageName(
+            context,
+            appPackageName = null,
+            isReceiver = false,
+        ) {
+            exceptionTriggered = true
+        }
+
+        assertThat(exceptionTriggered).isFalse()
+    }
+
+    @Test
     fun getIconInfoFromPackageName_invalidPackageName_returnsDefault() {
-        val iconInfo = MediaTttUtils.getIconInfoFromPackageName(context, "fakePackageName", logger)
+        val iconInfo =
+            MediaTttUtils.getIconInfoFromPackageName(
+                context,
+                appPackageName = "fakePackageName",
+                isReceiver = false,
+            ) {
+            }
 
         assertThat(iconInfo.isAppIcon).isFalse()
         assertThat(iconInfo.contentDescription.loadContentDescription(context))
@@ -87,8 +129,58 @@
     }
 
     @Test
+    fun getIconInfoFromPackageName_invalidPackageName_isReceiver_returnsDefault() {
+        val iconInfo =
+            MediaTttUtils.getIconInfoFromPackageName(
+                context,
+                appPackageName = "fakePackageName",
+                isReceiver = true,
+            ) {
+            }
+
+        assertThat(iconInfo.isAppIcon).isFalse()
+        assertThat(iconInfo.contentDescription.loadContentDescription(context))
+            .isEqualTo(
+                context.getString(R.string.media_transfer_receiver_content_description_unknown_app)
+            )
+        assertThat(iconInfo.icon).isEqualTo(MediaTttIcon.Resource(R.drawable.ic_cast))
+    }
+
+    @Test
+    fun getIconInfoFromPackageName_invalidPackageName_exceptionFnTriggered() {
+        var exceptionTriggered = false
+
+        MediaTttUtils.getIconInfoFromPackageName(
+            context,
+            appPackageName = "fakePackageName",
+            isReceiver = false
+        ) { exceptionTriggered = true }
+
+        assertThat(exceptionTriggered).isTrue()
+    }
+
+    @Test
+    fun getIconInfoFromPackageName_invalidPackageName_isReceiver_exceptionFnTriggered() {
+        var exceptionTriggered = false
+
+        MediaTttUtils.getIconInfoFromPackageName(
+            context,
+            appPackageName = "fakePackageName",
+            isReceiver = true
+        ) { exceptionTriggered = true }
+
+        assertThat(exceptionTriggered).isTrue()
+    }
+
+    @Test
     fun getIconInfoFromPackageName_validPackageName_returnsAppInfo() {
-        val iconInfo = MediaTttUtils.getIconInfoFromPackageName(context, PACKAGE_NAME, logger)
+        val iconInfo =
+            MediaTttUtils.getIconInfoFromPackageName(
+                context,
+                PACKAGE_NAME,
+                isReceiver = false,
+            ) {
+            }
 
         assertThat(iconInfo.isAppIcon).isTrue()
         assertThat(iconInfo.icon).isEqualTo(MediaTttIcon.Loaded(appIconFromPackageName))
@@ -96,6 +188,49 @@
     }
 
     @Test
+    fun getIconInfoFromPackageName_validPackageName_isReceiver_returnsAppInfo() {
+        val iconInfo =
+            MediaTttUtils.getIconInfoFromPackageName(
+                context,
+                PACKAGE_NAME,
+                isReceiver = true,
+            ) {
+            }
+
+        assertThat(iconInfo.isAppIcon).isTrue()
+        assertThat(iconInfo.icon).isEqualTo(MediaTttIcon.Loaded(appIconFromPackageName))
+        assertThat(iconInfo.contentDescription.loadContentDescription(context))
+            .isEqualTo(
+                context.getString(
+                    R.string.media_transfer_receiver_content_description_with_app_name,
+                    APP_NAME
+                )
+            )
+    }
+
+    @Test
+    fun getIconInfoFromPackageName_validPackageName_exceptionFnNotTriggered() {
+        var exceptionTriggered = false
+
+        MediaTttUtils.getIconInfoFromPackageName(context, PACKAGE_NAME, isReceiver = false) {
+            exceptionTriggered = true
+        }
+
+        assertThat(exceptionTriggered).isFalse()
+    }
+
+    @Test
+    fun getIconInfoFromPackageName_validPackageName_isReceiver_exceptionFnNotTriggered() {
+        var exceptionTriggered = false
+
+        MediaTttUtils.getIconInfoFromPackageName(context, PACKAGE_NAME, isReceiver = true) {
+            exceptionTriggered = true
+        }
+
+        assertThat(exceptionTriggered).isFalse()
+    }
+
+    @Test
     fun iconInfo_toTintedIcon_loaded() {
         val contentDescription = ContentDescription.Loaded("test")
         val drawable = context.getDrawable(R.drawable.ic_cake)!!
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
index b3e621e..bd042c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
@@ -24,7 +24,6 @@
 import android.view.accessibility.AccessibilityManager
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.media.taptotransfer.MediaTttFlags
-import com.android.systemui.media.taptotransfer.common.MediaTttLogger
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.concurrency.DelayableExecutor
@@ -35,7 +34,7 @@
 class FakeMediaTttChipControllerReceiver(
     commandQueue: CommandQueue,
     context: Context,
-    logger: MediaTttLogger<ChipReceiverInfo>,
+    logger: MediaTttReceiverLogger,
     windowManager: WindowManager,
     mainExecutor: DelayableExecutor,
     accessibilityManager: AccessibilityManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 5e40898..19dd2f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -36,7 +36,6 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.media.taptotransfer.MediaTttFlags
-import com.android.systemui.media.taptotransfer.common.MediaTttLogger
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.concurrency.FakeExecutor
@@ -68,7 +67,7 @@
     @Mock
     private lateinit var applicationInfo: ApplicationInfo
     @Mock
-    private lateinit var logger: MediaTttLogger<ChipReceiverInfo>
+    private lateinit var logger: MediaTttReceiverLogger
     @Mock
     private lateinit var accessibilityManager: AccessibilityManager
     @Mock
@@ -355,7 +354,11 @@
 
         val view = getChipView()
         assertThat(view.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
-        assertThat(view.getAppIconView().contentDescription).isEqualTo(APP_NAME)
+        assertThat(view.getAppIconView().contentDescription)
+            .isEqualTo(context.getString(
+                R.string.media_transfer_receiver_content_description_with_app_name,
+                APP_NAME,
+            ))
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLoggerTest.kt
similarity index 71%
rename from packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLoggerTest.kt
index 0e7bf8d..95df484 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLoggerTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.media.taptotransfer.common
+package com.android.systemui.media.taptotransfer.receiver
 
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -22,7 +22,6 @@
 import com.android.systemui.log.LogBufferFactory
 import com.android.systemui.plugins.log.LogBuffer
 import com.android.systemui.plugins.log.LogcatEchoTracker
-import com.android.systemui.temporarydisplay.TemporaryViewInfo
 import com.google.common.truth.Truth.assertThat
 import java.io.PrintWriter
 import java.io.StringWriter
@@ -31,16 +30,17 @@
 import org.mockito.Mockito.mock
 
 @SmallTest
-class MediaTttLoggerTest : SysuiTestCase() {
+class MediaTttReceiverLoggerTest : SysuiTestCase() {
 
     private lateinit var buffer: LogBuffer
-    private lateinit var logger: MediaTttLogger<TemporaryViewInfo>
+    private lateinit var logger: MediaTttReceiverLogger
 
     @Before
-    fun setUp () {
-        buffer = LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
-            .create("buffer", 10)
-        logger = MediaTttLogger(DEVICE_TYPE_TAG, buffer)
+    fun setUp() {
+        buffer =
+            LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
+                .create("buffer", 10)
+        logger = MediaTttReceiverLogger(buffer)
     }
 
     @Test
@@ -52,13 +52,20 @@
         logger.logStateChange(stateName, id, packageName)
 
         val actualString = getStringFromBuffer()
-        assertThat(actualString).contains(DEVICE_TYPE_TAG)
         assertThat(actualString).contains(stateName)
         assertThat(actualString).contains(id)
         assertThat(actualString).contains(packageName)
     }
 
     @Test
+    fun logStateChangeError_hasState() {
+        logger.logStateChangeError(3456)
+
+        val actualString = getStringFromBuffer()
+        assertThat(actualString).contains("3456")
+    }
+
+    @Test
     fun logPackageNotFound_bufferHasPackageName() {
         val packageName = "this.is.a.package"
 
@@ -68,23 +75,9 @@
         assertThat(actualString).contains(packageName)
     }
 
-    @Test
-    fun logRemovalBypass_bufferHasReasons() {
-        val removalReason = "fakeRemovalReason"
-        val bypassReason = "fakeBypassReason"
-
-        logger.logRemovalBypass(removalReason, bypassReason)
-
-        val actualString = getStringFromBuffer()
-        assertThat(actualString).contains(removalReason)
-        assertThat(actualString).contains(bypassReason)
-    }
-
     private fun getStringFromBuffer(): String {
         val stringWriter = StringWriter()
         buffer.dump(PrintWriter(stringWriter), tailLength = 0)
         return stringWriter.toString()
     }
 }
-
-private const val DEVICE_TYPE_TAG = "TEST TYPE"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
index 54d4460..db890f6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
@@ -40,16 +40,14 @@
 import com.android.systemui.common.shared.model.Text.Companion.loadText
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.media.taptotransfer.MediaTttFlags
-import com.android.systemui.media.taptotransfer.common.MediaTttLogger
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
+import com.android.systemui.temporarydisplay.chipbar.ChipbarAnimator
 import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
-import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo
 import com.android.systemui.temporarydisplay.chipbar.ChipbarLogger
-import com.android.systemui.temporarydisplay.chipbar.FakeChipbarCoordinator
 import com.android.systemui.temporarydisplay.chipbar.SwipeChipbarAwayGestureHandler
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
@@ -92,7 +90,7 @@
     @Mock private lateinit var falsingManager: FalsingManager
     @Mock private lateinit var falsingCollector: FalsingCollector
     @Mock private lateinit var chipbarLogger: ChipbarLogger
-    @Mock private lateinit var logger: MediaTttLogger<ChipbarInfo>
+    @Mock private lateinit var logger: MediaTttSenderLogger
     @Mock private lateinit var mediaTttFlags: MediaTttFlags
     @Mock private lateinit var packageManager: PackageManager
     @Mock private lateinit var powerManager: PowerManager
@@ -139,7 +137,7 @@
         uiEventLogger = MediaTttSenderUiEventLogger(uiEventLoggerFake)
 
         chipbarCoordinator =
-            FakeChipbarCoordinator(
+            ChipbarCoordinator(
                 context,
                 chipbarLogger,
                 windowManager,
@@ -148,6 +146,7 @@
                 configurationController,
                 dumpManager,
                 powerManager,
+                ChipbarAnimator(),
                 falsingManager,
                 falsingCollector,
                 swipeHandler,
@@ -163,6 +162,7 @@
                 chipbarCoordinator,
                 commandQueue,
                 context,
+                dumpManager,
                 logger,
                 mediaTttFlags,
                 uiEventLogger,
@@ -181,6 +181,7 @@
                 chipbarCoordinator,
                 commandQueue,
                 context,
+                dumpManager,
                 logger,
                 mediaTttFlags,
                 uiEventLogger,
@@ -542,6 +543,7 @@
         val viewCaptor = ArgumentCaptor.forClass(View::class.java)
         verify(windowManager).addView(viewCaptor.capture(), any())
         verify(windowManager).removeView(viewCaptor.value)
+        verify(logger).logStateMapRemoval(eq(DEFAULT_ID), any())
     }
 
     @Test
@@ -622,7 +624,7 @@
     }
 
     @Test
-    fun commandQueueCallback_receiverSucceededThenReceiverTriggered_invalidTransitionLogged() {
+    fun commandQueueCallback_receiverSucceededThenThisDeviceSucceeded_invalidTransitionLogged() {
         displayReceiverTriggered()
         commandQueueCallback.updateMediaTapToTransferSenderDisplay(
             StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
@@ -632,7 +634,7 @@
         reset(windowManager)
 
         commandQueueCallback.updateMediaTapToTransferSenderDisplay(
-            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
             routeInfo,
             null
         )
@@ -642,7 +644,7 @@
     }
 
     @Test
-    fun commandQueueCallback_thisDeviceSucceededThenThisDeviceTriggered_invalidTransitionLogged() {
+    fun commandQueueCallback_thisDeviceSucceededThenReceiverSucceeded_invalidTransitionLogged() {
         displayThisDeviceTriggered()
         commandQueueCallback.updateMediaTapToTransferSenderDisplay(
             StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
@@ -652,7 +654,7 @@
         reset(windowManager)
 
         commandQueueCallback.updateMediaTapToTransferSenderDisplay(
-            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
             routeInfo,
             null
         )
@@ -741,6 +743,99 @@
         verify(windowManager, never()).addView(any(), any())
     }
 
+    /** Regression test for b/266217596. */
+    @Test
+    fun toReceiver_triggeredThenFar_thenSucceeded_updatesToSucceeded() {
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
+            routeInfo,
+            null,
+        )
+
+        // WHEN a FAR command comes in
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
+            routeInfo,
+            null,
+        )
+
+        // THEN it is ignored and the chipbar is stilled displayed
+        val chipbarView = getChipbarView()
+        assertThat(chipbarView.getChipText())
+            .isEqualTo(ChipStateSender.TRANSFER_TO_RECEIVER_TRIGGERED.getExpectedStateText())
+        assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.VISIBLE)
+        verify(windowManager, never()).removeView(any())
+
+        // WHEN a SUCCEEDED command comes in
+        val succeededRouteInfo =
+            MediaRoute2Info.Builder(DEFAULT_ID, "Tablet Succeeded")
+                .addFeature("feature")
+                .setClientPackageName(PACKAGE_NAME)
+                .build()
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
+            succeededRouteInfo,
+            /* undoCallback= */ object : IUndoMediaTransferCallback.Stub() {
+                override fun onUndoTriggered() {}
+            },
+        )
+
+        // THEN it is *not* marked as an invalid transition and the chipbar updates to the succeeded
+        // state. (The "invalid transition" would be FAR => SUCCEEDED.)
+        assertThat(chipbarView.getChipText())
+            .isEqualTo(
+                ChipStateSender.TRANSFER_TO_RECEIVER_SUCCEEDED.getExpectedStateText(
+                    "Tablet Succeeded"
+                )
+            )
+        assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.GONE)
+        assertThat(chipbarView.getUndoButton().visibility).isEqualTo(View.VISIBLE)
+    }
+
+    /** Regression test for b/266217596. */
+    @Test
+    fun toThisDevice_triggeredThenFar_thenSucceeded_updatesToSucceeded() {
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
+            routeInfo,
+            null,
+        )
+
+        // WHEN a FAR command comes in
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
+            routeInfo,
+            null,
+        )
+
+        // THEN it is ignored and the chipbar is stilled displayed
+        val chipbarView = getChipbarView()
+        assertThat(chipbarView.getChipText())
+            .isEqualTo(ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED.getExpectedStateText())
+        assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.VISIBLE)
+        verify(windowManager, never()).removeView(any())
+
+        // WHEN a SUCCEEDED command comes in
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
+            routeInfo,
+            /* undoCallback= */ object : IUndoMediaTransferCallback.Stub() {
+                override fun onUndoTriggered() {}
+            },
+        )
+
+        // THEN it is *not* marked as an invalid transition and the chipbar updates to the succeeded
+        // state. (The "invalid transition" would be FAR => SUCCEEDED.)
+        assertThat(chipbarView.getChipText())
+            .isEqualTo(
+                ChipStateSender.TRANSFER_TO_THIS_DEVICE_SUCCEEDED.getExpectedStateText(
+                    "Tablet Succeeded"
+                )
+            )
+        assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.GONE)
+        assertThat(chipbarView.getUndoButton().visibility).isEqualTo(View.VISIBLE)
+    }
+
     @Test
     fun receivesNewStateFromCommandQueue_isLogged() {
         commandQueueCallback.updateMediaTapToTransferSenderDisplay(
@@ -934,6 +1029,7 @@
                 mockChipbarCoordinator,
                 commandQueue,
                 context,
+                dumpManager,
                 logger,
                 mediaTttFlags,
                 uiEventLogger,
@@ -960,6 +1056,7 @@
                 mockChipbarCoordinator,
                 commandQueue,
                 context,
+                dumpManager,
                 logger,
                 mediaTttFlags,
                 uiEventLogger,
@@ -977,9 +1074,10 @@
         verify(mockChipbarCoordinator).registerListener(capture(listenerCaptor))
 
         // WHEN the listener is notified that the view has been removed
-        listenerCaptor.value.onInfoPermanentlyRemoved(DEFAULT_ID)
+        listenerCaptor.value.onInfoPermanentlyRemoved(DEFAULT_ID, "reason")
 
         // THEN the media coordinator unregisters the listener
+        verify(logger).logStateMapRemoval(DEFAULT_ID, "reason")
         verify(mockChipbarCoordinator).unregisterListener(listenerCaptor.value)
     }
 
@@ -991,6 +1089,7 @@
                 mockChipbarCoordinator,
                 commandQueue,
                 context,
+                dumpManager,
                 logger,
                 mediaTttFlags,
                 uiEventLogger,
@@ -1008,7 +1107,7 @@
         verify(mockChipbarCoordinator).registerListener(capture(listenerCaptor))
 
         // WHEN the listener is notified that a different view has been removed
-        listenerCaptor.value.onInfoPermanentlyRemoved("differentViewId")
+        listenerCaptor.value.onInfoPermanentlyRemoved("differentViewId", "reason")
 
         // THEN the media coordinator doesn't unregister the listener
         verify(mockChipbarCoordinator, never()).unregisterListener(listenerCaptor.value)
@@ -1022,6 +1121,7 @@
                 mockChipbarCoordinator,
                 commandQueue,
                 context,
+                dumpManager,
                 logger,
                 mediaTttFlags,
                 uiEventLogger,
@@ -1057,6 +1157,7 @@
                 mockChipbarCoordinator,
                 commandQueue,
                 context,
+                dumpManager,
                 logger,
                 mediaTttFlags,
                 uiEventLogger,
@@ -1086,10 +1187,173 @@
         verify(mockChipbarCoordinator, atLeast(1)).registerListener(capture(listenerCaptor))
 
         // THEN one of them is removed
-        listenerCaptor.value.onInfoPermanentlyRemoved("route1")
+        listenerCaptor.value.onInfoPermanentlyRemoved("route1", "reason")
 
         // THEN the media coordinator doesn't unregister the listener (since route2 is still active)
         verify(mockChipbarCoordinator, never()).unregisterListener(listenerCaptor.value)
+        verify(logger).logStateMapRemoval("route1", "reason")
+    }
+
+    /** Regression test for b/266218672. */
+    @Test
+    fun twoIdsDisplayed_oldIdIsFar_viewStillDisplayed() {
+        // WHEN there are two different media transfers with different IDs
+        val route1 =
+            MediaRoute2Info.Builder("route1", OTHER_DEVICE_NAME)
+                .addFeature("feature")
+                .setClientPackageName(PACKAGE_NAME)
+                .build()
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
+            route1,
+            null,
+        )
+        verify(windowManager).addView(any(), any())
+        reset(windowManager)
+
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
+            MediaRoute2Info.Builder("route2", "Route 2 name")
+                .addFeature("feature")
+                .setClientPackageName(PACKAGE_NAME)
+                .build(),
+            null,
+        )
+        val newView = getChipbarView()
+
+        // WHEN there's a FAR event for the earlier one
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
+            route1,
+            null,
+        )
+
+        // THEN it's ignored and the more recent one is still displayed
+        assertThat(newView.getChipText())
+            .isEqualTo(
+                ChipStateSender.ALMOST_CLOSE_TO_START_CAST.getExpectedStateText("Route 2 name")
+            )
+    }
+
+    /** Regression test for b/266218672. */
+    @Test
+    fun receiverSucceededThenTimedOut_internalStateResetAndCanDisplayAlmostCloseToEnd() {
+        displayReceiverTriggered()
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
+            routeInfo,
+            null,
+        )
+
+        fakeClock.advanceTime(TIMEOUT + 1L)
+        verify(windowManager).removeView(any())
+
+        reset(windowManager)
+
+        // WHEN we try to show ALMOST_CLOSE_TO_END
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
+            routeInfo,
+            null,
+        )
+
+        // THEN it succeeds
+        val chipbarView = getChipbarView()
+        assertThat(chipbarView.getChipText())
+            .isEqualTo(ChipStateSender.ALMOST_CLOSE_TO_END_CAST.getExpectedStateText())
+    }
+
+    /** Regression test for b/266218672. */
+    @Test
+    fun receiverSucceededThenTimedOut_internalStateResetAndCanDisplayReceiverTriggered() {
+        displayReceiverTriggered()
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
+            routeInfo,
+            null,
+        )
+
+        fakeClock.advanceTime(TIMEOUT + 1L)
+        verify(windowManager).removeView(any())
+
+        reset(windowManager)
+
+        // WHEN we try to show RECEIVER_TRIGGERED
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
+            routeInfo,
+            null,
+        )
+
+        // THEN it succeeds
+        val chipbarView = getChipbarView()
+        assertThat(chipbarView.getChipText())
+            .isEqualTo(ChipStateSender.TRANSFER_TO_RECEIVER_TRIGGERED.getExpectedStateText())
+        assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.VISIBLE)
+    }
+
+    /** Regression test for b/266218672. */
+    @Test
+    fun toThisDeviceSucceededThenTimedOut_internalStateResetAndCanDisplayAlmostCloseToStart() {
+        displayThisDeviceTriggered()
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
+            routeInfo,
+            null,
+        )
+
+        fakeClock.advanceTime(TIMEOUT + 1L)
+        verify(windowManager).removeView(any())
+
+        reset(windowManager)
+
+        // WHEN we try to show ALMOST_CLOSE_TO_START
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
+            routeInfo,
+            null,
+        )
+
+        // THEN it succeeds
+        val chipbarView = getChipbarView()
+        assertThat(chipbarView.getChipText())
+            .isEqualTo(ChipStateSender.ALMOST_CLOSE_TO_START_CAST.getExpectedStateText())
+    }
+
+    /** Regression test for b/266218672. */
+    @Test
+    fun toThisDeviceSucceededThenTimedOut_internalStateResetAndCanDisplayThisDeviceTriggered() {
+        displayThisDeviceTriggered()
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
+            routeInfo,
+            null,
+        )
+
+        fakeClock.advanceTime(TIMEOUT + 1L)
+        verify(windowManager).removeView(any())
+
+        reset(windowManager)
+
+        // WHEN we try to show THIS_DEVICE_TRIGGERED
+        val newRouteInfo =
+            MediaRoute2Info.Builder(DEFAULT_ID, "New Name")
+                .addFeature("feature")
+                .setClientPackageName(PACKAGE_NAME)
+                .build()
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
+            newRouteInfo,
+            null,
+        )
+
+        // THEN it succeeds
+        val chipbarView = getChipbarView()
+        assertThat(chipbarView.getChipText())
+            .isEqualTo(
+                ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED.getExpectedStateText("New Name")
+            )
+        assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.VISIBLE)
     }
 
     private fun getChipbarView(): ViewGroup {
@@ -1109,8 +1373,10 @@
 
     private fun ViewGroup.getUndoButton(): View = this.requireViewById(R.id.end_button)
 
-    private fun ChipStateSender.getExpectedStateText(): String? {
-        return this.getChipTextString(context, OTHER_DEVICE_NAME).loadText(context)
+    private fun ChipStateSender.getExpectedStateText(
+        otherDeviceName: String = OTHER_DEVICE_NAME,
+    ): String? {
+        return this.getChipTextString(context, otherDeviceName).loadText(context)
     }
 
     // display receiver triggered state helper method to make sure we start from a valid state
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLoggerTest.kt
similarity index 62%
copy from packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
copy to packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLoggerTest.kt
index 0e7bf8d..0033757 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLoggerTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.media.taptotransfer.common
+package com.android.systemui.media.taptotransfer.sender
 
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -22,7 +22,6 @@
 import com.android.systemui.log.LogBufferFactory
 import com.android.systemui.plugins.log.LogBuffer
 import com.android.systemui.plugins.log.LogcatEchoTracker
-import com.android.systemui.temporarydisplay.TemporaryViewInfo
 import com.google.common.truth.Truth.assertThat
 import java.io.PrintWriter
 import java.io.StringWriter
@@ -31,16 +30,17 @@
 import org.mockito.Mockito.mock
 
 @SmallTest
-class MediaTttLoggerTest : SysuiTestCase() {
+class MediaTttSenderLoggerTest : SysuiTestCase() {
 
     private lateinit var buffer: LogBuffer
-    private lateinit var logger: MediaTttLogger<TemporaryViewInfo>
+    private lateinit var logger: MediaTttSenderLogger
 
     @Before
-    fun setUp () {
-        buffer = LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
-            .create("buffer", 10)
-        logger = MediaTttLogger(DEVICE_TYPE_TAG, buffer)
+    fun setUp() {
+        buffer =
+            LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
+                .create("buffer", 10)
+        logger = MediaTttSenderLogger(buffer)
     }
 
     @Test
@@ -52,13 +52,20 @@
         logger.logStateChange(stateName, id, packageName)
 
         val actualString = getStringFromBuffer()
-        assertThat(actualString).contains(DEVICE_TYPE_TAG)
         assertThat(actualString).contains(stateName)
         assertThat(actualString).contains(id)
         assertThat(actualString).contains(packageName)
     }
 
     @Test
+    fun logStateChangeError_hasState() {
+        logger.logStateChangeError(3456)
+
+        val actualString = getStringFromBuffer()
+        assertThat(actualString).contains("3456")
+    }
+
+    @Test
     fun logPackageNotFound_bufferHasPackageName() {
         val packageName = "this.is.a.package"
 
@@ -80,11 +87,35 @@
         assertThat(actualString).contains(bypassReason)
     }
 
+    @Test
+    fun logStateMap_bufferHasInfo() {
+        val map =
+            mapOf(
+                "123" to ChipStateSender.ALMOST_CLOSE_TO_START_CAST,
+                "456" to ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED,
+            )
+
+        logger.logStateMap(map)
+
+        val actualString = getStringFromBuffer()
+        assertThat(actualString).contains("123")
+        assertThat(actualString).contains(ChipStateSender.ALMOST_CLOSE_TO_START_CAST.name)
+        assertThat(actualString).contains("456")
+        assertThat(actualString).contains(ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED.name)
+    }
+
+    @Test
+    fun logStateMapRemoval_bufferHasInfo() {
+        logger.logStateMapRemoval("456", "testReason")
+
+        val actualString = getStringFromBuffer()
+        assertThat(actualString).contains("456")
+        assertThat(actualString).contains("testReason")
+    }
+
     private fun getStringFromBuffer(): String {
         val stringWriter = StringWriter()
         buffer.dump(PrintWriter(stringWriter), tailLength = 0)
         return stringWriter.toString()
     }
 }
-
-private const val DEVICE_TYPE_TAG = "TEST TYPE"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
index 1042ea7..4977775 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
@@ -24,6 +24,8 @@
     private val taskListProvider = TestRecentTaskListProvider()
     private val scope = CoroutineScope(Dispatchers.Unconfined)
     private val appSelectorComponentName = ComponentName("com.test", "AppSelector")
+    private val callerPackageName = "com.test.caller"
+    private val callerComponentName = ComponentName(callerPackageName, "Caller")
 
     private val hostUserHandle = UserHandle.of(123)
     private val otherUserHandle = UserHandle.of(456)
@@ -31,14 +33,16 @@
     private val view: MediaProjectionAppSelectorView = mock()
     private val featureFlags: FeatureFlags = mock()
 
-    private val controller = MediaProjectionAppSelectorController(
-        taskListProvider,
-        view,
-        featureFlags,
-        hostUserHandle,
-        scope,
-        appSelectorComponentName
-    )
+    private val controller =
+        MediaProjectionAppSelectorController(
+            taskListProvider,
+            view,
+            featureFlags,
+            hostUserHandle,
+            scope,
+            appSelectorComponentName,
+            callerPackageName
+        )
 
     @Test
     fun initNoRecentTasks_bindsEmptyList() {
@@ -51,104 +55,87 @@
 
     @Test
     fun initOneRecentTask_bindsList() {
-        taskListProvider.tasks = listOf(
-            createRecentTask(taskId = 1)
-        )
+        taskListProvider.tasks = listOf(createRecentTask(taskId = 1))
 
         controller.init()
 
-        verify(view).bind(
-            listOf(
-                createRecentTask(taskId = 1)
-            )
-        )
+        verify(view).bind(listOf(createRecentTask(taskId = 1)))
     }
 
     @Test
     fun initMultipleRecentTasksWithoutAppSelectorTask_bindsListInTheSameOrder() {
-        val tasks = listOf(
-            createRecentTask(taskId = 1),
-            createRecentTask(taskId = 2),
-            createRecentTask(taskId = 3),
-        )
-        taskListProvider.tasks = tasks
-
-        controller.init()
-
-        verify(view).bind(
+        val tasks =
             listOf(
                 createRecentTask(taskId = 1),
                 createRecentTask(taskId = 2),
                 createRecentTask(taskId = 3),
             )
-        )
-    }
-
-    @Test
-    fun initRecentTasksWithAppSelectorTasks_bindsAppSelectorTasksAtTheEnd() {
-        val tasks = listOf(
-            createRecentTask(taskId = 1),
-            createRecentTask(taskId = 2, topActivityComponent = appSelectorComponentName),
-            createRecentTask(taskId = 3),
-            createRecentTask(taskId = 4, topActivityComponent = appSelectorComponentName),
-            createRecentTask(taskId = 5),
-        )
         taskListProvider.tasks = tasks
 
         controller.init()
 
-        verify(view).bind(
+        verify(view)
+            .bind(
+                listOf(
+                    createRecentTask(taskId = 1),
+                    createRecentTask(taskId = 2),
+                    createRecentTask(taskId = 3),
+                )
+            )
+    }
+
+    @Test
+    fun initRecentTasksWithAppSelectorTasks_removeAppSelector() {
+        val tasks =
             listOf(
                 createRecentTask(taskId = 1),
-                createRecentTask(taskId = 3),
-                createRecentTask(taskId = 5),
                 createRecentTask(taskId = 2, topActivityComponent = appSelectorComponentName),
-                createRecentTask(taskId = 4, topActivityComponent = appSelectorComponentName),
+                createRecentTask(taskId = 3),
+                createRecentTask(taskId = 4),
             )
-        )
+        taskListProvider.tasks = tasks
+
+        controller.init()
+
+        verify(view)
+            .bind(
+                listOf(
+                    createRecentTask(taskId = 1),
+                    createRecentTask(taskId = 3),
+                    createRecentTask(taskId = 4),
+                )
+            )
+    }
+
+    @Test
+    fun initRecentTasksWithAppSelectorTasks_bindsCallerTasksAtTheEnd() {
+        val tasks =
+            listOf(
+                createRecentTask(taskId = 1),
+                createRecentTask(taskId = 2, topActivityComponent = callerComponentName),
+                createRecentTask(taskId = 3),
+                createRecentTask(taskId = 4),
+            )
+        taskListProvider.tasks = tasks
+
+        controller.init()
+
+        verify(view)
+            .bind(
+                listOf(
+                    createRecentTask(taskId = 1),
+                    createRecentTask(taskId = 3),
+                    createRecentTask(taskId = 4),
+                    createRecentTask(taskId = 2, topActivityComponent = callerComponentName),
+                )
+            )
     }
 
     @Test
     fun initRecentTasksWithAppSelectorTasks_enterprisePoliciesDisabled_bindsOnlyTasksWithHostProfile() {
         givenEnterprisePoliciesFeatureFlag(enabled = false)
 
-        val tasks = listOf(
-            createRecentTask(taskId = 1, userId = hostUserHandle.identifier),
-            createRecentTask(taskId = 2, userId = otherUserHandle.identifier),
-            createRecentTask(taskId = 3, userId = hostUserHandle.identifier),
-            createRecentTask(taskId = 4, userId = otherUserHandle.identifier),
-            createRecentTask(taskId = 5, userId = hostUserHandle.identifier),
-        )
-        taskListProvider.tasks = tasks
-
-        controller.init()
-
-        verify(view).bind(
-            listOf(
-                createRecentTask(taskId = 1, userId = hostUserHandle.identifier),
-                createRecentTask(taskId = 3, userId = hostUserHandle.identifier),
-                createRecentTask(taskId = 5, userId = hostUserHandle.identifier),
-            )
-        )
-    }
-
-    @Test
-    fun initRecentTasksWithAppSelectorTasks_enterprisePoliciesEnabled_bindsAllTasks() {
-        givenEnterprisePoliciesFeatureFlag(enabled = true)
-
-        val tasks = listOf(
-            createRecentTask(taskId = 1, userId = hostUserHandle.identifier),
-            createRecentTask(taskId = 2, userId = otherUserHandle.identifier),
-            createRecentTask(taskId = 3, userId = hostUserHandle.identifier),
-            createRecentTask(taskId = 4, userId = otherUserHandle.identifier),
-            createRecentTask(taskId = 5, userId = hostUserHandle.identifier),
-        )
-        taskListProvider.tasks = tasks
-
-        controller.init()
-
-        // TODO(b/233348916) should filter depending on the policies
-        verify(view).bind(
+        val tasks =
             listOf(
                 createRecentTask(taskId = 1, userId = hostUserHandle.identifier),
                 createRecentTask(taskId = 2, userId = otherUserHandle.identifier),
@@ -156,7 +143,47 @@
                 createRecentTask(taskId = 4, userId = otherUserHandle.identifier),
                 createRecentTask(taskId = 5, userId = hostUserHandle.identifier),
             )
-        )
+        taskListProvider.tasks = tasks
+
+        controller.init()
+
+        verify(view)
+            .bind(
+                listOf(
+                    createRecentTask(taskId = 1, userId = hostUserHandle.identifier),
+                    createRecentTask(taskId = 3, userId = hostUserHandle.identifier),
+                    createRecentTask(taskId = 5, userId = hostUserHandle.identifier),
+                )
+            )
+    }
+
+    @Test
+    fun initRecentTasksWithAppSelectorTasks_enterprisePoliciesEnabled_bindsAllTasks() {
+        givenEnterprisePoliciesFeatureFlag(enabled = true)
+
+        val tasks =
+            listOf(
+                createRecentTask(taskId = 1, userId = hostUserHandle.identifier),
+                createRecentTask(taskId = 2, userId = otherUserHandle.identifier),
+                createRecentTask(taskId = 3, userId = hostUserHandle.identifier),
+                createRecentTask(taskId = 4, userId = otherUserHandle.identifier),
+                createRecentTask(taskId = 5, userId = hostUserHandle.identifier),
+            )
+        taskListProvider.tasks = tasks
+
+        controller.init()
+
+        // TODO(b/233348916) should filter depending on the policies
+        verify(view)
+            .bind(
+                listOf(
+                    createRecentTask(taskId = 1, userId = hostUserHandle.identifier),
+                    createRecentTask(taskId = 2, userId = otherUserHandle.identifier),
+                    createRecentTask(taskId = 3, userId = hostUserHandle.identifier),
+                    createRecentTask(taskId = 4, userId = otherUserHandle.identifier),
+                    createRecentTask(taskId = 5, userId = hostUserHandle.identifier),
+                )
+            )
     }
 
     private fun givenEnterprisePoliciesFeatureFlag(enabled: Boolean) {
@@ -183,6 +210,5 @@
         var tasks: List<RecentTask> = emptyList()
 
         override suspend fun loadRecentTasks(): List<RecentTask> = tasks
-
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolverTest.kt
new file mode 100644
index 0000000..e8b6f9b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolverTest.kt
@@ -0,0 +1,703 @@
+/*
+ * 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.mediaprojection.devicepolicy
+
+import android.app.admin.DevicePolicyManager
+import android.os.UserHandle
+import android.os.UserManager
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.junit.runners.Parameterized.Parameters
+import org.mockito.ArgumentMatchers.any
+
+abstract class BaseScreenCaptureDevicePolicyResolverTest(private val precondition: Preconditions) :
+    SysuiTestCase() {
+
+    abstract class Preconditions(
+        val personalScreenCaptureDisabled: Boolean,
+        val workScreenCaptureDisabled: Boolean,
+        val disallowShareIntoManagedProfile: Boolean
+    )
+
+    protected val devicePolicyManager: DevicePolicyManager = mock()
+    protected val userManager: UserManager = mock()
+
+    protected val personalUserHandle: UserHandle = UserHandle.of(123)
+    protected val workUserHandle: UserHandle = UserHandle.of(456)
+
+    protected val policyResolver =
+        ScreenCaptureDevicePolicyResolver(
+            devicePolicyManager,
+            userManager,
+            personalUserHandle,
+            workUserHandle
+        )
+
+    @Before
+    fun setUp() {
+        setUpPolicies()
+    }
+
+    private fun setUpPolicies() {
+        whenever(
+                devicePolicyManager.getScreenCaptureDisabled(
+                    any(),
+                    eq(personalUserHandle.identifier)
+                )
+            )
+            .thenReturn(precondition.personalScreenCaptureDisabled)
+
+        whenever(devicePolicyManager.getScreenCaptureDisabled(any(), eq(workUserHandle.identifier)))
+            .thenReturn(precondition.workScreenCaptureDisabled)
+
+        whenever(
+                userManager.hasUserRestrictionForUser(
+                    eq(UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE),
+                    eq(workUserHandle)
+                )
+            )
+            .thenReturn(precondition.disallowShareIntoManagedProfile)
+    }
+}
+
+@RunWith(Parameterized::class)
+@SmallTest
+class IsAllowedScreenCaptureDevicePolicyResolverTest(
+    private val test: IsScreenCaptureAllowedTestCase
+) : BaseScreenCaptureDevicePolicyResolverTest(test.given) {
+
+    companion object {
+        @Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams() =
+            listOf(
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = false,
+                            isTargetInWorkProfile = false,
+                            personalScreenCaptureDisabled = false,
+                            workScreenCaptureDisabled = false,
+                            disallowShareIntoManagedProfile = false,
+                        ),
+                    expectedScreenCaptureAllowed = true,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = false,
+                            isTargetInWorkProfile = false,
+                            personalScreenCaptureDisabled = false,
+                            workScreenCaptureDisabled = false,
+                            disallowShareIntoManagedProfile = true,
+                        ),
+                    expectedScreenCaptureAllowed = true,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = false,
+                            isTargetInWorkProfile = false,
+                            personalScreenCaptureDisabled = false,
+                            workScreenCaptureDisabled = true,
+                            disallowShareIntoManagedProfile = false,
+                        ),
+                    expectedScreenCaptureAllowed = true,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = false,
+                            isTargetInWorkProfile = false,
+                            personalScreenCaptureDisabled = false,
+                            workScreenCaptureDisabled = true,
+                            disallowShareIntoManagedProfile = true,
+                        ),
+                    expectedScreenCaptureAllowed = true,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = false,
+                            isTargetInWorkProfile = false,
+                            personalScreenCaptureDisabled = true,
+                            workScreenCaptureDisabled = false,
+                            disallowShareIntoManagedProfile = false
+                        ),
+                    expectedScreenCaptureAllowed = false,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = false,
+                            isTargetInWorkProfile = false,
+                            personalScreenCaptureDisabled = true,
+                            workScreenCaptureDisabled = false,
+                            disallowShareIntoManagedProfile = true
+                        ),
+                    expectedScreenCaptureAllowed = false,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = false,
+                            isTargetInWorkProfile = false,
+                            personalScreenCaptureDisabled = true,
+                            workScreenCaptureDisabled = true,
+                            disallowShareIntoManagedProfile = false
+                        ),
+                    expectedScreenCaptureAllowed = false,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = false,
+                            isTargetInWorkProfile = false,
+                            personalScreenCaptureDisabled = true,
+                            workScreenCaptureDisabled = true,
+                            disallowShareIntoManagedProfile = true
+                        ),
+                    expectedScreenCaptureAllowed = false,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = false,
+                            isTargetInWorkProfile = true,
+                            personalScreenCaptureDisabled = false,
+                            workScreenCaptureDisabled = false,
+                            disallowShareIntoManagedProfile = false
+                        ),
+                    expectedScreenCaptureAllowed = true,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = false,
+                            isTargetInWorkProfile = true,
+                            personalScreenCaptureDisabled = false,
+                            workScreenCaptureDisabled = false,
+                            disallowShareIntoManagedProfile = true
+                        ),
+                    expectedScreenCaptureAllowed = true,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = false,
+                            isTargetInWorkProfile = true,
+                            personalScreenCaptureDisabled = false,
+                            workScreenCaptureDisabled = true,
+                            disallowShareIntoManagedProfile = false
+                        ),
+                    expectedScreenCaptureAllowed = false,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = false,
+                            isTargetInWorkProfile = true,
+                            personalScreenCaptureDisabled = false,
+                            workScreenCaptureDisabled = true,
+                            disallowShareIntoManagedProfile = true
+                        ),
+                    expectedScreenCaptureAllowed = false,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = false,
+                            isTargetInWorkProfile = true,
+                            personalScreenCaptureDisabled = true,
+                            workScreenCaptureDisabled = false,
+                            disallowShareIntoManagedProfile = false
+                        ),
+                    expectedScreenCaptureAllowed = false,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = false,
+                            isTargetInWorkProfile = true,
+                            personalScreenCaptureDisabled = true,
+                            workScreenCaptureDisabled = false,
+                            disallowShareIntoManagedProfile = true
+                        ),
+                    expectedScreenCaptureAllowed = false,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = false,
+                            isTargetInWorkProfile = true,
+                            personalScreenCaptureDisabled = true,
+                            workScreenCaptureDisabled = true,
+                            disallowShareIntoManagedProfile = false
+                        ),
+                    expectedScreenCaptureAllowed = false,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = false,
+                            isTargetInWorkProfile = true,
+                            personalScreenCaptureDisabled = true,
+                            workScreenCaptureDisabled = true,
+                            disallowShareIntoManagedProfile = true
+                        ),
+                    expectedScreenCaptureAllowed = false,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = true,
+                            isTargetInWorkProfile = false,
+                            personalScreenCaptureDisabled = false,
+                            workScreenCaptureDisabled = false,
+                            disallowShareIntoManagedProfile = false
+                        ),
+                    expectedScreenCaptureAllowed = true,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = true,
+                            isTargetInWorkProfile = false,
+                            personalScreenCaptureDisabled = false,
+                            workScreenCaptureDisabled = false,
+                            disallowShareIntoManagedProfile = true
+                        ),
+                    expectedScreenCaptureAllowed = false,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = true,
+                            isTargetInWorkProfile = false,
+                            personalScreenCaptureDisabled = false,
+                            workScreenCaptureDisabled = true,
+                            disallowShareIntoManagedProfile = false
+                        ),
+                    expectedScreenCaptureAllowed = false,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = true,
+                            isTargetInWorkProfile = false,
+                            personalScreenCaptureDisabled = false,
+                            workScreenCaptureDisabled = true,
+                            disallowShareIntoManagedProfile = true
+                        ),
+                    expectedScreenCaptureAllowed = false,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = true,
+                            isTargetInWorkProfile = false,
+                            personalScreenCaptureDisabled = true,
+                            workScreenCaptureDisabled = false,
+                            disallowShareIntoManagedProfile = false
+                        ),
+                    expectedScreenCaptureAllowed = false,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = true,
+                            isTargetInWorkProfile = false,
+                            personalScreenCaptureDisabled = true,
+                            workScreenCaptureDisabled = false,
+                            disallowShareIntoManagedProfile = true
+                        ),
+                    expectedScreenCaptureAllowed = false,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = true,
+                            isTargetInWorkProfile = false,
+                            personalScreenCaptureDisabled = true,
+                            workScreenCaptureDisabled = true,
+                            disallowShareIntoManagedProfile = false
+                        ),
+                    expectedScreenCaptureAllowed = false,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = true,
+                            isTargetInWorkProfile = false,
+                            personalScreenCaptureDisabled = true,
+                            workScreenCaptureDisabled = true,
+                            disallowShareIntoManagedProfile = true
+                        ),
+                    expectedScreenCaptureAllowed = false,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = true,
+                            isTargetInWorkProfile = true,
+                            personalScreenCaptureDisabled = false,
+                            workScreenCaptureDisabled = false,
+                            disallowShareIntoManagedProfile = false
+                        ),
+                    expectedScreenCaptureAllowed = true,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = true,
+                            isTargetInWorkProfile = true,
+                            personalScreenCaptureDisabled = false,
+                            workScreenCaptureDisabled = false,
+                            disallowShareIntoManagedProfile = true
+                        ),
+                    expectedScreenCaptureAllowed = true,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = true,
+                            isTargetInWorkProfile = true,
+                            personalScreenCaptureDisabled = false,
+                            workScreenCaptureDisabled = true,
+                            disallowShareIntoManagedProfile = false
+                        ),
+                    expectedScreenCaptureAllowed = false,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = true,
+                            isTargetInWorkProfile = true,
+                            personalScreenCaptureDisabled = false,
+                            workScreenCaptureDisabled = true,
+                            disallowShareIntoManagedProfile = true
+                        ),
+                    expectedScreenCaptureAllowed = false,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = true,
+                            isTargetInWorkProfile = true,
+                            personalScreenCaptureDisabled = true,
+                            workScreenCaptureDisabled = false,
+                            disallowShareIntoManagedProfile = false
+                        ),
+                    expectedScreenCaptureAllowed = true,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = true,
+                            isTargetInWorkProfile = true,
+                            personalScreenCaptureDisabled = true,
+                            workScreenCaptureDisabled = false,
+                            disallowShareIntoManagedProfile = true
+                        ),
+                    expectedScreenCaptureAllowed = true,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = true,
+                            isTargetInWorkProfile = true,
+                            personalScreenCaptureDisabled = true,
+                            workScreenCaptureDisabled = true,
+                            disallowShareIntoManagedProfile = false
+                        ),
+                    expectedScreenCaptureAllowed = false,
+                ),
+                IsScreenCaptureAllowedTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = true,
+                            isTargetInWorkProfile = true,
+                            personalScreenCaptureDisabled = true,
+                            workScreenCaptureDisabled = true,
+                            disallowShareIntoManagedProfile = true
+                        ),
+                    expectedScreenCaptureAllowed = false,
+                ),
+            )
+    }
+
+    class Preconditions(
+        personalScreenCaptureDisabled: Boolean,
+        workScreenCaptureDisabled: Boolean,
+        disallowShareIntoManagedProfile: Boolean,
+        val isHostInWorkProfile: Boolean,
+        val isTargetInWorkProfile: Boolean,
+    ) :
+        BaseScreenCaptureDevicePolicyResolverTest.Preconditions(
+            personalScreenCaptureDisabled,
+            workScreenCaptureDisabled,
+            disallowShareIntoManagedProfile
+        )
+
+    data class IsScreenCaptureAllowedTestCase(
+        val given: Preconditions,
+        val expectedScreenCaptureAllowed: Boolean
+    ) {
+        override fun toString(): String =
+            "isScreenCaptureAllowed: " +
+                "host[${if (given.isHostInWorkProfile) "work" else "personal"} profile], " +
+                "target[${if (given.isTargetInWorkProfile) "work" else "personal"} profile], " +
+                "personal screen capture disabled = ${given.personalScreenCaptureDisabled}, " +
+                "work screen capture disabled = ${given.workScreenCaptureDisabled}, " +
+                "disallow share into managed profile = ${given.disallowShareIntoManagedProfile}, " +
+                "expected screen capture allowed = $expectedScreenCaptureAllowed"
+    }
+
+    @Test
+    fun test() {
+        val targetAppUserHandle =
+            if (test.given.isTargetInWorkProfile) workUserHandle else personalUserHandle
+        val hostAppUserHandle =
+            if (test.given.isHostInWorkProfile) workUserHandle else personalUserHandle
+
+        val screenCaptureAllowed =
+            policyResolver.isScreenCaptureAllowed(targetAppUserHandle, hostAppUserHandle)
+
+        assertWithMessage("Screen capture policy resolved incorrectly")
+            .that(screenCaptureAllowed)
+            .isEqualTo(test.expectedScreenCaptureAllowed)
+    }
+}
+
+@RunWith(Parameterized::class)
+@SmallTest
+class IsCompletelyNotAllowedScreenCaptureDevicePolicyResolverTest(
+    private val test: IsScreenCaptureCompletelyDisabledTestCase
+) : BaseScreenCaptureDevicePolicyResolverTest(test.given) {
+
+    companion object {
+        @Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams() =
+            listOf(
+                IsScreenCaptureCompletelyDisabledTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = false,
+                            personalScreenCaptureDisabled = false,
+                            workScreenCaptureDisabled = false,
+                            disallowShareIntoManagedProfile = false
+                        ),
+                    expectedScreenCaptureCompletelyDisabled = false,
+                ),
+                IsScreenCaptureCompletelyDisabledTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = false,
+                            personalScreenCaptureDisabled = false,
+                            workScreenCaptureDisabled = false,
+                            disallowShareIntoManagedProfile = true
+                        ),
+                    expectedScreenCaptureCompletelyDisabled = false,
+                ),
+                IsScreenCaptureCompletelyDisabledTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = false,
+                            personalScreenCaptureDisabled = false,
+                            workScreenCaptureDisabled = true,
+                            disallowShareIntoManagedProfile = false
+                        ),
+                    expectedScreenCaptureCompletelyDisabled = false,
+                ),
+                IsScreenCaptureCompletelyDisabledTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = false,
+                            personalScreenCaptureDisabled = false,
+                            workScreenCaptureDisabled = true,
+                            disallowShareIntoManagedProfile = true
+                        ),
+                    expectedScreenCaptureCompletelyDisabled = false,
+                ),
+                IsScreenCaptureCompletelyDisabledTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = false,
+                            personalScreenCaptureDisabled = true,
+                            workScreenCaptureDisabled = false,
+                            disallowShareIntoManagedProfile = false
+                        ),
+                    expectedScreenCaptureCompletelyDisabled = true,
+                ),
+                IsScreenCaptureCompletelyDisabledTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = false,
+                            personalScreenCaptureDisabled = true,
+                            workScreenCaptureDisabled = false,
+                            disallowShareIntoManagedProfile = true
+                        ),
+                    expectedScreenCaptureCompletelyDisabled = true,
+                ),
+                IsScreenCaptureCompletelyDisabledTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = false,
+                            personalScreenCaptureDisabled = true,
+                            workScreenCaptureDisabled = true,
+                            disallowShareIntoManagedProfile = false
+                        ),
+                    expectedScreenCaptureCompletelyDisabled = true,
+                ),
+                IsScreenCaptureCompletelyDisabledTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = false,
+                            personalScreenCaptureDisabled = true,
+                            workScreenCaptureDisabled = true,
+                            disallowShareIntoManagedProfile = true
+                        ),
+                    expectedScreenCaptureCompletelyDisabled = true,
+                ),
+                IsScreenCaptureCompletelyDisabledTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = true,
+                            personalScreenCaptureDisabled = false,
+                            workScreenCaptureDisabled = false,
+                            disallowShareIntoManagedProfile = false
+                        ),
+                    expectedScreenCaptureCompletelyDisabled = false,
+                ),
+                IsScreenCaptureCompletelyDisabledTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = true,
+                            personalScreenCaptureDisabled = false,
+                            workScreenCaptureDisabled = false,
+                            disallowShareIntoManagedProfile = true
+                        ),
+                    expectedScreenCaptureCompletelyDisabled = false,
+                ),
+                IsScreenCaptureCompletelyDisabledTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = true,
+                            personalScreenCaptureDisabled = false,
+                            workScreenCaptureDisabled = true,
+                            disallowShareIntoManagedProfile = false
+                        ),
+                    expectedScreenCaptureCompletelyDisabled = true,
+                ),
+                IsScreenCaptureCompletelyDisabledTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = true,
+                            personalScreenCaptureDisabled = false,
+                            workScreenCaptureDisabled = true,
+                            disallowShareIntoManagedProfile = true
+                        ),
+                    expectedScreenCaptureCompletelyDisabled = true,
+                ),
+                IsScreenCaptureCompletelyDisabledTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = true,
+                            personalScreenCaptureDisabled = true,
+                            workScreenCaptureDisabled = false,
+                            disallowShareIntoManagedProfile = false
+                        ),
+                    expectedScreenCaptureCompletelyDisabled = false,
+                ),
+                IsScreenCaptureCompletelyDisabledTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = true,
+                            personalScreenCaptureDisabled = true,
+                            workScreenCaptureDisabled = false,
+                            disallowShareIntoManagedProfile = true
+                        ),
+                    expectedScreenCaptureCompletelyDisabled = false,
+                ),
+                IsScreenCaptureCompletelyDisabledTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = true,
+                            personalScreenCaptureDisabled = true,
+                            workScreenCaptureDisabled = true,
+                            disallowShareIntoManagedProfile = false
+                        ),
+                    expectedScreenCaptureCompletelyDisabled = true,
+                ),
+                IsScreenCaptureCompletelyDisabledTestCase(
+                    given =
+                        Preconditions(
+                            isHostInWorkProfile = true,
+                            personalScreenCaptureDisabled = true,
+                            workScreenCaptureDisabled = true,
+                            disallowShareIntoManagedProfile = true
+                        ),
+                    expectedScreenCaptureCompletelyDisabled = true,
+                )
+            )
+    }
+
+    class Preconditions(
+        personalScreenCaptureDisabled: Boolean,
+        workScreenCaptureDisabled: Boolean,
+        disallowShareIntoManagedProfile: Boolean,
+        val isHostInWorkProfile: Boolean,
+    ) :
+        BaseScreenCaptureDevicePolicyResolverTest.Preconditions(
+            personalScreenCaptureDisabled,
+            workScreenCaptureDisabled,
+            disallowShareIntoManagedProfile
+        )
+
+    data class IsScreenCaptureCompletelyDisabledTestCase(
+        val given: Preconditions,
+        val expectedScreenCaptureCompletelyDisabled: Boolean
+    ) {
+        override fun toString(): String =
+            "isScreenCaptureCompletelyDisabled: " +
+                "host[${if (given.isHostInWorkProfile) "work" else "personal"} profile], " +
+                "personal screen capture disabled = ${given.personalScreenCaptureDisabled}, " +
+                "work screen capture disabled = ${given.workScreenCaptureDisabled}, " +
+                "disallow share into managed profile = ${given.disallowShareIntoManagedProfile}, " +
+                "expected screen capture completely disabled = $expectedScreenCaptureCompletelyDisabled"
+    }
+
+    @Test
+    fun test() {
+        val hostAppUserHandle =
+            if (test.given.isHostInWorkProfile) workUserHandle else personalUserHandle
+
+        val completelyDisabled = policyResolver.isScreenCaptureCompletelyDisabled(hostAppUserHandle)
+
+        assertWithMessage("Screen capture policy resolved incorrectly")
+            .that(completelyDisabled)
+            .isEqualTo(test.expectedScreenCaptureCompletelyDisabled)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateTest.java
index 9bcfd5b..1a93adc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateTest.java
@@ -26,6 +26,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.settings.FakeDisplayTracker;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -46,7 +47,8 @@
 
     @Before
     public void setup() {
-        mFlagsContainer = new SysUiState();
+        FakeDisplayTracker displayTracker = new FakeDisplayTracker(mContext);
+        mFlagsContainer = new SysUiState(displayTracker);
         mCallback = mock(SysUiState.SysUiStateCallback.class);
         mFlagsContainer.addCallback(mCallback);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarButtonTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarButtonTest.java
index 92652a7..3eb7329 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarButtonTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarButtonTest.java
@@ -40,6 +40,7 @@
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
 import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.FakeDisplayTracker;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import org.junit.After;
@@ -65,6 +66,7 @@
     private ImageReader mReader;
     private NavigationBarView mNavBar;
     private VirtualDisplay mVirtualDisplay;
+    private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
 
     @Before
     public void setup() {
@@ -83,6 +85,7 @@
         mDependency.injectTestDependency(EdgeBackGestureHandler.Factory.class,
                 mEdgeBackGestureHandlerFactory);
         mNavBar = new NavigationBarView(context, null);
+        mNavBar.setDisplayTracker(mDisplayTracker);
     }
 
     private Display createVirtualDisplay() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index 8058b85..aacbf8f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -37,6 +37,7 @@
 import static org.mockito.Mockito.when;
 
 import android.content.res.Configuration;
+import android.hardware.display.DisplayManager;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
 import android.util.SparseArray;
@@ -50,6 +51,7 @@
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.FakeDisplayTracker;
 import com.android.systemui.shared.recents.utilities.Utilities;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.statusbar.CommandQueue;
@@ -81,6 +83,7 @@
     private NavigationBar mDefaultNavBar;
     private NavigationBar mSecondaryNavBar;
     private StaticMockitoSession mMockitoSession;
+    private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
 
     @Mock
     private CommandQueue mCommandQueue;
@@ -92,6 +95,7 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
         mNavigationBarController = spy(
                 new NavigationBarController(mContext,
                         mock(OverviewProxyService.class),
@@ -110,7 +114,8 @@
                         Optional.of(mock(Pip.class)),
                         Optional.of(mock(BackAnimation.class)),
                         mock(FeatureFlags.class),
-                        mock(SecureSettings.class)));
+                        mock(SecureSettings.class),
+                        mDisplayTracker));
         initializeNavigationBars();
         mMockitoSession = mockitoSession().mockStatic(Utilities.class).startMocking();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 2ad865e..764ddc4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -87,6 +87,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.recents.Recents;
+import com.android.systemui.settings.FakeDisplayTracker;
 import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shade.NotificationShadeWindowView;
@@ -495,7 +496,8 @@
                 Optional.of(mock(BackAnimation.class)),
                 mUserContextProvider,
                 mWakefulnessLifecycle,
-                mTaskStackChangeListeners));
+                mTaskStackChangeListeners,
+                new FakeDisplayTracker(mContext)));
     }
 
     private void processAllMessages() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java
index cafd2cf..5270737 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java
@@ -36,6 +36,7 @@
 import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.FakeDisplayTracker;
 import com.android.systemui.statusbar.phone.BarTransitions;
 import com.android.systemui.statusbar.phone.LightBarTransitionsController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -63,6 +64,7 @@
     IWindowManager mIWindowManager;
 
     private NavigationBarTransitions mTransitions;
+    private final FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
 
     @Before
     public void setup() {
@@ -86,7 +88,7 @@
         when(navBar.getCurrentView()).thenReturn(navBar);
         when(navBar.findViewById(anyInt())).thenReturn(navBar);
         mTransitions = new NavigationBarTransitions(
-                navBar, mIWindowManager, mLightBarTransitionsFactory);
+                navBar, mIWindowManager, mLightBarTransitionsFactory, mDisplayTracker);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
new file mode 100644
index 0000000..bc31a0e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
@@ -0,0 +1,158 @@
+/*
+ * 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.navigationbar.gestural
+
+import android.os.Handler
+import android.os.Looper
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.MotionEvent
+import android.view.MotionEvent.ACTION_DOWN
+import android.view.MotionEvent.ACTION_MOVE
+import android.view.MotionEvent.ACTION_UP
+import android.view.ViewConfiguration
+import android.view.WindowManager
+import androidx.test.filters.SmallTest
+import com.android.internal.util.LatencyTracker
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.NavigationEdgeBackPlugin
+import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class BackPanelControllerTest : SysuiTestCase() {
+    companion object {
+        private const val START_X: Float = 0f
+    }
+    private lateinit var mBackPanelController: BackPanelController
+    private lateinit var testableLooper: TestableLooper
+    private var triggerThreshold: Float = 0.0f
+    private val touchSlop = ViewConfiguration.get(context).scaledEdgeSlop
+    @Mock private lateinit var vibratorHelper: VibratorHelper
+    @Mock private lateinit var windowManager: WindowManager
+    @Mock private lateinit var configurationController: ConfigurationController
+    @Mock private lateinit var latencyTracker: LatencyTracker
+    @Mock private lateinit var layoutParams: WindowManager.LayoutParams
+    @Mock private lateinit var backCallback: NavigationEdgeBackPlugin.BackCallback
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        mBackPanelController =
+            BackPanelController(
+                context,
+                windowManager,
+                ViewConfiguration.get(context),
+                Handler.createAsync(Looper.myLooper()),
+                vibratorHelper,
+                configurationController,
+                latencyTracker
+            )
+        mBackPanelController.setLayoutParams(layoutParams)
+        mBackPanelController.setBackCallback(backCallback)
+        mBackPanelController.setIsLeftPanel(true)
+        testableLooper = TestableLooper.get(this)
+        triggerThreshold = mBackPanelController.params.staticTriggerThreshold
+    }
+
+    @Test
+    fun handlesActionDown() {
+        startTouch()
+
+        assertThat(mBackPanelController.currentState)
+            .isEqualTo(BackPanelController.GestureState.GONE)
+    }
+
+    @Test
+    fun staysHiddenBeforeSlopCrossed() {
+        startTouch()
+        // Move just enough to not cross the touch slop
+        continueTouch(START_X + touchSlop - 1)
+
+        assertThat(mBackPanelController.currentState)
+            .isEqualTo(BackPanelController.GestureState.GONE)
+    }
+
+    @Test
+    fun handlesBackCommitted() {
+        startTouch()
+        // Move once to cross the touch slop
+        continueTouch(START_X + touchSlop.toFloat() + 1)
+        // Move again to cross the back trigger threshold
+        continueTouch(START_X + touchSlop + triggerThreshold + 1)
+
+        assertThat(mBackPanelController.currentState)
+            .isEqualTo(BackPanelController.GestureState.ACTIVE)
+        verify(backCallback).setTriggerBack(true)
+        testableLooper.moveTimeForward(100)
+        testableLooper.processAllMessages()
+        verify(vibratorHelper).vibrate(VIBRATE_ACTIVATED_EFFECT)
+
+        finishTouchActionUp(START_X + touchSlop + triggerThreshold + 1)
+        assertThat(mBackPanelController.currentState)
+            .isEqualTo(BackPanelController.GestureState.FLUNG)
+        verify(backCallback).triggerBack()
+    }
+
+    @Test
+    fun handlesBackCancelled() {
+        startTouch()
+        continueTouch(START_X + touchSlop.toFloat() + 1)
+        continueTouch(
+            START_X + touchSlop + triggerThreshold -
+                mBackPanelController.params.deactivationSwipeTriggerThreshold
+        )
+        clearInvocations(backCallback)
+        Thread.sleep(MIN_DURATION_ACTIVE_ANIMATION)
+        // Move in the opposite direction to cross the deactivation threshold and cancel back
+        continueTouch(START_X)
+
+        assertThat(mBackPanelController.currentState)
+            .isEqualTo(BackPanelController.GestureState.INACTIVE)
+        verify(backCallback).setTriggerBack(false)
+        verify(vibratorHelper).vibrate(VIBRATE_DEACTIVATED_EFFECT)
+
+        finishTouchActionUp(START_X)
+        verify(backCallback).cancelBack()
+    }
+
+    private fun startTouch() {
+        mBackPanelController.onMotionEvent(createMotionEvent(ACTION_DOWN, START_X, 0f))
+    }
+
+    private fun continueTouch(x: Float) {
+        mBackPanelController.onMotionEvent(createMotionEvent(ACTION_MOVE, x, 0f))
+    }
+
+    private fun finishTouchActionUp(x: Float) {
+        mBackPanelController.onMotionEvent(createMotionEvent(ACTION_UP, x, 0f))
+    }
+
+    private fun createMotionEvent(action: Int, x: Float, y: Float): MotionEvent {
+        return MotionEvent.obtain(0L, 0L, action, x, y, 0)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt
index 36e02cb..5f206b3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt
@@ -13,43 +13,61 @@
 
 @RunWith(Parameterized::class)
 @SmallTest
-internal class FloatingRotationButtonPositionCalculatorTest(private val testCase: TestCase)
-    : SysuiTestCase() {
-
-    private val calculator = FloatingRotationButtonPositionCalculator(
-        MARGIN_DEFAULT, MARGIN_TASKBAR_LEFT, MARGIN_TASKBAR_BOTTOM
-    )
+internal class FloatingRotationButtonPositionCalculatorTest(
+        private val testCase: TestCase,
+) : SysuiTestCase() {
 
     @Test
     fun calculatePosition() {
-        val position = calculator.calculatePosition(
+        val position = testCase.calculator.calculatePosition(
             testCase.rotation,
             testCase.taskbarVisible,
             testCase.taskbarStashed
         )
-
         assertThat(position).isEqualTo(testCase.expectedPosition)
     }
 
     internal class TestCase(
+        val calculator: FloatingRotationButtonPositionCalculator,
         val rotation: Int,
         val taskbarVisible: Boolean,
         val taskbarStashed: Boolean,
         val expectedPosition: Position
     ) {
         override fun toString(): String =
-            "when rotation = $rotation, " +
-                "taskbarVisible = $taskbarVisible, " +
-                "taskbarStashed = $taskbarStashed - " +
-                "expected $expectedPosition"
+                buildString {
+                    append("when calculator = ")
+                    append(when (calculator) {
+                        posLeftCalculator -> "LEFT"
+                        posRightCalculator -> "RIGHT"
+                        else -> error("Unknown calculator: $calculator")
+                    })
+                    append(", rotation = $rotation")
+                    append(", taskbarVisible = $taskbarVisible")
+                    append(", taskbarStashed = $taskbarStashed")
+                    append(" - expected $expectedPosition")
+                }
     }
 
     companion object {
+        private const val MARGIN_DEFAULT = 10
+        private const val MARGIN_TASKBAR_LEFT = 20
+        private const val MARGIN_TASKBAR_BOTTOM = 30
+
+        private val posLeftCalculator = FloatingRotationButtonPositionCalculator(
+            MARGIN_DEFAULT, MARGIN_TASKBAR_LEFT, MARGIN_TASKBAR_BOTTOM, true
+        )
+        private val posRightCalculator = FloatingRotationButtonPositionCalculator(
+            MARGIN_DEFAULT, MARGIN_TASKBAR_LEFT, MARGIN_TASKBAR_BOTTOM, false
+        )
+
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): Collection<TestCase> =
             listOf(
+                // Position left
                 TestCase(
+                    calculator = posLeftCalculator,
                     rotation = Surface.ROTATION_0,
                     taskbarVisible = false,
                     taskbarStashed = false,
@@ -60,6 +78,7 @@
                     )
                 ),
                 TestCase(
+                    calculator = posLeftCalculator,
                     rotation = Surface.ROTATION_90,
                     taskbarVisible = false,
                     taskbarStashed = false,
@@ -70,6 +89,7 @@
                     )
                 ),
                 TestCase(
+                    calculator = posLeftCalculator,
                     rotation = Surface.ROTATION_180,
                     taskbarVisible = false,
                     taskbarStashed = false,
@@ -80,6 +100,7 @@
                     )
                 ),
                 TestCase(
+                    calculator = posLeftCalculator,
                     rotation = Surface.ROTATION_270,
                     taskbarVisible = false,
                     taskbarStashed = false,
@@ -90,6 +111,7 @@
                     )
                 ),
                 TestCase(
+                    calculator = posLeftCalculator,
                     rotation = Surface.ROTATION_0,
                     taskbarVisible = true,
                     taskbarStashed = false,
@@ -100,6 +122,7 @@
                     )
                 ),
                 TestCase(
+                    calculator = posLeftCalculator,
                     rotation = Surface.ROTATION_0,
                     taskbarVisible = true,
                     taskbarStashed = true,
@@ -110,6 +133,7 @@
                     )
                 ),
                 TestCase(
+                    calculator = posLeftCalculator,
                     rotation = Surface.ROTATION_90,
                     taskbarVisible = true,
                     taskbarStashed = false,
@@ -118,11 +142,86 @@
                         translationX = -MARGIN_TASKBAR_LEFT,
                         translationY = -MARGIN_TASKBAR_BOTTOM
                     )
+                ),
+
+                // Position right
+                TestCase(
+                    calculator = posRightCalculator,
+                    rotation = Surface.ROTATION_0,
+                    taskbarVisible = false,
+                    taskbarStashed = false,
+                    expectedPosition = Position(
+                        gravity = Gravity.BOTTOM or Gravity.RIGHT,
+                        translationX = -MARGIN_DEFAULT,
+                        translationY = -MARGIN_DEFAULT
+                    )
+                ),
+                TestCase(
+                    calculator = posRightCalculator,
+                    rotation = Surface.ROTATION_90,
+                    taskbarVisible = false,
+                    taskbarStashed = false,
+                    expectedPosition = Position(
+                        gravity = Gravity.TOP or Gravity.RIGHT,
+                        translationX = -MARGIN_DEFAULT,
+                        translationY = MARGIN_DEFAULT
+                    )
+                ),
+                TestCase(
+                    calculator = posRightCalculator,
+                    rotation = Surface.ROTATION_180,
+                    taskbarVisible = false,
+                    taskbarStashed = false,
+                    expectedPosition = Position(
+                        gravity = Gravity.TOP or Gravity.LEFT,
+                        translationX = MARGIN_DEFAULT,
+                        translationY = MARGIN_DEFAULT
+                    )
+                ),
+                TestCase(
+                    calculator = posRightCalculator,
+                    rotation = Surface.ROTATION_270,
+                    taskbarVisible = false,
+                    taskbarStashed = false,
+                    expectedPosition = Position(
+                        gravity = Gravity.BOTTOM or Gravity.LEFT,
+                        translationX = MARGIN_DEFAULT,
+                        translationY = -MARGIN_DEFAULT
+                    )
+                ),
+                TestCase(
+                    calculator = posRightCalculator,
+                    rotation = Surface.ROTATION_0,
+                    taskbarVisible = true,
+                    taskbarStashed = false,
+                    expectedPosition = Position(
+                        gravity = Gravity.BOTTOM or Gravity.RIGHT,
+                        translationX = -MARGIN_TASKBAR_LEFT,
+                        translationY = -MARGIN_TASKBAR_BOTTOM
+                    )
+                ),
+                TestCase(
+                    calculator = posRightCalculator,
+                    rotation = Surface.ROTATION_0,
+                    taskbarVisible = true,
+                    taskbarStashed = true,
+                    expectedPosition = Position(
+                        gravity = Gravity.BOTTOM or Gravity.RIGHT,
+                        translationX = -MARGIN_DEFAULT,
+                        translationY = -MARGIN_DEFAULT
+                    )
+                ),
+                TestCase(
+                    calculator = posRightCalculator,
+                    rotation = Surface.ROTATION_90,
+                    taskbarVisible = true,
+                    taskbarStashed = false,
+                    expectedPosition = Position(
+                        gravity = Gravity.TOP or Gravity.RIGHT,
+                        translationX = -MARGIN_TASKBAR_LEFT,
+                        translationY = MARGIN_TASKBAR_BOTTOM
+                    )
                 )
             )
-
-        private const val MARGIN_DEFAULT = 10
-        private const val MARGIN_TASKBAR_LEFT = 20
-        private const val MARGIN_TASKBAR_BOTTOM = 30
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index 8440455..39c4e06 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -23,10 +23,14 @@
 import android.os.UserManager
 import android.test.suitebuilder.annotation.SmallTest
 import androidx.test.runner.AndroidJUnit4
+import com.android.internal.logging.UiEventLogger
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.notetask.NoteTaskIntentResolver.Companion.ACTION_CREATE_NOTE
+import com.android.systemui.notetask.NoteTaskController.Companion.INTENT_EXTRA_USE_STYLUS_MODE
+import com.android.systemui.notetask.NoteTaskController.ShowNoteTaskUiEvent
+import com.android.systemui.notetask.NoteTaskInfoResolver.NoteTaskInfo
 import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
 import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.whenever
 import com.android.wm.shell.bubbles.Bubbles
@@ -36,8 +40,8 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
-import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyZeroInteractions
 import org.mockito.MockitoAnnotations
 
 /**
@@ -50,24 +54,23 @@
 @RunWith(AndroidJUnit4::class)
 internal class NoteTaskControllerTest : SysuiTestCase() {
 
-    private val notesIntent = Intent(ACTION_CREATE_NOTE)
-
     @Mock lateinit var context: Context
     @Mock lateinit var packageManager: PackageManager
-    @Mock lateinit var noteTaskIntentResolver: NoteTaskIntentResolver
+    @Mock lateinit var resolver: NoteTaskInfoResolver
     @Mock lateinit var bubbles: Bubbles
     @Mock lateinit var optionalBubbles: Optional<Bubbles>
     @Mock lateinit var keyguardManager: KeyguardManager
     @Mock lateinit var optionalKeyguardManager: Optional<KeyguardManager>
     @Mock lateinit var optionalUserManager: Optional<UserManager>
     @Mock lateinit var userManager: UserManager
+    @Mock lateinit var uiEventLogger: UiEventLogger
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
         whenever(context.packageManager).thenReturn(packageManager)
-        whenever(noteTaskIntentResolver.resolveIntent()).thenReturn(notesIntent)
+        whenever(resolver.resolveInfo()).thenReturn(NoteTaskInfo(NOTES_PACKAGE_NAME, NOTES_UID))
         whenever(optionalBubbles.orElse(null)).thenReturn(bubbles)
         whenever(optionalKeyguardManager.orElse(null)).thenReturn(keyguardManager)
         whenever(optionalUserManager.orElse(null)).thenReturn(userManager)
@@ -77,101 +80,182 @@
     private fun createNoteTaskController(isEnabled: Boolean = true): NoteTaskController {
         return NoteTaskController(
             context = context,
-            intentResolver = noteTaskIntentResolver,
+            resolver = resolver,
             optionalBubbles = optionalBubbles,
             optionalKeyguardManager = optionalKeyguardManager,
             optionalUserManager = optionalUserManager,
             isEnabled = isEnabled,
+            uiEventLogger = uiEventLogger,
         )
     }
 
     // region showNoteTask
     @Test
-    fun showNoteTask_keyguardIsLocked_shouldStartActivity() {
+    fun showNoteTask_keyguardIsLocked_shouldStartActivityAndLogUiEvent() {
         whenever(keyguardManager.isKeyguardLocked).thenReturn(true)
 
-        createNoteTaskController().showNoteTask(isInMultiWindowMode = false)
+        createNoteTaskController()
+            .showNoteTask(
+                isInMultiWindowMode = false,
+                uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE,
+            )
 
-        verify(context).startActivity(notesIntent)
-        verify(bubbles, never()).showOrHideAppBubble(notesIntent)
+        val intentCaptor = argumentCaptor<Intent>()
+        verify(context).startActivity(capture(intentCaptor))
+        intentCaptor.value.let { intent ->
+            assertThat(intent.action).isEqualTo(NoteTaskController.ACTION_CREATE_NOTE)
+            assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME)
+            assertThat(intent.flags).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK)
+            assertThat(intent.getBooleanExtra(INTENT_EXTRA_USE_STYLUS_MODE, false)).isTrue()
+        }
+        verifyZeroInteractions(bubbles)
+        verify(uiEventLogger)
+            .log(
+                ShowNoteTaskUiEvent.NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE,
+                NOTES_UID,
+                NOTES_PACKAGE_NAME
+            )
     }
 
     @Test
-    fun showNoteTask_keyguardIsUnlocked_shouldStartBubbles() {
+    fun showNoteTask_keyguardIsUnlocked_shouldStartBubblesAndLogUiEvent() {
         whenever(keyguardManager.isKeyguardLocked).thenReturn(false)
 
-        createNoteTaskController().showNoteTask(isInMultiWindowMode = false)
+        createNoteTaskController()
+            .showNoteTask(
+                isInMultiWindowMode = false,
+                uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON,
+            )
 
-        verify(bubbles).showOrHideAppBubble(notesIntent)
-        verify(context, never()).startActivity(notesIntent)
+        verifyZeroInteractions(context)
+        val intentCaptor = argumentCaptor<Intent>()
+        verify(bubbles).showOrHideAppBubble(capture(intentCaptor))
+        intentCaptor.value.let { intent ->
+            assertThat(intent.action).isEqualTo(NoteTaskController.ACTION_CREATE_NOTE)
+            assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME)
+            assertThat(intent.flags).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK)
+            assertThat(intent.getBooleanExtra(INTENT_EXTRA_USE_STYLUS_MODE, false)).isTrue()
+        }
+        verify(uiEventLogger)
+            .log(
+                ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON,
+                NOTES_UID,
+                NOTES_PACKAGE_NAME
+            )
     }
 
     @Test
-    fun showNoteTask_isInMultiWindowMode_shouldStartActivity() {
+    fun showNoteTask_keyguardIsUnlocked_uiEventIsNull_shouldStartBubblesWithoutLoggingUiEvent() {
         whenever(keyguardManager.isKeyguardLocked).thenReturn(false)
 
-        createNoteTaskController().showNoteTask(isInMultiWindowMode = true)
+        createNoteTaskController().showNoteTask(isInMultiWindowMode = false, uiEvent = null)
 
-        verify(context).startActivity(notesIntent)
-        verify(bubbles, never()).showOrHideAppBubble(notesIntent)
+        verifyZeroInteractions(context)
+        val intentCaptor = argumentCaptor<Intent>()
+        verify(bubbles).showOrHideAppBubble(capture(intentCaptor))
+        intentCaptor.value.let { intent ->
+            assertThat(intent.action).isEqualTo(NoteTaskController.ACTION_CREATE_NOTE)
+            assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME)
+            assertThat(intent.flags).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK)
+            assertThat(intent.getBooleanExtra(INTENT_EXTRA_USE_STYLUS_MODE, false)).isTrue()
+        }
+        verifyZeroInteractions(uiEventLogger)
+    }
+
+    @Test
+    fun showNoteTask_isInMultiWindowMode_shouldStartActivityAndLogUiEvent() {
+        whenever(keyguardManager.isKeyguardLocked).thenReturn(false)
+
+        createNoteTaskController()
+            .showNoteTask(
+                isInMultiWindowMode = true,
+                uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_SHORTCUT,
+            )
+
+        val intentCaptor = argumentCaptor<Intent>()
+        verify(context).startActivity(capture(intentCaptor))
+        intentCaptor.value.let { intent ->
+            assertThat(intent.action).isEqualTo(NoteTaskController.ACTION_CREATE_NOTE)
+            assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME)
+            assertThat(intent.flags).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK)
+            assertThat(intent.getBooleanExtra(INTENT_EXTRA_USE_STYLUS_MODE, false)).isTrue()
+        }
+        verifyZeroInteractions(bubbles)
+        verify(uiEventLogger)
+            .log(ShowNoteTaskUiEvent.NOTE_OPENED_VIA_SHORTCUT, NOTES_UID, NOTES_PACKAGE_NAME)
     }
 
     @Test
     fun showNoteTask_bubblesIsNull_shouldDoNothing() {
         whenever(optionalBubbles.orElse(null)).thenReturn(null)
 
-        createNoteTaskController().showNoteTask(isInMultiWindowMode = false)
+        createNoteTaskController()
+            .showNoteTask(
+                isInMultiWindowMode = false,
+                uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON
+            )
 
-        verify(context, never()).startActivity(notesIntent)
-        verify(bubbles, never()).showOrHideAppBubble(notesIntent)
+        verifyZeroInteractions(context, bubbles, uiEventLogger)
     }
 
     @Test
     fun showNoteTask_keyguardManagerIsNull_shouldDoNothing() {
         whenever(optionalKeyguardManager.orElse(null)).thenReturn(null)
 
-        createNoteTaskController().showNoteTask(isInMultiWindowMode = false)
+        createNoteTaskController()
+            .showNoteTask(
+                isInMultiWindowMode = false,
+                uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON,
+            )
 
-        verify(context, never()).startActivity(notesIntent)
-        verify(bubbles, never()).showOrHideAppBubble(notesIntent)
+        verifyZeroInteractions(context, bubbles, uiEventLogger)
     }
 
     @Test
     fun showNoteTask_userManagerIsNull_shouldDoNothing() {
         whenever(optionalUserManager.orElse(null)).thenReturn(null)
 
-        createNoteTaskController().showNoteTask(isInMultiWindowMode = false)
+        createNoteTaskController()
+            .showNoteTask(
+                isInMultiWindowMode = false,
+                uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON,
+            )
 
-        verify(context, never()).startActivity(notesIntent)
-        verify(bubbles, never()).showOrHideAppBubble(notesIntent)
+        verifyZeroInteractions(context, bubbles, uiEventLogger)
     }
 
     @Test
     fun showNoteTask_intentResolverReturnsNull_shouldDoNothing() {
-        whenever(noteTaskIntentResolver.resolveIntent()).thenReturn(null)
+        whenever(resolver.resolveInfo()).thenReturn(null)
 
-        createNoteTaskController().showNoteTask(isInMultiWindowMode = false)
+        createNoteTaskController()
+            .showNoteTask(
+                isInMultiWindowMode = false,
+                uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON,
+            )
 
-        verify(context, never()).startActivity(notesIntent)
-        verify(bubbles, never()).showOrHideAppBubble(notesIntent)
+        verifyZeroInteractions(context, bubbles, uiEventLogger)
     }
 
     @Test
     fun showNoteTask_flagDisabled_shouldDoNothing() {
-        createNoteTaskController(isEnabled = false).showNoteTask()
+        createNoteTaskController(isEnabled = false)
+            .showNoteTask(uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON)
 
-        verify(context, never()).startActivity(notesIntent)
-        verify(bubbles, never()).showOrHideAppBubble(notesIntent)
+        verifyZeroInteractions(context, bubbles, uiEventLogger)
     }
 
     @Test
     fun showNoteTask_userIsLocked_shouldDoNothing() {
         whenever(userManager.isUserUnlocked).thenReturn(false)
 
-        createNoteTaskController().showNoteTask(isInMultiWindowMode = false)
+        createNoteTaskController()
+            .showNoteTask(
+                isInMultiWindowMode = false,
+                uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON,
+            )
 
-        verify(context, never()).startActivity(notesIntent)
-        verify(bubbles, never()).showOrHideAppBubble(notesIntent)
+        verifyZeroInteractions(context, bubbles, uiEventLogger)
     }
     // endregion
 
@@ -206,4 +290,9 @@
         assertThat(argument.value.flattenToString()).isEqualTo(expected.flattenToString())
     }
     // endregion
+
+    private companion object {
+        const val NOTES_PACKAGE_NAME = "com.android.note.app"
+        const val NOTES_UID = 123456
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt
new file mode 100644
index 0000000..d6495d8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt
@@ -0,0 +1,108 @@
+/*
+ * 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.notetask
+
+import android.app.role.RoleManager
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.test.suitebuilder.annotation.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.MockitoAnnotations
+
+/**
+ * Tests for [NoteTaskInfoResolver].
+ *
+ * Build/Install/Run:
+ * - atest SystemUITests:NoteTaskInfoResolverTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+internal class NoteTaskInfoResolverTest : SysuiTestCase() {
+
+    @Mock lateinit var packageManager: PackageManager
+    @Mock lateinit var roleManager: RoleManager
+
+    private lateinit var underTest: NoteTaskInfoResolver
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        underTest = NoteTaskInfoResolver(context, roleManager, packageManager)
+    }
+
+    @Test
+    fun resolveInfo_shouldReturnInfo() {
+        val packageName = "com.android.note.app"
+        val uid = 123456
+        whenever(roleManager.getRoleHoldersAsUser(NoteTaskInfoResolver.ROLE_NOTES, context.user))
+            .then { listOf(packageName) }
+        whenever(
+                packageManager.getApplicationInfoAsUser(
+                    eq(packageName),
+                    any<PackageManager.ApplicationInfoFlags>(),
+                    eq(context.user)
+                )
+            )
+            .thenReturn(ApplicationInfo().apply { this.uid = uid })
+
+        val actual = underTest.resolveInfo()
+
+        requireNotNull(actual) { "Note task info must not be null" }
+        assertThat(actual.packageName).isEqualTo(packageName)
+        assertThat(actual.uid).isEqualTo(uid)
+    }
+
+    @Test
+    fun resolveInfo_packageManagerThrowsException_shouldReturnInfoWithZeroUid() {
+        val packageName = "com.android.note.app"
+        whenever(roleManager.getRoleHoldersAsUser(NoteTaskInfoResolver.ROLE_NOTES, context.user))
+            .then { listOf(packageName) }
+        whenever(
+                packageManager.getApplicationInfoAsUser(
+                    eq(packageName),
+                    any<PackageManager.ApplicationInfoFlags>(),
+                    eq(context.user)
+                )
+            )
+            .thenThrow(PackageManager.NameNotFoundException(packageName))
+
+        val actual = underTest.resolveInfo()
+
+        requireNotNull(actual) { "Note task info must not be null" }
+        assertThat(actual.packageName).isEqualTo(packageName)
+        assertThat(actual.uid).isEqualTo(0)
+    }
+
+    @Test
+    fun resolveInfo_noRoleHolderIsSet_shouldReturnNull() {
+        whenever(roleManager.getRoleHoldersAsUser(eq(NoteTaskInfoResolver.ROLE_NOTES), any()))
+            .then { listOf<String>() }
+
+        val actual = underTest.resolveInfo()
+
+        assertThat(actual).isNull()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
index 010ac5b..53720ff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
@@ -15,11 +15,14 @@
  */
 package com.android.systemui.notetask
 
+import android.app.KeyguardManager
 import android.test.suitebuilder.annotation.SmallTest
 import android.view.KeyEvent
 import androidx.test.runner.AndroidJUnit4
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.notetask.NoteTaskController.ShowNoteTaskUiEvent
 import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.android.wm.shell.bubbles.Bubbles
 import java.util.Optional
@@ -30,6 +33,7 @@
 import org.mockito.Mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyZeroInteractions
 import org.mockito.MockitoAnnotations
 
 /**
@@ -55,12 +59,16 @@
         whenever(optionalBubbles.orElse(null)).thenReturn(bubbles)
     }
 
-    private fun createNoteTaskInitializer(isEnabled: Boolean = true): NoteTaskInitializer {
+    private fun createNoteTaskInitializer(
+        isEnabled: Boolean = true,
+        optionalKeyguardManager: Optional<KeyguardManager> = Optional.empty(),
+    ): NoteTaskInitializer {
         return NoteTaskInitializer(
             optionalBubbles = optionalBubbles,
             noteTaskController = noteTaskController,
             commandQueue = commandQueue,
             isEnabled = isEnabled,
+            optionalKeyguardManager = optionalKeyguardManager,
         )
     }
 
@@ -105,19 +113,44 @@
 
     // region handleSystemKey
     @Test
-    fun handleSystemKey_receiveValidSystemKey_shouldShowNoteTask() {
-        createNoteTaskInitializer()
+    fun handleSystemKey_receiveValidSystemKey_keyguardNotLocked_shouldShowNoteTaskWithUnlocked() {
+        val keyguardManager =
+            mock<KeyguardManager>() { whenever(isKeyguardLocked).thenReturn(false) }
+        createNoteTaskInitializer(optionalKeyguardManager = Optional.of(keyguardManager))
             .callbacks
             .handleSystemKey(NoteTaskController.NOTE_TASK_KEY_EVENT)
 
-        verify(noteTaskController).showNoteTask()
+        verify(noteTaskController)
+            .showNoteTask(uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON)
+    }
+
+    @Test
+    fun handleSystemKey_receiveValidSystemKey_keyguardLocked_shouldShowNoteTaskWithLocked() {
+        val keyguardManager =
+            mock<KeyguardManager>() { whenever(isKeyguardLocked).thenReturn(true) }
+        createNoteTaskInitializer(optionalKeyguardManager = Optional.of(keyguardManager))
+            .callbacks
+            .handleSystemKey(NoteTaskController.NOTE_TASK_KEY_EVENT)
+
+        verify(noteTaskController)
+            .showNoteTask(uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED)
+    }
+
+    @Test
+    fun handleSystemKey_receiveValidSystemKey_nullKeyguardManager_shouldShowNoteTaskWithUnlocked() {
+        createNoteTaskInitializer(optionalKeyguardManager = Optional.empty())
+            .callbacks
+            .handleSystemKey(NoteTaskController.NOTE_TASK_KEY_EVENT)
+
+        verify(noteTaskController)
+            .showNoteTask(uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON)
     }
 
     @Test
     fun handleSystemKey_receiveInvalidSystemKey_shouldDoNothing() {
         createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent.KEYCODE_UNKNOWN)
 
-        verify(noteTaskController, never()).showNoteTask()
+        verifyZeroInteractions(noteTaskController)
     }
     // endregion
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskIntentResolverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskIntentResolverTest.kt
deleted file mode 100644
index bbe60f4..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskIntentResolverTest.kt
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * Copyright (C) 2022 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.notetask
-
-import android.content.ComponentName
-import android.content.Intent
-import android.content.pm.ActivityInfo
-import android.content.pm.ApplicationInfo
-import android.content.pm.PackageManager
-import android.content.pm.PackageManager.ResolveInfoFlags
-import android.content.pm.ResolveInfo
-import android.test.suitebuilder.annotation.SmallTest
-import androidx.test.runner.AndroidJUnit4
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.notetask.NoteTaskIntentResolver.Companion.ACTION_CREATE_NOTE
-import com.android.systemui.util.mockito.whenever
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.any
-import org.mockito.MockitoAnnotations
-
-/**
- * Tests for [NoteTaskIntentResolver].
- *
- * Build/Install/Run:
- * - atest SystemUITests:NoteTaskIntentResolverTest
- */
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-internal class NoteTaskIntentResolverTest : SysuiTestCase() {
-
-    @Mock lateinit var packageManager: PackageManager
-
-    private lateinit var resolver: NoteTaskIntentResolver
-
-    @Before
-    fun setUp() {
-        MockitoAnnotations.initMocks(this)
-        resolver = NoteTaskIntentResolver(packageManager)
-    }
-
-    private fun createResolveInfo(
-        activityInfo: ActivityInfo? = createActivityInfo(),
-    ): ResolveInfo {
-        return ResolveInfo().apply { this.activityInfo = activityInfo }
-    }
-
-    private fun createActivityInfo(
-        packageName: String = "PackageName",
-        name: String? = "ActivityName",
-        exported: Boolean = true,
-        enabled: Boolean = true,
-        showWhenLocked: Boolean = true,
-        turnScreenOn: Boolean = true,
-    ): ActivityInfo {
-        return ActivityInfo().apply {
-            this.name = name
-            this.exported = exported
-            this.enabled = enabled
-            if (showWhenLocked) {
-                flags = flags or ActivityInfo.FLAG_SHOW_WHEN_LOCKED
-            }
-            if (turnScreenOn) {
-                flags = flags or ActivityInfo.FLAG_TURN_SCREEN_ON
-            }
-            this.applicationInfo = ApplicationInfo().apply { this.packageName = packageName }
-        }
-    }
-
-    private fun givenQueryIntentActivities(block: () -> List<ResolveInfo>) {
-        whenever(packageManager.queryIntentActivities(any(), any<ResolveInfoFlags>()))
-            .thenReturn(block())
-    }
-
-    private fun givenResolveActivity(block: () -> ResolveInfo?) {
-        whenever(packageManager.resolveActivity(any(), any<ResolveInfoFlags>())).thenReturn(block())
-    }
-
-    @Test
-    fun resolveIntent_shouldReturnNotesIntent() {
-        givenQueryIntentActivities { listOf(createResolveInfo()) }
-        givenResolveActivity { createResolveInfo(activityInfo = createActivityInfo()) }
-
-        val actual = resolver.resolveIntent()
-
-        val expected =
-            Intent(ACTION_CREATE_NOTE)
-                .setPackage("PackageName")
-                .setComponent(ComponentName("PackageName", "ActivityName"))
-                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-        // Compares the string representation of both intents, as they are different instances.
-        assertThat(actual.toString()).isEqualTo(expected.toString())
-    }
-
-    @Test
-    fun resolveIntent_activityInfoEnabledIsFalse_shouldReturnNull() {
-        givenQueryIntentActivities { listOf(createResolveInfo()) }
-        givenResolveActivity {
-            createResolveInfo(activityInfo = createActivityInfo(enabled = false))
-        }
-
-        val actual = resolver.resolveIntent()
-
-        assertThat(actual).isNull()
-    }
-
-    @Test
-    fun resolveIntent_activityInfoExportedIsFalse_shouldReturnNull() {
-        givenQueryIntentActivities { listOf(createResolveInfo()) }
-        givenResolveActivity {
-            createResolveInfo(activityInfo = createActivityInfo(exported = false))
-        }
-
-        val actual = resolver.resolveIntent()
-
-        assertThat(actual).isNull()
-    }
-
-    @Test
-    fun resolveIntent_activityInfoShowWhenLockedIsFalse_shouldReturnNull() {
-        givenQueryIntentActivities { listOf(createResolveInfo()) }
-        givenResolveActivity {
-            createResolveInfo(activityInfo = createActivityInfo(showWhenLocked = false))
-        }
-
-        val actual = resolver.resolveIntent()
-
-        assertThat(actual).isNull()
-    }
-
-    @Test
-    fun resolveIntent_activityInfoTurnScreenOnIsFalse_shouldReturnNull() {
-        givenQueryIntentActivities { listOf(createResolveInfo()) }
-        givenResolveActivity {
-            createResolveInfo(activityInfo = createActivityInfo(turnScreenOn = false))
-        }
-
-        val actual = resolver.resolveIntent()
-
-        assertThat(actual).isNull()
-    }
-
-    @Test
-    fun resolveIntent_activityInfoNameIsBlank_shouldReturnNull() {
-        givenQueryIntentActivities { listOf(createResolveInfo()) }
-        givenResolveActivity { createResolveInfo(activityInfo = createActivityInfo(name = "")) }
-
-        val actual = resolver.resolveIntent()
-
-        assertThat(actual).isNull()
-    }
-
-    @Test
-    fun resolveIntent_activityInfoNameIsNull_shouldReturnNull() {
-        givenQueryIntentActivities { listOf(createResolveInfo()) }
-        givenResolveActivity { createResolveInfo(activityInfo = createActivityInfo(name = null)) }
-
-        val actual = resolver.resolveIntent()
-
-        assertThat(actual).isNull()
-    }
-
-    @Test
-    fun resolveIntent_activityInfoIsNull_shouldReturnNull() {
-        givenQueryIntentActivities { listOf(createResolveInfo()) }
-        givenResolveActivity { createResolveInfo(activityInfo = null) }
-
-        val actual = resolver.resolveIntent()
-
-        assertThat(actual).isNull()
-    }
-
-    @Test
-    fun resolveIntent_resolveActivityIsNull_shouldReturnNull() {
-        givenQueryIntentActivities { listOf(createResolveInfo()) }
-        givenResolveActivity { null }
-
-        val actual = resolver.resolveIntent()
-
-        assertThat(actual).isNull()
-    }
-
-    @Test
-    fun resolveIntent_packageNameIsBlank_shouldReturnNull() {
-        givenQueryIntentActivities {
-            listOf(createResolveInfo(createActivityInfo(packageName = "")))
-        }
-
-        val actual = resolver.resolveIntent()
-
-        assertThat(actual).isNull()
-    }
-
-    @Test
-    fun resolveIntent_activityNotFoundForAction_shouldReturnNull() {
-        givenQueryIntentActivities { emptyList() }
-
-        val actual = resolver.resolveIntent()
-
-        assertThat(actual).isNull()
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
index a1d42a0..cdc683f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
@@ -27,7 +27,7 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.LockScreenState
 import com.android.systemui.notetask.NoteTaskController
-import com.android.systemui.util.mockito.whenever
+import com.android.systemui.notetask.NoteTaskController.ShowNoteTaskUiEvent
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runTest
@@ -53,7 +53,6 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        whenever(noteTaskController.showNoteTask()).then {}
     }
 
     private fun createUnderTest(isEnabled: Boolean) =
@@ -96,6 +95,7 @@
 
         underTest.onTriggered(expandable = null)
 
-        verify(noteTaskController).showNoteTask()
+        verify(noteTaskController)
+            .showNoteTask(uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/process/condition/UserProcessConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/process/condition/UserProcessConditionTest.java
new file mode 100644
index 0000000..2293fc5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/process/condition/UserProcessConditionTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.process.condition;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.process.ProcessWrapper;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.shared.condition.Condition;
+import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class UserProcessConditionTest extends SysuiTestCase {
+    @Mock
+    UserTracker mUserTracker;
+
+    @Mock
+    ProcessWrapper mProcessWrapper;
+
+    @Mock
+    Monitor.Callback mCallback;
+
+    private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    /**
+     * Verifies condition reports false when tracker reports a different user id than the
+     * identifier from the process handle.
+     */
+    @Test
+    public void testConditionFailsWithDifferentIds() {
+
+        final Condition condition = new UserProcessCondition(mProcessWrapper, mUserTracker);
+        when(mProcessWrapper.getUserHandleIdentifier()).thenReturn(0);
+        when(mUserTracker.getUserId()).thenReturn(1);
+
+        final Monitor monitor = new Monitor(mExecutor);
+
+        monitor.addSubscription(new Monitor.Subscription.Builder(mCallback)
+                .addCondition(condition)
+                .build());
+
+        mExecutor.runAllReady();
+
+        verify(mCallback).onConditionsChanged(false);
+    }
+
+    /**
+     * Verifies condition reports false when tracker reports a different user id than the
+     * identifier from the process handle.
+     */
+    @Test
+    public void testConditionSucceedsWithSameIds() {
+
+        final Condition condition = new UserProcessCondition(mProcessWrapper, mUserTracker);
+        when(mProcessWrapper.getUserHandleIdentifier()).thenReturn(0);
+        when(mUserTracker.getUserId()).thenReturn(0);
+
+        final Monitor monitor = new Monitor(mExecutor);
+
+        monitor.addSubscription(new Monitor.Subscription.Builder(mCallback)
+                .addCondition(condition)
+                .build());
+
+        mExecutor.runAllReady();
+
+        verify(mCallback).onConditionsChanged(true);
+    }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
index 43fb1bd..dee1cc8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
@@ -59,6 +59,7 @@
 @SmallTest
 public class AutoAddTrackerTest extends SysuiTestCase {
 
+    private static final int END_POSITION = -1;
     private static final int USER = 0;
 
     @Mock
@@ -142,6 +143,29 @@
     }
 
     @Test
+    public void testRestoredTilePositionPreserved() {
+        verify(mBroadcastDispatcher).registerReceiver(
+                mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any(), anyInt(), any());
+        String restoredTiles = "saver,internet,work,cast";
+        Intent restoreTilesIntent = makeRestoreIntent(Secure.QS_TILES, null, restoredTiles);
+
+        mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
+
+        assertEquals(2, mAutoTracker.getRestoredTilePosition("work"));
+    }
+
+    @Test
+    public void testNoRestoredTileReturnsEndPosition() {
+        verify(mBroadcastDispatcher).registerReceiver(
+                mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any(), anyInt(), any());
+        Intent restoreTilesIntent = makeRestoreIntent(Secure.QS_TILES, null, null);
+
+        mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
+
+        assertEquals(END_POSITION, mAutoTracker.getRestoredTilePosition("work"));
+    }
+
+    @Test
     public void testBroadcastReceiverRegistered() {
         verify(mBroadcastDispatcher).registerReceiver(
                 any(), mIntentFilterArgumentCaptor.capture(), any(), eq(UserHandle.of(USER)),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 42ef9c2..89606bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -52,14 +52,14 @@
 import com.android.systemui.SysuiBaseFragmentTest;
 import com.android.systemui.animation.ShadeInterpolation;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FakeFeatureFlags;
 import com.android.systemui.media.controls.ui.MediaHost;
-import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.qs.customize.QSCustomizerController;
 import com.android.systemui.qs.dagger.QSFragmentComponent;
 import com.android.systemui.qs.external.TileServiceRequestController;
 import com.android.systemui.qs.footer.ui.binder.FooterActionsViewBinder;
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel;
+import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.settings.FakeDisplayTracker;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -85,7 +85,6 @@
     @Mock private MediaHost mQSMediaHost;
     @Mock private MediaHost mQQSMediaHost;
     @Mock private KeyguardBypassController mBypassController;
-    @Mock private FalsingManager mFalsingManager;
     @Mock private TileServiceRequestController.Builder mTileServiceRequestControllerBuilder;
     @Mock private TileServiceRequestController mTileServiceRequestController;
     @Mock private QSCustomizerController mQsCustomizerController;
@@ -494,7 +493,7 @@
     @Override
     protected Fragment instantiate(Context context, String className, Bundle arguments) {
         MockitoAnnotations.initMocks(this);
-        CommandQueue commandQueue = new CommandQueue(context);
+        CommandQueue commandQueue = new CommandQueue(context, new FakeDisplayTracker(context));
 
         setupQsComponent();
         setUpViews();
@@ -502,11 +501,9 @@
         setUpMedia();
         setUpOther();
 
-        FakeFeatureFlags featureFlags = new FakeFeatureFlags();
         return new QSFragment(
                 new RemoteInputQuickSettingsDisabler(
                         context, commandQueue, mock(ConfigurationController.class)),
-                mock(QSTileHost.class),
                 mStatusBarStateController,
                 commandQueue,
                 mQSMediaHost,
@@ -514,9 +511,8 @@
                 mBypassController,
                 mQsComponentFactory,
                 mock(QSFragmentDisableFlagsLogger.class),
-                mFalsingManager,
                 mock(DumpManager.class),
-                featureFlags,
+                mock(QSLogger.class),
                 mock(FooterActionsController.class),
                 mFooterActionsViewModelFactory);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
index 6cf642c..09156d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
@@ -70,7 +70,7 @@
 
         whenever(brightnessSliderFactory.create(any(), any())).thenReturn(brightnessSlider)
         whenever(brightnessControllerFactory.create(any())).thenReturn(brightnessController)
-        testableResources.addOverride(R.bool.config_use_split_notification_shade, false)
+        setShouldUseSplitShade(false)
         whenever(qsPanel.resources).thenReturn(testableResources.resources)
         whenever(qsPanel.getOrCreateTileLayout()).thenReturn(pagedTileLayout)
         whenever(statusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false)
@@ -133,12 +133,31 @@
 
     @Test
     fun configurationChange_onlySplitShadeConfigChanges_tileAreRedistributed() {
-        testableResources.addOverride(R.bool.config_use_split_notification_shade, false)
+        setShouldUseSplitShade(false)
         controller.mOnConfigurationChangedListener.onConfigurationChange(configuration)
         verify(pagedTileLayout, never()).forceTilesRedistribution(any())
 
-        testableResources.addOverride(R.bool.config_use_split_notification_shade, true)
+        setShouldUseSplitShade(true)
         controller.mOnConfigurationChangedListener.onConfigurationChange(configuration)
         verify(pagedTileLayout).forceTilesRedistribution("Split shade state changed")
     }
+
+    @Test
+    fun configurationChange_onlySplitShadeConfigChanges_qsPanelCanBeCollapsed() {
+        setShouldUseSplitShade(false)
+        controller.mOnConfigurationChangedListener.onConfigurationChange(configuration)
+        verify(qsPanel, never()).setCanCollapse(anyBoolean())
+
+        setShouldUseSplitShade(true)
+        controller.mOnConfigurationChangedListener.onConfigurationChange(configuration)
+        verify(qsPanel).setCanCollapse(false)
+
+        setShouldUseSplitShade(false)
+        controller.mOnConfigurationChangedListener.onConfigurationChange(configuration)
+        verify(qsPanel).setCanCollapse(true)
+    }
+
+    private fun setShouldUseSplitShade(shouldUse: Boolean) {
+        testableResources.addOverride(R.bool.config_use_split_notification_shade, shouldUse)
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
index d52b296..a8cfb25 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
@@ -37,6 +37,7 @@
 import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
@@ -196,6 +197,16 @@
         qsPanel.setSquishinessFraction(0.5f)
     }
 
+    @Test
+    fun testSplitShade_CollapseAccessibilityActionNotAnnounced() {
+        qsPanel.setCanCollapse(false)
+        val accessibilityInfo = mock(AccessibilityNodeInfo::class.java)
+        qsPanel.onInitializeAccessibilityNodeInfo(accessibilityInfo)
+
+        val actionCollapse = AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE
+        verify(accessibilityInfo, never()).addAction(actionCollapse)
+    }
+
     private infix fun View.isLeftOf(other: View): Boolean {
         val rect = Rect()
         getBoundsOnScreen(rect)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
index 2bd068a..8644b5e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
@@ -35,12 +35,14 @@
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.LaunchableFrameLayout
 import com.android.systemui.classifier.FalsingManagerFake
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.qs.QSHost
 import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.settings.FakeDisplayTracker
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.nullable
@@ -90,6 +92,7 @@
     private lateinit var customTile: CustomTile
     private lateinit var testableLooper: TestableLooper
     private lateinit var customTileBuilder: CustomTile.Builder
+    private val displayTracker = FakeDisplayTracker(mContext)
 
     @Before
     fun setUp() {
@@ -119,7 +122,8 @@
                 activityStarter,
                 qsLogger,
                 customTileStatePersister,
-                tileServices
+                tileServices,
+                displayTracker
         )
 
         customTile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
@@ -339,7 +343,7 @@
         val tile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
         tile.qsTile.activityLaunchForClick = pi
 
-        tile.handleClick(mock(View::class.java))
+        tile.handleClick(mock(LaunchableFrameLayout::class.java))
 
         testableLooper.processAllMessages()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
index 3281fa9..e222542 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
@@ -35,6 +35,7 @@
 import com.android.systemui.qs.tiles.DndTile
 import com.android.systemui.qs.tiles.DreamTile
 import com.android.systemui.qs.tiles.FlashlightTile
+import com.android.systemui.qs.tiles.FontScalingTile
 import com.android.systemui.qs.tiles.HotspotTile
 import com.android.systemui.qs.tiles.InternetTile
 import com.android.systemui.qs.tiles.LocationTile
@@ -51,14 +52,15 @@
 import com.android.systemui.qs.tiles.WorkModeTile
 import com.android.systemui.util.leak.GarbageMonitor
 import com.google.common.truth.Truth.assertThat
+import javax.inject.Provider
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Answers
 import org.mockito.Mock
 import org.mockito.Mockito.inOrder
-import org.mockito.MockitoAnnotations
 import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
 
 private val specMap = mapOf(
         "internet" to InternetTile::class.java,
@@ -87,7 +89,8 @@
         "qr_code_scanner" to QRCodeScannerTile::class.java,
         "onehanded" to OneHandedModeTile::class.java,
         "color_correction" to ColorCorrectionTile::class.java,
-        "dream" to DreamTile::class.java
+        "dream" to DreamTile::class.java,
+        "font_scaling" to FontScalingTile::class.java
 )
 
 @RunWith(AndroidTestingRunner::class)
@@ -126,6 +129,7 @@
     @Mock private lateinit var oneHandedModeTile: OneHandedModeTile
     @Mock private lateinit var colorCorrectionTile: ColorCorrectionTile
     @Mock private lateinit var dreamTile: DreamTile
+    @Mock private lateinit var fontScalingTile: FontScalingTile
 
     private lateinit var factory: QSFactoryImpl
 
@@ -137,39 +141,43 @@
         whenever(qsHost.userContext).thenReturn(mContext)
         whenever(customTileBuilder.build()).thenReturn(customTile)
 
+        val tileMap = mutableMapOf<String, Provider<QSTileImpl<*>>>(
+            "internet" to Provider { internetTile },
+            "bt" to Provider { bluetoothTile },
+            "dnd" to Provider { dndTile },
+            "inversion" to Provider { colorInversionTile },
+            "airplane" to Provider { airplaneTile },
+            "work" to Provider { workTile },
+            "rotation" to Provider { rotationTile },
+            "flashlight" to Provider { flashlightTile },
+            "location" to Provider { locationTile },
+            "cast" to Provider { castTile },
+            "hotspot" to Provider { hotspotTile },
+            "battery" to Provider { batterySaverTile },
+            "saver" to Provider { dataSaverTile },
+            "night" to Provider { nightDisplayTile },
+            "nfc" to Provider { nfcTile },
+            "dark" to Provider { darkModeTile },
+            "screenrecord" to Provider { screenRecordTile },
+            "reduce_brightness" to Provider { reduceBrightColorsTile },
+            "cameratoggle" to Provider { cameraToggleTile },
+            "mictoggle" to Provider { microphoneToggleTile },
+            "controls" to Provider { deviceControlsTile },
+            "alarm" to Provider { alarmTile },
+            "wallet" to Provider { quickAccessWalletTile },
+            "qr_code_scanner" to Provider { qrCodeScannerTile },
+            "onehanded" to Provider { oneHandedModeTile },
+            "color_correction" to Provider { colorCorrectionTile },
+            "dream" to Provider { dreamTile },
+            "font_scaling" to Provider { fontScalingTile }
+        )
+
         factory = QSFactoryImpl(
                 { qsHost },
                 { customTileBuilder },
-                { internetTile },
-                { bluetoothTile },
-                { dndTile },
-                { colorInversionTile },
-                { airplaneTile },
-                { workTile },
-                { rotationTile },
-                { flashlightTile },
-                { locationTile },
-                { castTile },
-                { hotspotTile },
-                { batterySaverTile },
-                { dataSaverTile },
-                { nightDisplayTile },
-                { nfcTile },
-                { memoryTile },
-                { darkModeTile },
-                { screenRecordTile },
-                { reduceBrightColorsTile },
-                { cameraToggleTile },
-                { microphoneToggleTile },
-                { deviceControlsTile },
-                { alarmTile },
-                { quickAccessWalletTile },
-                { qrCodeScannerTile },
-                { oneHandedModeTile },
-                { colorCorrectionTile },
-                { dreamTile }
+                tileMap,
         )
-        // When adding/removing tiles, fix also [specMap]
+        // When adding/removing tiles, fix also [specMap] and [tileMap]
     }
 
     @Test
@@ -205,4 +213,4 @@
         inOrder.verify(tile).initialize()
         inOrder.verify(tile).postStale()
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt
new file mode 100644
index 0000000..257d42a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt
@@ -0,0 +1,116 @@
+/*
+ * 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
+
+import android.os.Handler
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSTileHost
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.nullable
+import com.android.systemui.util.settings.FakeSettings
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class FontScalingTileTest : SysuiTestCase() {
+    @Mock private lateinit var qsHost: QSTileHost
+    @Mock private lateinit var metricsLogger: MetricsLogger
+    @Mock private lateinit var statusBarStateController: StatusBarStateController
+    @Mock private lateinit var activityStarter: ActivityStarter
+    @Mock private lateinit var qsLogger: QSLogger
+    @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
+    @Mock private lateinit var uiEventLogger: UiEventLogger
+
+    private lateinit var testableLooper: TestableLooper
+    private lateinit var fontScalingTile: FontScalingTile
+
+    val featureFlags = FakeFeatureFlags()
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        testableLooper = TestableLooper.get(this)
+        `when`(qsHost.getContext()).thenReturn(mContext)
+        `when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
+
+        fontScalingTile =
+            FontScalingTile(
+                qsHost,
+                testableLooper.looper,
+                Handler(testableLooper.looper),
+                FalsingManagerFake(),
+                metricsLogger,
+                statusBarStateController,
+                activityStarter,
+                qsLogger,
+                dialogLaunchAnimator,
+                FakeSettings(),
+                featureFlags
+            )
+        fontScalingTile.initialize()
+        testableLooper.processAllMessages()
+    }
+
+    @Test
+    fun isAvailable_whenFlagIsFalse_returnsFalse() {
+        featureFlags.set(Flags.ENABLE_FONT_SCALING_TILE, false)
+
+        val isAvailable = fontScalingTile.isAvailable()
+
+        assertThat(isAvailable).isFalse()
+    }
+
+    @Test
+    fun isAvailable_whenFlagIsTrue_returnsTrue() {
+        featureFlags.set(Flags.ENABLE_FONT_SCALING_TILE, true)
+
+        val isAvailable = fontScalingTile.isAvailable()
+
+        assertThat(isAvailable).isTrue()
+    }
+
+    @Test
+    fun clickTile_showDialog() {
+        val view = View(context)
+        fontScalingTile.click(view)
+        testableLooper.processAllMessages()
+
+        verify(dialogLaunchAnimator).showFromView(any(), eq(view), nullable(), anyBoolean())
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java
index 80c39cf..addca9d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java
@@ -37,10 +37,12 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
 import com.android.systemui.statusbar.connectivity.AccessPointController;
 import com.android.systemui.statusbar.connectivity.IconState;
 import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.WifiIndicators;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -135,4 +137,24 @@
         assertThat(mTile.getState().secondaryLabel)
             .isNotEqualTo(mContext.getString(R.string.status_bar_airplane));
     }
+
+    @Test
+    public void setIsAirplaneMode_APM_enabled_after_wifi_disconnected() {
+        WifiIndicators wifiIndicators = new WifiIndicators(
+            /* enabled= */ true,
+            /* statusIcon= */ null,
+            /* qsIcon= */ null,
+            /* activityIn= */ false,
+            /* activityOut= */ false,
+            /* description= */ null,
+            /* isTransient= */ false,
+            /* statusLabel= */ null
+        );
+        mTile.mSignalCallback.setWifiIndicators(wifiIndicators);
+        IconState state = new IconState(true, 0, "");
+        mTile.mSignalCallback.setIsAirplaneMode(state);
+        mTestableLooper.processAllMessages();
+        assertThat(mTile.getState().icon).isEqualTo(
+                QSTileImpl.ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable));
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
index 6d2972d..508327f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
@@ -879,6 +879,26 @@
         }
     }
 
+    @Test
+    public void getMobileNetworkSummary_withCarrierNetworkChange() {
+        Resources res = mock(Resources.class);
+        doReturn("Carrier network changing").when(res).getString(anyInt());
+        when(SubscriptionManager.getResourcesForSubId(any(), eq(SUB_ID))).thenReturn(res);
+        InternetDialogController spyController = spy(mInternetDialogController);
+        Map<Integer, TelephonyDisplayInfo> mSubIdTelephonyDisplayInfoMap =
+                spyController.mSubIdTelephonyDisplayInfoMap;
+        TelephonyDisplayInfo info = new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE,
+                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
+
+        mSubIdTelephonyDisplayInfoMap.put(SUB_ID, info);
+        doReturn(true).when(spyController).isMobileDataEnabled();
+        doReturn(true).when(spyController).activeNetworkIsCellular();
+        spyController.mCarrierNetworkChangeMode = true;
+        String dds = spyController.getMobileNetworkSummary(SUB_ID);
+
+        assertThat(dds).contains(mContext.getString(R.string.carrier_network_change_mode));
+    }
+
     private String getResourcesString(String name) {
         return mContext.getResources().getString(getResourcesId(name));
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
index ea0e454..9acd47e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
@@ -16,14 +16,13 @@
 
 package com.android.systemui.reardisplay;
 
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertTrue;
 
 import android.hardware.devicestate.DeviceStateManager;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
-import android.view.View;
+import android.widget.TextView;
 
 import androidx.test.filters.SmallTest;
 
@@ -37,8 +36,6 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 
-import java.util.concurrent.Executor;
-
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -63,9 +60,11 @@
 
         controller.showRearDisplayDialog(CLOSED_BASE_STATE);
         assertTrue(controller.mRearDisplayEducationDialog.isShowing());
-        View deviceOpenedWarningTextView = controller.mRearDisplayEducationDialog.findViewById(
-                R.id.rear_display_warning_text_view);
-        assertNull(deviceOpenedWarningTextView);
+        TextView deviceClosedTitleTextView = controller.mRearDisplayEducationDialog.findViewById(
+                R.id.rear_display_title_text_view);
+        assertEquals(deviceClosedTitleTextView.getText().toString(),
+                getContext().getResources().getString(
+                        R.string.rear_display_folded_bottom_sheet_title));
     }
 
     @Test
@@ -79,9 +78,11 @@
         controller.showRearDisplayDialog(OPEN_BASE_STATE);
 
         assertTrue(controller.mRearDisplayEducationDialog.isShowing());
-        View deviceOpenedWarningTextView = controller.mRearDisplayEducationDialog.findViewById(
-                R.id.rear_display_warning_text_view);
-        assertNotNull(deviceOpenedWarningTextView);
+        TextView deviceClosedTitleTextView = controller.mRearDisplayEducationDialog.findViewById(
+                R.id.rear_display_title_text_view);
+        assertEquals(deviceClosedTitleTextView.getText().toString(),
+                getContext().getResources().getString(
+                        R.string.rear_display_unfolded_bottom_sheet_title));
     }
 
     /**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
index 69f3e987..33aaa3f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
@@ -16,13 +16,15 @@
 
 package com.android.systemui.screenrecord;
 
+import static com.google.common.truth.Truth.assertThat;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
-
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
+import android.app.Dialog;
 import android.app.PendingIntent;
 import android.content.Intent;
 import android.os.Looper;
@@ -31,7 +33,13 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.Flags;
+import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
+import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
+import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.util.concurrency.FakeExecutor;
@@ -61,8 +69,15 @@
     @Mock
     private UserContextProvider mUserContextProvider;
     @Mock
+    private ScreenCaptureDevicePolicyResolver mDevicePolicyResolver;
+    @Mock
+    private DialogLaunchAnimator mDialogLaunchAnimator;
+    @Mock
+    private ActivityStarter mActivityStarter;
+    @Mock
     private UserTracker mUserTracker;
 
+    private FakeFeatureFlags mFeatureFlags;
     private RecordingController mController;
 
     private static final int USER_ID = 10;
@@ -70,8 +85,9 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mController = new RecordingController(mMainExecutor, mBroadcastDispatcher,
-                mUserContextProvider, mUserTracker);
+        mFeatureFlags = new FakeFeatureFlags();
+        mController = new RecordingController(mMainExecutor, mBroadcastDispatcher, mContext,
+                mFeatureFlags, mUserContextProvider, () -> mDevicePolicyResolver, mUserTracker);
         mController.addCallback(mCallback);
     }
 
@@ -190,4 +206,67 @@
         verify(mCallback).onRecordingEnd();
         assertFalse(mController.isRecording());
     }
+
+    @Test
+    public void testPoliciesFlagDisabled_screenCapturingNotAllowed_returnsNullDevicePolicyDialog() {
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+
+        mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING, true);
+        mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, false);
+        when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(true);
+
+        Dialog dialog = mController.createScreenRecordDialog(mContext, mFeatureFlags,
+                mDialogLaunchAnimator, mActivityStarter, /* onStartRecordingClicked= */ null);
+
+        assertThat(dialog).isInstanceOf(ScreenRecordPermissionDialog.class);
+    }
+
+    @Test
+    public void testPartialScreenSharingDisabled_returnsLegacyDialog() {
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+
+        mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING, false);
+        mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, false);
+
+        Dialog dialog = mController.createScreenRecordDialog(mContext, mFeatureFlags,
+                mDialogLaunchAnimator, mActivityStarter, /* onStartRecordingClicked= */ null);
+
+        assertThat(dialog).isInstanceOf(ScreenRecordDialog.class);
+    }
+
+    @Test
+    public void testPoliciesFlagEnabled_screenCapturingNotAllowed_returnsDevicePolicyDialog() {
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+
+        mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING, true);
+        mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, true);
+        when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(true);
+
+        Dialog dialog = mController.createScreenRecordDialog(mContext, mFeatureFlags,
+                mDialogLaunchAnimator, mActivityStarter, /* onStartRecordingClicked= */ null);
+
+        assertThat(dialog).isInstanceOf(ScreenCaptureDisabledDialog.class);
+    }
+
+    @Test
+    public void testPoliciesFlagEnabled_screenCapturingAllowed_returnsNullDevicePolicyDialog() {
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+
+        mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING, true);
+        mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, true);
+        when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(false);
+
+        Dialog dialog = mController.createScreenRecordDialog(mContext, mFeatureFlags,
+                mDialogLaunchAnimator, mActivityStarter, /* onStartRecordingClicked= */ null);
+
+        assertThat(dialog).isInstanceOf(ScreenRecordPermissionDialog.class);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
index 0aa3621..5b094c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.screenrecord
 
+import android.os.UserHandle
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.View
@@ -59,6 +60,7 @@
         dialog =
             ScreenRecordPermissionDialog(
                 context,
+                UserHandle.of(0),
                 controller,
                 starter,
                 dialogLaunchAnimator,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentCreatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentCreatorTest.kt
index b6a595b..7ba2cf7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentCreatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentCreatorTest.kt
@@ -35,9 +35,33 @@
     @Test
     fun testCreateShareIntent() {
         val uri = Uri.parse("content://fake")
+
+        val output = ActionIntentCreator.createShareIntent(uri)
+
+        assertThat(output.action).isEqualTo(Intent.ACTION_CHOOSER)
+        assertFlagsSet(
+            Intent.FLAG_ACTIVITY_NEW_TASK or
+                Intent.FLAG_ACTIVITY_CLEAR_TASK or
+                Intent.FLAG_GRANT_READ_URI_PERMISSION,
+            output.flags
+        )
+
+        val wrappedIntent = output.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java)
+        assertThat(wrappedIntent?.action).isEqualTo(Intent.ACTION_SEND)
+        assertThat(wrappedIntent?.data).isEqualTo(uri)
+        assertThat(wrappedIntent?.type).isEqualTo("image/png")
+        assertThat(wrappedIntent?.getStringExtra(Intent.EXTRA_SUBJECT)).isNull()
+        assertThat(wrappedIntent?.getStringExtra(Intent.EXTRA_TEXT)).isNull()
+        assertThat(wrappedIntent?.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java))
+            .isEqualTo(uri)
+    }
+
+    @Test
+    fun testCreateShareIntentWithSubject() {
+        val uri = Uri.parse("content://fake")
         val subject = "Example subject"
 
-        val output = ActionIntentCreator.createShareIntent(uri, subject)
+        val output = ActionIntentCreator.createShareIntentWithSubject(uri, subject)
 
         assertThat(output.action).isEqualTo(Intent.ACTION_CHOOSER)
         assertFlagsSet(
@@ -52,16 +76,34 @@
         assertThat(wrappedIntent?.data).isEqualTo(uri)
         assertThat(wrappedIntent?.type).isEqualTo("image/png")
         assertThat(wrappedIntent?.getStringExtra(Intent.EXTRA_SUBJECT)).isEqualTo(subject)
+        assertThat(wrappedIntent?.getStringExtra(Intent.EXTRA_TEXT)).isNull()
         assertThat(wrappedIntent?.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java))
             .isEqualTo(uri)
     }
 
     @Test
-    fun testCreateShareIntent_noSubject() {
+    fun testCreateShareIntentWithExtraText() {
         val uri = Uri.parse("content://fake")
-        val output = ActionIntentCreator.createShareIntent(uri, null)
+        val extraText = "Extra text"
+
+        val output = ActionIntentCreator.createShareIntentWithExtraText(uri, extraText)
+
+        assertThat(output.action).isEqualTo(Intent.ACTION_CHOOSER)
+        assertFlagsSet(
+            Intent.FLAG_ACTIVITY_NEW_TASK or
+                Intent.FLAG_ACTIVITY_CLEAR_TASK or
+                Intent.FLAG_GRANT_READ_URI_PERMISSION,
+            output.flags
+        )
+
         val wrappedIntent = output.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java)
+        assertThat(wrappedIntent?.action).isEqualTo(Intent.ACTION_SEND)
+        assertThat(wrappedIntent?.data).isEqualTo(uri)
+        assertThat(wrappedIntent?.type).isEqualTo("image/png")
         assertThat(wrappedIntent?.getStringExtra(Intent.EXTRA_SUBJECT)).isNull()
+        assertThat(wrappedIntent?.getStringExtra(Intent.EXTRA_TEXT)).isEqualTo(extraText)
+        assertThat(wrappedIntent?.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java))
+            .isEqualTo(uri)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
index e1eda11..d5014fa36 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
@@ -39,6 +39,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.settings.FakeDisplayTracker;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 
@@ -67,6 +68,7 @@
     private PendingIntent mMockPendingIntent;
 
     private Intent mIntent;
+    private final FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
 
     @Before
     public void setup() throws InterruptedException, ExecutionException, TimeoutException {
@@ -135,10 +137,11 @@
         if (withStatusBar) {
             return new ActionProxyReceiver(
                     Optional.of(mMockCentralSurfaces), mMockActivityManagerWrapper,
-                    mMockScreenshotSmartActions);
+                    mMockScreenshotSmartActions, mDisplayTracker);
         } else {
             return new ActionProxyReceiver(
-                    Optional.empty(), mMockActivityManagerWrapper, mMockScreenshotSmartActions);
+                    Optional.empty(), mMockActivityManagerWrapper, mMockScreenshotSmartActions,
+                    mDisplayTracker);
         }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt
new file mode 100644
index 0000000..9f0a803
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt
@@ -0,0 +1,143 @@
+package com.android.systemui.screenshot
+
+import android.graphics.drawable.Drawable
+import android.os.UserHandle
+import android.testing.AndroidTestingRunner
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.Guideline
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.whenever
+import junit.framework.Assert.assertEquals
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class MessageContainerControllerTest : SysuiTestCase() {
+    lateinit var messageContainer: MessageContainerController
+
+    @Mock lateinit var workProfileMessageController: WorkProfileMessageController
+
+    @Mock lateinit var screenshotDetectionController: ScreenshotDetectionController
+
+    @Mock lateinit var icon: Drawable
+
+    lateinit var workProfileFirstRunView: ViewGroup
+    lateinit var detectionNoticeView: ViewGroup
+    lateinit var container: FrameLayout
+
+    var featureFlags = FakeFeatureFlags()
+    lateinit var screenshotView: ViewGroup
+
+    val userHandle = UserHandle.of(5)
+    val screenshotData = ScreenshotData.forTesting()
+
+    val appName = "app name"
+    lateinit var workProfileData: WorkProfileMessageController.WorkProfileFirstRunData
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        messageContainer =
+            MessageContainerController(
+                workProfileMessageController,
+                screenshotDetectionController,
+                featureFlags
+            )
+        screenshotView = ConstraintLayout(mContext)
+        workProfileData = WorkProfileMessageController.WorkProfileFirstRunData(appName, icon)
+
+        val guideline = Guideline(mContext)
+        guideline.id = com.android.systemui.R.id.guideline
+        screenshotView.addView(guideline)
+
+        container = FrameLayout(mContext)
+        container.id = com.android.systemui.R.id.screenshot_message_container
+        screenshotView.addView(container)
+
+        workProfileFirstRunView = FrameLayout(mContext)
+        workProfileFirstRunView.id = com.android.systemui.R.id.work_profile_first_run
+        container.addView(workProfileFirstRunView)
+
+        detectionNoticeView = FrameLayout(mContext)
+        detectionNoticeView.id = com.android.systemui.R.id.screenshot_detection_notice
+        container.addView(detectionNoticeView)
+
+        messageContainer.setView(screenshotView)
+
+        screenshotData.userHandle = userHandle
+    }
+
+    @Test
+    fun testOnScreenshotTakenUserHandle_noWorkProfileFirstRun() {
+        featureFlags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, true)
+        // (just being explicit here)
+        whenever(workProfileMessageController.onScreenshotTaken(eq(userHandle))).thenReturn(null)
+
+        messageContainer.onScreenshotTaken(userHandle)
+
+        verify(workProfileMessageController, never()).populateView(any(), any(), any())
+    }
+
+    @Test
+    fun testOnScreenshotTakenUserHandle_noWorkProfileFlag() {
+        featureFlags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, false)
+
+        messageContainer.onScreenshotTaken(userHandle)
+
+        verify(workProfileMessageController, never()).onScreenshotTaken(any())
+        verify(workProfileMessageController, never()).populateView(any(), any(), any())
+    }
+
+    @Test
+    fun testOnScreenshotTakenUserHandle_withWorkProfileFirstRun() {
+        featureFlags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, true)
+        whenever(workProfileMessageController.onScreenshotTaken(eq(userHandle)))
+            .thenReturn(workProfileData)
+        messageContainer.onScreenshotTaken(userHandle)
+
+        verify(workProfileMessageController)
+            .populateView(eq(workProfileFirstRunView), eq(workProfileData), any())
+        assertEquals(View.VISIBLE, workProfileFirstRunView.visibility)
+        assertEquals(View.GONE, detectionNoticeView.visibility)
+    }
+
+    @Test
+    fun testOnScreenshotTakenScreenshotData_flagsOff() {
+        featureFlags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, false)
+        featureFlags.set(Flags.SCREENSHOT_DETECTION, false)
+
+        messageContainer.onScreenshotTaken(screenshotData)
+
+        verify(workProfileMessageController, never()).onScreenshotTaken(any())
+        verify(screenshotDetectionController, never()).maybeNotifyOfScreenshot(any())
+
+        assertEquals(View.GONE, container.visibility)
+    }
+
+    @Test
+    fun testOnScreenshotTakenScreenshotData_nothingToShow() {
+        featureFlags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, true)
+        featureFlags.set(Flags.SCREENSHOT_DETECTION, true)
+
+        messageContainer.onScreenshotTaken(screenshotData)
+
+        verify(workProfileMessageController, never()).populateView(any(), any(), any())
+        verify(screenshotDetectionController, never()).populateView(any(), any())
+
+        assertEquals(View.GONE, container.visibility)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
index ed3f1a0..2e73c0b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
@@ -23,7 +23,7 @@
 import android.graphics.Rect
 import android.hardware.HardwareBuffer
 import android.os.UserHandle
-import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD
+import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER
 import android.view.WindowManager.ScreenshotSource.SCREENSHOT_OTHER
 import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
 import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
@@ -35,6 +35,7 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.runBlocking
+import org.junit.Assert
 import org.junit.Test
 
 private const val USER_ID = 1
@@ -55,7 +56,7 @@
         flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, false)
 
         val request =
-            ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD).build()
+            ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER).build()
         val processor = RequestProcessor(imageCapture, policy, flags, scope)
 
         var result: ScreenshotRequest? = null
@@ -73,18 +74,49 @@
         assertThat(result).isEqualTo(request)
     }
 
+    /** Tests the Java-compatible function wrapper, ensures callback is invoked. */
+    @Test
+    fun testProcessAsync_ScreenshotData() {
+        flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, false)
+
+        val request =
+            ScreenshotData.fromRequest(
+                ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER).build()
+            )
+        val processor = RequestProcessor(imageCapture, policy, flags, scope)
+
+        var result: ScreenshotData? = null
+        var callbackCount = 0
+        val callback: (ScreenshotData) -> Unit = { processedRequest: ScreenshotData ->
+            result = processedRequest
+            callbackCount++
+        }
+
+        // runs synchronously, using Unconfined Dispatcher
+        processor.processAsync(request, callback)
+
+        // Callback invoked once returning the same request (no changes)
+        assertThat(callbackCount).isEqualTo(1)
+        assertThat(result).isEqualTo(request)
+    }
+
     @Test
     fun testFullScreenshot_workProfilePolicyDisabled() = runBlocking {
         flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, false)
 
         val request =
-            ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD).build()
+            ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER).build()
         val processor = RequestProcessor(imageCapture, policy, flags, scope)
 
         val processedRequest = processor.process(request)
 
         // No changes
         assertThat(processedRequest).isEqualTo(request)
+
+        val screenshotData = ScreenshotData.fromRequest(request)
+        val processedData = processor.process(screenshotData)
+
+        assertThat(processedData).isEqualTo(screenshotData)
     }
 
     @Test
@@ -108,6 +140,13 @@
         assertThat(processedRequest.type).isEqualTo(TAKE_SCREENSHOT_FULLSCREEN)
         assertThat(processedRequest.source).isEqualTo(SCREENSHOT_OTHER)
         assertThat(processedRequest.topComponent).isEqualTo(component)
+
+        val processedData = processor.process(ScreenshotData.fromRequest(request))
+
+        // Request has topComponent added, but otherwise unchanged.
+        assertThat(processedData.type).isEqualTo(TAKE_SCREENSHOT_FULLSCREEN)
+        assertThat(processedData.source).isEqualTo(SCREENSHOT_OTHER)
+        assertThat(processedData.topComponent).isEqualTo(component)
     }
 
     @Test
@@ -126,7 +165,7 @@
         )
 
         val request =
-            ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD).build()
+            ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER).build()
         val processor = RequestProcessor(imageCapture, policy, flags, scope)
 
         val processedRequest = processor.process(request)
@@ -140,6 +179,44 @@
         assertThat(imageCapture.requestedTaskId).isEqualTo(TASK_ID)
         assertThat(processedRequest.userId).isEqualTo(USER_ID)
         assertThat(processedRequest.topComponent).isEqualTo(component)
+
+        val processedData = processor.process(ScreenshotData.fromRequest(request))
+
+        // Expect a task snapshot is taken, overriding the full screen mode
+        assertThat(processedData.type).isEqualTo(TAKE_SCREENSHOT_PROVIDED_IMAGE)
+        assertThat(processedData.bitmap).isEqualTo(bitmap)
+        assertThat(processedData.screenBounds).isEqualTo(bounds)
+        assertThat(processedData.insets).isEqualTo(Insets.NONE)
+        assertThat(processedData.taskId).isEqualTo(TASK_ID)
+        assertThat(imageCapture.requestedTaskId).isEqualTo(TASK_ID)
+        assertThat(processedRequest.userId).isEqualTo(USER_ID)
+        assertThat(processedRequest.topComponent).isEqualTo(component)
+    }
+
+    @Test
+    fun testFullScreenshot_managedProfile_nullBitmap() {
+        flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, true)
+
+        // Provide a null task bitmap when asked
+        imageCapture.image = null
+
+        // Indicate that the primary content belongs to a manged profile
+        policy.setManagedProfile(USER_ID, true)
+        policy.setDisplayContentInfo(
+            policy.getDefaultDisplayId(),
+            DisplayContentInfo(component, bounds, UserHandle.of(USER_ID), TASK_ID)
+        )
+
+        val request =
+            ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER).build()
+        val processor = RequestProcessor(imageCapture, policy, flags, scope)
+
+        Assert.assertThrows(IllegalStateException::class.java) {
+            runBlocking { processor.process(request) }
+        }
+        Assert.assertThrows(IllegalStateException::class.java) {
+            runBlocking { processor.process(ScreenshotData.fromRequest(request)) }
+        }
     }
 
     @Test
@@ -165,6 +242,11 @@
 
         // No changes
         assertThat(processedRequest).isEqualTo(request)
+
+        val screenshotData = ScreenshotData.fromRequest(request)
+        val processedData = processor.process(screenshotData)
+
+        assertThat(processedData).isEqualTo(screenshotData)
     }
 
     @Test
@@ -192,6 +274,11 @@
 
         // No changes
         assertThat(processedRequest).isEqualTo(request)
+
+        val screenshotData = ScreenshotData.fromRequest(request)
+        val processedData = processor.process(screenshotData)
+
+        assertThat(processedData).isEqualTo(screenshotData)
     }
 
     @Test
@@ -220,6 +307,11 @@
 
         // Work profile, but already a task snapshot, so no changes
         assertThat(processedRequest).isEqualTo(request)
+
+        val screenshotData = ScreenshotData.fromRequest(request)
+        val processedData = processor.process(screenshotData)
+
+        assertThat(processedData).isEqualTo(screenshotData)
     }
 
     private fun makeHardwareBitmap(width: Int, height: Int): Bitmap {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDataTest.kt
new file mode 100644
index 0000000..43e9939
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDataTest.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.screenshot
+
+import android.content.ComponentName
+import android.graphics.Insets
+import android.graphics.Rect
+import android.os.UserHandle
+import android.view.WindowManager
+import com.android.internal.util.ScreenshotRequest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+class ScreenshotDataTest {
+    private val type = WindowManager.TAKE_SCREENSHOT_FULLSCREEN
+    private val source = WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER
+    private val bounds = Rect(1, 2, 3, 4)
+    private val taskId = 123
+    private val userId = 1
+    private val insets = Insets.of(1, 2, 3, 4)
+    private val component = ComponentName("android.test", "android.test.Component")
+
+    @Test
+    fun testConstruction() {
+        val request =
+            ScreenshotRequest.Builder(type, source)
+                .setBoundsOnScreen(bounds)
+                .setInsets(insets)
+                .setTaskId(taskId)
+                .setUserId(userId)
+                .setTopComponent(component)
+                .build()
+
+        val data = ScreenshotData.fromRequest(request)
+
+        assertThat(data.source).isEqualTo(source)
+        assertThat(data.type).isEqualTo(type)
+        assertThat(data.screenBounds).isEqualTo(bounds)
+        assertThat(data.insets).isEqualTo(insets)
+        assertThat(data.taskId).isEqualTo(taskId)
+        assertThat(data.userHandle).isEqualTo(UserHandle.of(userId))
+        assertThat(data.topComponent).isEqualTo(component)
+    }
+
+    @Test
+    fun testNegativeUserId() {
+        val request = ScreenshotRequest.Builder(type, source).setUserId(-1).build()
+
+        val data = ScreenshotData.fromRequest(request)
+
+        assertThat(data.userHandle).isNull()
+    }
+
+    @Test
+    fun testPackageNameAsString() {
+        val request = ScreenshotRequest.Builder(type, source).setTopComponent(component).build()
+
+        val data = ScreenshotData.fromRequest(request)
+
+        assertThat(data.packageNameString).isEqualTo("android.test")
+    }
+
+    @Test
+    fun testPackageNameAsString_null() {
+        val request = ScreenshotRequest.Builder(type, source).build()
+
+        val data = ScreenshotData.fromRequest(request)
+
+        assertThat(data.packageNameString).isEqualTo("")
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotPolicyImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotPolicyImplTest.kt
index 17396b1..e70fa2f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotPolicyImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotPolicyImplTest.kt
@@ -31,6 +31,7 @@
 import android.testing.AndroidTestingRunner
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.screenshot.ScreenshotPolicy.DisplayContentInfo
+import com.android.systemui.settings.FakeDisplayTracker
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.Dispatchers
@@ -126,8 +127,10 @@
         val userManager = mock<UserManager>()
         val atmService = mock<IActivityTaskManager>()
         val dispatcher = Dispatchers.Unconfined
+        val displayTracker = FakeDisplayTracker(mContext)
 
-        return object : ScreenshotPolicyImpl(context, userManager, atmService, dispatcher) {
+        return object : ScreenshotPolicyImpl(context, userManager, atmService, dispatcher,
+                displayTracker) {
             override suspend fun isManagedProfile(userId: Int) = (userId == MANAGED_PROFILE_USER)
             override suspend fun getAllRootTaskInfosOnDisplay(displayId: Int) = tasks
             override suspend fun isNotificationShadeExpanded() = shadeExpanded
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
index f935019..c40c287 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
@@ -29,7 +29,7 @@
 import android.os.UserHandle
 import android.os.UserManager
 import android.testing.AndroidTestingRunner
-import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD
+import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER
 import android.view.WindowManager.ScreenshotSource.SCREENSHOT_OVERVIEW
 import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
 import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
@@ -38,14 +38,17 @@
 import com.android.internal.util.ScreenshotRequest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags.SCREENSHOT_METADATA_REFACTOR
 import com.android.systemui.flags.Flags.SCREENSHOT_WORK_PROFILE_POLICY
-import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_REQUESTED_KEY_CHORD
+import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_CAPTURE_FAILED
+import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_REQUESTED_KEY_OTHER
 import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_REQUESTED_OVERVIEW
 import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argThat
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
 import java.util.function.Consumer
 import org.junit.Assert.assertEquals
 import org.junit.Before
@@ -54,10 +57,10 @@
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.isNull
 import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.doThrow
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.Mockito.`when` as whenever
 
 private const val USER_ID = 1
 private const val TASK_ID = 11
@@ -110,11 +113,20 @@
                 val consumer: Consumer<ScreenshotRequest> = it.getArgument(1)
                 consumer.accept(request)
             }
-            .`when`(requestProcessor)
-            .processAsync(/* request= */ any(), /* callback= */ any())
+            .whenever(requestProcessor)
+            .processAsync(/* request= */ any(ScreenshotRequest::class.java), /* callback= */ any())
+
+        doAnswer {
+                val request: ScreenshotData = it.getArgument(0) as ScreenshotData
+                val consumer: Consumer<ScreenshotData> = it.getArgument(1)
+                consumer.accept(request)
+            }
+            .whenever(requestProcessor)
+            .processAsync(/* screenshot= */ any(ScreenshotData::class.java), /* callback= */ any())
 
         // Flipped in selected test cases
         flags.set(SCREENSHOT_WORK_PROFILE_POLICY, false)
+        flags.set(SCREENSHOT_METADATA_REFACTOR, false)
 
         service.attach(
             mContext,
@@ -141,7 +153,7 @@
     @Test
     fun takeScreenshotFullscreen() {
         val request =
-            ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD)
+            ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER)
                 .setTopComponent(topComponent)
                 .build()
 
@@ -154,13 +166,46 @@
                 /* requestCallback = */ any()
             )
 
+        assertEquals("Expected one UiEvent", 1, eventLogger.numLogs())
+        val logEvent = eventLogger.get(0)
+
+        assertEquals(
+            "Expected SCREENSHOT_REQUESTED UiEvent",
+            logEvent.eventId,
+            SCREENSHOT_REQUESTED_KEY_OTHER.id
+        )
+        assertEquals(
+            "Expected supplied package name",
+            topComponent.packageName,
+            eventLogger.get(0).packageName
+        )
+    }
+
+    @Test
+    fun takeScreenshotFullscreen_screenshotDataEnabled() {
+        flags.set(SCREENSHOT_METADATA_REFACTOR, true)
+
+        val request =
+            ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER)
+                .setTopComponent(topComponent)
+                .build()
+
+        service.handleRequest(request, { /* onSaved */}, callback)
+
+        verify(controller, times(1))
+            .handleScreenshot(
+                eq(ScreenshotData.fromRequest(request)),
+                /* onSavedListener = */ any(),
+                /* requestCallback = */ any()
+            )
+
         assertEquals("Expected one UiEvent", eventLogger.numLogs(), 1)
         val logEvent = eventLogger.get(0)
 
         assertEquals(
             "Expected SCREENSHOT_REQUESTED UiEvent",
             logEvent.eventId,
-            SCREENSHOT_REQUESTED_KEY_CHORD.id
+            SCREENSHOT_REQUESTED_KEY_OTHER.id
         )
         assertEquals(
             "Expected supplied package name",
@@ -198,7 +243,7 @@
                 /* requestCallback = */ any()
             )
 
-        assertEquals("Expected one UiEvent", eventLogger.numLogs(), 1)
+        assertEquals("Expected one UiEvent", 1, eventLogger.numLogs())
         val logEvent = eventLogger.get(0)
 
         assertEquals(
@@ -215,10 +260,12 @@
 
     @Test
     fun takeScreenshotFullscreen_userLocked() {
+        flags.set(SCREENSHOT_METADATA_REFACTOR, true)
+
         whenever(userManager.isUserUnlocked).thenReturn(false)
 
         val request =
-            ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD)
+            ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER)
                 .setTopComponent(topComponent)
                 .build()
 
@@ -227,10 +274,36 @@
         verify(notificationsController, times(1)).notifyScreenshotError(anyInt())
         verify(callback, times(1)).reportError()
         verifyZeroInteractions(controller)
+
+        assertEquals("Expected two UiEvents", 2, eventLogger.numLogs())
+        val requestEvent = eventLogger.get(0)
+        assertEquals(
+            "Expected SCREENSHOT_REQUESTED_* UiEvent",
+            SCREENSHOT_REQUESTED_KEY_OTHER.id,
+            requestEvent.eventId
+        )
+        assertEquals(
+            "Expected supplied package name",
+            topComponent.packageName,
+            requestEvent.packageName
+        )
+        val failureEvent = eventLogger.get(1)
+        assertEquals(
+            "Expected SCREENSHOT_CAPTURE_FAILED UiEvent",
+            SCREENSHOT_CAPTURE_FAILED.id,
+            failureEvent.eventId
+        )
+        assertEquals(
+            "Expected supplied package name",
+            topComponent.packageName,
+            failureEvent.packageName
+        )
     }
 
     @Test
     fun takeScreenshotFullscreen_screenCaptureDisabled_allUsers() {
+        flags.set(SCREENSHOT_METADATA_REFACTOR, true)
+
         whenever(devicePolicyManager.getScreenCaptureDisabled(isNull(), eq(UserHandle.USER_ALL)))
             .thenReturn(true)
 
@@ -244,7 +317,7 @@
             .thenReturn("SCREENSHOT_BLOCKED_BY_ADMIN")
 
         val request =
-            ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD)
+            ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER)
                 .setTopComponent(topComponent)
                 .build()
 
@@ -253,6 +326,206 @@
         // error shown: Toast.makeText(...).show(), untestable
         verify(callback, times(1)).reportError()
         verifyZeroInteractions(controller)
+        assertEquals("Expected two UiEvents", 2, eventLogger.numLogs())
+        val requestEvent = eventLogger.get(0)
+        assertEquals(
+            "Expected SCREENSHOT_REQUESTED_* UiEvent",
+            SCREENSHOT_REQUESTED_KEY_OTHER.id,
+            requestEvent.eventId
+        )
+        assertEquals(
+            "Expected supplied package name",
+            topComponent.packageName,
+            requestEvent.packageName
+        )
+        val failureEvent = eventLogger.get(1)
+        assertEquals(
+            "Expected SCREENSHOT_CAPTURE_FAILED UiEvent",
+            SCREENSHOT_CAPTURE_FAILED.id,
+            failureEvent.eventId
+        )
+        assertEquals(
+            "Expected supplied package name",
+            topComponent.packageName,
+            failureEvent.packageName
+        )
+    }
+
+    @Test
+    fun takeScreenshotFullscreen_userLocked_metadataDisabled() {
+        flags.set(SCREENSHOT_METADATA_REFACTOR, false)
+        whenever(userManager.isUserUnlocked).thenReturn(false)
+
+        val request =
+            ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER)
+                .setTopComponent(topComponent)
+                .build()
+
+        service.handleRequest(request, { /* onSaved */}, callback)
+
+        verify(notificationsController, times(1)).notifyScreenshotError(anyInt())
+        verify(callback, times(1)).reportError()
+        verifyZeroInteractions(controller)
+
+        assertEquals("Expected two UiEvents", 2, eventLogger.numLogs())
+        val requestEvent = eventLogger.get(0)
+        assertEquals(
+            "Expected SCREENSHOT_REQUESTED_* UiEvent",
+            SCREENSHOT_REQUESTED_KEY_OTHER.id,
+            requestEvent.eventId
+        )
+        assertEquals(
+            "Expected supplied package name",
+            topComponent.packageName,
+            requestEvent.packageName
+        )
+        val failureEvent = eventLogger.get(1)
+        assertEquals(
+            "Expected SCREENSHOT_CAPTURE_FAILED UiEvent",
+            SCREENSHOT_CAPTURE_FAILED.id,
+            failureEvent.eventId
+        )
+        assertEquals(
+            "Expected supplied package name",
+            topComponent.packageName,
+            failureEvent.packageName
+        )
+    }
+
+    @Test
+    fun takeScreenshotFullscreen_screenCaptureDisabled_allUsers_metadataDisabled() {
+        flags.set(SCREENSHOT_METADATA_REFACTOR, false)
+
+        whenever(devicePolicyManager.getScreenCaptureDisabled(isNull(), eq(UserHandle.USER_ALL)))
+            .thenReturn(true)
+
+        whenever(
+                devicePolicyResourcesManager.getString(
+                    eq(SCREENSHOT_BLOCKED_BY_ADMIN),
+                    /* Supplier<String> */
+                    any(),
+                )
+            )
+            .thenReturn("SCREENSHOT_BLOCKED_BY_ADMIN")
+
+        val request =
+            ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER)
+                .setTopComponent(topComponent)
+                .build()
+
+        service.handleRequest(request, { /* onSaved */}, callback)
+
+        // error shown: Toast.makeText(...).show(), untestable
+        verify(callback, times(1)).reportError()
+        verifyZeroInteractions(controller)
+        assertEquals("Expected two UiEvents", 2, eventLogger.numLogs())
+        val requestEvent = eventLogger.get(0)
+        assertEquals(
+            "Expected SCREENSHOT_REQUESTED_* UiEvent",
+            SCREENSHOT_REQUESTED_KEY_OTHER.id,
+            requestEvent.eventId
+        )
+        assertEquals(
+            "Expected supplied package name",
+            topComponent.packageName,
+            requestEvent.packageName
+        )
+        val failureEvent = eventLogger.get(1)
+        assertEquals(
+            "Expected SCREENSHOT_CAPTURE_FAILED UiEvent",
+            SCREENSHOT_CAPTURE_FAILED.id,
+            failureEvent.eventId
+        )
+        assertEquals(
+            "Expected supplied package name",
+            topComponent.packageName,
+            failureEvent.packageName
+        )
+    }
+
+    @Test
+    fun takeScreenshot_workProfile_nullBitmap_metadataDisabled() {
+        flags.set(SCREENSHOT_METADATA_REFACTOR, false)
+
+        val request =
+            ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER)
+                .setTopComponent(topComponent)
+                .build()
+
+        doThrow(IllegalStateException::class.java)
+            .whenever(requestProcessor)
+            .processAsync(any(ScreenshotRequest::class.java), any())
+
+        service.handleRequest(request, { /* onSaved */}, callback)
+
+        verify(callback, times(1)).reportError()
+        verify(notificationsController, times(1)).notifyScreenshotError(anyInt())
+        verifyZeroInteractions(controller)
+        assertEquals("Expected two UiEvents", 2, eventLogger.numLogs())
+        val requestEvent = eventLogger.get(0)
+        assertEquals(
+            "Expected SCREENSHOT_REQUESTED_* UiEvent",
+            SCREENSHOT_REQUESTED_KEY_OTHER.id,
+            requestEvent.eventId
+        )
+        assertEquals(
+            "Expected supplied package name",
+            topComponent.packageName,
+            requestEvent.packageName
+        )
+        val failureEvent = eventLogger.get(1)
+        assertEquals(
+            "Expected SCREENSHOT_CAPTURE_FAILED UiEvent",
+            SCREENSHOT_CAPTURE_FAILED.id,
+            failureEvent.eventId
+        )
+        assertEquals(
+            "Expected supplied package name",
+            topComponent.packageName,
+            failureEvent.packageName
+        )
+    }
+    @Test
+    fun takeScreenshot_workProfile_nullBitmap() {
+        flags.set(SCREENSHOT_METADATA_REFACTOR, true)
+
+        val request =
+            ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER)
+                .setTopComponent(topComponent)
+                .build()
+
+        doThrow(IllegalStateException::class.java)
+            .whenever(requestProcessor)
+            .processAsync(any(ScreenshotData::class.java), any())
+
+        service.handleRequest(request, { /* onSaved */}, callback)
+
+        verify(callback, times(1)).reportError()
+        verify(notificationsController, times(1)).notifyScreenshotError(anyInt())
+        verifyZeroInteractions(controller)
+        assertEquals("Expected two UiEvents", 2, eventLogger.numLogs())
+        val requestEvent = eventLogger.get(0)
+        assertEquals(
+            "Expected SCREENSHOT_REQUESTED_* UiEvent",
+            SCREENSHOT_REQUESTED_KEY_OTHER.id,
+            requestEvent.eventId
+        )
+        assertEquals(
+            "Expected supplied package name",
+            topComponent.packageName,
+            requestEvent.packageName
+        )
+        val failureEvent = eventLogger.get(1)
+        assertEquals(
+            "Expected SCREENSHOT_CAPTURE_FAILED UiEvent",
+            SCREENSHOT_CAPTURE_FAILED.id,
+            failureEvent.eventId
+        )
+        assertEquals(
+            "Expected supplied package name",
+            topComponent.packageName,
+            failureEvent.packageName
+        )
     }
 }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/WorkProfileMessageControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/WorkProfileMessageControllerTest.java
index bd04b3c..3440f91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/WorkProfileMessageControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/WorkProfileMessageControllerTest.java
@@ -16,13 +16,11 @@
 
 package com.android.systemui.screenshot;
 
-import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.ComponentName;
@@ -33,28 +31,35 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.testing.AndroidTestingRunner;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
 
 import androidx.test.filters.SmallTest;
 
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
 import com.android.systemui.util.FakeSharedPreferences;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
 import org.mockito.ArgumentMatchers;
-import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import kotlin.Unit;
+
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-public class WorkProfileMessageControllerTest {
+public class WorkProfileMessageControllerTest extends SysuiTestCase {
     private static final String DEFAULT_LABEL = "default label";
-    private static final String BADGED_DEFAULT_LABEL = "badged default label";
     private static final String APP_LABEL = "app label";
-    private static final String BADGED_APP_LABEL = "badged app label";
     private static final UserHandle NON_WORK_USER = UserHandle.of(0);
     private static final UserHandle WORK_USER = UserHandle.of(10);
 
@@ -63,17 +68,13 @@
     @Mock
     private PackageManager mPackageManager;
     @Mock
-    private Context mContext;
-    @Mock
-    private WorkProfileMessageController.WorkProfileMessageDisplay mMessageDisplay;
+    private Context mMockContext;
     @Mock
     private Drawable mActivityIcon;
     @Mock
     private Drawable mBadgedActivityIcon;
     @Mock
     private ActivityInfo mActivityInfo;
-    @Captor
-    private ArgumentCaptor<Runnable> mRunnableArgumentCaptor;
 
     private FakeSharedPreferences mSharedPreferences = new FakeSharedPreferences();
 
@@ -84,14 +85,10 @@
         MockitoAnnotations.initMocks(this);
 
         when(mUserManager.isManagedProfile(eq(WORK_USER.getIdentifier()))).thenReturn(true);
-        when(mContext.getSharedPreferences(
+        when(mMockContext.getSharedPreferences(
                 eq(WorkProfileMessageController.SHARED_PREFERENCES_NAME),
                 eq(Context.MODE_PRIVATE))).thenReturn(mSharedPreferences);
-        when(mContext.getString(ArgumentMatchers.anyInt())).thenReturn(DEFAULT_LABEL);
-        when(mPackageManager.getUserBadgedLabel(eq(DEFAULT_LABEL), any()))
-                .thenReturn(BADGED_DEFAULT_LABEL);
-        when(mPackageManager.getUserBadgedLabel(eq(APP_LABEL), any()))
-                .thenReturn(BADGED_APP_LABEL);
+        when(mMockContext.getString(ArgumentMatchers.anyInt())).thenReturn(DEFAULT_LABEL);
         when(mPackageManager.getActivityIcon(any(ComponentName.class)))
                 .thenReturn(mActivityIcon);
         when(mPackageManager.getUserBadgedIcon(
@@ -103,16 +100,13 @@
         mSharedPreferences.edit().putBoolean(
                 WorkProfileMessageController.PREFERENCE_KEY, false).apply();
 
-        mMessageController = new WorkProfileMessageController(mContext, mUserManager,
+        mMessageController = new WorkProfileMessageController(mMockContext, mUserManager,
                 mPackageManager);
     }
 
     @Test
     public void testOnScreenshotTaken_notManaged() {
-        mMessageController.onScreenshotTaken(NON_WORK_USER, mMessageDisplay);
-
-        verify(mMessageDisplay, never())
-                .showWorkProfileMessage(any(), nullable(Drawable.class), any());
+        assertNull(mMessageController.onScreenshotTaken(NON_WORK_USER));
     }
 
     @Test
@@ -120,10 +114,7 @@
         mSharedPreferences.edit().putBoolean(
                 WorkProfileMessageController.PREFERENCE_KEY, true).apply();
 
-        mMessageController.onScreenshotTaken(WORK_USER, mMessageDisplay);
-
-        verify(mMessageDisplay, never())
-                .showWorkProfileMessage(any(), nullable(Drawable.class), any());
+        assertNull(mMessageController.onScreenshotTaken(WORK_USER));
     }
 
     @Test
@@ -133,28 +124,45 @@
                 any(PackageManager.ComponentInfoFlags.class))).thenThrow(
                 new PackageManager.NameNotFoundException());
 
-        mMessageController.onScreenshotTaken(WORK_USER, mMessageDisplay);
+        WorkProfileMessageController.WorkProfileFirstRunData data =
+                mMessageController.onScreenshotTaken(WORK_USER);
 
-        verify(mMessageDisplay).showWorkProfileMessage(
-                eq(BADGED_DEFAULT_LABEL), eq(null), any());
+        assertEquals(DEFAULT_LABEL, data.getAppName());
+        assertNull(data.getIcon());
     }
 
     @Test
     public void testOnScreenshotTaken() {
-        mMessageController.onScreenshotTaken(WORK_USER, mMessageDisplay);
+        WorkProfileMessageController.WorkProfileFirstRunData data =
+                mMessageController.onScreenshotTaken(WORK_USER);
 
-        verify(mMessageDisplay).showWorkProfileMessage(
-                eq(BADGED_APP_LABEL), eq(mBadgedActivityIcon), mRunnableArgumentCaptor.capture());
+        assertEquals(APP_LABEL, data.getAppName());
+        assertEquals(mBadgedActivityIcon, data.getIcon());
+    }
 
-        // Dismiss hasn't been tapped, preference untouched.
-        assertFalse(
-                mSharedPreferences.getBoolean(WorkProfileMessageController.PREFERENCE_KEY, false));
+    @Test
+    public void testPopulateView() throws InterruptedException {
+        ViewGroup layout = (ViewGroup) LayoutInflater.from(mContext).inflate(
+                R.layout.screenshot_work_profile_first_run, null);
+        WorkProfileMessageController.WorkProfileFirstRunData data =
+                new WorkProfileMessageController.WorkProfileFirstRunData(APP_LABEL,
+                        mBadgedActivityIcon);
+        final CountDownLatch countdown = new CountDownLatch(1);
+        mMessageController.populateView(layout, data, () -> {
+            countdown.countDown();
+            return Unit.INSTANCE;
+        });
 
-        mRunnableArgumentCaptor.getValue().run();
+        ImageView image = layout.findViewById(R.id.screenshot_message_icon);
+        assertEquals(mBadgedActivityIcon, image.getDrawable());
+        TextView text = layout.findViewById(R.id.screenshot_message_content);
+        // The app name is used in a template, but at least validate that it was inserted.
+        assertTrue(text.getText().toString().contains(APP_LABEL));
 
-        // After dismiss has been tapped, the setting should be updated.
-        assertTrue(
-                mSharedPreferences.getBoolean(WorkProfileMessageController.PREFERENCE_KEY, false));
+        // Validate that clicking the dismiss button calls back properly.
+        assertEquals(1, countdown.getCount());
+        layout.findViewById(R.id.message_dismiss_button).callOnClick();
+        countdown.await(1000, TimeUnit.MILLISECONDS);
     }
 }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/DisplayTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/DisplayTrackerImplTest.kt
new file mode 100644
index 0000000..ae976a0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/DisplayTrackerImplTest.kt
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2022 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.settings
+
+import android.hardware.display.DisplayManager
+import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
+import android.hardware.display.DisplayManagerGlobal
+import android.os.Handler
+import android.testing.AndroidTestingRunner
+import android.view.Display
+import android.view.DisplayAdjustments
+import android.view.DisplayInfo
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.Executor
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DisplayTrackerImplTest : SysuiTestCase() {
+    @Mock private lateinit var displayManager: DisplayManager
+    @Mock private lateinit var handler: Handler
+
+    private val executor = Executor(Runnable::run)
+    private lateinit var mDefaultDisplay: Display
+    private lateinit var mSecondaryDisplay: Display
+    private lateinit var tracker: DisplayTrackerImpl
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        mDefaultDisplay =
+            Display(
+                DisplayManagerGlobal.getInstance(),
+                Display.DEFAULT_DISPLAY,
+                DisplayInfo(),
+                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS
+            )
+        mSecondaryDisplay =
+            Display(
+                DisplayManagerGlobal.getInstance(),
+                Display.DEFAULT_DISPLAY + 1,
+                DisplayInfo(),
+                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS
+            )
+
+        `when`(displayManager.displays).thenReturn(arrayOf(mDefaultDisplay, mSecondaryDisplay))
+
+        tracker = DisplayTrackerImpl(displayManager, handler)
+    }
+
+    @Test
+    fun testGetDefaultDisplay() {
+        assertThat(tracker.defaultDisplayId).isEqualTo(Display.DEFAULT_DISPLAY)
+    }
+
+    @Test
+    fun testGetAllDisplays() {
+        assertThat(tracker.allDisplays).isEqualTo(arrayOf(mDefaultDisplay, mSecondaryDisplay))
+    }
+
+    @Test
+    fun registerCallback_registersDisplayListener() {
+        tracker.addDisplayChangeCallback(TestCallback(), executor)
+        verify(displayManager).registerDisplayListener(any(), any())
+    }
+
+    @Test
+    fun registerBrightnessCallback_registersDisplayListener() {
+        tracker.addBrightnessChangeCallback(TestCallback(), executor)
+        verify(displayManager)
+            .registerDisplayListener(any(), any(), eq(EVENT_FLAG_DISPLAY_BRIGHTNESS))
+    }
+
+    @Test
+    fun unregisterCallback_displayListenerStillRegistered() {
+        val callback1 = TestCallback()
+        tracker.addDisplayChangeCallback(callback1, executor)
+        tracker.addDisplayChangeCallback(TestCallback(), executor)
+        tracker.removeCallback(callback1)
+
+        verify(displayManager, never()).unregisterDisplayListener(any())
+    }
+
+    @Test
+    fun unregisterLastCallback_unregistersDisplayListener() {
+        val callback = TestCallback()
+        tracker.addDisplayChangeCallback(callback, executor)
+        tracker.removeCallback(callback)
+
+        verify(displayManager).unregisterDisplayListener(any())
+    }
+
+    @Test
+    fun callbackCalledOnDisplayAdd() {
+        val testDisplay = 2
+        val callback = TestCallback()
+        tracker.addDisplayChangeCallback(callback, executor)
+        tracker.displayChangedListener.onDisplayAdded(testDisplay)
+
+        assertThat(callback.lastDisplayAdded).isEqualTo(testDisplay)
+    }
+
+    @Test
+    fun callbackCalledOnDisplayRemoved() {
+        val testDisplay = 2
+        val callback = TestCallback()
+        tracker.addDisplayChangeCallback(callback, executor)
+        tracker.displayChangedListener.onDisplayRemoved(testDisplay)
+
+        assertThat(callback.lastDisplayRemoved).isEqualTo(testDisplay)
+    }
+
+    @Test
+    fun callbackCalledOnDisplayChanged() {
+        val testDisplay = 2
+        val callback = TestCallback()
+        tracker.addDisplayChangeCallback(callback, executor)
+        tracker.displayChangedListener.onDisplayChanged(testDisplay)
+
+        assertThat(callback.lastDisplayChanged).isEqualTo(testDisplay)
+    }
+
+    @Test
+    fun callbackCalledOnBrightnessChanged() {
+        val testDisplay = 2
+        val callback = TestCallback()
+        tracker.addBrightnessChangeCallback(callback, executor)
+        tracker.displayBrightnessChangedListener.onDisplayChanged(testDisplay)
+
+        assertThat(callback.lastDisplayChanged).isEqualTo(testDisplay)
+    }
+
+    private class TestCallback : DisplayTracker.Callback {
+        var lastDisplayAdded = -1
+        var lastDisplayRemoved = -1
+        var lastDisplayChanged = -1
+
+        override fun onDisplayAdded(displayId: Int) {
+            lastDisplayAdded = displayId
+        }
+
+        override fun onDisplayRemoved(displayId: Int) {
+            lastDisplayRemoved = displayId
+        }
+
+        override fun onDisplayChanged(displayId: Int) {
+            lastDisplayChanged = displayId
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt
index 020a866..3fd19ff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt
@@ -30,12 +30,14 @@
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
+import java.io.File
+import java.nio.file.Files
 import java.util.concurrent.Executor
-import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
+import org.mockito.Mockito.atLeastOnce
 import org.mockito.Mockito.isNull
 import org.mockito.Mockito.spy
 import org.mockito.Mockito.verify
@@ -61,36 +63,83 @@
             UserFileManagerImpl(context, userManager, broadcastDispatcher, backgroundExecutor)
     }
 
-    @After
-    fun end() {
-        val dir = Environment.buildPath(context.filesDir, UserFileManagerImpl.ID)
-        dir.deleteRecursively()
-    }
-
     @Test
     fun testGetFile() {
         assertThat(userFileManager.getFile(TEST_FILE_NAME, 0).path)
             .isEqualTo("${context.filesDir}/$TEST_FILE_NAME")
         assertThat(userFileManager.getFile(TEST_FILE_NAME, 11).path)
-            .isEqualTo("${context.filesDir}/${UserFileManagerImpl.ID}/11/files/$TEST_FILE_NAME")
+            .isEqualTo("${context.filesDir}/__USER_11_$TEST_FILE_NAME")
     }
 
     @Test
     fun testGetSharedPreferences() {
+        val primarySharedPref = userFileManager.getSharedPreferences(TEST_FILE_NAME, 0, 0)
         val secondarySharedPref = userFileManager.getSharedPreferences(TEST_FILE_NAME, 0, 11)
-        val secondaryUserDir =
-            Environment.buildPath(
-                context.filesDir,
-                UserFileManagerImpl.ID,
-                "11",
-                UserFileManagerImpl.SHARED_PREFS,
-                TEST_FILE_NAME
-            )
 
-        assertThat(secondarySharedPref).isNotNull()
-        assertThat(secondaryUserDir.exists())
-        assertThat(userFileManager.getSharedPreferences(TEST_FILE_NAME, 0, 0))
-            .isNotEqualTo(secondarySharedPref)
+        assertThat(primarySharedPref).isNotEqualTo(secondarySharedPref)
+
+        // Make sure these are different files
+        primarySharedPref.edit().putString("TEST", "ABC").commit()
+        assertThat(secondarySharedPref.getString("TEST", null)).isNull()
+
+        context.deleteSharedPreferences("TEST")
+        context.deleteSharedPreferences("__USER_11_TEST")
+    }
+
+    @Test
+    fun testMigrateFile() {
+        val userId = 12
+        val fileName = "myFile.txt"
+        val fileContents = "TestingFile"
+        val legacyFile =
+            UserFileManagerImpl.createLegacyFile(
+                context,
+                UserFileManagerImpl.FILES,
+                fileName,
+                userId
+            )!!
+
+        // Write file to legacy area
+        Files.createDirectories(legacyFile.getParentFile().toPath())
+        Files.write(legacyFile.toPath(), fileContents.toByteArray())
+        assertThat(legacyFile.exists()).isTrue()
+
+        // getFile() should migrate the legacy file to the new location
+        val file = userFileManager.getFile(fileName, userId)
+        val newContents = String(Files.readAllBytes(file.toPath()))
+
+        assertThat(newContents).isEqualTo(fileContents)
+        assertThat(legacyFile.exists()).isFalse()
+        assertThat(File(context.filesDir, UserFileManagerImpl.ROOT_DIR).exists()).isFalse()
+    }
+
+    @Test
+    fun testMigrateSharedPrefs() {
+        val userId = 13
+        val fileName = "myFile"
+        val contents = "TestingSharedPrefs"
+        val legacyFile =
+            UserFileManagerImpl.createLegacyFile(
+                context,
+                UserFileManagerImpl.SHARED_PREFS,
+                "$fileName.xml",
+                userId
+            )!!
+
+        // Write a valid shared prefs xml file to legacy area
+        val tmpPrefs = context.getSharedPreferences("tmp", Context.MODE_PRIVATE)
+        tmpPrefs.edit().putString(contents, contents).commit()
+        Files.createDirectories(legacyFile.getParentFile().toPath())
+        val tmpFile =
+            Environment.buildPath(context.dataDir, UserFileManagerImpl.SHARED_PREFS, "tmp.xml")
+        tmpFile.renameTo(legacyFile)
+        assertThat(legacyFile.exists()).isTrue()
+
+        // getSharedpreferences() should migrate the legacy file to the new location
+        val prefs = userFileManager.getSharedPreferences(fileName, Context.MODE_PRIVATE, userId)
+        assertThat(prefs.getString(contents, "")).isEqualTo(contents)
+        assertThat(legacyFile.exists()).isFalse()
+        assertThat(File(context.filesDir, UserFileManagerImpl.ROOT_DIR).exists()).isFalse()
     }
 
     @Test
@@ -111,44 +160,14 @@
 
     @Test
     fun testClearDeletedUserData() {
-        val dir = Environment.buildPath(context.filesDir, UserFileManagerImpl.ID, "11", "files")
-        dir.mkdirs()
-        val file =
-            Environment.buildPath(
-                context.filesDir,
-                UserFileManagerImpl.ID,
-                "11",
-                "files",
-                TEST_FILE_NAME
-            )
-        val secondaryUserDir =
-            Environment.buildPath(
-                context.filesDir,
-                UserFileManagerImpl.ID,
-                "11",
-            )
+        val file = userFileManager.getFile(TEST_FILE_NAME, 11)
         file.createNewFile()
-        assertThat(secondaryUserDir.exists()).isTrue()
+
         assertThat(file.exists()).isTrue()
         userFileManager.clearDeletedUserData()
         assertThat(backgroundExecutor.runAllReady()).isGreaterThan(0)
-        verify(userManager).aliveUsers
-        assertThat(secondaryUserDir.exists()).isFalse()
-        assertThat(file.exists()).isFalse()
-    }
+        verify(userManager, atLeastOnce()).aliveUsers
 
-    @Test
-    fun testEnsureParentDirExists() {
-        val file =
-            Environment.buildPath(
-                context.filesDir,
-                UserFileManagerImpl.ID,
-                "11",
-                "files",
-                TEST_FILE_NAME
-            )
-        assertThat(file.parentFile.exists()).isFalse()
-        UserFileManagerImpl.ensureParentDirExists(file)
-        assertThat(file.parentFile.exists()).isTrue()
+        assertThat(file.exists()).isFalse()
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
index 3710281..57b6b2b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
@@ -1,5 +1,6 @@
 package com.android.systemui.settings
 
+import android.app.IActivityManager
 import android.content.Context
 import android.content.Intent
 import android.content.pm.UserInfo
@@ -51,6 +52,7 @@
 
     @Mock private lateinit var context: Context
     @Mock private lateinit var userManager: UserManager
+    @Mock private lateinit var iActivityManager: IActivityManager
     @Mock(stubOnly = true) private lateinit var dumpManager: DumpManager
     @Mock(stubOnly = true) private lateinit var handler: Handler
 
@@ -67,7 +69,7 @@
         `when`(context.user).thenReturn(UserHandle.SYSTEM)
         `when`(context.createContextAsUser(ArgumentMatchers.any(), anyInt())).thenReturn(context)
 
-        tracker = UserTrackerImpl(context, userManager, dumpManager, handler)
+        tracker = UserTrackerImpl(context, userManager, iActivityManager, dumpManager, handler)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
index e65bbb1..71ba215 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -16,11 +16,14 @@
 
 package com.android.systemui.settings
 
+import android.app.IActivityManager
+import android.app.IUserSwitchObserver
 import android.content.Context
 import android.content.Intent
 import android.content.IntentFilter
 import android.content.pm.UserInfo
 import android.os.Handler
+import android.os.IRemoteCallback
 import android.os.UserHandle
 import android.os.UserManager
 import android.testing.AndroidTestingRunner
@@ -29,19 +32,20 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.util.mockito.capture
 import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.Executor
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers.any
 import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyString
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.ArgumentMatchers.isNull
 import org.mockito.Mock
-import org.mockito.Mockito.`when`
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
-import java.util.concurrent.Executor
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -51,6 +55,10 @@
     private lateinit var context: Context
     @Mock
     private lateinit var userManager: UserManager
+    @Mock
+    private lateinit var iActivityManager: IActivityManager
+    @Mock
+    private lateinit var userSwitchingReply: IRemoteCallback
     @Mock(stubOnly = true)
     private lateinit var dumpManager: DumpManager
     @Mock(stubOnly = true)
@@ -76,7 +84,7 @@
             listOf(info)
         }
 
-        tracker = UserTrackerImpl(context, userManager, dumpManager, handler)
+        tracker = UserTrackerImpl(context, userManager, iActivityManager, dumpManager, handler)
     }
 
     @Test
@@ -125,8 +133,7 @@
         verify(context).registerReceiverForAllUsers(
                 eq(tracker), capture(captor), isNull(), eq(handler))
         with(captor.value) {
-            assertThat(countActions()).isEqualTo(7)
-            assertThat(hasAction(Intent.ACTION_USER_SWITCHED)).isTrue()
+            assertThat(countActions()).isEqualTo(6)
             assertThat(hasAction(Intent.ACTION_USER_INFO_CHANGED)).isTrue()
             assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)).isTrue()
             assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)).isTrue()
@@ -158,8 +165,10 @@
         tracker.initialize(0)
         val newID = 5
 
-        val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, newID)
-        tracker.onReceive(context, intent)
+        val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
+        verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+        captor.value.onUserSwitching(newID, userSwitchingReply)
+        verify(userSwitchingReply).sendResult(any())
 
         verify(userManager).getProfiles(newID)
 
@@ -272,6 +281,24 @@
     }
 
     @Test
+    fun testCallbackCalledOnUserChanging() {
+        tracker.initialize(0)
+        val callback = TestCallback()
+        tracker.addCallback(callback, executor)
+
+        val newID = 5
+
+        val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
+        verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+        captor.value.onUserSwitching(newID, userSwitchingReply)
+        verify(userSwitchingReply).sendResult(any())
+
+        assertThat(callback.calledOnUserChanging).isEqualTo(1)
+        assertThat(callback.lastUser).isEqualTo(newID)
+        assertThat(callback.lastUserContext?.userId).isEqualTo(newID)
+    }
+
+    @Test
     fun testCallbackCalledOnUserChanged() {
         tracker.initialize(0)
         val callback = TestCallback()
@@ -279,8 +306,9 @@
 
         val newID = 5
 
-        val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, newID)
-        tracker.onReceive(context, intent)
+        val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
+        verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+        captor.value.onUserSwitchComplete(newID)
 
         assertThat(callback.calledOnUserChanged).isEqualTo(1)
         assertThat(callback.lastUser).isEqualTo(newID)
@@ -330,25 +358,36 @@
         tracker.addCallback(callback, executor)
         tracker.removeCallback(callback)
 
-        val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, 5)
-        tracker.onReceive(context, intent)
+        val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
+        verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+        captor.value.onUserSwitching(newID, userSwitchingReply)
+        verify(userSwitchingReply).sendResult(any())
+        captor.value.onUserSwitchComplete(newID)
 
         val intentProfiles = Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
                 .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
 
         tracker.onReceive(context, intentProfiles)
 
+        assertThat(callback.calledOnUserChanging).isEqualTo(0)
         assertThat(callback.calledOnUserChanged).isEqualTo(0)
         assertThat(callback.calledOnProfilesChanged).isEqualTo(0)
     }
 
     private class TestCallback : UserTracker.Callback {
+        var calledOnUserChanging = 0
         var calledOnUserChanged = 0
         var calledOnProfilesChanged = 0
         var lastUser: Int? = null
         var lastUserContext: Context? = null
         var lastUserProfiles = emptyList<UserInfo>()
 
+        override fun onUserChanging(newUser: Int, userContext: Context) {
+            calledOnUserChanging++
+            lastUser = newUser
+            lastUserContext = userContext
+        }
+
         override fun onUserChanged(newUser: Int, userContext: Context) {
             calledOnUserChanged++
             lastUser = newUser
@@ -360,4 +399,4 @@
             lastUserProfiles = profiles
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
index 9d1802a..58ade49 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
@@ -28,6 +28,7 @@
 import androidx.test.runner.intercepting.SingleActivityFactory
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.FakeDisplayTracker
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.util.mockito.any
 import com.google.common.truth.Truth.assertThat
@@ -51,6 +52,7 @@
     @Mock private lateinit var mainExecutor: Executor
     @Mock private lateinit var backgroundHandler: Handler
     @Mock private lateinit var brightnessSliderController: BrightnessSliderController
+    private val displayTracker = FakeDisplayTracker(mContext)
 
     @Rule
     @JvmField
@@ -60,6 +62,7 @@
                 override fun create(intent: Intent?): TestDialog {
                     return TestDialog(
                         userTracker,
+                        displayTracker,
                         brightnessSliderControllerFactory,
                         mainExecutor,
                         backgroundHandler
@@ -105,12 +108,14 @@
 
     class TestDialog(
         userTracker: UserTracker,
+        displayTracker: FakeDisplayTracker,
         brightnessSliderControllerFactory: BrightnessSliderController.Factory,
         mainExecutor: Executor,
         backgroundHandler: Handler
     ) :
         BrightnessDialog(
             userTracker,
+            displayTracker,
             brightnessSliderControllerFactory,
             mainExecutor,
             backgroundHandler
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
index ed9baf5b1..3706859 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
@@ -90,7 +90,7 @@
     fun testEdgeElementsAlignedWithEdgeOrGuide_qs() {
         with(qsConstraint) {
             assertThat(getConstraint(R.id.clock).layout.startToStart).isEqualTo(PARENT_ID)
-            assertThat(getConstraint(R.id.clock).layout.horizontalBias).isEqualTo(0f)
+            assertThat(getConstraint(R.id.clock).layout.horizontalBias).isEqualTo(0.5f)
 
             assertThat(getConstraint(R.id.date).layout.startToStart).isEqualTo(PARENT_ID)
             assertThat(getConstraint(R.id.date).layout.horizontalBias).isEqualTo(0.5f)
@@ -342,7 +342,6 @@
                 R.id.clock to "clock",
                 R.id.date to "date",
                 R.id.privacy_container to "privacy",
-                R.id.carrier_group to "carriers",
         )
         views.forEach { (id, name) ->
             assertWithMessage("$name has 0 height in qqs")
@@ -361,7 +360,6 @@
         val views = mapOf(
                 R.id.clock to "clock",
                 R.id.privacy_container to "privacy",
-                R.id.carrier_group to "carriers",
         )
         views.forEach { (id, name) ->
             expect.withMessage("$name changes height")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
index f580f5e..ee5f61c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
@@ -21,6 +21,7 @@
 import android.content.res.XmlResourceParser
 import android.graphics.Rect
 import android.testing.AndroidTestingRunner
+import android.view.Display
 import android.view.DisplayCutout
 import android.view.View
 import android.view.ViewPropertyAnimator
@@ -77,9 +78,11 @@
 import org.mockito.Mockito.inOrder
 import org.mockito.Mockito.never
 import org.mockito.Mockito.reset
+import org.mockito.Mockito.same
+import org.mockito.Mockito.spy
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
 import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
 
 private val EMPTY_CHANGES = ConstraintsChanges()
 
@@ -133,6 +136,7 @@
 
     @Mock
     private lateinit var mockedContext: Context
+    private lateinit var viewContext: Context
     @Mock(answer = Answers.RETURNS_MOCKS)
     private lateinit var view: MotionLayout
 
@@ -143,6 +147,7 @@
     @Mock
     private lateinit var largeScreenConstraints: ConstraintSet
     @Mock private lateinit var demoModeController: DemoModeController
+    @Mock private lateinit var qsBatteryModeController: QsBatteryModeController
 
     @JvmField @Rule
     val mockitoRule = MockitoJUnit.rule()
@@ -175,7 +180,8 @@
             .thenReturn(qsCarrierGroupControllerBuilder)
         whenever(qsCarrierGroupControllerBuilder.build()).thenReturn(qsCarrierGroupController)
 
-        whenever(view.context).thenReturn(context)
+        viewContext = spy(context)
+        whenever(view.context).thenReturn(viewContext)
         whenever(view.resources).thenReturn(context.resources)
         whenever(view.setVisibility(ArgumentMatchers.anyInt())).then {
             viewVisibility = it.arguments[0] as Int
@@ -192,19 +198,20 @@
         setUpMotionLayout(view)
 
         controller = LargeScreenShadeHeaderController(
-            view,
-            statusBarIconController,
-            iconManagerFactory,
-            privacyIconsController,
-            insetsProvider,
-            configurationController,
-            variableDateViewControllerFactory,
-            batteryMeterViewController,
-            dumpManager,
-            featureFlags,
-            qsCarrierGroupControllerBuilder,
-            combinedShadeHeadersConstraintManager,
-            demoModeController
+                view,
+                statusBarIconController,
+                iconManagerFactory,
+                privacyIconsController,
+                insetsProvider,
+                configurationController,
+                variableDateViewControllerFactory,
+                batteryMeterViewController,
+                dumpManager,
+                featureFlags,
+                qsCarrierGroupControllerBuilder,
+                combinedShadeHeadersConstraintManager,
+                demoModeController,
+                qsBatteryModeController,
         )
         whenever(view.isAttachedToWindow).thenReturn(true)
         controller.init()
@@ -218,7 +225,6 @@
 
         verify(batteryMeterViewController).init()
         verify(batteryMeterViewController).ignoreTunerUpdates()
-        verify(batteryMeterView).setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)
 
         val inOrder = inOrder(qsCarrierGroupControllerBuilder)
         inOrder.verify(qsCarrierGroupControllerBuilder).setQSCarrierGroup(carrierGroup)
@@ -226,6 +232,23 @@
     }
 
     @Test
+    fun `battery mode controller called when qsExpandedFraction changes`() {
+        whenever(qsBatteryModeController.getBatteryMode(same(null), eq(0f)))
+                .thenReturn(BatteryMeterView.MODE_ON)
+        whenever(qsBatteryModeController.getBatteryMode(same(null), eq(1f)))
+                .thenReturn(BatteryMeterView.MODE_ESTIMATE)
+        controller.qsVisible = true
+
+        val times = 10
+        repeat(times) {
+            controller.qsExpandedFraction = it / (times - 1).toFloat()
+        }
+
+        verify(batteryMeterView).setPercentShowMode(BatteryMeterView.MODE_ON)
+        verify(batteryMeterView).setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)
+    }
+
+    @Test
     fun testClockPivotLtr() {
         val width = 200
         whenever(clock.width).thenReturn(width)
@@ -684,14 +707,27 @@
         configurationController.notifyDensityOrFontScaleChanged()
 
         val captor = ArgumentCaptor.forClass(XmlResourceParser::class.java)
-        verify(qqsConstraints).load(eq(context), capture(captor))
+        verify(qqsConstraints).load(eq(viewContext), capture(captor))
         assertThat(captor.value.getResId()).isEqualTo(R.xml.qqs_header)
-        verify(qsConstraints).load(eq(context), capture(captor))
+        verify(qsConstraints).load(eq(viewContext), capture(captor))
         assertThat(captor.value.getResId()).isEqualTo(R.xml.qs_header)
-        verify(largeScreenConstraints).load(eq(context), capture(captor))
+        verify(largeScreenConstraints).load(eq(viewContext), capture(captor))
         assertThat(captor.value.getResId()).isEqualTo(R.xml.large_screen_shade_header)
     }
 
+    @Test
+    fun `carrier left padding is set when clock layout changes`() {
+        val width = 200
+        whenever(clock.width).thenReturn(width)
+        whenever(clock.scaleX).thenReturn(2.57f) // 2.57 comes from qs_header.xml
+        val captor = ArgumentCaptor.forClass(View.OnLayoutChangeListener::class.java)
+
+        verify(clock).addOnLayoutChangeListener(capture(captor))
+        captor.value.onLayoutChange(clock, 0, 0, width, 0, 0, 0, 0, 0)
+
+        verify(carrierGroup).setPaddingRelative(514, 0, 0, 0)
+    }
+
     private fun View.executeLayoutChange(
             left: Int,
             top: Int,
@@ -773,6 +809,13 @@
         whenever(insetsProvider.getStatusBarContentInsetsForCurrentRotation())
             .thenReturn(Pair(0, 0).toAndroidPair())
         whenever(insetsProvider.currentRotationHasCornerCutout()).thenReturn(false)
+        setupCurrentInsets(null)
+    }
+
+    private fun setupCurrentInsets(cutout: DisplayCutout?) {
+        val mockedDisplay =
+                mock<Display>().also { display -> whenever(display.cutout).thenReturn(cutout) }
+        whenever(viewContext.display).thenReturn(mockedDisplay)
     }
 
     private fun<T, U> Pair<T, U>.toAndroidPair(): android.util.Pair<T, U> {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
index b568122..e684007 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
@@ -1,6 +1,6 @@
 package com.android.systemui.shade
 
-import android.animation.ValueAnimator
+import android.animation.Animator
 import android.app.StatusBarManager
 import android.content.Context
 import android.testing.AndroidTestingRunner
@@ -43,10 +43,11 @@
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mock
 import org.mockito.Mockito.mock
+import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.Mockito.`when` as whenever
 import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -75,6 +76,7 @@
 
     @Mock private lateinit var mockedContext: Context
     @Mock private lateinit var demoModeController: DemoModeController
+    @Mock private lateinit var qsBatteryModeController: QsBatteryModeController
 
     @JvmField @Rule val mockitoRule = MockitoJUnit.rule()
     var viewVisibility = View.GONE
@@ -129,8 +131,9 @@
                 featureFlags,
                 qsCarrierGroupControllerBuilder,
                 combinedShadeHeadersConstraintManager,
-                demoModeController
-                )
+                demoModeController,
+                qsBatteryModeController,
+        )
         whenever(view.isAttachedToWindow).thenReturn(true)
         mLargeScreenShadeHeaderController.init()
         carrierIconSlots = listOf(
@@ -155,10 +158,20 @@
     fun updateListeners_registersWhenVisible() {
         makeShadeVisible()
         verify(qsCarrierGroupController).setListening(true)
+    }
+
+    @Test
+    fun statusIconsAddedWhenAttached() {
         verify(statusBarIconController).addIconGroup(any())
     }
 
     @Test
+    fun statusIconsRemovedWhenDettached() {
+        mLargeScreenShadeHeaderController.simulateViewDetached()
+        verify(statusBarIconController).removeIconGroup(any())
+    }
+
+    @Test
     fun shadeExpandedFraction_updatesAlpha() {
         makeShadeVisible()
         mLargeScreenShadeHeaderController.shadeExpandedFraction = 0.5f
@@ -166,16 +179,6 @@
     }
 
     @Test
-    fun alphaChangesUpdateVisibility() {
-        makeShadeVisible()
-        mLargeScreenShadeHeaderController.shadeExpandedFraction = 0f
-        assertThat(viewVisibility).isEqualTo(View.INVISIBLE)
-
-        mLargeScreenShadeHeaderController.shadeExpandedFraction = 1f
-        assertThat(viewVisibility).isEqualTo(View.VISIBLE)
-    }
-
-    @Test
     fun singleCarrier_enablesCarrierIconsInStatusIcons() {
         whenever(qsCarrierGroupController.isSingleCarrier).thenReturn(true)
 
@@ -260,36 +263,41 @@
     }
 
     @Test
-    fun testShadeExpanded_true_alpha_zero_invisible() {
-        view.alpha = 0f
-        mLargeScreenShadeHeaderController.largeScreenActive = true
-        mLargeScreenShadeHeaderController.qsVisible = true
+    fun customizerAnimatorChangesViewVisibility() {
+        makeShadeVisible()
 
+        val animator = mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
+        val duration = 1000L
+        whenever(view.animate()).thenReturn(animator)
+        val listenerCaptor = argumentCaptor<Animator.AnimatorListener>()
+
+        mLargeScreenShadeHeaderController.startCustomizingAnimation(show = true, duration)
+        verify(animator).setListener(capture(listenerCaptor))
+        // Start and end the animation
+        listenerCaptor.value.onAnimationStart(mock())
+        listenerCaptor.value.onAnimationEnd(mock())
         assertThat(viewVisibility).isEqualTo(View.INVISIBLE)
+
+        reset(animator)
+        mLargeScreenShadeHeaderController.startCustomizingAnimation(show = false, duration)
+        verify(animator).setListener(capture(listenerCaptor))
+        // Start and end the animation
+        listenerCaptor.value.onAnimationStart(mock())
+        listenerCaptor.value.onAnimationEnd(mock())
+        assertThat(viewVisibility).isEqualTo(View.VISIBLE)
     }
 
     @Test
-    fun animatorCallsUpdateVisibilityOnUpdate() {
+    fun animatorListenerClearedAtEnd() {
         val animator = mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
         whenever(view.animate()).thenReturn(animator)
 
-        mLargeScreenShadeHeaderController.startCustomizingAnimation(show = false, 0L)
+        mLargeScreenShadeHeaderController.startCustomizingAnimation(show = true, 0L)
+        val listenerCaptor = argumentCaptor<Animator.AnimatorListener>()
+        verify(animator).setListener(capture(listenerCaptor))
 
-        val updateCaptor = argumentCaptor<ValueAnimator.AnimatorUpdateListener>()
-        verify(animator).setUpdateListener(capture(updateCaptor))
-
-        mLargeScreenShadeHeaderController.largeScreenActive = true
-        mLargeScreenShadeHeaderController.qsVisible = true
-
-        view.alpha = 1f
-        updateCaptor.value.onAnimationUpdate(mock())
-
-        assertThat(viewVisibility).isEqualTo(View.VISIBLE)
-
-        view.alpha = 0f
-        updateCaptor.value.onAnimationUpdate(mock())
-
-        assertThat(viewVisibility).isEqualTo(View.INVISIBLE)
+        listenerCaptor.value.onAnimationEnd(mock())
+        verify(animator).setListener(null)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 0f3d4a8..996d9fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -97,6 +97,7 @@
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.classifier.FalsingCollectorFake;
 import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.common.ui.view.LongPressHandlingView;
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlags;
@@ -105,10 +106,12 @@
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel;
@@ -193,11 +196,12 @@
 import java.util.List;
 import java.util.Optional;
 
+import dagger.Lazy;
 import kotlinx.coroutines.CoroutineDispatcher;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class NotificationPanelViewControllerTest extends SysuiTestCase {
 
     private static final int SPLIT_SHADE_FULL_TRANSITION_DISTANCE = 400;
@@ -267,6 +271,7 @@
     @Mock private KeyguardMediaController mKeyguardMediaController;
     @Mock private NavigationModeController mNavigationModeController;
     @Mock private NavigationBarController mNavigationBarController;
+    @Mock private QuickSettingsController mQsController;
     @Mock private LargeScreenShadeHeaderController mLargeScreenShadeHeaderController;
     @Mock private ContentResolver mContentResolver;
     @Mock private TapAgainViewController mTapAgainViewController;
@@ -302,6 +307,8 @@
     @Mock private GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel;
 
     @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+    @Mock private KeyguardInteractor mKeyguardInteractor;
+    @Mock private KeyguardLongPressViewModel mKeyuardLongPressViewModel;
     @Mock private CoroutineDispatcher mMainDispatcher;
     @Mock private AlternateBouncerInteractor mAlternateBouncerInteractor;
     @Mock private MotionEvent mDownMotionEvent;
@@ -324,6 +331,10 @@
     private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
     private final ShadeExpansionStateManager mShadeExpansionStateManager =
             new ShadeExpansionStateManager();
+
+    private QuickSettingsController mQuickSettingsController;
+    @Mock private Lazy<NotificationPanelViewController> mNotificationPanelViewControllerLazy;
+
     private FragmentHostManager.FragmentListener mFragmentListener;
 
     @Before
@@ -459,6 +470,9 @@
 
         mMainHandler = new Handler(Looper.getMainLooper());
 
+        when(mView.requireViewById(R.id.keyguard_long_press))
+                .thenReturn(mock(LongPressHandlingView.class));
+
         mNotificationPanelViewController = new NotificationPanelViewController(
                 mView,
                 mMainHandler,
@@ -498,6 +512,7 @@
                 mTapAgainViewController,
                 mNavigationModeController,
                 mNavigationBarController,
+                mQsController,
                 mFragmentService,
                 mContentResolver,
                 mRecordingController,
@@ -507,8 +522,6 @@
                 mShadeExpansionStateManager,
                 mNotificationRemoteInputManager,
                 mSysUIUnfoldComponent,
-                mInteractionJankMonitor,
-                mQsFrameTranslateController,
                 mSysUiState,
                 () -> mKeyguardBottomAreaViewController,
                 mKeyguardUnlockAnimationController,
@@ -528,7 +541,9 @@
                 mLockscreenToOccludedTransitionViewModel,
                 mMainDispatcher,
                 mKeyguardTransitionInteractor,
-                mDumpManager);
+                mDumpManager,
+                mKeyuardLongPressViewModel,
+                mKeyguardInteractor);
         mNotificationPanelViewController.initDependencies(
                 mCentralSurfaces,
                 null,
@@ -562,6 +577,40 @@
                 .setOnEmptySpaceClickListener(mEmptySpaceClickListenerCaptor.capture());
         verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
         reset(mKeyguardStatusViewController);
+
+        when(mNotificationPanelViewControllerLazy.get())
+                .thenReturn(mNotificationPanelViewController);
+        mQuickSettingsController = new QuickSettingsController(
+                mNotificationPanelViewControllerLazy,
+                mView,
+                mQsFrameTranslateController,
+                mShadeTransitionController,
+                expansionHandler,
+                mNotificationRemoteInputManager,
+                mShadeExpansionStateManager,
+                mStatusBarKeyguardViewManager,
+                mNotificationStackScrollLayoutController,
+                mLockscreenShadeTransitionController,
+                mNotificationShadeDepthController,
+                mLargeScreenShadeHeaderController,
+                mStatusBarTouchableRegionManager,
+                mKeyguardStateController,
+                mKeyguardBypassController,
+                mUpdateMonitor,
+                mScrimController,
+                mMediaDataManager,
+                mMediaHierarchyManager,
+                mAmbientState,
+                mRecordingController,
+                mFalsingManager,
+                new FalsingCollectorFake(),
+                mAccessibilityManager,
+                mLockscreenGestureLogger,
+                mMetricsLogger,
+                mFeatureFlags,
+                mInteractionJankMonitor,
+                mShadeLog
+        );
     }
 
     @After
@@ -745,27 +794,14 @@
 
     @Test
     public void testOnTouchEvent_expansionCanBeBlocked() {
-        onTouchEvent(MotionEvent.obtain(0L /* downTime */,
-                0L /* eventTime */, MotionEvent.ACTION_DOWN, 0f /* x */, 0f /* y */,
-                0 /* metaState */));
-        onTouchEvent(MotionEvent.obtain(0L /* downTime */,
-                0L /* eventTime */, MotionEvent.ACTION_MOVE, 0f /* x */, 200f /* y */,
-                0 /* metaState */));
+        onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0));
+        onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 200f, 0));
         assertThat((int) mNotificationPanelViewController.getExpandedHeight()).isEqualTo(200);
-        assertThat(mNotificationPanelViewController.isTrackingBlocked()).isFalse();
 
         mNotificationPanelViewController.blockExpansionForCurrentTouch();
-        onTouchEvent(MotionEvent.obtain(0L /* downTime */,
-                0L /* eventTime */, MotionEvent.ACTION_MOVE, 0f /* x */, 300f /* y */,
-                0 /* metaState */));
+        onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 300f, 0));
         // Expansion should not have changed because it was blocked
         assertThat((int) mNotificationPanelViewController.getExpandedHeight()).isEqualTo(200);
-        assertThat(mNotificationPanelViewController.isTrackingBlocked()).isTrue();
-
-        onTouchEvent(MotionEvent.obtain(0L /* downTime */,
-                0L /* eventTime */, MotionEvent.ACTION_UP, 0f /* x */, 300f /* y */,
-                0 /* metaState */));
-        assertThat(mNotificationPanelViewController.isTrackingBlocked()).isFalse();
     }
 
     @Test
@@ -1035,7 +1071,7 @@
     @Test
     public void testCanCollapsePanelOnTouch_trueWhenInSettings() {
         mStatusBarStateController.setState(SHADE);
-        mNotificationPanelViewController.setQsExpanded(true);
+        when(mQsController.getExpanded()).thenReturn(true);
 
         assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isTrue();
     }
@@ -1044,7 +1080,7 @@
     public void testCanCollapsePanelOnTouch_falseInDualPaneShade() {
         mStatusBarStateController.setState(SHADE);
         enableSplitShade(/* enabled= */ true);
-        mNotificationPanelViewController.setQsExpanded(true);
+        when(mQsController.getExpanded()).thenReturn(true);
 
         assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isFalse();
     }
@@ -1115,7 +1151,7 @@
     @Test
     public void testRotatingToSplitShadeWithQsExpanded_transitionsToShadeLocked() {
         mStatusBarStateController.setState(KEYGUARD);
-        mNotificationPanelViewController.setQsExpanded(true);
+        when(mQsController.getExpanded()).thenReturn(true);
 
         enableSplitShade(true);
 
@@ -1126,24 +1162,18 @@
     public void testUnlockedSplitShadeTransitioningToKeyguard_closesQS() {
         enableSplitShade(true);
         mStatusBarStateController.setState(SHADE);
-        mNotificationPanelViewController.setQsExpanded(true);
-
         mStatusBarStateController.setState(KEYGUARD);
 
-        assertThat(mNotificationPanelViewController.isQsExpanded()).isEqualTo(false);
-        assertThat(mNotificationPanelViewController.isQsExpandImmediate()).isEqualTo(false);
+        verify(mQsController).closeQs();
     }
 
     @Test
     public void testLockedSplitShadeTransitioningToKeyguard_closesQS() {
         enableSplitShade(true);
         mStatusBarStateController.setState(SHADE_LOCKED);
-        mNotificationPanelViewController.setQsExpanded(true);
-
         mStatusBarStateController.setState(KEYGUARD);
 
-        assertThat(mNotificationPanelViewController.isQsExpanded()).isEqualTo(false);
-        assertThat(mNotificationPanelViewController.isQsExpandImmediate()).isEqualTo(false);
+        verify(mQsController).closeQs();
     }
 
     @Test
@@ -1155,7 +1185,7 @@
         verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
 
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
-        mNotificationPanelViewController.closeQs();
+        triggerPositionClockAndNotifications();
         verify(mKeyguardStatusViewController).displayClock(SMALL, /* animate */ true);
     }
 
@@ -1283,18 +1313,6 @@
     }
 
     @Test
-    public void testLargeScreenHeaderMadeActiveForLargeScreen() {
-        mStatusBarStateController.setState(SHADE);
-        when(mResources.getBoolean(R.bool.config_use_large_screen_shade_header)).thenReturn(true);
-        mNotificationPanelViewController.updateResources();
-        verify(mLargeScreenShadeHeaderController).setLargeScreenActive(true);
-
-        when(mResources.getBoolean(R.bool.config_use_large_screen_shade_header)).thenReturn(false);
-        mNotificationPanelViewController.updateResources();
-        verify(mLargeScreenShadeHeaderController).setLargeScreenActive(false);
-    }
-
-    @Test
     public void testExpandWithQsMethodIsUsingLockscreenTransitionController() {
         enableSplitShade(/* enabled= */ true);
         mStatusBarStateController.setState(KEYGUARD);
@@ -1348,12 +1366,14 @@
     @Test
     public void testQsToBeImmediatelyExpandedWhenOpeningPanelInSplitShade() {
         enableSplitShade(/* enabled= */ true);
+        mShadeExpansionStateManager.updateState(STATE_OPEN);
+        verify(mQsController).setExpandImmediate(false);
+
         mShadeExpansionStateManager.updateState(STATE_CLOSED);
-        assertThat(mNotificationPanelViewController.isQsExpandImmediate()).isFalse();
+        verify(mQsController, times(2)).setExpandImmediate(false);
 
         mShadeExpansionStateManager.updateState(STATE_OPENING);
-
-        assertThat(mNotificationPanelViewController.isQsExpandImmediate()).isTrue();
+        verify(mQsController).setExpandImmediate(true);
     }
 
     @Test
@@ -1365,33 +1385,28 @@
         // going to lockscreen would trigger STATE_OPENING
         mShadeExpansionStateManager.updateState(STATE_OPENING);
 
-        assertThat(mNotificationPanelViewController.isQsExpandImmediate()).isFalse();
+        verify(mQsController, never()).setExpandImmediate(true);
     }
 
     @Test
     public void testQsImmediateResetsWhenPanelOpensOrCloses() {
-        mNotificationPanelViewController.setQsExpandImmediate(true);
         mShadeExpansionStateManager.updateState(STATE_OPEN);
-        assertThat(mNotificationPanelViewController.isQsExpandImmediate()).isFalse();
-
-        mNotificationPanelViewController.setQsExpandImmediate(true);
         mShadeExpansionStateManager.updateState(STATE_CLOSED);
-        assertThat(mNotificationPanelViewController.isQsExpandImmediate()).isFalse();
+        verify(mQsController, times(2)).setExpandImmediate(false);
     }
 
     @Test
     public void testQsExpansionChangedToDefaultWhenRotatingFromOrToSplitShade() {
         // to make sure shade is in expanded state
         mNotificationPanelViewController.startWaitingForOpenPanelGesture();
-        assertThat(mNotificationPanelViewController.isQsExpanded()).isFalse();
 
         // switch to split shade from portrait (default state)
         enableSplitShade(/* enabled= */ true);
-        assertThat(mNotificationPanelViewController.isQsExpanded()).isTrue();
+        verify(mQsController).setExpanded(true);
 
         // switch to portrait from split shade
         enableSplitShade(/* enabled= */ false);
-        assertThat(mNotificationPanelViewController.isQsExpanded()).isFalse();
+        verify(mQsController).setExpanded(false);
     }
 
     @Test
@@ -1403,60 +1418,11 @@
 
         assertThat(mNotificationPanelViewController.isClosing()).isFalse();
         mNotificationPanelViewController.animateCloseQs(false);
+
         assertThat(mNotificationPanelViewController.isClosing()).isTrue();
     }
 
     @Test
-    public void testPanelStaysOpenWhenClosingQs() {
-        mShadeExpansionStateManager.onPanelExpansionChanged(/* fraction= */ 1,
-                /* expanded= */ true, /* tracking= */ false, /* dragDownPxAmount= */ 0);
-        mNotificationPanelViewController.setExpandedFraction(1f);
-
-        assertThat(mNotificationPanelViewController.isClosing()).isFalse();
-        mNotificationPanelViewController.animateCloseQs(false);
-        assertThat(mNotificationPanelViewController.isClosing()).isFalse();
-    }
-
-    @Test
-    public void interceptTouchEvent_withinQs_shadeExpanded_startsQsTracking() {
-        mNotificationPanelViewController.setQs(mQs);
-        when(mQsFrame.getX()).thenReturn(0f);
-        when(mQsFrame.getWidth()).thenReturn(1000);
-        when(mQsHeader.getTop()).thenReturn(0);
-        when(mQsHeader.getBottom()).thenReturn(1000);
-        NotificationPanelViewController.TouchHandler touchHandler =
-                mNotificationPanelViewController.createTouchHandler();
-
-        mNotificationPanelViewController.setExpandedFraction(1f);
-        touchHandler.onInterceptTouchEvent(
-                createMotionEvent(/* x= */ 0, /* y= */ 0, MotionEvent.ACTION_DOWN));
-        touchHandler.onInterceptTouchEvent(
-                createMotionEvent(/* x= */ 0, /* y= */ 500, MotionEvent.ACTION_MOVE));
-
-        assertThat(mNotificationPanelViewController.isQsTracking()).isTrue();
-    }
-
-    @Test
-    public void interceptTouchEvent_withinQs_shadeExpanded_inSplitShade_doesNotStartQsTracking() {
-        enableSplitShade(true);
-        mNotificationPanelViewController.setQs(mQs);
-        when(mQsFrame.getX()).thenReturn(0f);
-        when(mQsFrame.getWidth()).thenReturn(1000);
-        when(mQsHeader.getTop()).thenReturn(0);
-        when(mQsHeader.getBottom()).thenReturn(1000);
-        NotificationPanelViewController.TouchHandler touchHandler =
-                mNotificationPanelViewController.createTouchHandler();
-
-        mNotificationPanelViewController.setExpandedFraction(1f);
-        touchHandler.onInterceptTouchEvent(
-                createMotionEvent(/* x= */ 0, /* y= */ 0, MotionEvent.ACTION_DOWN));
-        touchHandler.onInterceptTouchEvent(
-                createMotionEvent(/* x= */ 0, /* y= */ 500, MotionEvent.ACTION_MOVE));
-
-        assertThat(mNotificationPanelViewController.isQsTracking()).isFalse();
-    }
-
-    @Test
     public void testOnAttachRefreshStatusBarState() {
         mStatusBarStateController.setState(KEYGUARD);
         when(mKeyguardStateController.isKeyguardFadingAway()).thenReturn(false);
@@ -1484,11 +1450,14 @@
         enableSplitShade(true);
         mNotificationPanelViewController.expandWithQs();
         when(mHeadsUpManager.isTrackingHeadsUp()).thenReturn(true);
+        when(mQsController.calculatePanelHeightExpanded(anyInt())).thenReturn(10000);
         mNotificationPanelViewController.setHeadsUpDraggingStartingHeight(
                 SPLIT_SHADE_FULL_TRANSITION_DISTANCE);
 
         int maxDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance();
 
+        // make sure we're ignoring the placeholder value for Qs max height
+        assertThat(maxDistance).isLessThan(10000);
         assertThat(maxDistance).isGreaterThan(SPLIT_SHADE_FULL_TRANSITION_DISTANCE);
     }
 
@@ -1515,95 +1484,26 @@
 
     @Test
     public void onLayoutChange_fullWidth_updatesQSWithFullWithTrue() {
-        mNotificationPanelViewController.setQs(mQs);
-
         setIsFullWidth(true);
 
-        verify(mQs).setIsNotificationPanelFullWidth(true);
+        verify(mQsController).setNotificationPanelFullWidth(true);
     }
 
     @Test
     public void onLayoutChange_notFullWidth_updatesQSWithFullWithFalse() {
-        mNotificationPanelViewController.setQs(mQs);
-
         setIsFullWidth(false);
 
-        verify(mQs).setIsNotificationPanelFullWidth(false);
+        verify(mQsController).setNotificationPanelFullWidth(false);
     }
 
     @Test
     public void onLayoutChange_qsNotSet_doesNotCrash() {
-        mNotificationPanelViewController.setQs(null);
+        mQuickSettingsController.setQs(null);
 
         triggerLayoutChange();
     }
 
     @Test
-    public void onQsFragmentAttached_fullWidth_setsFullWidthTrueOnQS() {
-        setIsFullWidth(true);
-        givenViewAttached();
-        mFragmentListener.onFragmentViewCreated(QS.TAG, mQSFragment);
-
-        verify(mQSFragment).setIsNotificationPanelFullWidth(true);
-    }
-
-    @Test
-    public void onQsFragmentAttached_notFullWidth_setsFullWidthFalseOnQS() {
-        setIsFullWidth(false);
-        givenViewAttached();
-        mFragmentListener.onFragmentViewCreated(QS.TAG, mQSFragment);
-
-        verify(mQSFragment).setIsNotificationPanelFullWidth(false);
-    }
-
-    @Test
-    public void setQsExpansion_lockscreenShadeTransitionInProgress_usesLockscreenSquishiness() {
-        float squishinessFraction = 0.456f;
-        mNotificationPanelViewController.setQs(mQs);
-        when(mLockscreenShadeTransitionController.getQsSquishTransitionFraction())
-                .thenReturn(squishinessFraction);
-        when(mNotificationStackScrollLayoutController.getNotificationSquishinessFraction())
-                .thenReturn(0.987f);
-        // Call setTransitionToFullShadeAmount to get into the full shade transition in progress
-        // state.
-        mNotificationPanelViewController.setTransitionToFullShadeAmount(
-                /* pxAmount= */ 234,
-                /* animate= */ false,
-                /* delay= */ 0
-        );
-
-        mNotificationPanelViewController.setQsExpansionHeight(/* height= */ 123);
-
-        // First for setTransitionToFullShadeAmount and then setQsExpansion
-        verify(mQs, times(2)).setQsExpansion(
-                /* expansion= */ anyFloat(),
-                /* panelExpansionFraction= */ anyFloat(),
-                /* proposedTranslation= */ anyFloat(),
-                eq(squishinessFraction)
-        );
-    }
-
-    @Test
-    public void setQsExpansion_lockscreenShadeTransitionNotInProgress_usesStandardSquishiness() {
-        float lsSquishinessFraction = 0.456f;
-        float nsslSquishinessFraction = 0.987f;
-        mNotificationPanelViewController.setQs(mQs);
-        when(mLockscreenShadeTransitionController.getQsSquishTransitionFraction())
-                .thenReturn(lsSquishinessFraction);
-        when(mNotificationStackScrollLayoutController.getNotificationSquishinessFraction())
-                .thenReturn(nsslSquishinessFraction);
-
-        mNotificationPanelViewController.setQsExpansionHeight(/* height= */ 123);
-
-        verify(mQs).setQsExpansion(
-                /* expansion= */ anyFloat(),
-                /* panelExpansionFraction= */ anyFloat(),
-                /* proposedTranslation= */ anyFloat(),
-                eq(nsslSquishinessFraction)
-        );
-    }
-
-    @Test
     public void onEmptySpaceClicked_notDozingAndOnKeyguard_requestsFaceAuth() {
         StatusBarStateController.StateListener statusBarStateListener =
                 mNotificationPanelViewController.getStatusBarStateListener();
@@ -1728,15 +1628,6 @@
         int transitionDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance();
         mNotificationPanelViewController.setExpandedHeight(transitionDistance);
         assertThat(mNotificationPanelViewController.isShadeFullyOpen()).isFalse();
-
-        // set maxQsExpansion in NPVC
-        int maxQsExpansion = 123;
-        mNotificationPanelViewController.setQs(mQs);
-        when(mQs.getDesiredHeight()).thenReturn(maxQsExpansion);
-        triggerLayoutChange();
-
-        mNotificationPanelViewController.setQsExpansionHeight(maxQsExpansion);
-        assertThat(mNotificationPanelViewController.isShadeFullyOpen()).isTrue();
     }
 
     @Test
@@ -1751,7 +1642,7 @@
     }
 
     private void triggerPositionClockAndNotifications() {
-        mNotificationPanelViewController.closeQs();
+        mNotificationPanelViewController.onQsSetExpansionHeightCalled(false);
     }
 
     private FalsingManager.FalsingTapListener getFalsingTapListener() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt
index bdafc7d..c915502a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt
@@ -13,8 +13,11 @@
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.fragments.FragmentHostManager
+import com.android.systemui.fragments.FragmentService
 import com.android.systemui.navigationbar.NavigationModeController
 import com.android.systemui.navigationbar.NavigationModeController.ModeChangedListener
+import com.android.systemui.plugins.qs.QS
 import com.android.systemui.recents.OverviewProxyService
 import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
 import com.android.systemui.util.concurrency.FakeExecutor
@@ -29,6 +32,7 @@
 import org.mockito.Mock
 import org.mockito.Mockito
 import org.mockito.Mockito.RETURNS_DEEP_STUBS
+import org.mockito.Mockito.any
 import org.mockito.Mockito.anyInt
 import org.mockito.Mockito.doNothing
 import org.mockito.Mockito.eq
@@ -69,6 +73,10 @@
     private lateinit var shadeExpansionStateManager: ShadeExpansionStateManager
     @Mock
     private lateinit var featureFlags: FeatureFlags
+    @Mock
+    private lateinit var fragmentService: FragmentService
+    @Mock
+    private lateinit var fragmentHostManager: FragmentHostManager
     @Captor
     lateinit var navigationModeCaptor: ArgumentCaptor<ModeChangedListener>
     @Captor
@@ -77,6 +85,8 @@
     lateinit var windowInsetsCallbackCaptor: ArgumentCaptor<Consumer<WindowInsets>>
     @Captor
     lateinit var constraintSetCaptor: ArgumentCaptor<ConstraintSet>
+    @Captor
+    lateinit var attachStateListenerCaptor: ArgumentCaptor<View.OnAttachStateChangeListener>
 
     private lateinit var controller: NotificationsQSContainerController
     private lateinit var navigationModeCallback: ModeChangedListener
@@ -91,8 +101,10 @@
         mContext.ensureTestableResources()
         whenever(notificationsQSContainer.context).thenReturn(mContext)
         whenever(notificationsQSContainer.resources).thenReturn(mContext.resources)
+        whenever(fragmentService.getFragmentHostManager(any())).thenReturn(fragmentHostManager)
         fakeSystemClock = FakeSystemClock()
         delayableExecutor = FakeExecutor(fakeSystemClock)
+
         controller = NotificationsQSContainerController(
                 notificationsQSContainer,
                 navigationModeController,
@@ -100,6 +112,7 @@
                 largeScreenShadeHeaderController,
                 shadeExpansionStateManager,
                 featureFlags,
+                fragmentService,
                 delayableExecutor
         )
 
@@ -114,9 +127,10 @@
         doNothing().`when`(notificationsQSContainer)
                 .setInsetsChangedListener(windowInsetsCallbackCaptor.capture())
         doNothing().`when`(notificationsQSContainer).applyConstraints(constraintSetCaptor.capture())
-
+        doNothing().`when`(notificationsQSContainer)
+            .addOnAttachStateChangeListener(attachStateListenerCaptor.capture())
         controller.init()
-        controller.onViewAttached()
+        attachStateListenerCaptor.value.onViewAttachedToWindow(notificationsQSContainer)
 
         navigationModeCallback = navigationModeCaptor.value
         taskbarVisibilityCallback = taskbarVisibilityCaptor.value
@@ -385,6 +399,7 @@
                 largeScreenShadeHeaderController,
                 shadeExpansionStateManager,
                 featureFlags,
+                fragmentService,
                 delayableExecutor
         )
         controller.updateConstraints()
@@ -426,6 +441,17 @@
         verify(largeScreenShadeHeaderController).startCustomizingAnimation(false, 100L)
     }
 
+    @Test
+    fun testTagListenerAdded() {
+        verify(fragmentHostManager).addTagListener(eq(QS.TAG), eq(notificationsQSContainer))
+    }
+
+    @Test
+    fun testTagListenerRemoved() {
+        attachStateListenerCaptor.value.onViewDetachedFromWindow(notificationsQSContainer)
+        verify(fragmentHostManager).removeTagListener(eq(QS.TAG), eq(notificationsQSContainer))
+    }
+
     private fun disableSplitShade() {
         setSplitShadeEnabled(false)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 4c76825..d229a08 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -21,7 +21,7 @@
 import android.view.MotionEvent
 import android.view.ViewGroup
 import androidx.test.filters.SmallTest
-import com.android.keyguard.KeyguardHostViewController
+import com.android.keyguard.KeyguardSecurityContainerController
 import com.android.keyguard.LockIconViewController
 import com.android.keyguard.dagger.KeyguardBouncerComponent
 import com.android.systemui.R
@@ -45,13 +45,16 @@
 import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
 import com.android.systemui.statusbar.window.StatusBarWindowStateController
+import com.android.systemui.util.mockito.any
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
 import org.mockito.Mock
+import org.mockito.Mockito
 import org.mockito.Mockito.anyFloat
+import org.mockito.Mockito.mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
@@ -102,9 +105,8 @@
     @Mock
     private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
     @Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory
-    @Mock lateinit var keyguardBouncerContainer: ViewGroup
     @Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent
-    @Mock lateinit var keyguardHostViewController: KeyguardHostViewController
+    @Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController
     @Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
 
     private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
@@ -116,6 +118,12 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         whenever(view.bottom).thenReturn(VIEW_BOTTOM)
+        whenever(view.findViewById<ViewGroup>(R.id.keyguard_bouncer_container))
+                .thenReturn(mock(ViewGroup::class.java))
+        whenever(keyguardBouncerComponentFactory.create(any(ViewGroup::class.java)))
+                .thenReturn(keyguardBouncerComponent)
+        whenever(keyguardBouncerComponent.securityContainerController)
+                .thenReturn(keyguardSecurityContainerController)
         underTest = NotificationShadeWindowViewController(
             lockscreenShadeTransitionController,
             FalsingCollectorFake(),
@@ -275,6 +283,7 @@
 
     @Test
     fun testGetBouncerContainer() {
+        Mockito.clearInvocations(view)
         underTest.bouncerContainer
         verify(view).findViewById<ViewGroup>(R.id.keyguard_bouncer_container)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
index d435624..5e9c219 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
@@ -29,9 +29,11 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.MotionEvent;
+import android.view.ViewGroup;
 
 import androidx.test.filters.SmallTest;
 
+import com.android.keyguard.KeyguardSecurityContainerController;
 import com.android.keyguard.LockIconViewController;
 import com.android.keyguard.dagger.KeyguardBouncerComponent;
 import com.android.systemui.R;
@@ -94,6 +96,8 @@
     @Mock private FeatureFlags mFeatureFlags;
     @Mock private KeyguardBouncerViewModel mKeyguardBouncerViewModel;
     @Mock private KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory;
+    @Mock private KeyguardBouncerComponent mKeyguardBouncerComponent;
+    @Mock private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
     @Mock private NotificationInsetsController mNotificationInsetsController;
     @Mock private AlternateBouncerInteractor mAlternateBouncerInteractor;
     @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@@ -110,6 +114,12 @@
         when(mView.findViewById(R.id.notification_stack_scroller))
                 .thenReturn(mNotificationStackScrollLayout);
 
+        when(mView.findViewById(R.id.keyguard_bouncer_container)).thenReturn(mock(ViewGroup.class));
+        when(mKeyguardBouncerComponentFactory.create(any(ViewGroup.class))).thenReturn(
+                mKeyguardBouncerComponent);
+        when(mKeyguardBouncerComponent.getSecurityContainerController()).thenReturn(
+                mKeyguardSecurityContainerController);
+
         when(mStatusBarStateController.isDozing()).thenReturn(false);
         mDependency.injectTestDependency(ShadeController.class, mShadeController);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt
new file mode 100644
index 0000000..b547318
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt
@@ -0,0 +1,100 @@
+package com.android.systemui.shade
+
+import android.content.Context
+import android.content.res.Resources
+import android.graphics.Rect
+import android.testing.AndroidTestingRunner
+import android.view.DisplayCutout
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.battery.BatteryMeterView
+import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class QsBatteryModeControllerTest : SysuiTestCase() {
+
+    private companion object {
+        val CENTER_TOP_CUTOUT: DisplayCutout =
+            mock<DisplayCutout>().also {
+                whenever(it.boundingRectTop).thenReturn(Rect(10, 0, 20, 10))
+            }
+
+        const val MOTION_LAYOUT_MAX_FRAME = 100
+        const val QQS_START_FRAME = 14
+        const val QS_END_FRAME = 58
+    }
+
+    @JvmField @Rule val mockitoRule = MockitoJUnit.rule()!!
+
+    @Mock private lateinit var insetsProvider: StatusBarContentInsetsProvider
+    @Mock private lateinit var mockedContext: Context
+    @Mock private lateinit var mockedResources: Resources
+
+    private lateinit var controller: QsBatteryModeController // under test
+
+    @Before
+    fun setup() {
+        whenever(mockedContext.resources).thenReturn(mockedResources)
+        whenever(mockedResources.getInteger(R.integer.fade_in_start_frame)).thenReturn(QS_END_FRAME)
+        whenever(mockedResources.getInteger(R.integer.fade_out_complete_frame))
+            .thenReturn(QQS_START_FRAME)
+
+        controller = QsBatteryModeController(mockedContext, insetsProvider)
+    }
+
+    @Test
+    fun `returns MODE_ON for qqs with center cutout`() {
+        assertThat(
+                controller.getBatteryMode(CENTER_TOP_CUTOUT, QQS_START_FRAME.prevFrameToFraction())
+            )
+            .isEqualTo(BatteryMeterView.MODE_ON)
+    }
+
+    @Test
+    fun `returns MODE_ESTIMATE for qs with center cutout`() {
+        assertThat(controller.getBatteryMode(CENTER_TOP_CUTOUT, QS_END_FRAME.nextFrameToFraction()))
+            .isEqualTo(BatteryMeterView.MODE_ESTIMATE)
+    }
+
+    @Test
+    fun `returns MODE_ON for qqs with corner cutout`() {
+        whenever(insetsProvider.currentRotationHasCornerCutout()).thenReturn(true)
+
+        assertThat(
+                controller.getBatteryMode(CENTER_TOP_CUTOUT, QQS_START_FRAME.prevFrameToFraction())
+            )
+            .isEqualTo(BatteryMeterView.MODE_ESTIMATE)
+    }
+
+    @Test
+    fun `returns MODE_ESTIMATE for qs with corner cutout`() {
+        whenever(insetsProvider.currentRotationHasCornerCutout()).thenReturn(true)
+
+        assertThat(controller.getBatteryMode(CENTER_TOP_CUTOUT, QS_END_FRAME.nextFrameToFraction()))
+            .isEqualTo(BatteryMeterView.MODE_ESTIMATE)
+    }
+
+    @Test
+    fun `returns null in-between`() {
+        assertThat(
+                controller.getBatteryMode(CENTER_TOP_CUTOUT, QQS_START_FRAME.nextFrameToFraction())
+            )
+            .isNull()
+        assertThat(controller.getBatteryMode(CENTER_TOP_CUTOUT, QS_END_FRAME.prevFrameToFraction()))
+            .isNull()
+    }
+
+    private fun Int.prevFrameToFraction(): Float = (this - 1) / MOTION_LAYOUT_MAX_FRAME.toFloat()
+    private fun Int.nextFrameToFraction(): Float = (this + 1) / MOTION_LAYOUT_MAX_FRAME.toFloat()
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
new file mode 100644
index 0000000..c2fca6f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2022 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.shade;
+
+import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+import static com.android.systemui.statusbar.StatusBarState.SHADE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Looper;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.MotionEvent;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.view.accessibility.AccessibilityManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.jank.InteractionJankMonitor;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
+import com.android.keyguard.KeyguardStatusView;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.media.controls.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.ui.MediaHierarchyManager;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.qs.QSFragment;
+import com.android.systemui.screenrecord.RecordingController;
+import com.android.systemui.shade.transition.ShadeTransitionController;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationShadeDepthController;
+import com.android.systemui.statusbar.PulseExpansionHandler;
+import com.android.systemui.statusbar.QsFrameTranslateController;
+import com.android.systemui.statusbar.StatusBarStateControllerImpl;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.notification.stack.AmbientState;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
+import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
+import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
+import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import dagger.Lazy;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class QuickSettingsControllerTest extends SysuiTestCase {
+
+    private static final int SPLIT_SHADE_FULL_TRANSITION_DISTANCE = 400;
+
+    private QuickSettingsController mQsController;
+
+    @Mock private Resources mResources;
+    @Mock private KeyguardBottomAreaView mQsFrame;
+    @Mock private KeyguardStatusBarView mKeyguardStatusBar;
+    @Mock private QS mQs;
+    @Mock private QSFragment mQSFragment;
+
+    @Mock private Lazy<NotificationPanelViewController> mPanelViewControllerLazy;
+    @Mock private NotificationPanelViewController mNotificationPanelViewController;
+    @Mock private NotificationPanelView mPanelView;
+    @Mock private ViewGroup mQsHeader;
+    @Mock private ViewParent mPanelViewParent;
+    @Mock private QsFrameTranslateController mQsFrameTranslateController;
+    @Mock private ShadeTransitionController mShadeTransitionController;
+    @Mock private PulseExpansionHandler mPulseExpansionHandler;
+    @Mock private NotificationRemoteInputManager mNotificationRemoteInputManager;
+    @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+    @Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
+    @Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+    @Mock private NotificationShadeDepthController mNotificationShadeDepthController;
+    @Mock private LargeScreenShadeHeaderController mLargeScreenShadeHeaderController;
+    @Mock private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
+    @Mock private KeyguardStateController mKeyguardStateController;
+    @Mock private KeyguardBypassController mKeyguardBypassController;
+    @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    @Mock private ScrimController mScrimController;
+    @Mock private MediaDataManager mMediaDataManager;
+    @Mock private MediaHierarchyManager mMediaHierarchyManager;
+    @Mock private AmbientState mAmbientState;
+    @Mock private RecordingController mRecordingController;
+    @Mock private FalsingManager mFalsingManager;
+    @Mock private FalsingCollector mFalsingCollector;
+    @Mock private AccessibilityManager mAccessibilityManager;
+    @Mock private LockscreenGestureLogger mLockscreenGestureLogger;
+    @Mock private MetricsLogger mMetricsLogger;
+    @Mock private FeatureFlags mFeatureFlags;
+    @Mock private InteractionJankMonitor mInteractionJankMonitor;
+    @Mock private ShadeLogger mShadeLogger;
+
+    @Mock private DumpManager mDumpManager;
+    @Mock private DozeParameters mDozeParameters;
+    @Mock private ScreenOffAnimationController mScreenOffAnimationController;
+    @Mock private HeadsUpManagerPhone mHeadsUpManager;
+    @Mock private UiEventLogger mUiEventLogger;
+
+    private SysuiStatusBarStateController mStatusBarStateController;
+
+    private Handler mMainHandler;
+    private LockscreenShadeTransitionController.Callback mLockscreenShadeTransitionCallback;
+
+    private final ShadeExpansionStateManager mShadeExpansionStateManager =
+            new ShadeExpansionStateManager();
+
+    private FragmentHostManager.FragmentListener mFragmentListener;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        when(mPanelViewControllerLazy.get()).thenReturn(mNotificationPanelViewController);
+        mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager,
+                mInteractionJankMonitor, mShadeExpansionStateManager);
+
+        KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext);
+        keyguardStatusView.setId(R.id.keyguard_status_view);
+
+        when(mPanelView.getResources()).thenReturn(mResources);
+        when(mPanelView.getContext()).thenReturn(getContext());
+        when(mPanelView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar);
+        when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(1000);
+        when(mPanelView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame);
+        when(mPanelView.findViewById(R.id.keyguard_status_view))
+                .thenReturn(mock(KeyguardStatusView.class));
+        when(mQs.getView()).thenReturn(mPanelView);
+        when(mQSFragment.getView()).thenReturn(mPanelView);
+
+        when(mNotificationRemoteInputManager.isRemoteInputActive())
+                .thenReturn(false);
+        when(mInteractionJankMonitor.begin(any(), anyInt()))
+                .thenReturn(true);
+        when(mInteractionJankMonitor.end(anyInt()))
+                .thenReturn(true);
+
+        when(mPanelView.getParent()).thenReturn(mPanelViewParent);
+        when(mQs.getHeader()).thenReturn(mQsHeader);
+
+        doAnswer(invocation -> {
+            mLockscreenShadeTransitionCallback = invocation.getArgument(0);
+            return null;
+        }).when(mLockscreenShadeTransitionController).addCallback(any());
+
+
+        mMainHandler = new Handler(Looper.getMainLooper());
+
+        mQsController = new QuickSettingsController(
+                mPanelViewControllerLazy,
+                mPanelView,
+                mQsFrameTranslateController,
+                mShadeTransitionController,
+                mPulseExpansionHandler,
+                mNotificationRemoteInputManager,
+                mShadeExpansionStateManager,
+                mStatusBarKeyguardViewManager,
+                mNotificationStackScrollLayoutController,
+                mLockscreenShadeTransitionController,
+                mNotificationShadeDepthController,
+                mLargeScreenShadeHeaderController,
+                mStatusBarTouchableRegionManager,
+                mKeyguardStateController,
+                mKeyguardBypassController,
+                mKeyguardUpdateMonitor,
+                mScrimController,
+                mMediaDataManager,
+                mMediaHierarchyManager,
+                mAmbientState,
+                mRecordingController,
+                mFalsingManager,
+                mFalsingCollector,
+                mAccessibilityManager,
+                mLockscreenGestureLogger,
+                mMetricsLogger,
+                mFeatureFlags,
+                mInteractionJankMonitor,
+                mShadeLogger
+        );
+
+        mFragmentListener = mQsController.getQsFragmentListener();
+    }
+
+    @After
+    public void tearDown() {
+        mNotificationPanelViewController.cancelHeightAnimator();
+        mMainHandler.removeCallbacksAndMessages(null);
+    }
+
+    @Test
+    public void testOnTouchEvent_isConflictingExpansionGestureSet() {
+        assertThat(mQsController.isConflictingExpansionGesture()).isFalse();
+        mShadeExpansionStateManager.onPanelExpansionChanged(1f, true, false, 0f);
+        mQsController.handleTouch(MotionEvent.obtain(0L /* downTime */,
+                0L /* eventTime */, MotionEvent.ACTION_DOWN, 0f /* x */, 0f /* y */,
+                0 /* metaState */), false, false);
+        assertThat(mQsController.isConflictingExpansionGesture()).isTrue();
+    }
+
+    @Test
+    public void testCloseQsSideEffects() {
+        enableSplitShade(true);
+        mQsController.setExpandImmediate(true);
+        mQsController.setExpanded(true);
+        mQsController.closeQs();
+
+        assertThat(mQsController.getExpanded()).isEqualTo(false);
+        assertThat(mQsController.isExpandImmediate()).isEqualTo(false);
+    }
+
+    @Test
+    public void testLargeScreenHeaderMadeActiveForLargeScreen() {
+        mStatusBarStateController.setState(SHADE);
+        when(mResources.getBoolean(R.bool.config_use_large_screen_shade_header)).thenReturn(true);
+        mQsController.updateResources();
+        verify(mLargeScreenShadeHeaderController).setLargeScreenActive(true);
+
+        when(mResources.getBoolean(R.bool.config_use_large_screen_shade_header)).thenReturn(false);
+        mQsController.updateResources();
+        verify(mLargeScreenShadeHeaderController).setLargeScreenActive(false);
+    }
+
+    @Test
+    public void testPanelStaysOpenWhenClosingQs() {
+        mShadeExpansionStateManager.onPanelExpansionChanged(/* fraction= */ 1,
+                /* expanded= */ true, /* tracking= */ false, /* dragDownPxAmount= */ 0);
+        mNotificationPanelViewController.setExpandedFraction(1f);
+
+        float shadeExpandedHeight = mQsController.getShadeExpandedHeight();
+        mQsController.animateCloseQs(false);
+
+        assertThat(mQsController.getShadeExpandedHeight()).isEqualTo(shadeExpandedHeight);
+    }
+
+    @Test
+    public void interceptTouchEvent_withinQs_shadeExpanded_startsQsTracking() {
+        mQsController.setQs(mQs);
+        when(mQsFrame.getX()).thenReturn(0f);
+        when(mQsFrame.getWidth()).thenReturn(1000);
+        when(mQsHeader.getTop()).thenReturn(0);
+        when(mQsHeader.getBottom()).thenReturn(1000);
+
+        mQsController.setShadeExpandedHeight(1f);
+        mQsController.onIntercept(
+                createMotionEvent(0, 0, MotionEvent.ACTION_DOWN));
+        mQsController.onIntercept(
+                createMotionEvent(0, 500, MotionEvent.ACTION_MOVE));
+
+        assertThat(mQsController.isTracking()).isTrue();
+    }
+
+    @Test
+    public void interceptTouchEvent_withinQs_shadeExpanded_inSplitShade_doesNotStartQsTracking() {
+        enableSplitShade(true);
+        mQsController.setQs(mQs);
+        when(mQsFrame.getX()).thenReturn(0f);
+        when(mQsFrame.getWidth()).thenReturn(1000);
+        when(mQsHeader.getTop()).thenReturn(0);
+        when(mQsHeader.getBottom()).thenReturn(1000);
+
+        mQsController.setShadeExpandedHeight(1f);
+        mQsController.onIntercept(
+                createMotionEvent(0, 0, MotionEvent.ACTION_DOWN));
+        mQsController.onIntercept(
+                createMotionEvent(0, 500, MotionEvent.ACTION_MOVE));
+
+        assertThat(mQsController.isTracking()).isFalse();
+    }
+
+    @Test
+    public void onQsFragmentAttached_fullWidth_setsFullWidthTrueOnQS() {
+        setIsFullWidth(true);
+        mFragmentListener.onFragmentViewCreated(QS.TAG, mQSFragment);
+
+        verify(mQSFragment).setIsNotificationPanelFullWidth(true);
+    }
+
+    @Test
+    public void onQsFragmentAttached_notFullWidth_setsFullWidthFalseOnQS() {
+        setIsFullWidth(false);
+        mFragmentListener.onFragmentViewCreated(QS.TAG, mQSFragment);
+
+        verify(mQSFragment).setIsNotificationPanelFullWidth(false);
+    }
+
+    @Test
+    public void getMaxPanelTransitionDistance_inSplitShade_withHeadsUp_returnsBiggerValue() {
+        enableSplitShade(true);
+        mNotificationPanelViewController.expandWithQs();
+        when(mHeadsUpManager.isTrackingHeadsUp()).thenReturn(true);
+
+        mNotificationPanelViewController.setHeadsUpDraggingStartingHeight(
+                SPLIT_SHADE_FULL_TRANSITION_DISTANCE);
+
+        assertThat(mQsController.calculatePanelHeightExpanded(0))
+                .isGreaterThan(SPLIT_SHADE_FULL_TRANSITION_DISTANCE);
+    }
+
+    @Test
+    public void setQsExpansion_lockscreenShadeTransitionInProgress_usesLockscreenSquishiness() {
+        float squishinessFraction = 0.456f;
+        mQsController.setQs(mQs);
+        when(mLockscreenShadeTransitionController.getQsSquishTransitionFraction())
+                .thenReturn(squishinessFraction);
+        when(mNotificationStackScrollLayoutController.getNotificationSquishinessFraction())
+                .thenReturn(0.987f);
+        // Call setTransitionToFullShadeAmount to get into the full shade transition in progress
+        // state.
+        mLockscreenShadeTransitionCallback.setTransitionToFullShadeAmount(234, false, 0);
+
+        mQsController.setExpansionHeight(123);
+
+        // First for setTransitionToFullShadeAmount and then setQsExpansion
+        verify(mQs, times(2)).setQsExpansion(anyFloat(), anyFloat(), anyFloat(),
+                eq(squishinessFraction)
+        );
+    }
+
+    @Test
+    public void setQsExpansion_lockscreenShadeTransitionNotInProgress_usesStandardSquishiness() {
+        float lsSquishinessFraction = 0.456f;
+        float nsslSquishinessFraction = 0.987f;
+        mQsController.setQs(mQs);
+        when(mLockscreenShadeTransitionController.getQsSquishTransitionFraction())
+                .thenReturn(lsSquishinessFraction);
+        when(mNotificationStackScrollLayoutController.getNotificationSquishinessFraction())
+                .thenReturn(nsslSquishinessFraction);
+
+        mQsController.setExpansionHeight(123);
+
+        verify(mQs).setQsExpansion(anyFloat(), anyFloat(), anyFloat(), eq(nsslSquishinessFraction)
+        );
+    }
+
+    @Test
+    public void shadeExpanded_onKeyguard() {
+        mStatusBarStateController.setState(KEYGUARD);
+        // set maxQsExpansion in NPVC
+        int maxQsExpansion = 123;
+        mQsController.setQs(mQs);
+        when(mQs.getDesiredHeight()).thenReturn(maxQsExpansion);
+
+        int oldMaxHeight = mQsController.updateHeightsOnShadeLayoutChange();
+        mQsController.handleShadeLayoutChanged(oldMaxHeight);
+
+        mQsController.setExpansionHeight(maxQsExpansion);
+        assertThat(mQsController.computeExpansionFraction()).isEqualTo(1f);
+    }
+
+    private static MotionEvent createMotionEvent(int x, int y, int action) {
+        return MotionEvent.obtain(
+                /* downTime= */ 0, /* eventTime= */ 0, action, x, y, /* metaState= */ 0);
+    }
+
+    private void enableSplitShade(boolean enabled) {
+        when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(enabled);
+        mQsController.updateResources();
+    }
+
+    private void setIsFullWidth(boolean fullWidth) {
+        mQsController.setNotificationPanelFullWidth(fullWidth);
+        triggerLayoutChange();
+    }
+
+    private void triggerLayoutChange() {
+        int oldMaxHeight = mQsController.updateHeightsOnShadeLayoutChange();
+        mQsController.handleShadeLayoutChanged(oldMaxHeight);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index 4d7741ad..26eff61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -18,8 +18,6 @@
 import android.content.ContentResolver
 import android.content.Context
 import android.graphics.drawable.Drawable
-import android.os.Handler
-import android.os.UserHandle
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -27,12 +25,16 @@
 import com.android.systemui.plugins.ClockId
 import com.android.systemui.plugins.ClockMetadata
 import com.android.systemui.plugins.ClockProviderPlugin
+import com.android.systemui.plugins.ClockSettings
 import com.android.systemui.plugins.PluginListener
 import com.android.systemui.plugins.PluginManager
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.eq
 import junit.framework.Assert.assertEquals
 import junit.framework.Assert.fail
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
 import org.json.JSONException
 import org.junit.Before
 import org.junit.Rule
@@ -48,19 +50,19 @@
 class ClockRegistryTest : SysuiTestCase() {
 
     @JvmField @Rule val mockito = MockitoJUnit.rule()
+    private lateinit var dispatcher: CoroutineDispatcher
+    private lateinit var scope: TestScope
+
     @Mock private lateinit var mockContext: Context
     @Mock private lateinit var mockPluginManager: PluginManager
     @Mock private lateinit var mockClock: ClockController
     @Mock private lateinit var mockDefaultClock: ClockController
     @Mock private lateinit var mockThumbnail: Drawable
-    @Mock private lateinit var mockHandler: Handler
     @Mock private lateinit var mockContentResolver: ContentResolver
     private lateinit var fakeDefaultProvider: FakeClockPlugin
     private lateinit var pluginListener: PluginListener<ClockProviderPlugin>
     private lateinit var registry: ClockRegistry
 
-    private var settingValue: String = ""
-
     companion object {
         private fun failFactory(): ClockController {
             fail("Unexpected call to createClock")
@@ -79,7 +81,8 @@
         private val thumbnailCallbacks = mutableMapOf<ClockId, () -> Drawable?>()
 
         override fun getClocks() = metadata
-        override fun createClock(id: ClockId): ClockController = createCallbacks[id]!!()
+        override fun createClock(settings: ClockSettings): ClockController =
+            createCallbacks[settings.clockId!!]!!()
         override fun getClockThumbnail(id: ClockId): Drawable? = thumbnailCallbacks[id]!!()
 
         fun addClock(
@@ -97,6 +100,9 @@
 
     @Before
     fun setUp() {
+        dispatcher = StandardTestDispatcher()
+        scope = TestScope(dispatcher)
+
         fakeDefaultProvider = FakeClockPlugin()
             .addClock(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME, { mockDefaultClock }, { mockThumbnail })
         whenever(mockContext.contentResolver).thenReturn(mockContentResolver)
@@ -105,15 +111,22 @@
         registry = object : ClockRegistry(
             mockContext,
             mockPluginManager,
-            mockHandler,
+            scope = scope.backgroundScope,
+            mainDispatcher = dispatcher,
+            bgDispatcher = dispatcher,
             isEnabled = true,
-            userHandle = UserHandle.USER_ALL,
-            defaultClockProvider = fakeDefaultProvider
+            handleAllUsers = true,
+            defaultClockProvider = fakeDefaultProvider,
         ) {
-            override var currentClockId: ClockId
-                get() = settingValue
-                set(value) { settingValue = value }
+            override fun querySettings() { }
+            override fun applySettings(value: ClockSettings?) {
+                settings = value
+            }
+            // Unit Test does not validate threading
+            override fun assertMainThread() {}
+            override fun assertNotMainThread() {}
         }
+        registry.registerListeners()
 
         verify(mockPluginManager)
             .addPluginListener(captor.capture(), eq(ClockProviderPlugin::class.java), eq(true))
@@ -185,16 +198,16 @@
             .addClock("clock_1", "clock 1")
             .addClock("clock_2", "clock 2")
 
-        settingValue = "clock_3"
         val plugin2 = FakeClockPlugin()
             .addClock("clock_3", "clock 3", { mockClock })
             .addClock("clock_4", "clock 4")
 
+        registry.applySettings(ClockSettings("clock_3", null))
         pluginListener.onPluginConnected(plugin1, mockContext)
         pluginListener.onPluginConnected(plugin2, mockContext)
 
         val clock = registry.createCurrentClock()
-        assertEquals(clock, mockClock)
+        assertEquals(mockClock, clock)
     }
 
     @Test
@@ -203,11 +216,11 @@
             .addClock("clock_1", "clock 1")
             .addClock("clock_2", "clock 2")
 
-        settingValue = "clock_3"
         val plugin2 = FakeClockPlugin()
             .addClock("clock_3", "clock 3")
             .addClock("clock_4", "clock 4")
 
+        registry.applySettings(ClockSettings("clock_3", null))
         pluginListener.onPluginConnected(plugin1, mockContext)
         pluginListener.onPluginConnected(plugin2, mockContext)
         pluginListener.onPluginDisconnected(plugin2)
@@ -217,33 +230,49 @@
     }
 
     @Test
-    fun pluginRemoved_clockChanged() {
+    fun pluginRemoved_clockAndListChanged() {
         val plugin1 = FakeClockPlugin()
             .addClock("clock_1", "clock 1")
             .addClock("clock_2", "clock 2")
 
-        settingValue = "clock_3"
         val plugin2 = FakeClockPlugin()
             .addClock("clock_3", "clock 3", { mockClock })
             .addClock("clock_4", "clock 4")
 
-        pluginListener.onPluginConnected(plugin1, mockContext)
-        pluginListener.onPluginConnected(plugin2, mockContext)
 
         var changeCallCount = 0
-        registry.registerClockChangeListener { changeCallCount++ }
+        var listChangeCallCount = 0
+        registry.registerClockChangeListener(object : ClockRegistry.ClockChangeListener {
+            override fun onCurrentClockChanged() { changeCallCount++ }
+            override fun onAvailableClocksChanged() { listChangeCallCount++ }
+        })
+
+        registry.applySettings(ClockSettings("clock_3", null))
+        assertEquals(0, changeCallCount)
+        assertEquals(0, listChangeCallCount)
+
+        pluginListener.onPluginConnected(plugin1, mockContext)
+        assertEquals(0, changeCallCount)
+        assertEquals(1, listChangeCallCount)
+
+        pluginListener.onPluginConnected(plugin2, mockContext)
+        assertEquals(1, changeCallCount)
+        assertEquals(2, listChangeCallCount)
 
         pluginListener.onPluginDisconnected(plugin1)
-        assertEquals(0, changeCallCount)
+        assertEquals(1, changeCallCount)
+        assertEquals(3, listChangeCallCount)
 
         pluginListener.onPluginDisconnected(plugin2)
-        assertEquals(1, changeCallCount)
+        assertEquals(2, changeCallCount)
+        assertEquals(4, listChangeCallCount)
     }
 
+
     @Test
     fun jsonDeserialization_gotExpectedObject() {
-        val expected = ClockRegistry.ClockSetting("ID", 500)
-        val actual = ClockRegistry.ClockSetting.deserialize("""{
+        val expected = ClockSettings("ID", null).apply { _applied_timestamp = 500 }
+        val actual = ClockSettings.deserialize("""{
             "clockId":"ID",
             "_applied_timestamp":500
         }""")
@@ -252,15 +281,15 @@
 
     @Test
     fun jsonDeserialization_noTimestamp_gotExpectedObject() {
-        val expected = ClockRegistry.ClockSetting("ID", null)
-        val actual = ClockRegistry.ClockSetting.deserialize("{\"clockId\":\"ID\"}")
+        val expected = ClockSettings("ID", null)
+        val actual = ClockSettings.deserialize("{\"clockId\":\"ID\"}")
         assertEquals(expected, actual)
     }
 
     @Test
     fun jsonDeserialization_nullTimestamp_gotExpectedObject() {
-        val expected = ClockRegistry.ClockSetting("ID", null)
-        val actual = ClockRegistry.ClockSetting.deserialize("""{
+        val expected = ClockSettings("ID", null)
+        val actual = ClockSettings.deserialize("""{
             "clockId":"ID",
             "_applied_timestamp":null
         }""")
@@ -269,22 +298,23 @@
 
     @Test(expected = JSONException::class)
     fun jsonDeserialization_noId_threwException() {
-        val expected = ClockRegistry.ClockSetting("ID", 500)
-        val actual = ClockRegistry.ClockSetting.deserialize("{\"_applied_timestamp\":500}")
+        val expected = ClockSettings(null, null).apply { _applied_timestamp = 500 }
+        val actual = ClockSettings.deserialize("{\"_applied_timestamp\":500}")
         assertEquals(expected, actual)
     }
 
     @Test
     fun jsonSerialization_gotExpectedString() {
         val expected = "{\"clockId\":\"ID\",\"_applied_timestamp\":500}"
-        val actual = ClockRegistry.ClockSetting.serialize( ClockRegistry.ClockSetting("ID", 500))
+        val actual = ClockSettings.serialize(ClockSettings("ID", null)
+            .apply { _applied_timestamp = 500 })
         assertEquals(expected, actual)
     }
 
     @Test
     fun jsonSerialization_noTimestamp_gotExpectedString() {
         val expected = "{\"clockId\":\"ID\"}"
-        val actual = ClockRegistry.ClockSetting.serialize( ClockRegistry.ClockSetting("ID", null))
+        val actual = ClockSettings.serialize(ClockSettings("ID", null))
         assertEquals(expected, actual)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
index a7588dd..cd2efc0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
@@ -114,7 +114,8 @@
     @Test
     fun defaultClock_events_onTimeTick() {
         val clock = provider.createClock(DEFAULT_CLOCK_ID)
-        clock.events.onTimeTick()
+        clock.smallClock.events.onTimeTick()
+        clock.largeClock.events.onTimeTick()
 
         verify(mockSmallClockView).refreshTime()
         verify(mockLargeClockView).refreshTime()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java
index 7693fee..aa1636d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java
@@ -249,6 +249,21 @@
     }
 
     @Test
+    public void addCallback_preCondition_noConditions_reportAllConditionsMet() {
+        final Monitor
+                monitor = new Monitor(mExecutor, new HashSet<>(Arrays.asList(mCondition1)));
+        final Monitor.Callback callback = mock(
+                Monitor.Callback.class);
+
+        monitor.addSubscription(new Monitor.Subscription.Builder(callback).build());
+        mExecutor.runAllReady();
+        verify(callback, never()).onConditionsChanged(true);
+        mCondition1.fakeUpdateCondition(true);
+        mExecutor.runAllReady();
+        verify(callback).onConditionsChanged(true);
+    }
+
+    @Test
     public void removeCallback_noFailureOnDoubleRemove() {
         final Condition condition = mock(
                 Condition.class);
@@ -471,4 +486,142 @@
         mExecutor.runAllReady();
         verify(callback).onConditionsChanged(true);
     }
+
+    /**
+     * Ensures that the result of a condition being true leads to its nested condition being
+     * activated.
+     */
+    @Test
+    public void testNestedCondition() {
+        mCondition1.fakeUpdateCondition(false);
+        final Monitor.Callback callback =
+                mock(Monitor.Callback.class);
+
+        mCondition2.fakeUpdateCondition(false);
+
+        // Create a nested condition
+        mConditionMonitor.addSubscription(new Monitor.Subscription.Builder(
+                new Monitor.Subscription.Builder(callback)
+                        .addCondition(mCondition2)
+                        .build())
+                .addCondition(mCondition1)
+                .build());
+
+        mExecutor.runAllReady();
+
+        // Ensure the nested condition callback is not called at all.
+        verify(callback, never()).onActiveChanged(anyBoolean());
+        verify(callback, never()).onConditionsChanged(anyBoolean());
+
+        // Update the inner condition to true and ensure that the nested condition is not triggered.
+        mCondition2.fakeUpdateCondition(true);
+        verify(callback, never()).onConditionsChanged(anyBoolean());
+        mCondition2.fakeUpdateCondition(false);
+
+        // Set outer condition and make sure the inner condition becomes active and reports that
+        // conditions aren't met
+        mCondition1.fakeUpdateCondition(true);
+        mExecutor.runAllReady();
+
+        verify(callback).onActiveChanged(eq(true));
+        verify(callback).onConditionsChanged(eq(false));
+
+        Mockito.clearInvocations(callback);
+
+        // Update the inner condition and make sure the callback is updated.
+        mCondition2.fakeUpdateCondition(true);
+        mExecutor.runAllReady();
+
+        verify(callback).onConditionsChanged(true);
+
+        Mockito.clearInvocations(callback);
+        // Invalidate outer condition and make sure callback is informed, but the last state is
+        // not affected.
+        mCondition1.fakeUpdateCondition(false);
+        mExecutor.runAllReady();
+
+        verify(callback).onActiveChanged(eq(false));
+        verify(callback, never()).onConditionsChanged(anyBoolean());
+    }
+
+    /**
+     * Ensures a subscription is predicated on its precondition.
+     */
+    @Test
+    public void testPrecondition() {
+        mCondition1.fakeUpdateCondition(false);
+        final Monitor.Callback callback =
+                mock(Monitor.Callback.class);
+
+        mCondition2.fakeUpdateCondition(false);
+
+        // Create a nested condition
+        mConditionMonitor.addSubscription(new Monitor.Subscription.Builder(callback)
+                .addPrecondition(mCondition1)
+                .addCondition(mCondition2)
+                .build());
+
+        mExecutor.runAllReady();
+
+        // Ensure the nested condition callback is not called at all.
+        verify(callback, never()).onActiveChanged(anyBoolean());
+        verify(callback, never()).onConditionsChanged(anyBoolean());
+
+        // Update the condition to true and ensure that the nested condition is not triggered.
+        mCondition2.fakeUpdateCondition(true);
+        verify(callback, never()).onConditionsChanged(anyBoolean());
+        mCondition2.fakeUpdateCondition(false);
+
+        // Set precondition and make sure the inner condition becomes active and reports that
+        // conditions aren't met
+        mCondition1.fakeUpdateCondition(true);
+        mExecutor.runAllReady();
+
+        verify(callback).onActiveChanged(eq(true));
+        verify(callback).onConditionsChanged(eq(false));
+
+        Mockito.clearInvocations(callback);
+
+        // Update the condition and make sure the callback is updated.
+        mCondition2.fakeUpdateCondition(true);
+        mExecutor.runAllReady();
+
+        verify(callback).onConditionsChanged(true);
+
+        Mockito.clearInvocations(callback);
+        // Invalidate precondition and make sure callback is informed, but the last state is
+        // not affected.
+        mCondition1.fakeUpdateCondition(false);
+        mExecutor.runAllReady();
+
+        verify(callback).onActiveChanged(eq(false));
+        verify(callback, never()).onConditionsChanged(anyBoolean());
+    }
+
+    /**
+     * Ensure preconditions are applied to every subscription added to a monitor.
+     */
+    @Test
+    public void testPreconditionMonitor() {
+        final Monitor.Callback callback =
+                mock(Monitor.Callback.class);
+
+        mCondition2.fakeUpdateCondition(true);
+        final Monitor monitor = new Monitor(mExecutor, new HashSet<>(Arrays.asList(mCondition1)));
+
+        monitor.addSubscription(new Monitor.Subscription.Builder(callback)
+                .addCondition(mCondition2)
+                .build());
+
+        mExecutor.runAllReady();
+
+        verify(callback, never()).onActiveChanged(anyBoolean());
+        verify(callback, never()).onConditionsChanged(anyBoolean());
+
+        mCondition1.fakeUpdateCondition(true);
+        mExecutor.runAllReady();
+
+        verify(callback).onActiveChanged(eq(true));
+        verify(callback).onConditionsChanged(eq(true));
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
index c5432c5..a280510 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
@@ -99,8 +99,6 @@
 
         override fun setPrimaryTextColor(color: Int) {}
 
-        override fun setIsDreaming(isDreaming: Boolean) {}
-
         override fun setUiSurface(uiSurface: String) {}
 
         override fun setDozeAmount(amount: Float) {}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt
index d29e9a6..fa7d869 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt
@@ -20,8 +20,6 @@
 import android.testing.TestableLooper
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.smartspace.preconditions.LockscreenPrecondition
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
 import com.android.systemui.util.concurrency.Execution
@@ -41,9 +39,6 @@
 @TestableLooper.RunWithLooper
 class LockscreenPreconditionTest : SysuiTestCase() {
     @Mock
-    private lateinit var featureFlags: FeatureFlags
-
-    @Mock
     private lateinit var deviceProvisionedController: DeviceProvisionedController
 
     @Mock
@@ -64,10 +59,7 @@
     fun testFullyEnabled() {
         `when`(deviceProvisionedController.isCurrentUserSetup).thenReturn(true)
         `when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
-        `when`(featureFlags.isEnabled(Mockito.eq(Flags.SMARTSPACE) ?: Flags.SMARTSPACE))
-                .thenReturn(true)
-        val precondition = LockscreenPrecondition(featureFlags, deviceProvisionedController,
-                execution)
+        val precondition = LockscreenPrecondition(deviceProvisionedController, execution)
         precondition.addListener(listener)
 
         `verify`(listener).onCriteriaChanged()
@@ -81,10 +73,8 @@
     fun testProvisioning() {
         `when`(deviceProvisionedController.isCurrentUserSetup).thenReturn(true)
         `when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(false)
-        `when`(featureFlags.isEnabled(Mockito.eq(Flags.SMARTSPACE) ?: Flags.SMARTSPACE))
-                .thenReturn(true)
         val precondition =
-                LockscreenPrecondition(featureFlags, deviceProvisionedController, execution)
+                LockscreenPrecondition(deviceProvisionedController, execution)
         precondition.addListener(listener)
 
         verify(listener).onCriteriaChanged()
@@ -109,10 +99,8 @@
     fun testUserSetup() {
         `when`(deviceProvisionedController.isCurrentUserSetup).thenReturn(false)
         `when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
-        `when`(featureFlags.isEnabled(Mockito.eq(Flags.SMARTSPACE) ?: Flags.SMARTSPACE))
-                .thenReturn(true)
         val precondition =
-                LockscreenPrecondition(featureFlags, deviceProvisionedController, execution)
+                LockscreenPrecondition(deviceProvisionedController, execution)
         precondition.addListener(listener)
 
         verify(listener).onCriteriaChanged()
@@ -129,4 +117,4 @@
         verify(listener).onCriteriaChanged()
         assertThat(precondition.conditionsMet()).isTrue()
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 8aaa181..e68d3b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -45,6 +45,7 @@
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.view.AppearanceRegion;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.settings.FakeDisplayTracker;
 import com.android.systemui.statusbar.CommandQueue.Callbacks;
 
 import org.junit.After;
@@ -62,12 +63,14 @@
     };
 
     private CommandQueue mCommandQueue;
+
+    private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
     private Callbacks mCallbacks;
     private static final int SECONDARY_DISPLAY = 1;
 
     @Before
     public void setup() {
-        mCommandQueue = new CommandQueue(mContext);
+        mCommandQueue = new CommandQueue(mContext, mDisplayTracker);
         mCallbacks = mock(Callbacks.class);
         mCommandQueue.addCallback(mCallbacks);
         verify(mCallbacks).disable(anyInt(), eq(0), eq(0), eq(false));
@@ -415,7 +418,7 @@
 
     @Test
     public void testOnDisplayRemoved() {
-        mCommandQueue.onDisplayRemoved(SECONDARY_DISPLAY);
+        mDisplayTracker.triggerOnDisplayRemoved(SECONDARY_DISPLAY);
         waitForIdleSync();
         verify(mCallbacks).onDisplayRemoved(eq(SECONDARY_DISPLAY));
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 610bb13..406826b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -23,6 +23,7 @@
 import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT;
 import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_TIMEOUT;
 
+import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_AVAILABLE;
 import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED;
 import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED;
 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT;
@@ -58,6 +59,7 @@
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
+import android.app.AlarmManager;
 import android.app.Instrumentation;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyResourcesManager;
@@ -183,6 +185,8 @@
     private ScreenLifecycle mScreenLifecycle;
     @Mock
     private AuthController mAuthController;
+    @Mock
+    private AlarmManager mAlarmManager;
     @Captor
     private ArgumentCaptor<DockManager.AlignmentStateListener> mAlignmentListener;
     @Captor
@@ -277,7 +281,9 @@
                 mAuthController, mLockPatternUtils, mScreenLifecycle,
                 mKeyguardBypassController, mAccessibilityManager,
                 mFaceHelpMessageDeferral, mock(KeyguardLogger.class),
-                mAlternateBouncerInteractor);
+                mAlternateBouncerInteractor,
+                mAlarmManager
+        );
         mController.init();
         mController.setIndicationArea(mIndicationArea);
         verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
@@ -615,6 +621,109 @@
     }
 
     @Test
+    public void onBiometricHelp_coEx_faceUnavailable() {
+        createController();
+
+        // GIVEN unlocking with fingerprint is possible
+        when(mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(anyInt()))
+                .thenReturn(true);
+
+        String message = "A message";
+        mController.setVisible(true);
+
+        // WHEN there's a face unavailable message
+        mController.getKeyguardCallback().onBiometricHelp(
+                BIOMETRIC_HELP_FACE_NOT_AVAILABLE,
+                message,
+                BiometricSourceType.FACE);
+
+        // THEN show sequential messages such as: 'face unlock unavailable' and
+        // 'try fingerprint instead'
+        verifyIndicationMessage(
+                INDICATION_TYPE_BIOMETRIC_MESSAGE,
+                message);
+        verifyIndicationMessage(
+                INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
+                mContext.getString(R.string.keyguard_suggest_fingerprint));
+    }
+
+    @Test
+    public void onBiometricHelp_coEx_fpFailure_faceAlreadyUnlocked() {
+        createController();
+
+        // GIVEN face has already unlocked the device
+        when(mKeyguardUpdateMonitor.getUserUnlockedWithFace(anyInt())).thenReturn(true);
+
+        String message = "A message";
+        mController.setVisible(true);
+
+        // WHEN there's a fingerprint not recognized message
+        mController.getKeyguardCallback().onBiometricHelp(
+                BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED,
+                message,
+                BiometricSourceType.FINGERPRINT);
+
+        // THEN show sequential messages such as: 'Unlocked by face' and
+        // 'Swipe up to open'
+        verifyIndicationMessage(
+                INDICATION_TYPE_BIOMETRIC_MESSAGE,
+                mContext.getString(R.string.keyguard_face_successful_unlock));
+        verifyIndicationMessage(
+                INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
+                mContext.getString(R.string.keyguard_unlock));
+    }
+
+    @Test
+    public void onBiometricHelp_coEx_fpFailure_trustAgentAlreadyUnlocked() {
+        createController();
+
+        // GIVEN trust agent has already unlocked the device
+        when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
+
+        String message = "A message";
+        mController.setVisible(true);
+
+        // WHEN there's a fingerprint not recognized message
+        mController.getKeyguardCallback().onBiometricHelp(
+                BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED,
+                message,
+                BiometricSourceType.FINGERPRINT);
+
+        // THEN show sequential messages such as: 'Kept unlocked by TrustAgent' and
+        // 'Swipe up to open'
+        verifyIndicationMessage(
+                INDICATION_TYPE_BIOMETRIC_MESSAGE,
+                mContext.getString(R.string.keyguard_indication_trust_unlocked));
+        verifyIndicationMessage(
+                INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
+                mContext.getString(R.string.keyguard_unlock));
+    }
+
+    @Test
+    public void onBiometricHelp_coEx_fpFailure_trustAgentUnlocked_emptyTrustGrantedMessage() {
+        createController();
+
+        // GIVEN trust agent has already unlocked the device & trust granted message is empty
+        when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
+        mController.showTrustGrantedMessage(false, "");
+
+        String message = "A message";
+        mController.setVisible(true);
+
+        // WHEN there's a fingerprint not recognized message
+        mController.getKeyguardCallback().onBiometricHelp(
+                BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED,
+                message,
+                BiometricSourceType.FINGERPRINT);
+
+        // THEN show action to unlock (ie: 'Swipe up to open')
+        verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE);
+        verifyIndicationMessage(
+                INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
+                mContext.getString(R.string.keyguard_unlock));
+    }
+
+    @Test
     public void transientIndication_visibleWhenDozing_unlessSwipeUp_fromError() {
         createController();
         String message = mContext.getString(R.string.keyguard_unlock);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 702f278..d99cdd51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -79,6 +79,7 @@
     @Mock lateinit var singleShadeOverScroller: SingleShadeLockScreenOverScroller
     @Mock lateinit var splitShadeOverScroller: SplitShadeLockScreenOverScroller
     @Mock lateinit var qsTransitionController: LockscreenShadeQsTransitionController
+    @Mock lateinit var transitionControllerCallback: LockscreenShadeTransitionController.Callback
     @JvmField @Rule val mockito = MockitoJUnit.rule()
 
     private val configurationController = FakeConfigurationController()
@@ -124,6 +125,7 @@
                 },
                 qsTransitionControllerFactory = { qsTransitionController },
             )
+        transitionController.addCallback(transitionControllerCallback)
         whenever(nsslController.view).thenReturn(stackscroller)
         whenever(nsslController.expandHelperCallback).thenReturn(expandHelperCallback)
         transitionController.notificationPanelController = notificationPanelController
@@ -258,7 +260,7 @@
         verify(nsslController, never()).setTransitionToFullShadeAmount(anyFloat())
         verify(mediaHierarchyManager, never()).setTransitionToFullShadeAmount(anyFloat())
         verify(scrimController, never()).setTransitionToFullShadeProgress(anyFloat(), anyFloat())
-        verify(notificationPanelController, never()).setTransitionToFullShadeAmount(anyFloat(),
+        verify(transitionControllerCallback, never()).setTransitionToFullShadeAmount(anyFloat(),
                 anyBoolean(), anyLong())
         verify(qsTransitionController, never()).dragDownAmount = anyFloat()
     }
@@ -269,7 +271,7 @@
         verify(nsslController).setTransitionToFullShadeAmount(anyFloat())
         verify(mediaHierarchyManager).setTransitionToFullShadeAmount(anyFloat())
         verify(scrimController).setTransitionToFullShadeProgress(anyFloat(), anyFloat())
-        verify(notificationPanelController).setTransitionToFullShadeAmount(anyFloat(),
+        verify(transitionControllerCallback).setTransitionToFullShadeAmount(anyFloat(),
                 anyBoolean(), anyLong())
         verify(qsTransitionController).dragDownAmount = 10f
         verify(depthController).transitionToFullShadeProgress = anyFloat()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index 5124eb9..e6f272b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -37,6 +37,7 @@
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mock
+import org.mockito.Mockito
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
@@ -152,4 +153,18 @@
         // and cause us to drop a frame during the LOCKSCREEN_TRANSITION_FROM_AOD CUJ.
         assertEquals(0.99f, controller.dozeAmount, 0.009f)
     }
+
+    @Test
+    fun testSetDreamState_invokesCallback() {
+        val listener = mock(StatusBarStateController.StateListener::class.java)
+        controller.addCallback(listener)
+
+        controller.setIsDreaming(true)
+        verify(listener).onDreamingChanged(true)
+
+        Mockito.clearInvocations(listener)
+
+        controller.setIsDreaming(false)
+        verify(listener).onDreamingChanged(false)
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt
index ea06647..a9c3d5d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt
@@ -6,6 +6,7 @@
 import android.view.MotionEvent
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.FakeDisplayTracker
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
@@ -17,6 +18,7 @@
 class GenericGestureDetectorTest : SysuiTestCase() {
 
     private lateinit var gestureDetector: TestGestureDetector
+    private val displayTracker = FakeDisplayTracker(mContext)
 
     @Before
     fun setUp() {
@@ -101,12 +103,21 @@
         gestureDetector.addOnGestureDetectedCallback("tag2"){}
 
         gestureDetector.removeOnGestureDetectedCallback("tag")
-        gestureDetector.onInputEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, CORRECT_X, 0f, 0))
+        gestureDetector.onInputEvent(
+            MotionEvent.obtain(
+                0,
+                0,
+                MotionEvent.ACTION_DOWN,
+                CORRECT_X,
+                0f,
+                0
+            )
+        )
 
         assertThat(oldCallbackNotified).isFalse()
     }
 
-    inner class TestGestureDetector : GenericGestureDetector("fakeTag") {
+    inner class TestGestureDetector : GenericGestureDetector("fakeTag", displayTracker) {
         var isGestureListening = false
 
         override fun onInputEvent(ev: InputEvent) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index 4bcb54d..7fdcfb2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -32,7 +32,9 @@
 import android.view.View
 import android.widget.FrameLayout
 import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.plugins.ActivityStarter
@@ -104,6 +106,9 @@
     private lateinit var keyguardBypassController: KeyguardBypassController
 
     @Mock
+    private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+
+    @Mock
     private lateinit var deviceProvisionedController: DeviceProvisionedController
 
     @Mock
@@ -113,12 +118,21 @@
     private lateinit var handler: Handler
 
     @Mock
+    private lateinit var datePlugin: BcSmartspaceDataPlugin
+
+    @Mock
+    private lateinit var weatherPlugin: BcSmartspaceDataPlugin
+
+    @Mock
     private lateinit var plugin: BcSmartspaceDataPlugin
 
     @Mock
     private lateinit var configPlugin: BcSmartspaceConfigPlugin
 
     @Mock
+    private lateinit var dumpManager: DumpManager
+
+    @Mock
     private lateinit var controllerListener: SmartspaceTargetListener
 
     @Captor
@@ -152,6 +166,8 @@
         KeyguardBypassController.OnBypassStateChangedListener
     private lateinit var deviceProvisionedListener: DeviceProvisionedListener
 
+    private lateinit var dateSmartspaceView: SmartspaceView
+    private lateinit var weatherSmartspaceView: SmartspaceView
     private lateinit var smartspaceView: SmartspaceView
 
     private val clock = FakeSystemClock()
@@ -177,18 +193,24 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
-        `when`(featureFlags.isEnabled(Flags.SMARTSPACE)).thenReturn(true)
+        // Todo(b/261760571): flip the flag value here when feature is launched, and update relevant
+        //  tests.
+        `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(false)
 
         `when`(secureSettings.getUriFor(PRIVATE_LOCKSCREEN_SETTING))
                 .thenReturn(fakePrivateLockscreenSettingUri)
         `when`(secureSettings.getUriFor(NOTIF_ON_LOCKSCREEN_SETTING))
                 .thenReturn(fakeNotifOnLockscreenSettingUri)
         `when`(smartspaceManager.createSmartspaceSession(any())).thenReturn(smartspaceSession)
+        `when`(datePlugin.getView(any())).thenReturn(
+                createDateSmartspaceView(), createDateSmartspaceView())
+        `when`(weatherPlugin.getView(any())).thenReturn(
+                createWeatherSmartspaceView(), createWeatherSmartspaceView())
         `when`(plugin.getView(any())).thenReturn(createSmartspaceView(), createSmartspaceView())
         `when`(userTracker.userProfiles).thenReturn(userList)
         `when`(statusBarStateController.dozeAmount).thenReturn(0.5f)
-        `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(true)
-        `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(true)
+        `when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+        `when`(deviceProvisionedController.isCurrentUserSetup).thenReturn(true)
 
         setActiveUser(userHandlePrimary)
         setAllowPrivateNotifications(userHandlePrimary, true)
@@ -209,10 +231,14 @@
                 statusBarStateController,
                 deviceProvisionedController,
                 keyguardBypassController,
+                keyguardUpdateMonitor,
+                dumpManager,
                 execution,
                 executor,
                 bgExecutor,
                 handler,
+                Optional.of(datePlugin),
+                Optional.of(weatherPlugin),
                 Optional.of(plugin),
                 Optional.of(configPlugin),
         )
@@ -222,21 +248,21 @@
     }
 
     @Test(expected = RuntimeException::class)
-    fun testThrowsIfFlagIsDisabled() {
+    fun testBuildAndConnectWeatherView_throwsIfDecouplingDisabled() {
         // GIVEN the feature flag is disabled
-        `when`(featureFlags.isEnabled(Flags.SMARTSPACE)).thenReturn(false)
+        `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(false)
 
         // WHEN we try to build the view
-        controller.buildAndConnectView(fakeParent)
+        controller.buildAndConnectWeatherView(fakeParent)
 
         // THEN an exception is thrown
     }
 
     @Test
-    fun connectOnlyAfterDeviceIsProvisioned() {
+    fun testBuildAndConnectView_connectsOnlyAfterDeviceIsProvisioned() {
         // GIVEN an unprovisioned device and an attempt to connect
-        `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(false)
-        `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(false)
+        `when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(false)
+        `when`(deviceProvisionedController.isCurrentUserSetup).thenReturn(false)
 
         // WHEN a connection attempt is made and view is attached
         val view = controller.buildAndConnectView(fakeParent)
@@ -246,8 +272,8 @@
         verify(smartspaceManager, never()).createSmartspaceSession(any())
 
         // WHEN it does become provisioned
-        `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(true)
-        `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(true)
+        `when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+        `when`(deviceProvisionedController.isCurrentUserSetup).thenReturn(true)
         deviceProvisionedListener.onUserSetupChanged()
 
         // THEN the session is created
@@ -257,7 +283,7 @@
     }
 
     @Test
-    fun testListenersAreRegistered() {
+    fun testAddListener_registersListenersForPlugin() {
         // GIVEN a listener is added after a session is created
         connectSession()
 
@@ -266,10 +292,13 @@
 
         // THEN the listener is registered to the underlying plugin
         verify(plugin).registerListener(controllerListener)
+        // The listener is registered only for the plugin, not the date, or weather plugin.
+        verify(datePlugin, never()).registerListener(any())
+        verify(weatherPlugin, never()).registerListener(any())
     }
 
     @Test
-    fun testEarlyRegisteredListenersAreAttachedAfterConnected() {
+    fun testAddListener_earlyRegisteredListenersAreAttachedAfterConnected() {
         // GIVEN a listener that is registered before the session is created
         controller.addListener(controllerListener)
 
@@ -278,10 +307,13 @@
 
         // THEN the listener is subsequently registered
         verify(plugin).registerListener(controllerListener)
+        // The listener is registered only for the plugin, not the date, or the weather plugin.
+        verify(datePlugin, never()).registerListener(any())
+        verify(weatherPlugin, never()).registerListener(any())
     }
 
     @Test
-    fun testEmptyListIsEmittedAndNotifierRemovedAfterDisconnect() {
+    fun testDisconnect_emitsEmptyListAndRemovesNotifier() {
         // GIVEN a registered listener on an active session
         connectSession()
         clearInvocations(plugin)
@@ -293,10 +325,13 @@
         // THEN the listener receives an empty list of targets and unregisters the notifier
         verify(plugin).onTargetsAvailable(emptyList())
         verify(plugin).registerSmartspaceEventNotifier(null)
+        verify(weatherPlugin).onTargetsAvailable(emptyList())
+        verify(weatherPlugin).registerSmartspaceEventNotifier(null)
+        verify(datePlugin).registerSmartspaceEventNotifier(null)
     }
 
     @Test
-    fun testUserChangeReloadsSmartspace() {
+    fun testUserChange_reloadsSmartspace() {
         // GIVEN a connected smartspace session
         connectSession()
 
@@ -308,7 +343,7 @@
     }
 
     @Test
-    fun testSettingsChangeReloadsSmartspace() {
+    fun testSettingsChange_reloadsSmartspace() {
         // GIVEN a connected smartspace session
         connectSession()
 
@@ -320,7 +355,7 @@
     }
 
     @Test
-    fun testThemeChangeUpdatesTextColor() {
+    fun testThemeChange_updatesTextColor() {
         // GIVEN a connected smartspace session
         connectSession()
 
@@ -332,7 +367,23 @@
     }
 
     @Test
-    fun testDozeAmountChangeUpdatesView() {
+    fun testThemeChange_ifDecouplingEnabled_updatesTextColor() {
+        `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)
+
+        // GIVEN a connected smartspace session
+        connectSession()
+
+        // WHEN the theme changes
+        configChangeListener.onThemeChanged()
+
+        // We update the new text color to match the wallpaper color
+        verify(dateSmartspaceView).setPrimaryTextColor(anyInt())
+        verify(weatherSmartspaceView).setPrimaryTextColor(anyInt())
+        verify(smartspaceView).setPrimaryTextColor(anyInt())
+    }
+
+    @Test
+    fun testDozeAmountChange_updatesView() {
         // GIVEN a connected smartspace session
         connectSession()
 
@@ -344,7 +395,23 @@
     }
 
     @Test
-    fun testKeyguardBypassEnabledUpdatesView() {
+    fun testDozeAmountChange_ifDecouplingEnabled_updatesViews() {
+        `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)
+
+        // GIVEN a connected smartspace session
+        connectSession()
+
+        // WHEN the doze amount changes
+        statusBarStateListener.onDozeAmountChanged(0.1f, 0.7f)
+
+        // We pass that along to the view
+        verify(dateSmartspaceView).setDozeAmount(0.7f)
+        verify(weatherSmartspaceView).setDozeAmount(0.7f)
+        verify(smartspaceView).setDozeAmount(0.7f)
+    }
+
+    @Test
+    fun testKeyguardBypassEnabled_updatesView() {
         // GIVEN a connected smartspace session
         connectSession()
         `when`(keyguardBypassController.bypassEnabled).thenReturn(true)
@@ -439,6 +506,29 @@
     }
 
     @Test
+    fun testSessionListener_ifDecouplingEnabled_weatherTargetIsFilteredOut() {
+        `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)
+        connectSession()
+
+        // WHEN we receive a list of targets
+        val targets = listOf(
+                makeTarget(1, userHandlePrimary, isSensitive = true),
+                makeTarget(2, userHandlePrimary),
+                makeTarget(3, userHandleManaged),
+                makeTarget(4, userHandlePrimary, featureType = SmartspaceTarget.FEATURE_WEATHER)
+        )
+
+        sessionListener.onTargetsAvailable(targets)
+
+        // THEN all non-sensitive content is still shown
+        verify(plugin).onTargetsAvailable(eq(listOf(targets[0], targets[1], targets[2])))
+        // No filtering is applied for the weather plugin
+        verify(weatherPlugin).onTargetsAvailable(eq(targets))
+        // No targets needed for the date plugin
+        verify(datePlugin, never()).onTargetsAvailable(any())
+    }
+
+    @Test
     fun testSettingsAreReloaded() {
         // GIVEN a connected session where the privacy settings later flip to false
         connectSession()
@@ -529,6 +619,16 @@
     }
 
     @Test
+    fun testWeatherViewUsesSameSession() {
+        `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)
+        // GIVEN a connected session
+        connectSession()
+
+        // No checks is needed here, since connectSession() already checks internally that
+        // createSmartspaceSession is invoked only once.
+    }
+
+    @Test
     fun testViewGetInitializedWithBypassEnabledState() {
         // GIVEN keyguard bypass is enabled.
         `when`(keyguardBypassController.bypassEnabled).thenReturn(true)
@@ -545,8 +645,8 @@
     fun testConnectAttemptBeforeInitializationShouldNotCreateSession() {
         // GIVEN an uninitalized smartspaceView
         // WHEN the device is provisioned
-        `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(true)
-        `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(true)
+        `when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+        `when`(deviceProvisionedController.isCurrentUserSetup).thenReturn(true)
         deviceProvisionedListener.onDeviceProvisionedChanged()
 
         // THEN no calls to createSmartspaceSession should occur
@@ -556,9 +656,35 @@
     }
 
     private fun connectSession() {
+        if (controller.isDateWeatherDecoupled()) {
+            val dateView = controller.buildAndConnectDateView(fakeParent)
+            dateSmartspaceView = dateView as SmartspaceView
+            fakeParent.addView(dateView)
+            controller.stateChangeListener.onViewAttachedToWindow(dateView)
+
+            verify(dateSmartspaceView).setUiSurface(
+                    BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
+            verify(dateSmartspaceView).registerDataProvider(datePlugin)
+
+            verify(dateSmartspaceView).setPrimaryTextColor(anyInt())
+            verify(dateSmartspaceView).setDozeAmount(0.5f)
+
+            val weatherView = controller.buildAndConnectWeatherView(fakeParent)
+            weatherSmartspaceView = weatherView as SmartspaceView
+            fakeParent.addView(weatherView)
+            controller.stateChangeListener.onViewAttachedToWindow(weatherView)
+
+            verify(weatherSmartspaceView).setUiSurface(
+                    BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
+            verify(weatherSmartspaceView).registerDataProvider(weatherPlugin)
+
+            verify(weatherSmartspaceView).setPrimaryTextColor(anyInt())
+            verify(weatherSmartspaceView).setDozeAmount(0.5f)
+        }
+
         val view = controller.buildAndConnectView(fakeParent)
         smartspaceView = view as SmartspaceView
-
+        fakeParent.addView(view)
         controller.stateChangeListener.onViewAttachedToWindow(view)
 
         verify(smartspaceView).setUiSurface(BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
@@ -568,6 +694,8 @@
                 .addOnTargetsAvailableListener(any(), capture(sessionListenerCaptor))
         sessionListener = sessionListenerCaptor.value
 
+        verify(smartspaceManager).createSmartspaceSession(any())
+
         verify(userTracker).addCallback(capture(userTrackerCaptor), any())
         userListener = userTrackerCaptor.value
 
@@ -592,9 +720,12 @@
 
         verify(smartspaceView).setPrimaryTextColor(anyInt())
         verify(smartspaceView).setDozeAmount(0.5f)
-        clearInvocations(view)
 
-        fakeParent.addView(view)
+        if (controller.isDateWeatherDecoupled()) {
+            clearInvocations(dateSmartspaceView)
+            clearInvocations(weatherSmartspaceView)
+        }
+        clearInvocations(smartspaceView)
     }
 
     private fun setActiveUser(userHandle: UserHandle) {
@@ -640,6 +771,56 @@
         ).thenReturn(if (value) 1 else 0)
     }
 
+    // Separate function for the date view, which implements a specific subset of all functions.
+    private fun createDateSmartspaceView(): SmartspaceView {
+        return spy(object : View(context), SmartspaceView {
+            override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) {
+            }
+
+            override fun setPrimaryTextColor(color: Int) {
+            }
+
+            override fun setUiSurface(uiSurface: String) {
+            }
+
+            override fun setDozeAmount(amount: Float) {
+            }
+
+            override fun setIntentStarter(intentStarter: BcSmartspaceDataPlugin.IntentStarter?) {
+            }
+
+            override fun setFalsingManager(falsingManager: FalsingManager?) {
+            }
+
+            override fun setDnd(image: Drawable?, description: String?) {
+            }
+
+            override fun setNextAlarm(image: Drawable?, description: String?) {
+            }
+        })
+    }
+    // Separate function for the weather view, which implements a specific subset of all functions.
+    private fun createWeatherSmartspaceView(): SmartspaceView {
+        return spy(object : View(context), SmartspaceView {
+            override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) {
+            }
+
+            override fun setPrimaryTextColor(color: Int) {
+            }
+
+            override fun setUiSurface(uiSurface: String) {
+            }
+
+            override fun setDozeAmount(amount: Float) {
+            }
+
+            override fun setIntentStarter(intentStarter: BcSmartspaceDataPlugin.IntentStarter?) {
+            }
+
+            override fun setFalsingManager(falsingManager: FalsingManager?) {
+            }
+        })
+    }
     private fun createSmartspaceView(): SmartspaceView {
         return spy(object : View(context), SmartspaceView {
             override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) {
@@ -651,9 +832,6 @@
             override fun setPrimaryTextColor(color: Int) {
             }
 
-            override fun setIsDreaming(isDreaming: Boolean) {
-            }
-
             override fun setUiSurface(uiSurface: String) {
             }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 94e3e6c..edb2965 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -105,6 +105,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.Spy;
+import org.mockito.stubbing.Answer;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -376,6 +377,90 @@
     }
 
     @Test
+    public void testScheduleBuildNotificationListWhenChannelChanged() {
+        // GIVEN
+        final NotificationEntryBuilder neb = buildNotif(TEST_PACKAGE, 48);
+        final NotificationChannel channel = new NotificationChannel(
+                "channelId",
+                "channelName",
+                NotificationManager.IMPORTANCE_DEFAULT);
+        neb.setChannel(channel);
+
+        final NotifEvent notif = mNoMan.postNotif(neb);
+        final NotificationEntry entry = mCollectionListener.getEntry(notif.key);
+
+        when(mMainHandler.hasCallbacks(any())).thenReturn(false);
+
+        clearInvocations(mBuildListener);
+
+        // WHEN
+        mNotifHandler.onNotificationChannelModified(TEST_PACKAGE,
+                entry.getSbn().getUser(), channel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
+
+        // THEN
+        verify(mMainHandler).postDelayed(any(), eq(1000L));
+    }
+
+    @Test
+    public void testCancelScheduledBuildNotificationListEventWhenNotifUpdatedSynchronously() {
+        // GIVEN
+        final NotificationEntry entry1 = buildNotif(TEST_PACKAGE, 1)
+                .setGroup(mContext, "group_1")
+                .build();
+        final NotificationEntry entry2 = buildNotif(TEST_PACKAGE, 2)
+                .setGroup(mContext, "group_1")
+                .setContentTitle(mContext, "New version")
+                .build();
+        final NotificationEntry entry3 = buildNotif(TEST_PACKAGE, 3)
+                .setGroup(mContext, "group_1")
+                .build();
+
+        final List<CoalescedEvent> entriesToBePosted = Arrays.asList(
+                new CoalescedEvent(entry1.getKey(), 0, entry1.getSbn(), entry1.getRanking(), null),
+                new CoalescedEvent(entry2.getKey(), 1, entry2.getSbn(), entry2.getRanking(), null),
+                new CoalescedEvent(entry3.getKey(), 2, entry3.getSbn(), entry3.getRanking(), null)
+        );
+
+        when(mMainHandler.hasCallbacks(any())).thenReturn(true);
+
+        // WHEN
+        mNotifHandler.onNotificationBatchPosted(entriesToBePosted);
+
+        // THEN
+        verify(mMainHandler).removeCallbacks(any());
+    }
+
+    @Test
+    public void testBuildNotificationListWhenChannelChanged() {
+        // GIVEN
+        final NotificationEntryBuilder neb = buildNotif(TEST_PACKAGE, 48);
+        final NotificationChannel channel = new NotificationChannel(
+                "channelId",
+                "channelName",
+                NotificationManager.IMPORTANCE_DEFAULT);
+        neb.setChannel(channel);
+
+        final NotifEvent notif = mNoMan.postNotif(neb);
+        final NotificationEntry entry = mCollectionListener.getEntry(notif.key);
+
+        when(mMainHandler.hasCallbacks(any())).thenReturn(false);
+        when(mMainHandler.postDelayed(any(), eq(1000L))).thenAnswer((Answer) invocation -> {
+            final Runnable runnable = invocation.getArgument(0);
+            runnable.run();
+            return null;
+        });
+
+        clearInvocations(mBuildListener);
+
+        // WHEN
+        mNotifHandler.onNotificationChannelModified(TEST_PACKAGE,
+                entry.getSbn().getUser(), channel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
+
+        // THEN
+        verifyBuiltList(List.of(entry));
+    }
+
+    @Test
     public void testRankingsAreUpdatedForOtherNotifs() {
         // GIVEN a collection with one notif
         NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
index 2686238..8109e24 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
@@ -23,6 +23,7 @@
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.advanceTimeBy
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.StatusBarState
@@ -38,6 +39,8 @@
 import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderImpl
 import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.policy.HeadsUpManager
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.withArgCaptor
@@ -63,6 +66,7 @@
 @RunWith(AndroidTestingRunner::class)
 class KeyguardCoordinatorTest : SysuiTestCase() {
 
+    private val headsUpManager: HeadsUpManager = mock()
     private val keyguardNotifVisibilityProvider: KeyguardNotificationVisibilityProvider = mock()
     private val keyguardRepository = FakeKeyguardRepository()
     private val notifPipelineFlags: NotifPipelineFlags = mock()
@@ -90,8 +94,9 @@
     fun unseenFilterSuppressesSeenNotifWhileKeyguardShowing() {
         whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
 
-        // GIVEN: Keyguard is not showing, and a notification is present
+        // GIVEN: Keyguard is not showing, shade is expanded, and a notification is present
         keyguardRepository.setKeyguardShowing(false)
+        whenever(statusBarStateController.isExpanded).thenReturn(true)
         runKeyguardCoordinatorTest {
             val fakeEntry = NotificationEntryBuilder().build()
             collectionListener.onEntryAdded(fakeEntry)
@@ -113,11 +118,44 @@
     }
 
     @Test
+    fun unseenFilter_headsUpMarkedAsSeen() {
+        whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
+
+        // GIVEN: Keyguard is not showing, shade is not expanded
+        keyguardRepository.setKeyguardShowing(false)
+        whenever(statusBarStateController.isExpanded).thenReturn(false)
+        runKeyguardCoordinatorTest {
+            // WHEN: A notification is posted
+            val fakeEntry = NotificationEntryBuilder().build()
+            collectionListener.onEntryAdded(fakeEntry)
+
+            // WHEN: That notification is heads up
+            onHeadsUpChangedListener.onHeadsUpStateChanged(fakeEntry, /* isHeadsUp= */ true)
+            testScheduler.runCurrent()
+
+            // WHEN: The keyguard is now showing
+            keyguardRepository.setKeyguardShowing(true)
+            testScheduler.runCurrent()
+
+            // THEN: The notification is recognized as "seen" and is filtered out.
+            assertThat(unseenFilter.shouldFilterOut(fakeEntry, 0L)).isTrue()
+
+            // WHEN: The keyguard goes away
+            keyguardRepository.setKeyguardShowing(false)
+            testScheduler.runCurrent()
+
+            // THEN: The notification is shown regardless
+            assertThat(unseenFilter.shouldFilterOut(fakeEntry, 0L)).isFalse()
+        }
+    }
+
+    @Test
     fun unseenFilterDoesNotSuppressSeenOngoingNotifWhileKeyguardShowing() {
         whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
 
-        // GIVEN: Keyguard is not showing, and an ongoing notification is present
+        // GIVEN: Keyguard is not showing, shade is expanded, and an ongoing notification is present
         keyguardRepository.setKeyguardShowing(false)
+        whenever(statusBarStateController.isExpanded).thenReturn(true)
         runKeyguardCoordinatorTest {
             val fakeEntry = NotificationEntryBuilder()
                 .setNotification(Notification.Builder(mContext).setOngoing(true).build())
@@ -137,8 +175,9 @@
     fun unseenFilterDoesNotSuppressSeenMediaNotifWhileKeyguardShowing() {
         whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
 
-        // GIVEN: Keyguard is not showing, and a media notification is present
+        // GIVEN: Keyguard is not showing, shade is expanded, and a media notification is present
         keyguardRepository.setKeyguardShowing(false)
+        whenever(statusBarStateController.isExpanded).thenReturn(true)
         runKeyguardCoordinatorTest {
             val fakeEntry = NotificationEntryBuilder().build().apply {
                 row = mock<ExpandableNotificationRow>().apply {
@@ -160,8 +199,9 @@
     fun unseenFilterUpdatesSeenProviderWhenSuppressing() {
         whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
 
-        // GIVEN: Keyguard is not showing, and a notification is present
+        // GIVEN: Keyguard is not showing, shade is expanded, and a notification is present
         keyguardRepository.setKeyguardShowing(false)
+        whenever(statusBarStateController.isExpanded).thenReturn(true)
         runKeyguardCoordinatorTest {
             val fakeEntry = NotificationEntryBuilder().build()
             collectionListener.onEntryAdded(fakeEntry)
@@ -185,8 +225,9 @@
     fun unseenFilterInvalidatesWhenSettingChanges() {
         whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
 
-        // GIVEN: Keyguard is not showing
+        // GIVEN: Keyguard is not showing, and shade is expanded
         keyguardRepository.setKeyguardShowing(false)
+        whenever(statusBarStateController.isExpanded).thenReturn(true)
         runKeyguardCoordinatorTest {
             // GIVEN: A notification is present
             val fakeEntry = NotificationEntryBuilder().build()
@@ -237,8 +278,9 @@
     fun unseenFilterSeenGroupSummaryWithUnseenChild() {
         whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
 
-        // GIVEN: Keyguard is not showing, and a notification is present
+        // GIVEN: Keyguard is not showing, shade is expanded, and a notification is present
         keyguardRepository.setKeyguardShowing(false)
+        whenever(statusBarStateController.isExpanded).thenReturn(true)
         runKeyguardCoordinatorTest {
             // WHEN: A new notification is posted
             val fakeSummary = NotificationEntryBuilder().build()
@@ -270,16 +312,19 @@
     fun unseenNotificationIsMarkedAsSeenWhenKeyguardGoesAway() {
         whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
 
-        // GIVEN: Keyguard is showing, unseen notification is present
+        // GIVEN: Keyguard is showing, not dozing, unseen notification is present
         keyguardRepository.setKeyguardShowing(true)
+        keyguardRepository.setDozing(false)
         runKeyguardCoordinatorTest {
             val fakeEntry = NotificationEntryBuilder().build()
             collectionListener.onEntryAdded(fakeEntry)
 
-            // WHEN: Keyguard is no longer showing for 5 seconds
-            keyguardRepository.setKeyguardShowing(false)
+            // WHEN: five seconds have passed
+            testScheduler.advanceTimeBy(5.seconds)
             testScheduler.runCurrent()
-            testScheduler.advanceTimeBy(5.seconds.inWholeMilliseconds)
+
+            // WHEN: Keyguard is no longer showing
+            keyguardRepository.setKeyguardShowing(false)
             testScheduler.runCurrent()
 
             // WHEN: Keyguard is shown again
@@ -292,7 +337,7 @@
     }
 
     @Test
-    fun unseenNotificationIsNotMarkedAsSeenIfTimeThresholdNotMet() {
+    fun unseenNotificationIsNotMarkedAsSeenIfShadeNotExpanded() {
         whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
 
         // GIVEN: Keyguard is showing, unseen notification is present
@@ -301,10 +346,8 @@
             val fakeEntry = NotificationEntryBuilder().build()
             collectionListener.onEntryAdded(fakeEntry)
 
-            // WHEN: Keyguard is no longer showing for <5 seconds
+            // WHEN: Keyguard is no longer showing
             keyguardRepository.setKeyguardShowing(false)
-            testScheduler.runCurrent()
-            testScheduler.advanceTimeBy(1.seconds.inWholeMilliseconds)
 
             // WHEN: Keyguard is shown again
             keyguardRepository.setKeyguardShowing(true)
@@ -327,6 +370,7 @@
         val keyguardCoordinator =
             KeyguardCoordinator(
                 testDispatcher,
+                headsUpManager,
                 keyguardNotifVisibilityProvider,
                 keyguardRepository,
                 notifPipelineFlags,
@@ -364,12 +408,21 @@
         val unseenFilter: NotifFilter
             get() = keyguardCoordinator.unseenNotifFilter
 
-        // TODO(254647461): Remove lazy once Flags.FILTER_UNSEEN_NOTIFS_ON_KEYGUARD is enabled and
-        //  removed
+        // TODO(254647461): Remove lazy from these properties once
+        //    Flags.FILTER_UNSEEN_NOTIFS_ON_KEYGUARD is enabled and removed
+
         val collectionListener: NotifCollectionListener by lazy {
             withArgCaptor { verify(notifPipeline).addCollectionListener(capture()) }
         }
 
+        val onHeadsUpChangedListener: OnHeadsUpChangedListener by lazy {
+            withArgCaptor { verify(headsUpManager).addListener(capture()) }
+        }
+
+        val statusBarStateListener: StatusBarStateController.StateListener by lazy {
+            withArgCaptor { verify(statusBarStateController).addCallback(capture()) }
+        }
+
         var showOnlyUnseenNotifsOnKeyguardSetting: Boolean
             get() =
                 fakeSettings.getIntForUser(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
index 03af527..fbec95b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
@@ -741,7 +741,7 @@
     }
 
     @Test
-    public void testShouldFullScreen_snoozed_occluding_withStrictRules() throws Exception {
+    public void testShouldNotFullScreen_snoozed_occluding_withStrictRules() throws Exception {
         when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
         NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
         when(mPowerManager.isInteractive()).thenReturn(true);
@@ -753,16 +753,41 @@
         when(mKeyguardStateController.isOccluded()).thenReturn(true);
 
         assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
-                .isEqualTo(FullScreenIntentDecision.FSI_KEYGUARD_OCCLUDED);
+                .isEqualTo(FullScreenIntentDecision.NO_FSI_EXPECTED_TO_HUN);
         assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
-                .isTrue();
-        verify(mLogger, never()).logNoFullscreen(any(), any());
+                .isFalse();
+        verify(mLogger).logNoFullscreen(entry, "Expected to HUN");
         verify(mLogger, never()).logNoFullscreenWarning(any(), any());
-        verify(mLogger).logFullscreen(entry, "Expected not to HUN while keyguard occluded");
+        verify(mLogger, never()).logFullscreen(any(), any());
     }
 
     @Test
-    public void testShouldFullScreen_snoozed_lockedShade_withStrictRules() throws Exception {
+    public void testShouldHeadsUp_snoozed_occluding_withStrictRules() throws Exception {
+        when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
+        NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
+        when(mPowerManager.isInteractive()).thenReturn(true);
+        when(mPowerManager.isScreenOn()).thenReturn(true);
+        when(mDreamManager.isDreaming()).thenReturn(false);
+        when(mStatusBarStateController.getState()).thenReturn(SHADE);
+        when(mHeadsUpManager.isSnoozed("a")).thenReturn(true);
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+        when(mKeyguardStateController.isOccluded()).thenReturn(true);
+
+        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
+
+        verify(mLogger).logHeadsUpPackageSnoozeBypassedHasFsi(entry);
+        verify(mLogger, never()).logHeadsUp(any());
+
+        assertThat(mUiEventLoggerFake.numLogs()).isEqualTo(1);
+        UiEventLoggerFake.FakeUiEvent fakeUiEvent = mUiEventLoggerFake.get(0);
+        assertThat(fakeUiEvent.eventId).isEqualTo(
+                NotificationInterruptEvent.HUN_SNOOZE_BYPASSED_POTENTIALLY_SUPPRESSED_FSI.getId());
+        assertThat(fakeUiEvent.uid).isEqualTo(entry.getSbn().getUid());
+        assertThat(fakeUiEvent.packageName).isEqualTo(entry.getSbn().getPackageName());
+    }
+
+    @Test
+    public void testShouldNotFullScreen_snoozed_lockedShade_withStrictRules() throws Exception {
         when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
         NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
         when(mPowerManager.isInteractive()).thenReturn(true);
@@ -774,12 +799,37 @@
         when(mKeyguardStateController.isOccluded()).thenReturn(false);
 
         assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
-                .isEqualTo(FullScreenIntentDecision.FSI_LOCKED_SHADE);
+                .isEqualTo(FullScreenIntentDecision.NO_FSI_EXPECTED_TO_HUN);
         assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
-                .isTrue();
-        verify(mLogger, never()).logNoFullscreen(any(), any());
+                .isFalse();
+        verify(mLogger).logNoFullscreen(entry, "Expected to HUN");
         verify(mLogger, never()).logNoFullscreenWarning(any(), any());
-        verify(mLogger).logFullscreen(entry, "Keyguard is showing and not occluded");
+        verify(mLogger, never()).logFullscreen(any(), any());
+    }
+
+    @Test
+    public void testShouldHeadsUp_snoozed_lockedShade_withStrictRules() throws Exception {
+        when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
+        NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
+        when(mPowerManager.isInteractive()).thenReturn(true);
+        when(mPowerManager.isScreenOn()).thenReturn(true);
+        when(mDreamManager.isDreaming()).thenReturn(false);
+        when(mStatusBarStateController.getState()).thenReturn(SHADE_LOCKED);
+        when(mHeadsUpManager.isSnoozed("a")).thenReturn(true);
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+        when(mKeyguardStateController.isOccluded()).thenReturn(false);
+
+        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
+
+        verify(mLogger).logHeadsUpPackageSnoozeBypassedHasFsi(entry);
+        verify(mLogger, never()).logHeadsUp(any());
+
+        assertThat(mUiEventLoggerFake.numLogs()).isEqualTo(1);
+        UiEventLoggerFake.FakeUiEvent fakeUiEvent = mUiEventLoggerFake.get(0);
+        assertThat(fakeUiEvent.eventId).isEqualTo(
+                NotificationInterruptEvent.HUN_SNOOZE_BYPASSED_POTENTIALLY_SUPPRESSED_FSI.getId());
+        assertThat(fakeUiEvent.uid).isEqualTo(entry.getSbn().getUid());
+        assertThat(fakeUiEvent.packageName).isEqualTo(entry.getSbn().getPackageName());
     }
 
     @Test
@@ -795,21 +845,41 @@
         when(mKeyguardStateController.isOccluded()).thenReturn(false);
 
         assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
-                .isEqualTo(FullScreenIntentDecision.NO_FSI_NO_HUN_OR_KEYGUARD);
+                .isEqualTo(FullScreenIntentDecision.NO_FSI_EXPECTED_TO_HUN);
         assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
                 .isFalse();
-        verify(mLogger, never()).logNoFullscreen(any(), any());
-        verify(mLogger).logNoFullscreenWarning(entry, "Expected not to HUN while not on keyguard");
+        verify(mLogger).logNoFullscreen(entry, "Expected to HUN");
+        verify(mLogger, never()).logNoFullscreenWarning(any(), any());
         verify(mLogger, never()).logFullscreen(any(), any());
+    }
+
+    @Test
+    public void testShouldHeadsUp_snoozed_unlocked_withStrictRules() throws Exception {
+        when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
+        NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
+        when(mPowerManager.isInteractive()).thenReturn(true);
+        when(mPowerManager.isScreenOn()).thenReturn(true);
+        when(mDreamManager.isDreaming()).thenReturn(false);
+        when(mStatusBarStateController.getState()).thenReturn(SHADE);
+        when(mHeadsUpManager.isSnoozed("a")).thenReturn(true);
+        when(mKeyguardStateController.isShowing()).thenReturn(false);
+        when(mKeyguardStateController.isOccluded()).thenReturn(false);
+
+        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
+
+        verify(mLogger).logHeadsUpPackageSnoozeBypassedHasFsi(entry);
+        verify(mLogger, never()).logHeadsUp(any());
 
         assertThat(mUiEventLoggerFake.numLogs()).isEqualTo(1);
         UiEventLoggerFake.FakeUiEvent fakeUiEvent = mUiEventLoggerFake.get(0);
         assertThat(fakeUiEvent.eventId).isEqualTo(
-                NotificationInterruptEvent.FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD.getId());
+                NotificationInterruptEvent.HUN_SNOOZE_BYPASSED_POTENTIALLY_SUPPRESSED_FSI.getId());
         assertThat(fakeUiEvent.uid).isEqualTo(entry.getSbn().getUid());
         assertThat(fakeUiEvent.packageName).isEqualTo(entry.getSbn().getPackageName());
     }
 
+    /* TODO: Verify the FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD UiEvent some other way. */
+
     /**
      * Bubbles can happen.
      */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryLoggerTest.kt
index 33b94e3..33a838e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryLoggerTest.kt
@@ -20,6 +20,7 @@
 import android.app.StatsManager
 import android.graphics.Bitmap
 import android.graphics.drawable.Icon
+import android.stats.sysui.NotificationEnums
 import android.testing.AndroidTestingRunner
 import android.util.StatsEvent
 import androidx.test.filters.SmallTest
@@ -31,9 +32,12 @@
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Expect
 import com.google.common.truth.Truth.assertThat
+import java.lang.RuntimeException
 import kotlinx.coroutines.Dispatchers
 import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
@@ -44,6 +48,8 @@
 @RunWith(AndroidTestingRunner::class)
 class NotificationMemoryLoggerTest : SysuiTestCase() {
 
+    @Rule @JvmField val expect = Expect.create()
+
     private val bgExecutor = FakeExecutor(FakeSystemClock())
     private val immediate = Dispatchers.Main.immediate
 
@@ -113,6 +119,141 @@
         assertThat(data).hasSize(2)
     }
 
+    @Test
+    fun onPullAtom_throwsInterruptedException_failsGracefully() {
+        val pipeline: NotifPipeline = mock()
+        whenever(pipeline.allNotifs).thenAnswer { throw InterruptedException("Timeout") }
+        val logger = NotificationMemoryLogger(pipeline, statsManager, immediate, bgExecutor)
+        assertThat(logger.onPullAtom(SysUiStatsLog.NOTIFICATION_MEMORY_USE, mutableListOf()))
+            .isEqualTo(StatsManager.PULL_SKIP)
+    }
+
+    @Test
+    fun onPullAtom_throwsRuntimeException_failsGracefully() {
+        val pipeline: NotifPipeline = mock()
+        whenever(pipeline.allNotifs).thenThrow(RuntimeException("Something broke!"))
+        val logger = NotificationMemoryLogger(pipeline, statsManager, immediate, bgExecutor)
+        assertThat(logger.onPullAtom(SysUiStatsLog.NOTIFICATION_MEMORY_USE, mutableListOf()))
+            .isEqualTo(StatsManager.PULL_SKIP)
+    }
+
+    @Test
+    fun aggregateMemoryUsageData_returnsCorrectlyAggregatedSamePackageData() {
+        val usage = getPresetMemoryUsages()
+        val aggregateUsage = aggregateMemoryUsageData(usage)
+
+        assertThat(aggregateUsage).hasSize(3)
+        assertThat(aggregateUsage)
+            .containsKey(Pair("package 1", NotificationEnums.STYLE_BIG_PICTURE))
+
+        // Aggregated fields
+        val aggregatedData =
+            aggregateUsage[Pair("package 1", NotificationEnums.STYLE_BIG_PICTURE)]!!
+        val presetUsage1 = usage[0]
+        val presetUsage2 = usage[1]
+        assertAggregatedData(
+            aggregatedData,
+            2,
+            2,
+            smallIconObject =
+                presetUsage1.objectUsage.smallIcon + presetUsage2.objectUsage.smallIcon,
+            smallIconBitmapCount = 2,
+            largeIconObject =
+                presetUsage1.objectUsage.largeIcon + presetUsage2.objectUsage.largeIcon,
+            largeIconBitmapCount = 2,
+            bigPictureObject =
+                presetUsage1.objectUsage.bigPicture + presetUsage2.objectUsage.bigPicture,
+            bigPictureBitmapCount = 2,
+            extras = presetUsage1.objectUsage.extras + presetUsage2.objectUsage.extras,
+            extenders = presetUsage1.objectUsage.extender + presetUsage2.objectUsage.extender,
+            // Only totals need to be summarized.
+            smallIconViews =
+                presetUsage1.viewUsage[0].smallIcon + presetUsage2.viewUsage[0].smallIcon,
+            largeIconViews =
+                presetUsage1.viewUsage[0].largeIcon + presetUsage2.viewUsage[0].largeIcon,
+            systemIconViews =
+                presetUsage1.viewUsage[0].systemIcons + presetUsage2.viewUsage[0].systemIcons,
+            styleViews = presetUsage1.viewUsage[0].style + presetUsage2.viewUsage[0].style,
+            customViews =
+                presetUsage1.viewUsage[0].customViews + presetUsage2.viewUsage[0].customViews,
+            softwareBitmaps =
+                presetUsage1.viewUsage[0].softwareBitmapsPenalty +
+                    presetUsage2.viewUsage[0].softwareBitmapsPenalty,
+            seenCount = 0
+        )
+    }
+
+    @Test
+    fun aggregateMemoryUsageData_correctlySeparatesDifferentStyles() {
+        val usage = getPresetMemoryUsages()
+        val aggregateUsage = aggregateMemoryUsageData(usage)
+
+        assertThat(aggregateUsage).hasSize(3)
+        assertThat(aggregateUsage)
+            .containsKey(Pair("package 1", NotificationEnums.STYLE_BIG_PICTURE))
+        assertThat(aggregateUsage).containsKey(Pair("package 1", NotificationEnums.STYLE_BIG_TEXT))
+
+        // Different style should be separate
+        val separateStyleData =
+            aggregateUsage[Pair("package 1", NotificationEnums.STYLE_BIG_TEXT)]!!
+        val presetUsage = usage[2]
+        assertAggregatedData(
+            separateStyleData,
+            1,
+            1,
+            presetUsage.objectUsage.smallIcon,
+            1,
+            presetUsage.objectUsage.largeIcon,
+            1,
+            presetUsage.objectUsage.bigPicture,
+            1,
+            presetUsage.objectUsage.extras,
+            presetUsage.objectUsage.extender,
+            presetUsage.viewUsage[0].smallIcon,
+            presetUsage.viewUsage[0].largeIcon,
+            presetUsage.viewUsage[0].systemIcons,
+            presetUsage.viewUsage[0].style,
+            presetUsage.viewUsage[0].customViews,
+            presetUsage.viewUsage[0].softwareBitmapsPenalty,
+            0
+        )
+    }
+
+    @Test
+    fun aggregateMemoryUsageData_correctlySeparatesDifferentProcess() {
+        val usage = getPresetMemoryUsages()
+        val aggregateUsage = aggregateMemoryUsageData(usage)
+
+        assertThat(aggregateUsage).hasSize(3)
+        assertThat(aggregateUsage)
+            .containsKey(Pair("package 2", NotificationEnums.STYLE_BIG_PICTURE))
+
+        // Different UID/package should also be separate
+        val separatePackageData =
+            aggregateUsage[Pair("package 2", NotificationEnums.STYLE_BIG_PICTURE)]!!
+        val presetUsage = usage[3]
+        assertAggregatedData(
+            separatePackageData,
+            1,
+            1,
+            presetUsage.objectUsage.smallIcon,
+            1,
+            presetUsage.objectUsage.largeIcon,
+            1,
+            presetUsage.objectUsage.bigPicture,
+            1,
+            presetUsage.objectUsage.extras,
+            presetUsage.objectUsage.extender,
+            presetUsage.viewUsage[0].smallIcon,
+            presetUsage.viewUsage[0].largeIcon,
+            presetUsage.viewUsage[0].systemIcons,
+            presetUsage.viewUsage[0].style,
+            presetUsage.viewUsage[0].customViews,
+            presetUsage.viewUsage[0].softwareBitmapsPenalty,
+            0
+        )
+    }
+
     private fun createLoggerWithNotifications(
         notifications: List<Notification>
     ): NotificationMemoryLogger {
@@ -124,4 +265,182 @@
         whenever(pipeline.allNotifs).thenReturn(notifications)
         return NotificationMemoryLogger(pipeline, statsManager, immediate, bgExecutor)
     }
+
+    /**
+     * Short hand for making sure the passed NotificationMemoryUseAtomBuilder object contains
+     * expected values.
+     */
+    private fun assertAggregatedData(
+        value: NotificationMemoryLogger.NotificationMemoryUseAtomBuilder,
+        count: Int,
+        countWithInflatedViews: Int,
+        smallIconObject: Int,
+        smallIconBitmapCount: Int,
+        largeIconObject: Int,
+        largeIconBitmapCount: Int,
+        bigPictureObject: Int,
+        bigPictureBitmapCount: Int,
+        extras: Int,
+        extenders: Int,
+        smallIconViews: Int,
+        largeIconViews: Int,
+        systemIconViews: Int,
+        styleViews: Int,
+        customViews: Int,
+        softwareBitmaps: Int,
+        seenCount: Int
+    ) {
+        expect.withMessage("count").that(value.count).isEqualTo(count)
+        expect
+            .withMessage("countWithInflatedViews")
+            .that(value.countWithInflatedViews)
+            .isEqualTo(countWithInflatedViews)
+        expect.withMessage("smallIconObject").that(value.smallIconObject).isEqualTo(smallIconObject)
+        expect
+            .withMessage("smallIconBitmapCount")
+            .that(value.smallIconBitmapCount)
+            .isEqualTo(smallIconBitmapCount)
+        expect.withMessage("largeIconObject").that(value.largeIconObject).isEqualTo(largeIconObject)
+        expect
+            .withMessage("largeIconBitmapCount")
+            .that(value.largeIconBitmapCount)
+            .isEqualTo(largeIconBitmapCount)
+        expect
+            .withMessage("bigPictureObject")
+            .that(value.bigPictureObject)
+            .isEqualTo(bigPictureObject)
+        expect
+            .withMessage("bigPictureBitmapCount")
+            .that(value.bigPictureBitmapCount)
+            .isEqualTo(bigPictureBitmapCount)
+        expect.withMessage("extras").that(value.extras).isEqualTo(extras)
+        expect.withMessage("extenders").that(value.extenders).isEqualTo(extenders)
+        expect.withMessage("smallIconViews").that(value.smallIconViews).isEqualTo(smallIconViews)
+        expect.withMessage("largeIconViews").that(value.largeIconViews).isEqualTo(largeIconViews)
+        expect.withMessage("systemIconViews").that(value.systemIconViews).isEqualTo(systemIconViews)
+        expect.withMessage("styleViews").that(value.styleViews).isEqualTo(styleViews)
+        expect.withMessage("customViews").that(value.customViews).isEqualTo(customViews)
+        expect.withMessage("softwareBitmaps").that(value.softwareBitmaps).isEqualTo(softwareBitmaps)
+        expect.withMessage("seenCount").that(value.seenCount).isEqualTo(seenCount)
+    }
+
+    /** Generates a static set of [NotificationMemoryUsage] objects. */
+    private fun getPresetMemoryUsages() =
+        listOf(
+            // A pair of notifications that have to be aggregated, same UID and style
+            NotificationMemoryUsage(
+                "package 1",
+                384,
+                "key1",
+                Notification.Builder(context).setStyle(Notification.BigPictureStyle()).build(),
+                NotificationObjectUsage(
+                    23,
+                    45,
+                    67,
+                    NotificationEnums.STYLE_BIG_PICTURE,
+                    12,
+                    483,
+                    4382,
+                    true
+                ),
+                listOf(
+                    NotificationViewUsage(ViewType.TOTAL, 493, 584, 4833, 584, 4888, 5843),
+                    NotificationViewUsage(
+                        ViewType.PRIVATE_CONTRACTED_VIEW,
+                        100,
+                        250,
+                        300,
+                        594,
+                        6000,
+                        5843
+                    )
+                )
+            ),
+            NotificationMemoryUsage(
+                "package 1",
+                384,
+                "key2",
+                Notification.Builder(context).setStyle(Notification.BigPictureStyle()).build(),
+                NotificationObjectUsage(
+                    77,
+                    54,
+                    34,
+                    NotificationEnums.STYLE_BIG_PICTURE,
+                    77,
+                    432,
+                    2342,
+                    true
+                ),
+                listOf(
+                    NotificationViewUsage(ViewType.TOTAL, 3245, 1234, 7653, 543, 765, 7655),
+                    NotificationViewUsage(
+                        ViewType.PRIVATE_CONTRACTED_VIEW,
+                        160,
+                        350,
+                        300,
+                        5544,
+                        66500,
+                        5433
+                    )
+                )
+            ),
+            // Different style is different aggregation
+            NotificationMemoryUsage(
+                "package 1",
+                384,
+                "key2",
+                Notification.Builder(context).setStyle(Notification.BigTextStyle()).build(),
+                NotificationObjectUsage(
+                    77,
+                    54,
+                    34,
+                    NotificationEnums.STYLE_BIG_TEXT,
+                    77,
+                    432,
+                    2342,
+                    true
+                ),
+                listOf(
+                    NotificationViewUsage(ViewType.TOTAL, 3245, 1234, 7653, 543, 765, 7655),
+                    NotificationViewUsage(
+                        ViewType.PRIVATE_CONTRACTED_VIEW,
+                        160,
+                        350,
+                        300,
+                        5544,
+                        66500,
+                        5433
+                    )
+                )
+            ),
+            // Different package is also different aggregation
+            NotificationMemoryUsage(
+                "package 2",
+                684,
+                "key2",
+                Notification.Builder(context).setStyle(Notification.BigPictureStyle()).build(),
+                NotificationObjectUsage(
+                    32,
+                    654,
+                    234,
+                    NotificationEnums.STYLE_BIG_PICTURE,
+                    211,
+                    776,
+                    435,
+                    true
+                ),
+                listOf(
+                    NotificationViewUsage(ViewType.TOTAL, 4355, 6543, 4322, 5435, 6546, 65485),
+                    NotificationViewUsage(
+                        ViewType.PRIVATE_CONTRACTED_VIEW,
+                        6546,
+                        7657,
+                        4353,
+                        6546,
+                        76575,
+                        54654
+                    )
+                )
+            )
+        )
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 4559a23..9e23d54 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -82,19 +82,14 @@
 
 import java.util.Arrays;
 import java.util.List;
+import java.util.function.Consumer;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper
 public class ExpandableNotificationRowTest extends SysuiTestCase {
 
-    private ExpandableNotificationRow mGroupRow;
-    private ExpandableNotificationRow mNotifRow;
-    private ExpandableNotificationRow mPublicRow;
-
     private NotificationTestHelper mNotificationTestHelper;
-    boolean mHeadsUpAnimatingAway = false;
-
     @Rule public MockitoRule mockito = MockitoJUnit.rule();
 
     @Before
@@ -105,112 +100,108 @@
                 mDependency,
                 TestableLooper.get(this));
         mNotificationTestHelper.setDefaultInflationFlags(FLAG_CONTENT_VIEW_ALL);
+
         FakeFeatureFlags fakeFeatureFlags = new FakeFeatureFlags();
         fakeFeatureFlags.set(Flags.NOTIFICATION_ANIMATE_BIG_PICTURE, true);
         mNotificationTestHelper.setFeatureFlags(fakeFeatureFlags);
-        // create a standard private notification row
-        Notification normalNotif = mNotificationTestHelper.createNotification();
-        normalNotif.publicVersion = null;
-        mNotifRow = mNotificationTestHelper.createRow(normalNotif);
+    }
+
+    @Test
+    public void testUpdateBackgroundColors_isRecursive() throws Exception {
+        ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
+        group.setTintColor(Color.RED);
+        group.getChildNotificationAt(0).setTintColor(Color.GREEN);
+        group.getChildNotificationAt(1).setTintColor(Color.BLUE);
+
+        assertThat(group.getCurrentBackgroundTint()).isEqualTo(Color.RED);
+        assertThat(group.getChildNotificationAt(0).getCurrentBackgroundTint())
+                .isEqualTo(Color.GREEN);
+        assertThat(group.getChildNotificationAt(1).getCurrentBackgroundTint())
+                .isEqualTo(Color.BLUE);
+
+        group.updateBackgroundColors();
+
+        int resetTint = group.getCurrentBackgroundTint();
+        assertThat(resetTint).isNotEqualTo(Color.RED);
+        assertThat(group.getChildNotificationAt(0).getCurrentBackgroundTint())
+                .isEqualTo(resetTint);
+        assertThat(group.getChildNotificationAt(1).getCurrentBackgroundTint())
+                .isEqualTo(resetTint);
+    }
+
+    @Test
+    public void testSetSensitiveOnNotifRowNotifiesOfHeightChange() throws Exception {
+        // GIVEN a sensitive notification row that's currently redacted
+        ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+        measureAndLayout(row);
+        row.setHideSensitiveForIntrinsicHeight(true);
+        row.setSensitive(true, true);
+        assertThat(row.getShowingLayout()).isSameInstanceAs(row.getPublicLayout());
+        assertThat(row.getIntrinsicHeight()).isGreaterThan(0);
+
+        // GIVEN that the row has a height change listener
+        OnHeightChangedListener listener = mock(OnHeightChangedListener.class);
+        row.setOnHeightChangedListener(listener);
+
+        // WHEN the row is set to no longer be sensitive
+        row.setSensitive(false, true);
+
+        // VERIFY that the height change listener is invoked
+        assertThat(row.getShowingLayout()).isSameInstanceAs(row.getPrivateLayout());
+        assertThat(row.getIntrinsicHeight()).isGreaterThan(0);
+        verify(listener).onHeightChanged(eq(row), eq(false));
+    }
+
+    @Test
+    public void testSetSensitiveOnGroupRowNotifiesOfHeightChange() throws Exception {
+        // GIVEN a sensitive group row that's currently redacted
+        ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
+        measureAndLayout(group);
+        group.setHideSensitiveForIntrinsicHeight(true);
+        group.setSensitive(true, true);
+        assertThat(group.getShowingLayout()).isSameInstanceAs(group.getPublicLayout());
+        assertThat(group.getIntrinsicHeight()).isGreaterThan(0);
+
+        // GIVEN that the row has a height change listener
+        OnHeightChangedListener listener = mock(OnHeightChangedListener.class);
+        group.setOnHeightChangedListener(listener);
+
+        // WHEN the row is set to no longer be sensitive
+        group.setSensitive(false, true);
+
+        // VERIFY that the height change listener is invoked
+        assertThat(group.getShowingLayout()).isSameInstanceAs(group.getPrivateLayout());
+        assertThat(group.getIntrinsicHeight()).isGreaterThan(0);
+        verify(listener).onHeightChanged(eq(group), eq(false));
+    }
+
+    @Test
+    public void testSetSensitiveOnPublicRowDoesNotNotifyOfHeightChange() throws Exception {
         // create a notification row whose public version is identical
         Notification publicNotif = mNotificationTestHelper.createNotification();
         publicNotif.publicVersion = mNotificationTestHelper.createNotification();
-        mPublicRow = mNotificationTestHelper.createRow(publicNotif);
-        // create a group row
-        mGroupRow = mNotificationTestHelper.createGroup();
-        mGroupRow.setHeadsUpAnimatingAwayListener(
-                animatingAway -> mHeadsUpAnimatingAway = animatingAway);
+        ExpandableNotificationRow publicRow = mNotificationTestHelper.createRow(publicNotif);
 
-    }
-
-    @Test
-    public void testUpdateBackgroundColors_isRecursive() {
-        mGroupRow.setTintColor(Color.RED);
-        mGroupRow.getChildNotificationAt(0).setTintColor(Color.GREEN);
-        mGroupRow.getChildNotificationAt(1).setTintColor(Color.BLUE);
-
-        assertThat(mGroupRow.getCurrentBackgroundTint()).isEqualTo(Color.RED);
-        assertThat(mGroupRow.getChildNotificationAt(0).getCurrentBackgroundTint())
-                .isEqualTo(Color.GREEN);
-        assertThat(mGroupRow.getChildNotificationAt(1).getCurrentBackgroundTint())
-                .isEqualTo(Color.BLUE);
-
-        mGroupRow.updateBackgroundColors();
-
-        int resetTint = mGroupRow.getCurrentBackgroundTint();
-        assertThat(resetTint).isNotEqualTo(Color.RED);
-        assertThat(mGroupRow.getChildNotificationAt(0).getCurrentBackgroundTint())
-                .isEqualTo(resetTint);
-        assertThat(mGroupRow.getChildNotificationAt(1).getCurrentBackgroundTint())
-                .isEqualTo(resetTint);
-    }
-
-    @Test
-    public void testSetSensitiveOnNotifRowNotifiesOfHeightChange() throws InterruptedException {
-        // GIVEN a sensitive notification row that's currently redacted
-        measureAndLayout(mNotifRow);
-        mNotifRow.setHideSensitiveForIntrinsicHeight(true);
-        mNotifRow.setSensitive(true, true);
-        assertThat(mNotifRow.getShowingLayout()).isSameInstanceAs(mNotifRow.getPublicLayout());
-        assertThat(mNotifRow.getIntrinsicHeight()).isGreaterThan(0);
-
-        // GIVEN that the row has a height change listener
-        OnHeightChangedListener listener = mock(OnHeightChangedListener.class);
-        mNotifRow.setOnHeightChangedListener(listener);
-
-        // WHEN the row is set to no longer be sensitive
-        mNotifRow.setSensitive(false, true);
-
-        // VERIFY that the height change listener is invoked
-        assertThat(mNotifRow.getShowingLayout()).isSameInstanceAs(mNotifRow.getPrivateLayout());
-        assertThat(mNotifRow.getIntrinsicHeight()).isGreaterThan(0);
-        verify(listener).onHeightChanged(eq(mNotifRow), eq(false));
-    }
-
-    @Test
-    public void testSetSensitiveOnGroupRowNotifiesOfHeightChange() {
-        // GIVEN a sensitive group row that's currently redacted
-        measureAndLayout(mGroupRow);
-        mGroupRow.setHideSensitiveForIntrinsicHeight(true);
-        mGroupRow.setSensitive(true, true);
-        assertThat(mGroupRow.getShowingLayout()).isSameInstanceAs(mGroupRow.getPublicLayout());
-        assertThat(mGroupRow.getIntrinsicHeight()).isGreaterThan(0);
-
-        // GIVEN that the row has a height change listener
-        OnHeightChangedListener listener = mock(OnHeightChangedListener.class);
-        mGroupRow.setOnHeightChangedListener(listener);
-
-        // WHEN the row is set to no longer be sensitive
-        mGroupRow.setSensitive(false, true);
-
-        // VERIFY that the height change listener is invoked
-        assertThat(mGroupRow.getShowingLayout()).isSameInstanceAs(mGroupRow.getPrivateLayout());
-        assertThat(mGroupRow.getIntrinsicHeight()).isGreaterThan(0);
-        verify(listener).onHeightChanged(eq(mGroupRow), eq(false));
-    }
-
-    @Test
-    public void testSetSensitiveOnPublicRowDoesNotNotifyOfHeightChange() {
         // GIVEN a sensitive public row that's currently redacted
-        measureAndLayout(mPublicRow);
-        mPublicRow.setHideSensitiveForIntrinsicHeight(true);
-        mPublicRow.setSensitive(true, true);
-        assertThat(mPublicRow.getShowingLayout()).isSameInstanceAs(mPublicRow.getPublicLayout());
-        assertThat(mPublicRow.getIntrinsicHeight()).isGreaterThan(0);
+        measureAndLayout(publicRow);
+        publicRow.setHideSensitiveForIntrinsicHeight(true);
+        publicRow.setSensitive(true, true);
+        assertThat(publicRow.getShowingLayout()).isSameInstanceAs(publicRow.getPublicLayout());
+        assertThat(publicRow.getIntrinsicHeight()).isGreaterThan(0);
 
         // GIVEN that the row has a height change listener
         OnHeightChangedListener listener = mock(OnHeightChangedListener.class);
-        mPublicRow.setOnHeightChangedListener(listener);
+        publicRow.setOnHeightChangedListener(listener);
 
         // WHEN the row is set to no longer be sensitive
-        mPublicRow.setSensitive(false, true);
+        publicRow.setSensitive(false, true);
 
         // VERIFY that the height change listener is not invoked, because the height didn't change
-        assertThat(mPublicRow.getShowingLayout()).isSameInstanceAs(mPublicRow.getPrivateLayout());
-        assertThat(mPublicRow.getIntrinsicHeight()).isGreaterThan(0);
-        assertThat(mPublicRow.getPrivateLayout().getMinHeight())
-                .isEqualTo(mPublicRow.getPublicLayout().getMinHeight());
-        verify(listener, never()).onHeightChanged(eq(mPublicRow), eq(false));
+        assertThat(publicRow.getShowingLayout()).isSameInstanceAs(publicRow.getPrivateLayout());
+        assertThat(publicRow.getIntrinsicHeight()).isGreaterThan(0);
+        assertThat(publicRow.getPrivateLayout().getMinHeight())
+                .isEqualTo(publicRow.getPublicLayout().getMinHeight());
+        verify(listener, never()).onHeightChanged(eq(publicRow), eq(false));
     }
 
     private void measureAndLayout(ExpandableNotificationRow row) {
@@ -227,36 +218,43 @@
     }
 
     @Test
-    public void testGroupSummaryNotShowingIconWhenPublic() {
-        mGroupRow.setSensitive(true, true);
-        mGroupRow.setHideSensitiveForIntrinsicHeight(true);
-        assertTrue(mGroupRow.isSummaryWithChildren());
-        assertFalse(mGroupRow.isShowingIcon());
+    public void testGroupSummaryNotShowingIconWhenPublic() throws Exception {
+        ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
+
+        group.setSensitive(true, true);
+        group.setHideSensitiveForIntrinsicHeight(true);
+        assertTrue(group.isSummaryWithChildren());
+        assertFalse(group.isShowingIcon());
     }
 
     @Test
-    public void testNotificationHeaderVisibleWhenAnimating() {
-        mGroupRow.setSensitive(true, true);
-        mGroupRow.setHideSensitive(true, false, 0, 0);
-        mGroupRow.setHideSensitive(false, true, 0, 0);
-        assertEquals(View.VISIBLE, mGroupRow.getChildrenContainer().getVisibleWrapper()
+    public void testNotificationHeaderVisibleWhenAnimating() throws Exception {
+        ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
+
+        group.setSensitive(true, true);
+        group.setHideSensitive(true, false, 0, 0);
+        group.setHideSensitive(false, true, 0, 0);
+        assertEquals(View.VISIBLE, group.getChildrenContainer().getVisibleWrapper()
                 .getNotificationHeader().getVisibility());
     }
 
     @Test
-    public void testUserLockedResetEvenWhenNoChildren() {
-        mGroupRow.setUserLocked(true);
-        mGroupRow.setUserLocked(false);
+    public void testUserLockedResetEvenWhenNoChildren() throws Exception {
+        ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
+
+        group.setUserLocked(true);
+        group.setUserLocked(false);
         assertFalse("The childrencontainer should not be userlocked but is, the state "
-                + "seems out of sync.", mGroupRow.getChildrenContainer().isUserLocked());
+                + "seems out of sync.", group.getChildrenContainer().isUserLocked());
     }
 
     @Test
-    public void testReinflatedOnDensityChange() {
+    public void testReinflatedOnDensityChange() throws Exception {
+        ExpandableNotificationRow row = mNotificationTestHelper.createRow();
         NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class);
-        mNotifRow.setChildrenContainer(mockContainer);
+        row.setChildrenContainer(mockContainer);
 
-        mNotifRow.onDensityOrFontScaleChanged();
+        row.onDensityOrFontScaleChanged();
 
         verify(mockContainer).reInflateViews(any(), any());
     }
@@ -299,64 +297,73 @@
     @Test
     public void testAboveShelfChangedListenerCalledWhenGoingBelow() throws Exception {
         ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        row.setHeadsUp(true);
         AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class);
         row.setAboveShelfChangedListener(listener);
+        Mockito.reset(listener);
+        row.setHeadsUp(true);
         row.setAboveShelf(false);
         verify(listener).onAboveShelfStateChanged(false);
     }
 
     @Test
     public void testClickSound() throws Exception {
-        assertTrue("Should play sounds by default.", mGroupRow.isSoundEffectsEnabled());
+        ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
+
+        assertTrue("Should play sounds by default.", group.isSoundEffectsEnabled());
         StatusBarStateController mock = mNotificationTestHelper.getStatusBarStateController();
         when(mock.isDozing()).thenReturn(true);
-        mGroupRow.setSecureStateProvider(()-> false);
+        group.setSecureStateProvider(()-> false);
         assertFalse("Shouldn't play sounds when dark and trusted.",
-                mGroupRow.isSoundEffectsEnabled());
-        mGroupRow.setSecureStateProvider(()-> true);
+                group.isSoundEffectsEnabled());
+        group.setSecureStateProvider(()-> true);
         assertTrue("Should always play sounds when not trusted.",
-                mGroupRow.isSoundEffectsEnabled());
+                group.isSoundEffectsEnabled());
     }
 
     @Test
-    public void testSetDismissed_longPressListenerRemoved() {
+    public void testSetDismissed_longPressListenerRemoved() throws Exception {
+        ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
+
         ExpandableNotificationRow.LongPressListener listener =
                 mock(ExpandableNotificationRow.LongPressListener.class);
-        mGroupRow.setLongPressListener(listener);
-        mGroupRow.doLongClickCallback(0,0);
-        verify(listener, times(1)).onLongPress(eq(mGroupRow), eq(0), eq(0),
+        group.setLongPressListener(listener);
+        group.doLongClickCallback(0, 0);
+        verify(listener, times(1)).onLongPress(eq(group), eq(0), eq(0),
                 any(NotificationMenuRowPlugin.MenuItem.class));
         reset(listener);
 
-        mGroupRow.dismiss(true);
-        mGroupRow.doLongClickCallback(0,0);
-        verify(listener, times(0)).onLongPress(eq(mGroupRow), eq(0), eq(0),
+        group.dismiss(true);
+        group.doLongClickCallback(0, 0);
+        verify(listener, times(0)).onLongPress(eq(group), eq(0), eq(0),
                 any(NotificationMenuRowPlugin.MenuItem.class));
     }
 
     @Test
-    public void testFeedback_noHeader() {
+    public void testFeedback_noHeader() throws Exception {
+        ExpandableNotificationRow groupRow = mNotificationTestHelper.createGroup();
+
         // public notification is custom layout - no header
-        mGroupRow.setSensitive(true, true);
-        mGroupRow.setOnFeedbackClickListener(null);
-        mGroupRow.setFeedbackIcon(null);
+        groupRow.setSensitive(true, true);
+        groupRow.setOnFeedbackClickListener(null);
+        groupRow.setFeedbackIcon(null);
     }
 
     @Test
-    public void testFeedback_header() {
+    public void testFeedback_header() throws Exception {
+        ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
+
         NotificationContentView publicLayout = mock(NotificationContentView.class);
-        mGroupRow.setPublicLayout(publicLayout);
+        group.setPublicLayout(publicLayout);
         NotificationContentView privateLayout = mock(NotificationContentView.class);
-        mGroupRow.setPrivateLayout(privateLayout);
+        group.setPrivateLayout(privateLayout);
         NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class);
         when(mockContainer.getNotificationChildCount()).thenReturn(1);
-        mGroupRow.setChildrenContainer(mockContainer);
+        group.setChildrenContainer(mockContainer);
 
         final boolean show = true;
         final FeedbackIcon icon = new FeedbackIcon(
                 R.drawable.ic_feedback_alerted, R.string.notification_feedback_indicator_alerted);
-        mGroupRow.setFeedbackIcon(icon);
+        group.setFeedbackIcon(icon);
 
         verify(mockContainer, times(1)).setFeedbackIcon(icon);
         verify(privateLayout, times(1)).setFeedbackIcon(icon);
@@ -364,43 +371,60 @@
     }
 
     @Test
-    public void testFeedbackOnClick() {
+    public void testFeedbackOnClick() throws Exception {
+        ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
+
         ExpandableNotificationRow.CoordinateOnClickListener l = mock(
                 ExpandableNotificationRow.CoordinateOnClickListener.class);
         View view = mock(View.class);
 
-        mGroupRow.setOnFeedbackClickListener(l);
+        group.setOnFeedbackClickListener(l);
 
-        mGroupRow.getFeedbackOnClickListener().onClick(view);
+        group.getFeedbackOnClickListener().onClick(view);
         verify(l, times(1)).onClick(any(), anyInt(), anyInt(), any());
     }
 
     @Test
-    public void testHeadsUpAnimatingAwayListener() {
-        mGroupRow.setHeadsUpAnimatingAway(true);
-        Assert.assertEquals(true, mHeadsUpAnimatingAway);
-        mGroupRow.setHeadsUpAnimatingAway(false);
-        Assert.assertEquals(false, mHeadsUpAnimatingAway);
+    public void testHeadsUpAnimatingAwayListener() throws Exception {
+        ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
+        Consumer<Boolean> headsUpListener = mock(Consumer.class);
+        AboveShelfChangedListener aboveShelfChangedListener = mock(AboveShelfChangedListener.class);
+        group.setHeadsUpAnimatingAwayListener(headsUpListener);
+        group.setAboveShelfChangedListener(aboveShelfChangedListener);
+
+        group.setHeadsUpAnimatingAway(true);
+        verify(headsUpListener).accept(true);
+        verify(aboveShelfChangedListener).onAboveShelfStateChanged(true);
+
+        group.setHeadsUpAnimatingAway(false);
+        verify(headsUpListener).accept(false);
+        verify(aboveShelfChangedListener).onAboveShelfStateChanged(false);
     }
 
     @Test
-    public void testIsBlockingHelperShowing_isCorrectlyUpdated() {
-        mGroupRow.setBlockingHelperShowing(true);
-        assertTrue(mGroupRow.isBlockingHelperShowing());
+    public void testIsBlockingHelperShowing_isCorrectlyUpdated() throws Exception {
+        ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
 
-        mGroupRow.setBlockingHelperShowing(false);
-        assertFalse(mGroupRow.isBlockingHelperShowing());
+        group.setBlockingHelperShowing(true);
+        assertTrue(group.isBlockingHelperShowing());
+
+        group.setBlockingHelperShowing(false);
+        assertFalse(group.isBlockingHelperShowing());
     }
 
     @Test
-    public void testGetNumUniqueChildren_defaultChannel() {
-        assertEquals(1, mGroupRow.getNumUniqueChannels());
+    public void testGetNumUniqueChildren_defaultChannel() throws Exception {
+        ExpandableNotificationRow groupRow = mNotificationTestHelper.createGroup();
+
+        assertEquals(1, groupRow.getNumUniqueChannels());
     }
 
     @Test
-    public void testGetNumUniqueChildren_multiChannel() {
+    public void testGetNumUniqueChildren_multiChannel() throws Exception {
+        ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
+
         List<ExpandableNotificationRow> childRows =
-                mGroupRow.getChildrenContainer().getAttachedChildren();
+                group.getChildrenContainer().getAttachedChildren();
         // Give each child a unique channel id/name.
         int i = 0;
         for (ExpandableNotificationRow childRow : childRows) {
@@ -412,25 +436,29 @@
             i++;
         }
 
-        assertEquals(3, mGroupRow.getNumUniqueChannels());
+        assertEquals(3, group.getNumUniqueChannels());
     }
 
     @Test
     public void testIconScrollXAfterTranslationAndReset() throws Exception {
-        mGroupRow.setDismissUsingRowTranslationX(false);
-        mGroupRow.setTranslation(50);
-        assertEquals(50, -mGroupRow.getEntry().getIcons().getShelfIcon().getScrollX());
+        ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
 
-        mGroupRow.resetTranslation();
-        assertEquals(0, mGroupRow.getEntry().getIcons().getShelfIcon().getScrollX());
+        group.setDismissUsingRowTranslationX(false);
+        group.setTranslation(50);
+        assertEquals(50, -group.getEntry().getIcons().getShelfIcon().getScrollX());
+
+        group.resetTranslation();
+        assertEquals(0, group.getEntry().getIcons().getShelfIcon().getScrollX());
     }
 
     @Test
-    public void testIsExpanded_userExpanded() {
-        mGroupRow.setExpandable(true);
-        Assert.assertFalse(mGroupRow.isExpanded());
-        mGroupRow.setUserExpanded(true);
-        Assert.assertTrue(mGroupRow.isExpanded());
+    public void testIsExpanded_userExpanded() throws Exception {
+        ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
+
+        group.setExpandable(true);
+        Assert.assertFalse(group.isExpanded());
+        group.setUserExpanded(true);
+        Assert.assertTrue(group.isExpanded());
     }
 
     @Test
@@ -549,72 +577,80 @@
     }
 
     @Test
-    public void applyRoundnessAndInv_should_be_immediately_applied_on_childrenContainer_legacy() {
-        mGroupRow.useRoundnessSourceTypes(false);
-        Assert.assertEquals(0f, mGroupRow.getBottomRoundness(), 0.001f);
-        Assert.assertEquals(0f, mGroupRow.getChildrenContainer().getBottomRoundness(), 0.001f);
+    public void applyRoundnessAndInv_should_be_immediately_applied_on_childrenContainer_legacy()
+            throws Exception {
+        ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
+        group.useRoundnessSourceTypes(false);
+        Assert.assertEquals(0f, group.getBottomRoundness(), 0.001f);
+        Assert.assertEquals(0f, group.getChildrenContainer().getBottomRoundness(), 0.001f);
 
-        mGroupRow.requestBottomRoundness(1f, SourceType.from(""), false);
+        group.requestBottomRoundness(1f, SourceType.from(""), false);
 
-        Assert.assertEquals(1f, mGroupRow.getBottomRoundness(), 0.001f);
-        Assert.assertEquals(1f, mGroupRow.getChildrenContainer().getBottomRoundness(), 0.001f);
+        Assert.assertEquals(1f, group.getBottomRoundness(), 0.001f);
+        Assert.assertEquals(1f, group.getChildrenContainer().getBottomRoundness(), 0.001f);
     }
 
     @Test
-    public void applyRoundnessAndInvalidate_should_be_immediately_applied_on_childrenContainer() {
-        mGroupRow.useRoundnessSourceTypes(true);
-        Assert.assertEquals(0f, mGroupRow.getBottomRoundness(), 0.001f);
-        Assert.assertEquals(0f, mGroupRow.getChildrenContainer().getBottomRoundness(), 0.001f);
+    public void applyRoundnessAndInvalidate_should_be_immediately_applied_on_childrenContainer()
+            throws Exception {
+        ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
+        group.useRoundnessSourceTypes(true);
+        Assert.assertEquals(0f, group.getBottomRoundness(), 0.001f);
+        Assert.assertEquals(0f, group.getChildrenContainer().getBottomRoundness(), 0.001f);
 
-        mGroupRow.requestBottomRoundness(1f, SourceType.from(""), false);
+        group.requestBottomRoundness(1f, SourceType.from(""), false);
 
-        Assert.assertEquals(1f, mGroupRow.getBottomRoundness(), 0.001f);
-        Assert.assertEquals(1f, mGroupRow.getChildrenContainer().getBottomRoundness(), 0.001f);
+        Assert.assertEquals(1f, group.getBottomRoundness(), 0.001f);
+        Assert.assertEquals(1f, group.getChildrenContainer().getBottomRoundness(), 0.001f);
     }
 
     @Test
     public void testSetContentAnimationRunning_Run() throws Exception {
         // Create views for the notification row.
+        ExpandableNotificationRow row = mNotificationTestHelper.createRow();
         NotificationContentView publicLayout = mock(NotificationContentView.class);
-        mNotifRow.setPublicLayout(publicLayout);
+        row.setPublicLayout(publicLayout);
         NotificationContentView privateLayout = mock(NotificationContentView.class);
-        mNotifRow.setPrivateLayout(privateLayout);
+        row.setPrivateLayout(privateLayout);
 
-        mNotifRow.setAnimationRunning(true);
+        row.setAnimationRunning(true);
         verify(publicLayout, times(1)).setContentAnimationRunning(true);
         verify(privateLayout, times(1)).setContentAnimationRunning(true);
     }
 
     @Test
-    public void testSetContentAnimationRunning_Stop() {
+    public void testSetContentAnimationRunning_Stop() throws Exception {
         // Create views for the notification row.
+        ExpandableNotificationRow row = mNotificationTestHelper.createRow();
         NotificationContentView publicLayout = mock(NotificationContentView.class);
-        mNotifRow.setPublicLayout(publicLayout);
+        row.setPublicLayout(publicLayout);
         NotificationContentView privateLayout = mock(NotificationContentView.class);
-        mNotifRow.setPrivateLayout(privateLayout);
+        row.setPrivateLayout(privateLayout);
 
-        mNotifRow.setAnimationRunning(false);
+        row.setAnimationRunning(false);
         verify(publicLayout, times(1)).setContentAnimationRunning(false);
         verify(privateLayout, times(1)).setContentAnimationRunning(false);
     }
 
     @Test
-    public void testSetContentAnimationRunningInGroupChild_Run() {
-        // Creates parent views on mGroupRow.
+    public void testSetContentAnimationRunningInGroupChild_Run() throws Exception {
+        // Creates parent views on groupRow.
+        ExpandableNotificationRow groupRow = mNotificationTestHelper.createGroup();
         NotificationContentView publicParentLayout = mock(NotificationContentView.class);
-        mGroupRow.setPublicLayout(publicParentLayout);
+        groupRow.setPublicLayout(publicParentLayout);
         NotificationContentView privateParentLayout = mock(NotificationContentView.class);
-        mGroupRow.setPrivateLayout(privateParentLayout);
+        groupRow.setPrivateLayout(privateParentLayout);
 
-        // Create child views on mNotifRow.
+        // Create child views on row.
+        ExpandableNotificationRow row = mNotificationTestHelper.createRow();
         NotificationContentView publicChildLayout = mock(NotificationContentView.class);
-        mNotifRow.setPublicLayout(publicChildLayout);
+        row.setPublicLayout(publicChildLayout);
         NotificationContentView privateChildLayout = mock(NotificationContentView.class);
-        mNotifRow.setPrivateLayout(privateChildLayout);
-        when(mNotifRow.isGroupExpanded()).thenReturn(true);
-        setMockChildrenContainer(mGroupRow, mNotifRow);
+        row.setPrivateLayout(privateChildLayout);
+        when(row.isGroupExpanded()).thenReturn(true);
+        setMockChildrenContainer(groupRow, row);
 
-        mGroupRow.setAnimationRunning(true);
+        groupRow.setAnimationRunning(true);
         verify(publicParentLayout, times(1)).setContentAnimationRunning(true);
         verify(privateParentLayout, times(1)).setContentAnimationRunning(true);
         // The child layouts should be started too.
@@ -624,23 +660,25 @@
 
 
     @Test
-    public void testSetIconAnimationRunningGroup_Run() {
+    public void testSetIconAnimationRunningGroup_Run() throws Exception {
         // Create views for a group row.
+        ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
+        ExpandableNotificationRow child = mNotificationTestHelper.createRow();
         NotificationContentView publicParentLayout = mock(NotificationContentView.class);
-        mGroupRow.setPublicLayout(publicParentLayout);
+        group.setPublicLayout(publicParentLayout);
         NotificationContentView privateParentLayout = mock(NotificationContentView.class);
-        mGroupRow.setPrivateLayout(privateParentLayout);
-        when(mGroupRow.isGroupExpanded()).thenReturn(true);
+        group.setPrivateLayout(privateParentLayout);
+        when(group.isGroupExpanded()).thenReturn(true);
 
-        // Sets up mNotifRow as a child ExpandableNotificationRow.
+        // Add the child to the group.
         NotificationContentView publicChildLayout = mock(NotificationContentView.class);
-        mNotifRow.setPublicLayout(publicChildLayout);
+        child.setPublicLayout(publicChildLayout);
         NotificationContentView privateChildLayout = mock(NotificationContentView.class);
-        mNotifRow.setPrivateLayout(privateChildLayout);
-        when(mNotifRow.isGroupExpanded()).thenReturn(true);
+        child.setPrivateLayout(privateChildLayout);
+        when(child.isGroupExpanded()).thenReturn(true);
 
         NotificationChildrenContainer mockContainer =
-                setMockChildrenContainer(mGroupRow, mNotifRow);
+                setMockChildrenContainer(group, child);
 
         // Mock the children view wrappers, and give them each an icon.
         NotificationViewWrapper mockViewWrapper = mock(NotificationViewWrapper.class);
@@ -663,7 +701,7 @@
         AnimatedVectorDrawable lowPriVectorDrawable = mock(AnimatedVectorDrawable.class);
         setDrawableIconsInImageView(mockLowPriorityIcon, lowPriDrawable, lowPriVectorDrawable);
 
-        mGroupRow.setAnimationRunning(true);
+        group.setAnimationRunning(true);
         verify(drawable, times(1)).start();
         verify(vectorDrawable, times(1)).start();
         verify(lowPriDrawable, times(1)).start();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index 5394d88..3face35 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -24,6 +24,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
@@ -42,6 +43,7 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
+import android.util.TypedValue;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.RemoteViews;
@@ -332,6 +334,38 @@
                 eq(FLAG_CONTENT_VIEW_HEADS_UP));
     }
 
+    @Test
+    public void testNotificationViewHeightTooSmallFailsValidation() {
+        View view = mock(View.class);
+        when(view.getHeight())
+                .thenReturn((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 10,
+                        mContext.getResources().getDisplayMetrics()));
+        String result = NotificationContentInflater.isValidView(view, mRow.getEntry(),
+                mContext.getResources());
+        assertNotNull(result);
+    }
+
+    @Test
+    public void testNotificationViewPassesValidation() {
+        View view = mock(View.class);
+        when(view.getHeight())
+                .thenReturn((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 17,
+                        mContext.getResources().getDisplayMetrics()));
+        String result = NotificationContentInflater.isValidView(view, mRow.getEntry(),
+                mContext.getResources());
+        assertNull(result);
+    }
+
+    @Test
+    public void testInvalidNotificationDoesNotInvokeCallback() throws Exception {
+        mRow.getPrivateLayout().removeAllViews();
+        mRow.getEntry().getSbn().getNotification().contentView =
+                new RemoteViews(mContext.getPackageName(), R.layout.invalid_notification_height);
+        inflateAndWait(true, mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
+        assertEquals(0, mRow.getPrivateLayout().getChildCount());
+        verify(mRow, times(0)).onNotificationUpdated();
+    }
+
     private static void inflateAndWait(NotificationContentInflater inflater,
             @InflationFlag int contentToInflate,
             ExpandableNotificationRow row)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index b6a1bb3..d7ac6b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -66,9 +66,9 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.testing.UiEventLoggerFake;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dump.DumpManager;
 import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -133,18 +133,13 @@
     @Mock private ShadeController mShadeController;
     @Mock private PeopleSpaceWidgetManager mPeopleSpaceWidgetManager;
     @Mock private AssistantFeedbackController mAssistantFeedbackController;
+    @Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
+    @Mock private StatusBarStateController mStatusBarStateController;
 
     @Before
     public void setUp() {
         mTestableLooper = TestableLooper.get(this);
         allowTestableLooperAsMainThread();
-        mDependency.injectTestDependency(DeviceProvisionedController.class,
-                mDeviceProvisionedController);
-        mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
-        mDependency.injectTestDependency(
-                OnUserInteractionCallback.class,
-                mOnUserInteractionCallback);
-        mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
         mHandler = Handler.createAsync(mTestableLooper.getLooper());
         mHelper = new NotificationTestHelper(mContext, mDependency, TestableLooper.get(this));
         when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
@@ -155,7 +150,11 @@
                 mPeopleSpaceWidgetManager, mLauncherApps, mShortcutManager,
                 mChannelEditorDialogController, mContextTracker, mAssistantFeedbackController,
                 Optional.of(mBubblesManager), new UiEventLoggerFake(), mOnUserInteractionCallback,
-                mShadeController);
+                mShadeController,
+                mNotificationLockscreenUserManager,
+                mStatusBarStateController,
+                mDeviceProvisionedController,
+                mMetricsLogger);
         mGutsManager.setUpWithPresenter(mPresenter, mNotificationListContainer,
                 mOnSettingsClickListener);
         mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
@@ -372,7 +371,8 @@
                 eq(false),
                 eq(false),
                 eq(true), /* wasShownHighPriority */
-                eq(mAssistantFeedbackController));
+                eq(mAssistantFeedbackController),
+                any(MetricsLogger.class));
     }
 
     @Test
@@ -406,7 +406,8 @@
                 eq(true),
                 eq(false),
                 eq(false), /* wasShownHighPriority */
-                eq(mAssistantFeedbackController));
+                eq(mAssistantFeedbackController),
+                any(MetricsLogger.class));
     }
 
     @Test
@@ -438,7 +439,8 @@
                 eq(false),
                 eq(false),
                 eq(false), /* wasShownHighPriority */
-                eq(mAssistantFeedbackController));
+                eq(mAssistantFeedbackController),
+                any(MetricsLogger.class));
     }
 
     ////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index 80a81a5..8dd0488 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -130,7 +130,6 @@
         mContext.addMockSystemService(TelecomManager.class, mTelecomManager);
 
         mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
-        mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
         // Inflate the layout
         final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
         mNotificationInfo = (NotificationInfo) layoutInflater.inflate(R.layout.notification_info,
@@ -194,7 +193,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final TextView textView = mNotificationInfo.findViewById(R.id.pkg_name);
         assertTrue(textView.getText().toString().contains("App Name"));
         assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
@@ -220,7 +220,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final ImageView iconView = mNotificationInfo.findViewById(R.id.pkg_icon);
         assertEquals(iconDrawable, iconView.getDrawable());
     }
@@ -242,7 +243,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
         assertEquals(GONE, nameView.getVisibility());
     }
@@ -273,7 +275,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
         assertEquals(VISIBLE, nameView.getVisibility());
         assertTrue(nameView.getText().toString().contains("Proxied"));
@@ -296,7 +299,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
         assertEquals(GONE, groupNameView.getVisibility());
     }
@@ -324,7 +328,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
         assertEquals(View.VISIBLE, groupNameView.getVisibility());
         assertEquals("Test Group Name", groupNameView.getText());
@@ -347,7 +352,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(TEST_CHANNEL_NAME, textView.getText());
     }
@@ -369,7 +375,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(GONE, textView.getVisibility());
     }
@@ -395,7 +402,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(VISIBLE, textView.getVisibility());
     }
@@ -417,7 +425,8 @@
                 true,
                 true,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(VISIBLE, textView.getVisibility());
     }
@@ -443,7 +452,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         settingsButton.performClick();
@@ -468,7 +478,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         assertTrue(settingsButton.getVisibility() != View.VISIBLE);
     }
@@ -493,7 +504,8 @@
                 false,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         assertTrue(settingsButton.getVisibility() != View.VISIBLE);
     }
@@ -515,7 +527,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         mNotificationInfo.bindNotification(
                 mMockPackageManager,
                 mMockINotificationManager,
@@ -531,7 +544,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         assertEquals(View.VISIBLE, settingsButton.getVisibility());
     }
@@ -556,7 +570,8 @@
                 true,
                 true,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.findViewById(R.id.info).performClick();
         // Verify that listener was triggered.
@@ -582,7 +597,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final TextView channelNameView =
                 mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(GONE, channelNameView.getVisibility());
@@ -606,7 +622,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         assertEquals(GONE, mNotificationInfo.findViewById(
                 R.id.interruptiveness_settings).getVisibility());
         assertEquals(VISIBLE, mNotificationInfo.findViewById(
@@ -630,7 +647,8 @@
                 true,
                 true,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final TextView view = mNotificationInfo.findViewById(R.id.non_configurable_text);
         assertEquals(View.VISIBLE, view.getVisibility());
         assertEquals(mContext.getString(R.string.notification_unblockable_desc),
@@ -673,7 +691,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         final TextView view = mNotificationInfo.findViewById(R.id.non_configurable_call_text);
         assertEquals(View.VISIBLE, view.getVisibility());
         assertEquals(mContext.getString(R.string.notification_unblockable_call_desc),
@@ -716,7 +735,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         assertEquals(GONE,
                 mNotificationInfo.findViewById(R.id.non_configurable_call_text).getVisibility());
         assertEquals(VISIBLE,
@@ -743,7 +763,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.automatic).getVisibility());
         assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.automatic_summary).getVisibility());
     }
@@ -765,7 +786,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         assertEquals(GONE, mNotificationInfo.findViewById(R.id.automatic).getVisibility());
         assertEquals(GONE, mNotificationInfo.findViewById(R.id.automatic_summary).getVisibility());
     }
@@ -789,7 +811,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         assertTrue(mNotificationInfo.findViewById(R.id.automatic).isSelected());
     }
 
@@ -810,7 +833,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         assertTrue(mNotificationInfo.findViewById(R.id.alert).isSelected());
     }
 
@@ -831,7 +855,8 @@
                 true,
                 false,
                 false,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         assertTrue(mNotificationInfo.findViewById(R.id.silence).isSelected());
     }
 
@@ -852,7 +877,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         mTestableLooper.processAllMessages();
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
                 anyString(), eq(TEST_UID), any());
@@ -875,7 +901,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
         assertEquals(1, mUiEventLogger.numLogs());
         assertEquals(NotificationControlsEvent.NOTIFICATION_CONTROLS_OPEN.getId(),
                 mUiEventLogger.eventId(0));
@@ -899,7 +926,8 @@
                 true,
                 false,
                 false,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.findViewById(R.id.alert).performClick();
         mTestableLooper.processAllMessages();
@@ -926,7 +954,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.findViewById(R.id.silence).performClick();
         mTestableLooper.processAllMessages();
@@ -953,7 +982,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.findViewById(R.id.automatic).performClick();
         mTestableLooper.processAllMessages();
@@ -981,7 +1011,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.handleCloseControls(true, false);
         mTestableLooper.processAllMessages();
@@ -1008,7 +1039,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.handleCloseControls(true, false);
         mTestableLooper.processAllMessages();
@@ -1043,7 +1075,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.handleCloseControls(true, false);
 
@@ -1071,7 +1104,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.findViewById(R.id.silence).performClick();
         mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1112,7 +1146,8 @@
                 true,
                 false,
                 false,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.findViewById(R.id.alert).performClick();
         mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1149,7 +1184,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.findViewById(R.id.automatic).performClick();
         mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1181,7 +1217,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.findViewById(R.id.silence).performClick();
         mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1217,7 +1254,8 @@
                 true,
                 false,
                 false,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         assertEquals(mContext.getString(R.string.inline_done_button),
                 ((TextView) mNotificationInfo.findViewById(R.id.done)).getText());
@@ -1255,7 +1293,8 @@
                 true,
                 false,
                 false,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.findViewById(R.id.silence).performClick();
         mNotificationInfo.handleCloseControls(false, false);
@@ -1286,7 +1325,8 @@
                 true,
                 false,
                 false,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         assertEquals(mContext.getString(R.string.inline_done_button),
                 ((TextView) mNotificationInfo.findViewById(R.id.done)).getText());
@@ -1324,7 +1364,8 @@
                 true,
                 false,
                 true,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.findViewById(R.id.silence).performClick();
         mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1353,7 +1394,8 @@
                 true,
                 false,
                 false,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         assertEquals(mContext.getString(R.string.inline_done_button),
                 ((TextView) mNotificationInfo.findViewById(R.id.done)).getText());
@@ -1384,7 +1426,8 @@
                 true,
                 false,
                 false,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.findViewById(R.id.alert).performClick();
         mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1419,7 +1462,8 @@
                 true,
                 false,
                 false,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.findViewById(R.id.alert).performClick();
         mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1452,7 +1496,8 @@
                 true,
                 false,
                 false,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.findViewById(R.id.alert).performClick();
         mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1485,7 +1530,8 @@
                 true,
                 false,
                 false,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         mNotificationInfo.findViewById(R.id.alert).performClick();
 
@@ -1511,7 +1557,8 @@
                 true,
                 false,
                 false,
-                mAssistantFeedbackController);
+                mAssistantFeedbackController,
+                mMetricsLogger);
 
         assertFalse(mNotificationInfo.willBeRemoved());
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index e4fc4d5..aca9c56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -40,7 +40,6 @@
 import android.content.Intent;
 import android.content.pm.LauncherApps;
 import android.graphics.drawable.Icon;
-import android.os.Handler;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
 import android.testing.TestableLooper;
@@ -49,7 +48,6 @@
 import android.widget.RemoteViews;
 
 import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.TestableDependency;
 import com.android.systemui.classifier.FalsingCollectorFake;
 import com.android.systemui.classifier.FalsingManagerFake;
@@ -57,7 +55,6 @@
 import com.android.systemui.media.controls.util.MediaFeatureFlag;
 import com.android.systemui.media.dialog.MediaOutputDialogFactory;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shade.ShadeExpansionStateManager;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -68,7 +65,6 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
-import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
 import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.icon.IconBuilder;
@@ -77,11 +73,8 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpandableNotificationRowLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
-import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
-import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
 import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
 import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder;
 import com.android.systemui.statusbar.policy.SmartReplyConstants;
@@ -121,12 +114,12 @@
     private final GroupMembershipManager mGroupMembershipManager;
     private final GroupExpansionManager mGroupExpansionManager;
     private ExpandableNotificationRow mRow;
-    private HeadsUpManagerPhone mHeadsUpManager;
+    private final HeadsUpManagerPhone mHeadsUpManager;
     private final NotifBindPipeline mBindPipeline;
     private final NotifCollectionListener mBindPipelineEntryListener;
     private final RowContentBindStage mBindStage;
     private final IconManager mIconManager;
-    private StatusBarStateController mStatusBarStateController;
+    private final StatusBarStateController mStatusBarStateController;
     private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
     public final OnUserInteractionCallback mOnUserInteractionCallback;
     public final Runnable mFutureDismissalRunnable;
@@ -146,21 +139,7 @@
         mStatusBarStateController = mock(StatusBarStateController.class);
         mGroupMembershipManager = mock(GroupMembershipManager.class);
         mGroupExpansionManager = mock(GroupExpansionManager.class);
-        mHeadsUpManager = new HeadsUpManagerPhone(
-                mContext,
-                mock(HeadsUpManagerLogger.class),
-                mStatusBarStateController,
-                mock(KeyguardBypassController.class),
-                mock(GroupMembershipManager.class),
-                mock(VisualStabilityProvider.class),
-                mock(ConfigurationControllerImpl.class),
-                new Handler(mTestLooper.getLooper()),
-                mock(AccessibilityManagerWrapper.class),
-                mock(UiEventLogger.class),
-                mock(ShadeExpansionStateManager.class)
-        );
-        mHeadsUpManager.mHandler.removeCallbacksAndMessages(null);
-        mHeadsUpManager.mHandler = new Handler(mTestLooper.getLooper());
+        mHeadsUpManager = mock(HeadsUpManagerPhone.class);
         mIconManager = new IconManager(
                 mock(CommonNotifCollection.class),
                 mock(LauncherApps.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 5832569..4d9db8c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -518,7 +518,7 @@
         val childHunView = createHunViewMock(
                 isShadeOpen = true,
                 fullyVisible = false,
-                headerVisibleAmount = 1f,
+                headerVisibleAmount = 1f
         )
         val algorithmState = StackScrollAlgorithm.StackScrollAlgorithmState()
         algorithmState.visibleChildren.add(childHunView)
@@ -526,6 +526,7 @@
         // When: updateChildZValue() is called for the top HUN
         stackScrollAlgorithm.updateChildZValue(
                 /* i= */ 0,
+                /* childrenOnTop= */ 0.0f,
                 /* StackScrollAlgorithmState= */ algorithmState,
                 /* ambientState= */ ambientState,
                 /* shouldElevateHun= */ true
@@ -545,7 +546,7 @@
         val childHunView = createHunViewMock(
                 isShadeOpen = true,
                 fullyVisible = false,
-                headerVisibleAmount = 1f,
+                headerVisibleAmount = 1f
         )
         // Use half of the HUN's height as overlap
         childHunView.viewState.yTranslation = (childHunView.viewState.height + 1 shr 1).toFloat()
@@ -555,6 +556,7 @@
         // When: updateChildZValue() is called for the top HUN
         stackScrollAlgorithm.updateChildZValue(
                 /* i= */ 0,
+                /* childrenOnTop= */ 0.0f,
                 /* StackScrollAlgorithmState= */ algorithmState,
                 /* ambientState= */ ambientState,
                 /* shouldElevateHun= */ true
@@ -578,7 +580,7 @@
         val childHunView = createHunViewMock(
                 isShadeOpen = true,
                 fullyVisible = true,
-                headerVisibleAmount = 1f,
+                headerVisibleAmount = 1f
         )
         // HUN doesn't overlap with QQS Panel
         childHunView.viewState.yTranslation = ambientState.topPadding +
@@ -589,6 +591,7 @@
         // When: updateChildZValue() is called for the top HUN
         stackScrollAlgorithm.updateChildZValue(
                 /* i= */ 0,
+                /* childrenOnTop= */ 0.0f,
                 /* StackScrollAlgorithmState= */ algorithmState,
                 /* ambientState= */ ambientState,
                 /* shouldElevateHun= */ true
@@ -608,7 +611,7 @@
         val childHunView = createHunViewMock(
                 isShadeOpen = false,
                 fullyVisible = false,
-                headerVisibleAmount = 0f,
+                headerVisibleAmount = 0f
         )
         childHunView.viewState.yTranslation = 0f
         // Shade is closed, thus childHunView's headerVisibleAmount is 0
@@ -619,6 +622,7 @@
         // When: updateChildZValue() is called for the top HUN
         stackScrollAlgorithm.updateChildZValue(
                 /* i= */ 0,
+                /* childrenOnTop= */ 0.0f,
                 /* StackScrollAlgorithmState= */ algorithmState,
                 /* ambientState= */ ambientState,
                 /* shouldElevateHun= */ true
@@ -638,7 +642,7 @@
         val childHunView = createHunViewMock(
                 isShadeOpen = false,
                 fullyVisible = false,
-                headerVisibleAmount = 0.5f,
+                headerVisibleAmount = 0.5f
         )
         childHunView.viewState.yTranslation = 0f
         // Shade is being opened, thus childHunView's headerVisibleAmount is between 0 and 1
@@ -650,6 +654,7 @@
         // When: updateChildZValue() is called for the top HUN
         stackScrollAlgorithm.updateChildZValue(
                 /* i= */ 0,
+                /* childrenOnTop= */ 0.0f,
                 /* StackScrollAlgorithmState= */ algorithmState,
                 /* ambientState= */ ambientState,
                 /* shouldElevateHun= */ true
@@ -664,7 +669,7 @@
     private fun createHunViewMock(
             isShadeOpen: Boolean,
             fullyVisible: Boolean,
-            headerVisibleAmount: Float,
+            headerVisibleAmount: Float
     ) =
             mock<ExpandableNotificationRow>().apply {
                 val childViewStateMock = createHunChildViewState(isShadeOpen, fullyVisible)
@@ -675,10 +680,7 @@
             }
 
 
-    private fun createHunChildViewState(
-            isShadeOpen: Boolean,
-            fullyVisible: Boolean,
-    ) =
+    private fun createHunChildViewState(isShadeOpen: Boolean, fullyVisible: Boolean) =
             ExpandableViewState().apply {
                 // Mock the HUN's height with ambientState.topPadding +
                 // ambientState.stackTranslation
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index 091bb54..f568547 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.phone;
 
 import static com.android.systemui.qs.dagger.QSFlagsModule.RBC_AVAILABLE;
+import static com.android.systemui.statusbar.phone.AutoTileManager.DEVICE_CONTROLS;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -50,6 +51,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.qs.AutoAddTracker;
 import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.qs.ReduceBrightColorsController;
@@ -70,6 +72,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.Mockito;
@@ -77,6 +80,7 @@
 import org.mockito.Spy;
 import org.mockito.stubbing.Answer;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
@@ -510,6 +514,7 @@
     @Test
     public void managedProfileAdded_tileAdded() {
         when(mAutoAddTracker.isAdded(eq("work"))).thenReturn(false);
+        when(mAutoAddTracker.getRestoredTilePosition(eq("work"))).thenReturn(2);
         mAutoTileManager = createAutoTileManager(mContext);
         Mockito.doAnswer((Answer<Object>) invocation -> {
             mManagedProfileCallback = invocation.getArgument(0);
@@ -520,7 +525,7 @@
 
         mManagedProfileCallback.onManagedProfileChanged();
 
-        verify(mQsTileHost, times(1)).addTile(eq("work"));
+        verify(mQsTileHost, times(1)).addTile(eq("work"), eq(2));
         verify(mAutoAddTracker, times(1)).setTileAdded(eq("work"));
     }
 
@@ -542,6 +547,61 @@
     }
 
     @Test
+    public void testAddControlsTileIfNotPresent() {
+        String spec = DEVICE_CONTROLS;
+        when(mAutoAddTracker.isAdded(eq(spec))).thenReturn(false);
+        when(mQsTileHost.getTiles()).thenReturn(new ArrayList<>());
+
+        mAutoTileManager.init();
+        ArgumentCaptor<DeviceControlsController.Callback> captor =
+                ArgumentCaptor.forClass(DeviceControlsController.Callback.class);
+
+        verify(mDeviceControlsController).setCallback(captor.capture());
+
+        captor.getValue().onControlsUpdate(3);
+        verify(mQsTileHost).addTile(spec, 3);
+        verify(mAutoAddTracker).setTileAdded(spec);
+    }
+
+    @Test
+    public void testDontAddControlsTileIfPresent() {
+        String spec = DEVICE_CONTROLS;
+        when(mAutoAddTracker.isAdded(eq(spec))).thenReturn(false);
+        when(mQsTileHost.getTiles()).thenReturn(new ArrayList<>());
+
+        mAutoTileManager.init();
+        ArgumentCaptor<DeviceControlsController.Callback> captor =
+                ArgumentCaptor.forClass(DeviceControlsController.Callback.class);
+
+        verify(mDeviceControlsController).setCallback(captor.capture());
+
+        captor.getValue().removeControlsAutoTracker();
+        verify(mQsTileHost, never()).addTile(spec, 3);
+        verify(mAutoAddTracker, never()).setTileAdded(spec);
+        verify(mAutoAddTracker).setTileRemoved(spec);
+    }
+
+    @Test
+    public void testRemoveControlsTileFromTrackerWhenRequested() {
+        String spec = "controls";
+        when(mAutoAddTracker.isAdded(eq(spec))).thenReturn(true);
+        QSTile mockTile = mock(QSTile.class);
+        when(mockTile.getTileSpec()).thenReturn(spec);
+        when(mQsTileHost.getTiles()).thenReturn(List.of(mockTile));
+
+        mAutoTileManager.init();
+        ArgumentCaptor<DeviceControlsController.Callback> captor =
+                ArgumentCaptor.forClass(DeviceControlsController.Callback.class);
+
+        verify(mDeviceControlsController).setCallback(captor.capture());
+
+        captor.getValue().onControlsUpdate(3);
+        verify(mQsTileHost, never()).addTile(spec, 3);
+        verify(mAutoAddTracker, never()).setTileAdded(spec);
+    }
+
+
+    @Test
     public void testEmptyArray_doesNotCrash() {
         mContext.getOrCreateTestableResources().addOverride(
                 R.array.config_quickSettingsAutoAdd, new String[0]);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index b879cf2..48573c6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -46,6 +46,7 @@
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shade.CameraLauncher;
 import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.QuickSettingsController;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.DisableFlagsLogger;
@@ -73,6 +74,7 @@
     @Mock private CentralSurfaces mCentralSurfaces;
     @Mock private ShadeController mShadeController;
     @Mock private CommandQueue mCommandQueue;
+    @Mock private QuickSettingsController mQuickSettingsController;
     @Mock private NotificationPanelViewController mNotificationPanelViewController;
     @Mock private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
     private final MetricsLogger mMetricsLogger = new FakeMetricsLogger();
@@ -101,6 +103,7 @@
 
         mSbcqCallbacks = new CentralSurfacesCommandQueueCallbacks(
                 mCentralSurfaces,
+                mQuickSettingsController,
                 mContext,
                 mContext.getResources(),
                 mShadeController,
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 b1363a0..dbf416e 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
@@ -126,6 +126,7 @@
 import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.shade.NotificationShadeWindowView;
 import com.android.systemui.shade.NotificationShadeWindowViewController;
+import com.android.systemui.shade.QuickSettingsController;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.shade.ShadeControllerImpl;
 import com.android.systemui.shade.ShadeExpansionStateManager;
@@ -218,6 +219,7 @@
     @Mock private HeadsUpManagerPhone mHeadsUpManager;
     @Mock private NotificationPanelViewController mNotificationPanelViewController;
     @Mock private NotificationPanelView mNotificationPanelView;
+    @Mock private QuickSettingsController mQuickSettingsController;
     @Mock private IStatusBarService mBarService;
     @Mock private IDreamManager mDreamManager;
     @Mock private LightRevealScrimViewModel mLightRevealScrimViewModel;
@@ -546,6 +548,7 @@
         // initialized automatically and make NPVC private.
         mCentralSurfaces.mNotificationShadeWindowView = mNotificationShadeWindowView;
         mCentralSurfaces.mNotificationPanelViewController = mNotificationPanelViewController;
+        mCentralSurfaces.mQsController = mQuickSettingsController;
         mCentralSurfaces.mDozeScrimController = mDozeScrimController;
         mCentralSurfaces.mPresenter = mNotificationPresenter;
         mCentralSurfaces.mKeyguardIndicationController = mKeyguardIndicationController;
@@ -1010,6 +1013,18 @@
     }
 
     @Test
+    public void testSetDozingNotUnlocking_transitionToAOD_cancelKeyguardFadingAway() {
+        setDozing(true);
+        when(mKeyguardStateController.isShowing()).thenReturn(false);
+        when(mKeyguardStateController.isKeyguardFadingAway()).thenReturn(true);
+
+        mCentralSurfaces.updateScrimController();
+
+        verify(mScrimController, times(2)).transitionTo(eq(ScrimState.AOD));
+        verify(mStatusBarKeyguardViewManager).onKeyguardFadedAway();
+    }
+
+    @Test
     public void testShowKeyguardImplementation_setsState() {
         when(mLockscreenUserManager.getCurrentProfiles()).thenReturn(new SparseArray<>());
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java
index a986777..c669c6f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java
@@ -53,7 +53,7 @@
     }
 
     @Override
-    public boolean isBouncerShowing() {
+    public boolean isPrimaryBouncerShowing() {
         return false;
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
deleted file mode 100644
index 2f49535..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ /dev/null
@@ -1,526 +0,0 @@
-/*
- * Copyright (C) 2017 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.statusbar.phone;
-
-import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN;
-import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyFloat;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-
-import android.content.res.ColorStateList;
-import android.graphics.Color;
-import android.hardware.biometrics.BiometricSourceType;
-import android.os.Handler;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.keyguard.KeyguardHostViewController;
-import com.android.keyguard.KeyguardSecurityModel;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.ViewMediatorCallback;
-import com.android.keyguard.dagger.KeyguardBouncerComponent;
-import com.android.systemui.DejankUtils;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.classifier.FalsingCollector;
-import com.android.systemui.keyguard.DismissCallbackRegistry;
-import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
-import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class KeyguardBouncerTest extends SysuiTestCase {
-
-    @Mock
-    private FalsingCollector mFalsingCollector;
-    @Mock
-    private ViewMediatorCallback mViewMediatorCallback;
-    @Mock
-    private DismissCallbackRegistry mDismissCallbackRegistry;
-    @Mock
-    private KeyguardHostViewController mKeyguardHostViewController;
-    @Mock
-    private PrimaryBouncerExpansionCallback mExpansionCallback;
-    @Mock
-    private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    @Mock
-    private KeyguardStateController mKeyguardStateController;
-    @Mock
-    private KeyguardBypassController mKeyguardBypassController;
-    @Mock
-    private Handler mHandler;
-    @Mock
-    private KeyguardSecurityModel mKeyguardSecurityModel;
-    @Mock
-    private KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory;
-    @Mock
-    private KeyguardBouncerComponent mKeyguardBouncerComponent;
-    private ViewGroup mContainer;
-    @Rule
-    public MockitoRule mRule = MockitoJUnit.rule();
-    private Integer mRootVisibility = View.INVISIBLE;
-    private KeyguardBouncer mBouncer;
-
-    @Before
-    public void setup() {
-        allowTestableLooperAsMainThread();
-        when(mKeyguardSecurityModel.getSecurityMode(anyInt()))
-                .thenReturn(KeyguardSecurityModel.SecurityMode.None);
-        DejankUtils.setImmediate(true);
-
-        mContainer = spy(new FrameLayout(getContext()));
-        when(mKeyguardBouncerComponentFactory.create(mContainer)).thenReturn(
-                mKeyguardBouncerComponent);
-        when(mKeyguardBouncerComponent.getKeyguardHostViewController())
-                .thenReturn(mKeyguardHostViewController);
-
-        mBouncer = new KeyguardBouncer.Factory(getContext(), mViewMediatorCallback,
-                mDismissCallbackRegistry, mFalsingCollector,
-                mKeyguardStateController, mKeyguardUpdateMonitor,
-                mKeyguardBypassController, mHandler, mKeyguardSecurityModel,
-                mKeyguardBouncerComponentFactory)
-                .create(mContainer, mExpansionCallback);
-    }
-
-    @Test
-    public void testInflateView_doesntCrash() {
-        mBouncer.inflateView();
-    }
-
-    @Test
-    public void testShow_notifiesFalsingManager() {
-        mBouncer.show(true);
-        verify(mFalsingCollector).onBouncerShown();
-
-        mBouncer.show(true, false);
-        verifyNoMoreInteractions(mFalsingCollector);
-    }
-
-    /**
-     * Regression test: Invisible bouncer when occluded.
-     */
-    @Test
-    public void testShow_bouncerIsVisible() {
-        // Expand notification panel as if we were in the keyguard.
-        mBouncer.ensureView();
-        mBouncer.setExpansion(1);
-
-        reset(mKeyguardHostViewController);
-
-        mBouncer.show(true);
-        verify(mKeyguardHostViewController).setExpansion(0);
-    }
-
-    @Test
-    public void testShow_notifiesVisibility() {
-        mBouncer.show(true);
-        verify(mKeyguardStateController).notifyBouncerShowing(eq(true));
-        verify(mExpansionCallback).onStartingToShow();
-
-        // Not called again when visible
-        reset(mViewMediatorCallback);
-        mBouncer.show(true);
-        verifyNoMoreInteractions(mViewMediatorCallback);
-    }
-
-    @Test
-    public void testShow_triesToDismissKeyguard() {
-        mBouncer.show(true);
-        verify(mKeyguardHostViewController).dismiss(anyInt());
-    }
-
-    @Test
-    public void testShow_resetsSecuritySelection() {
-        mBouncer.show(false);
-        verify(mKeyguardHostViewController, never()).showPrimarySecurityScreen();
-
-        mBouncer.hide(false);
-        mBouncer.show(true);
-        verify(mKeyguardHostViewController).showPrimarySecurityScreen();
-    }
-
-    @Test
-    public void testShow_animatesKeyguardView() {
-        mBouncer.show(true);
-        verify(mKeyguardHostViewController).appear(anyInt());
-    }
-
-    @Test
-    public void testShow_showsErrorMessage() {
-        final String errorMessage = "an error message";
-        when(mViewMediatorCallback.consumeCustomMessage()).thenReturn(errorMessage);
-        mBouncer.show(true);
-        verify(mKeyguardHostViewController).showErrorMessage(eq(errorMessage));
-    }
-
-    @Test
-    public void testSetExpansion_notifiesFalsingManager() {
-        mBouncer.ensureView();
-        mBouncer.setExpansion(0.5f);
-
-        mBouncer.setExpansion(EXPANSION_HIDDEN);
-        verify(mFalsingCollector).onBouncerHidden();
-        verify(mExpansionCallback).onFullyHidden();
-
-        mBouncer.setExpansion(EXPANSION_VISIBLE);
-        verify(mFalsingCollector).onBouncerShown();
-        verify(mExpansionCallback).onFullyShown();
-
-        verify(mExpansionCallback, never()).onStartingToHide();
-        verify(mKeyguardHostViewController, never()).onStartingToHide();
-        mBouncer.setExpansion(0.9f);
-        verify(mExpansionCallback).onStartingToHide();
-        verify(mKeyguardHostViewController).onStartingToHide();
-    }
-
-    @Test
-    public void testSetExpansion_notifiesKeyguardView() {
-        mBouncer.ensureView();
-        mBouncer.setExpansion(0.1f);
-
-        mBouncer.setExpansion(0);
-        verify(mKeyguardHostViewController).onResume();
-        verify(mContainer).announceForAccessibility(any());
-    }
-
-    @Test
-    public void show_notifiesKeyguardViewController() {
-        mBouncer.ensureView();
-
-        mBouncer.show(/* resetSecuritySelection= */ false);
-
-        verify(mKeyguardHostViewController).onBouncerVisibilityChanged(View.VISIBLE);
-    }
-
-    @Test
-    public void testHide_notifiesFalsingManager() {
-        mBouncer.hide(false);
-        verify(mFalsingCollector).onBouncerHidden();
-    }
-
-    @Test
-    public void testHide_notifiesVisibility() {
-        mBouncer.hide(false);
-        verify(mKeyguardStateController).notifyBouncerShowing(eq(false));
-    }
-
-    @Test
-    public void testHide_notifiesDismissCallbackIfVisible() {
-        mBouncer.hide(false);
-        verifyZeroInteractions(mDismissCallbackRegistry);
-        mBouncer.show(false);
-        mBouncer.hide(false);
-        verify(mDismissCallbackRegistry).notifyDismissCancelled();
-    }
-
-    @Test
-    public void testHide_notShowingAnymore() {
-        mBouncer.ensureView();
-        mBouncer.show(false /* resetSecuritySelection */);
-        mBouncer.hide(false /* destroyViews */);
-        Assert.assertFalse("Not showing", mBouncer.isShowing());
-    }
-
-    @Test
-    public void testShowPromptReason_propagates() {
-        mBouncer.ensureView();
-        mBouncer.showPromptReason(1);
-        verify(mKeyguardHostViewController).showPromptReason(eq(1));
-    }
-
-    @Test
-    public void testShowMessage_propagates() {
-        final String message = "a message";
-        mBouncer.ensureView();
-        mBouncer.showMessage(message, ColorStateList.valueOf(Color.GREEN));
-        verify(mKeyguardHostViewController).showMessage(
-                eq(message), eq(ColorStateList.valueOf(Color.GREEN)));
-    }
-
-    @Test
-    public void testShowOnDismissAction_showsBouncer() {
-        final OnDismissAction dismissAction = () -> false;
-        final Runnable cancelAction = () -> {};
-        mBouncer.showWithDismissAction(dismissAction, cancelAction);
-        verify(mKeyguardHostViewController).setOnDismissAction(dismissAction, cancelAction);
-        Assert.assertTrue("Should be showing", mBouncer.isShowing());
-    }
-
-    @Test
-    public void testStartPreHideAnimation_notifiesView() {
-        final boolean[] ran = {false};
-        final Runnable r = () -> ran[0] = true;
-        mBouncer.startPreHideAnimation(r);
-        Assert.assertTrue("Callback should have been invoked", ran[0]);
-
-        ran[0] = false;
-        mBouncer.ensureView();
-        mBouncer.startPreHideAnimation(r);
-        verify(mKeyguardHostViewController).startDisappearAnimation(r);
-        Assert.assertFalse("Callback should have been deferred", ran[0]);
-    }
-
-    @Test
-    public void testIsShowing_animated() {
-        Assert.assertFalse("Show wasn't invoked yet", mBouncer.isShowing());
-        mBouncer.show(true /* reset */);
-        Assert.assertTrue("Should be showing", mBouncer.isShowing());
-    }
-
-    @Test
-    public void testIsShowing_forSwipeUp() {
-        mBouncer.setExpansion(1f);
-        mBouncer.show(true /* reset */, false /* animated */);
-        Assert.assertFalse("Should only be showing after collapsing notification panel",
-                mBouncer.isShowing());
-        mBouncer.setExpansion(0f);
-        Assert.assertTrue("Should be showing", mBouncer.isShowing());
-    }
-
-    @Test
-    public void testSetExpansion() {
-        mBouncer.ensureView();
-        mBouncer.setExpansion(0.5f);
-        verify(mKeyguardHostViewController).setExpansion(0.5f);
-    }
-
-    @Test
-    public void testIsFullscreenBouncer_asksKeyguardView() {
-        mBouncer.ensureView();
-        mBouncer.isFullscreenBouncer();
-        verify(mKeyguardHostViewController).getCurrentSecurityMode();
-    }
-
-    @Test
-    public void testIsHiding_preHideOrHide() {
-        Assert.assertFalse("Should not be hiding on initial state", mBouncer.isAnimatingAway());
-        mBouncer.startPreHideAnimation(null /* runnable */);
-        Assert.assertTrue("Should be hiding during pre-hide", mBouncer.isAnimatingAway());
-        mBouncer.hide(false /* destroyView */);
-        Assert.assertFalse("Should be hidden after hide()", mBouncer.isAnimatingAway());
-    }
-
-    @Test
-    public void testIsHiding_skipsTranslation() {
-        mBouncer.show(false /* reset */);
-        reset(mKeyguardHostViewController);
-        mBouncer.startPreHideAnimation(null /* runnable */);
-        mBouncer.setExpansion(0.5f);
-        verify(mKeyguardHostViewController, never()).setExpansion(anyFloat());
-    }
-
-    @Test
-    public void testIsSecure() {
-        mBouncer.ensureView();
-        for (KeyguardSecurityModel.SecurityMode mode : KeyguardSecurityModel.SecurityMode.values()){
-            reset(mKeyguardSecurityModel);
-            when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(mode);
-            Assert.assertEquals("Security doesn't match for mode: " + mode,
-                    mBouncer.isSecure(), mode != KeyguardSecurityModel.SecurityMode.None);
-        }
-    }
-
-    @Test
-    public void testIsShowingScrimmed_true() {
-        doAnswer(invocation -> {
-            assertThat(mBouncer.isScrimmed()).isTrue();
-            return null;
-        }).when(mExpansionCallback).onFullyShown();
-        mBouncer.show(false /* resetSecuritySelection */, true /* animate */);
-        assertThat(mBouncer.isScrimmed()).isTrue();
-        mBouncer.hide(false /* destroyView */);
-        assertThat(mBouncer.isScrimmed()).isFalse();
-    }
-
-    @Test
-    public void testIsShowingScrimmed_false() {
-        doAnswer(invocation -> {
-            assertThat(mBouncer.isScrimmed()).isFalse();
-            return null;
-        }).when(mExpansionCallback).onFullyShown();
-        mBouncer.show(false /* resetSecuritySelection */, false /* animate */);
-        assertThat(mBouncer.isScrimmed()).isFalse();
-    }
-
-    @Test
-    public void testWillDismissWithAction() {
-        mBouncer.ensureView();
-        Assert.assertFalse("Action not set yet", mBouncer.willDismissWithAction());
-        when(mKeyguardHostViewController.hasDismissActions()).thenReturn(true);
-        Assert.assertTrue("Action should exist", mBouncer.willDismissWithAction());
-    }
-
-    @Test
-    public void testShow_delaysIfFaceAuthIsRunning() {
-        when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE))
-                .thenReturn(true);
-        when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true);
-        mBouncer.show(true /* reset */);
-
-        ArgumentCaptor<Runnable> showRunnable = ArgumentCaptor.forClass(Runnable.class);
-        verify(mHandler).postDelayed(showRunnable.capture(),
-                eq(KeyguardBouncer.BOUNCER_FACE_DELAY));
-
-        mBouncer.hide(false /* destroyView */);
-        verify(mHandler).removeCallbacks(eq(showRunnable.getValue()));
-    }
-
-    @Test
-    public void testShow_doesNotDelaysIfFaceAuthIsNotAllowed() {
-        when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true);
-        when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE))
-                .thenReturn(false);
-        mBouncer.show(true /* reset */);
-
-        verify(mHandler, never()).postDelayed(any(), anyLong());
-    }
-
-    @Test
-    public void testShow_delaysIfFaceAuthIsRunning_unlessBypassEnabled() {
-        when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true);
-        when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
-        mBouncer.show(true /* reset */);
-
-        verify(mHandler, never()).postDelayed(any(), anyLong());
-    }
-
-    @Test
-    public void testShow_delaysIfFaceAuthIsRunning_unlessFingerprintEnrolled() {
-        when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true);
-        when(mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0))
-                .thenReturn(true);
-        mBouncer.show(true /* reset */);
-
-        verify(mHandler, never()).postDelayed(any(), anyLong());
-    }
-
-    @Test
-    public void testRegisterUpdateMonitorCallback() {
-        verify(mKeyguardUpdateMonitor).registerCallback(any());
-    }
-
-    @Test
-    public void testInTransit_whenTranslation() {
-        mBouncer.show(true);
-        mBouncer.setExpansion(EXPANSION_HIDDEN);
-        assertThat(mBouncer.inTransit()).isFalse();
-        mBouncer.setExpansion(0.5f);
-        assertThat(mBouncer.inTransit()).isTrue();
-        mBouncer.setExpansion(EXPANSION_VISIBLE);
-        assertThat(mBouncer.inTransit()).isFalse();
-    }
-
-    @Test
-    public void testUpdateResources_delegatesToRootView() {
-        mBouncer.ensureView();
-        mBouncer.updateResources();
-
-        // This is mocked, so won't pick up on the call to updateResources via
-        // mKeyguardViewController.init(), only updateResources above.
-        verify(mKeyguardHostViewController).updateResources();
-    }
-
-    @Test
-    public void testUpdateKeyguardPosition_delegatesToRootView() {
-        mBouncer.ensureView();
-        mBouncer.updateKeyguardPosition(1.0f);
-
-        verify(mKeyguardHostViewController).updateKeyguardPosition(1.0f);
-    }
-
-    @Test
-    public void testExpansion_notifiesCallback() {
-        mBouncer.ensureView();
-        mBouncer.setExpansion(0.5f);
-
-        final PrimaryBouncerExpansionCallback callback =
-                mock(PrimaryBouncerExpansionCallback.class);
-        mBouncer.addBouncerExpansionCallback(callback);
-
-        mBouncer.setExpansion(EXPANSION_HIDDEN);
-        verify(callback).onFullyHidden();
-        verify(callback).onExpansionChanged(EXPANSION_HIDDEN);
-
-        Mockito.clearInvocations(callback);
-        mBouncer.setExpansion(EXPANSION_VISIBLE);
-        verify(callback).onFullyShown();
-        verify(callback).onExpansionChanged(EXPANSION_VISIBLE);
-
-        Mockito.clearInvocations(callback);
-        float bouncerHideAmount = 0.9f;
-        // Ensure the callback only triggers once despite multiple calls to setExpansion
-        // with the same value.
-        mBouncer.setExpansion(bouncerHideAmount);
-        mBouncer.setExpansion(bouncerHideAmount);
-        verify(callback, times(1)).onStartingToHide();
-        verify(callback, times(1)).onExpansionChanged(bouncerHideAmount);
-
-        Mockito.clearInvocations(callback);
-        mBouncer.removeBouncerExpansionCallback(callback);
-        bouncerHideAmount = 0.5f;
-        mBouncer.setExpansion(bouncerHideAmount);
-        verify(callback, never()).onExpansionChanged(bouncerHideAmount);
-    }
-
-    @Test
-    public void testOnResumeCalledForFullscreenBouncerOnSecondShow() {
-        // GIVEN a security mode which requires fullscreen bouncer
-        when(mKeyguardSecurityModel.getSecurityMode(anyInt()))
-                .thenReturn(KeyguardSecurityModel.SecurityMode.SimPin);
-        mBouncer.show(true);
-
-        // WHEN a second call to show occurs, the bouncer will already by visible
-        reset(mKeyguardHostViewController);
-        mBouncer.show(true);
-
-        // THEN ensure the ViewController is told to resume
-        verify(mKeyguardHostViewController).onResume();
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index ed3f710..7e69efa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -264,6 +264,19 @@
     }
 
     @Test
+    public void notifPositionAlignedWithClockAndBurnInOffsetInSplitShadeMode() {
+        setSplitShadeTopMargin(100); // this makes clock to be at 100
+        givenAOD();
+        mIsSplitShade = true;
+        givenMaxBurnInOffset(100);
+        givenHighestBurnInOffset(); // this makes clock to be at 200
+        // WHEN the position algorithm is run
+        positionClock();
+        // THEN the notif padding adjusts for burn-in offset: clock position - burn-in offset
+        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(100);
+    }
+
+    @Test
     public void clockPositionedDependingOnMarginInSplitShade() {
         setSplitShadeTopMargin(400);
         givenLockScreen();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
index cc4abfc..529519a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
@@ -38,6 +38,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.settings.FakeDisplayTracker;
 import com.android.systemui.statusbar.policy.BatteryController;
 
 import org.junit.Before;
@@ -67,7 +68,8 @@
                 mStatusBarIconController,
                 mock(BatteryController.class),
                 mock(NavigationModeController.class),
-                mock(DumpManager.class));
+                mock(DumpManager.class),
+                new FakeDisplayTracker(mContext));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java
index 189aa0f..0a68406 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java
@@ -28,6 +28,7 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.settings.FakeDisplayTracker;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.LightBarTransitionsController.DarkIntensityApplier;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -56,7 +57,8 @@
     public void setup() {
         MockitoAnnotations.initMocks(this);
         mLightBarTransitionsController = new LightBarTransitionsController(mContext, mApplier,
-                new CommandQueue(mContext), mKeyguardStateController, mStatusBarStateController);
+                new CommandQueue(mContext, new FakeDisplayTracker(mContext)),
+                mKeyguardStateController, mStatusBarStateController);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
index 6fb6893..8aaa57f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
@@ -23,6 +23,8 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.testing.AndroidTestingRunner;
@@ -33,7 +35,10 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.statusbar.StatusBarIcon;
+import com.android.systemui.demomode.DemoModeController;
+import com.android.systemui.dump.DumpManager;
 import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.StatusBarMobileView;
 import com.android.systemui.statusbar.StatusBarWifiView;
@@ -46,6 +51,8 @@
 import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags;
 import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter;
 import com.android.systemui.statusbar.pipeline.wifi.ui.WifiUiAdapter;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.tuner.TunerService;
 import com.android.systemui.utils.leaks.LeakCheckedTest;
 
 import org.junit.Before;
@@ -87,6 +94,61 @@
         testCallOnAdd_forManager(manager);
     }
 
+    @Test
+    public void testRemoveIcon_ignoredForNewPipeline() {
+        IconManager manager = mock(IconManager.class);
+
+        // GIVEN the new pipeline is on
+        StatusBarPipelineFlags flags = mock(StatusBarPipelineFlags.class);
+        when(flags.isIconControlledByFlags("test_icon")).thenReturn(true);
+
+        StatusBarIconController iconController = new StatusBarIconControllerImpl(
+                mContext,
+                mock(CommandQueue.class),
+                mock(DemoModeController.class),
+                mock(ConfigurationController.class),
+                mock(TunerService.class),
+                mock(DumpManager.class),
+                mock(StatusBarIconList.class),
+                flags
+        );
+
+        iconController.addIconGroup(manager);
+
+        // WHEN a request to remove a new icon is sent
+        iconController.removeIcon("test_icon", 0);
+
+        // THEN it is not removed for those icons
+        verify(manager, never()).onRemoveIcon(anyInt());
+    }
+
+    @Test
+    public void testRemoveAllIconsForSlot_ignoredForNewPipeline() {
+        IconManager manager = mock(IconManager.class);
+
+        // GIVEN the new pipeline is on
+        StatusBarPipelineFlags flags = mock(StatusBarPipelineFlags.class);
+        when(flags.isIconControlledByFlags("test_icon")).thenReturn(true);
+
+        StatusBarIconController iconController = new StatusBarIconControllerImpl(
+                mContext,
+                mock(CommandQueue.class),
+                mock(DemoModeController.class),
+                mock(ConfigurationController.class),
+                mock(TunerService.class),
+                mock(DumpManager.class),
+                mock(StatusBarIconList.class),
+                flags
+        );
+
+        iconController.addIconGroup(manager);
+
+        // WHEN a request to remove a new icon is sent
+        iconController.removeAllIconsForSlot("test_icon");
+
+        // THEN it is not removed for those icons
+        verify(manager, never()).onRemoveIcon(anyInt());
+    }
 
     private <T extends IconManager & TestableIconManager> void testCallOnAdd_forManager(T manager) {
         StatusBarIconHolder holder = holderForType(TYPE_ICON);
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 1ba0a36..d8446f4 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
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static com.android.systemui.flags.Flags.MODERN_BOUNCER;
 import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN;
 import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE;
 
@@ -109,7 +108,6 @@
     @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Mock private View mNotificationContainer;
     @Mock private KeyguardBypassController mBypassController;
-    @Mock private KeyguardBouncer.Factory mKeyguardBouncerFactory;
     @Mock private KeyguardMessageAreaController.Factory mKeyguardMessageAreaFactory;
     @Mock private KeyguardMessageAreaController mKeyguardMessageAreaController;
     @Mock private KeyguardMessageArea mKeyguardMessageArea;
@@ -153,8 +151,6 @@
                 .isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM))
                 .thenReturn(true);
 
-        when(mFeatureFlags.isEnabled(MODERN_BOUNCER)).thenReturn(true);
-
         mStatusBarKeyguardViewManager =
                 new StatusBarKeyguardViewManager(
                         getContext(),
@@ -169,7 +165,6 @@
                         mock(NotificationShadeWindowController.class),
                         mKeyguardStateController,
                         mock(NotificationMediaManager.class),
-                        mKeyguardBouncerFactory,
                         mKeyguardMessageAreaFactory,
                         Optional.of(mSysUiUnfoldComponent),
                         () -> mShadeController,
@@ -660,7 +655,6 @@
                         mock(NotificationShadeWindowController.class),
                         mKeyguardStateController,
                         mock(NotificationMediaManager.class),
-                        mKeyguardBouncerFactory,
                         mKeyguardMessageAreaFactory,
                         Optional.of(mSysUiUnfoldComponent),
                         () -> mShadeController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest_Old.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest_Old.java
deleted file mode 100644
index 55ab681..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest_Old.java
+++ /dev/null
@@ -1,581 +0,0 @@
-/*
- * Copyright (C) 2018 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.statusbar.phone;
-
-import static com.android.systemui.flags.Flags.MODERN_BOUNCER;
-import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN;
-import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE;
-
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyFloat;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewRootImpl;
-import android.window.OnBackInvokedCallback;
-import android.window.OnBackInvokedDispatcher;
-import android.window.WindowOnBackInvokedDispatcher;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.util.LatencyTracker;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardMessageArea;
-import com.android.keyguard.KeyguardMessageAreaController;
-import com.android.keyguard.KeyguardSecurityModel;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dock.DockManager;
-import com.android.systemui.dreams.DreamOverlayStateController;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.keyguard.data.BouncerView;
-import com.android.systemui.keyguard.data.BouncerViewDelegate;
-import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
-import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor;
-import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
-import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor;
-import com.android.systemui.navigationbar.NavigationModeController;
-import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
-import com.android.systemui.shade.NotificationPanelViewController;
-import com.android.systemui.shade.ShadeController;
-import com.android.systemui.shade.ShadeExpansionChangeEvent;
-import com.android.systemui.shade.ShadeExpansionStateManager;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.unfold.SysUIUnfoldComponent;
-
-import com.google.common.truth.Truth;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Optional;
-
-/**
- * StatusBarKeyguardViewManager Test with deprecated KeyguardBouncer.java.
- * TODO: Delete when deleting {@link KeyguardBouncer}
- */
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class StatusBarKeyguardViewManagerTest_Old extends SysuiTestCase {
-    private static final ShadeExpansionChangeEvent EXPANSION_EVENT =
-            expansionEvent(/* fraction= */ 0.5f, /* expanded= */ false, /* tracking= */ true);
-
-    @Mock private ViewMediatorCallback mViewMediatorCallback;
-    @Mock private LockPatternUtils mLockPatternUtils;
-    @Mock private CentralSurfaces mCentralSurfaces;
-    @Mock private ViewGroup mContainer;
-    @Mock private NotificationPanelViewController mNotificationPanelView;
-    @Mock private BiometricUnlockController mBiometricUnlockController;
-    @Mock private SysuiStatusBarStateController mStatusBarStateController;
-    @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    @Mock private View mNotificationContainer;
-    @Mock private KeyguardBypassController mBypassController;
-    @Mock private KeyguardBouncer.Factory mKeyguardBouncerFactory;
-    @Mock private KeyguardMessageAreaController.Factory mKeyguardMessageAreaFactory;
-    @Mock private KeyguardMessageAreaController mKeyguardMessageAreaController;
-    @Mock private KeyguardBouncer mPrimaryBouncer;
-    @Mock private KeyguardMessageArea mKeyguardMessageArea;
-    @Mock private ShadeController mShadeController;
-    @Mock private SysUIUnfoldComponent mSysUiUnfoldComponent;
-    @Mock private DreamOverlayStateController mDreamOverlayStateController;
-    @Mock private LatencyTracker mLatencyTracker;
-    @Mock private FeatureFlags mFeatureFlags;
-    @Mock private KeyguardSecurityModel mKeyguardSecurityModel;
-    @Mock private PrimaryBouncerCallbackInteractor mPrimaryBouncerCallbackInteractor;
-    @Mock private PrimaryBouncerInteractor mPrimaryBouncerInteractor;
-    @Mock private AlternateBouncerInteractor mAlternateBouncerInteractor;
-    @Mock private BouncerView mBouncerView;
-    @Mock private BouncerViewDelegate mBouncerViewDelegate;
-
-    private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
-    private PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback
-            mBouncerExpansionCallback;
-    private FakeKeyguardStateController mKeyguardStateController =
-            spy(new FakeKeyguardStateController());
-
-    @Mock private ViewRootImpl mViewRootImpl;
-    @Mock private WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher;
-    @Captor
-    private ArgumentCaptor<OnBackInvokedCallback> mOnBackInvokedCallback;
-
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        when(mKeyguardBouncerFactory.create(
-                any(ViewGroup.class),
-                any(PrimaryBouncerExpansionCallback.class)))
-                .thenReturn(mPrimaryBouncer);
-        when(mCentralSurfaces.getBouncerContainer()).thenReturn(mContainer);
-        when(mContainer.findViewById(anyInt())).thenReturn(mKeyguardMessageArea);
-        when(mKeyguardMessageAreaFactory.create(any(KeyguardMessageArea.class)))
-                .thenReturn(mKeyguardMessageAreaController);
-        when(mBouncerView.getDelegate()).thenReturn(mBouncerViewDelegate);
-
-        mStatusBarKeyguardViewManager =
-                new StatusBarKeyguardViewManager(
-                        getContext(),
-                        mViewMediatorCallback,
-                        mLockPatternUtils,
-                        mStatusBarStateController,
-                        mock(ConfigurationController.class),
-                        mKeyguardUpdateMonitor,
-                        mDreamOverlayStateController,
-                        mock(NavigationModeController.class),
-                        mock(DockManager.class),
-                        mock(NotificationShadeWindowController.class),
-                        mKeyguardStateController,
-                        mock(NotificationMediaManager.class),
-                        mKeyguardBouncerFactory,
-                        mKeyguardMessageAreaFactory,
-                        Optional.of(mSysUiUnfoldComponent),
-                        () -> mShadeController,
-                        mLatencyTracker,
-                        mKeyguardSecurityModel,
-                        mFeatureFlags,
-                        mPrimaryBouncerCallbackInteractor,
-                        mPrimaryBouncerInteractor,
-                        mBouncerView,
-                        mAlternateBouncerInteractor) {
-                    @Override
-                    public ViewRootImpl getViewRootImpl() {
-                        return mViewRootImpl;
-                    }
-                };
-        when(mViewRootImpl.getOnBackInvokedDispatcher())
-                .thenReturn(mOnBackInvokedDispatcher);
-        mStatusBarKeyguardViewManager.registerCentralSurfaces(
-                mCentralSurfaces,
-                mNotificationPanelView,
-                new ShadeExpansionStateManager(),
-                mBiometricUnlockController,
-                mNotificationContainer,
-                mBypassController);
-        mStatusBarKeyguardViewManager.show(null);
-        ArgumentCaptor<PrimaryBouncerExpansionCallback> callbackArgumentCaptor =
-                ArgumentCaptor.forClass(PrimaryBouncerExpansionCallback.class);
-        verify(mKeyguardBouncerFactory).create(any(ViewGroup.class),
-                callbackArgumentCaptor.capture());
-        mBouncerExpansionCallback = callbackArgumentCaptor.getValue();
-    }
-
-    @Test
-    public void dismissWithAction_AfterKeyguardGoneSetToFalse() {
-        OnDismissAction action = () -> false;
-        Runnable cancelAction = () -> {};
-        mStatusBarKeyguardViewManager.dismissWithAction(
-                action, cancelAction, false /* afterKeyguardGone */);
-        verify(mPrimaryBouncer).showWithDismissAction(eq(action), eq(cancelAction));
-    }
-
-    @Test
-    public void showBouncer_onlyWhenShowing() {
-        mStatusBarKeyguardViewManager.hide(0 /* startTime */, 0 /* fadeoutDuration */);
-        mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */);
-        verify(mPrimaryBouncer, never()).show(anyBoolean(), anyBoolean());
-        verify(mPrimaryBouncer, never()).show(anyBoolean());
-    }
-
-    @Test
-    public void showBouncer_notWhenBouncerAlreadyShowing() {
-        mStatusBarKeyguardViewManager.hide(0 /* startTime */, 0 /* fadeoutDuration */);
-        when(mPrimaryBouncer.isSecure()).thenReturn(true);
-        mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */);
-        verify(mPrimaryBouncer, never()).show(anyBoolean(), anyBoolean());
-        verify(mPrimaryBouncer, never()).show(anyBoolean());
-    }
-
-    @Test
-    public void showBouncer_showsTheBouncer() {
-        mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */);
-        verify(mPrimaryBouncer).show(anyBoolean(), eq(true));
-    }
-
-    @Test
-    public void onPanelExpansionChanged_neverShowsDuringHintAnimation() {
-        when(mNotificationPanelView.isUnlockHintRunning()).thenReturn(true);
-        mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
-        verify(mPrimaryBouncer, never()).setExpansion(anyFloat());
-    }
-
-    @Test
-    public void onPanelExpansionChanged_propagatesToBouncerOnlyIfShowing() {
-        mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
-        verify(mPrimaryBouncer, never()).setExpansion(eq(0.5f));
-
-        when(mPrimaryBouncer.isShowing()).thenReturn(true);
-        mStatusBarKeyguardViewManager.onPanelExpansionChanged(
-                expansionEvent(/* fraction= */ 0.6f, /* expanded= */ false, /* tracking= */ true));
-        verify(mPrimaryBouncer).setExpansion(eq(0.6f));
-    }
-
-    @Test
-    public void onPanelExpansionChanged_duplicateEventsAreIgnored() {
-        when(mPrimaryBouncer.isShowing()).thenReturn(true);
-        mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
-        verify(mPrimaryBouncer).setExpansion(eq(0.5f));
-
-        reset(mPrimaryBouncer);
-        mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
-        verify(mPrimaryBouncer, never()).setExpansion(eq(0.5f));
-    }
-
-    @Test
-    public void onPanelExpansionChanged_hideBouncer_afterKeyguardHidden() {
-        mStatusBarKeyguardViewManager.hide(0, 0);
-        when(mPrimaryBouncer.inTransit()).thenReturn(true);
-
-        mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
-        verify(mPrimaryBouncer).setExpansion(eq(EXPANSION_HIDDEN));
-    }
-
-    @Test
-    public void onPanelExpansionChanged_showsBouncerWhenSwiping() {
-        mKeyguardStateController.setCanDismissLockScreen(false);
-        mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
-        verify(mPrimaryBouncer).show(eq(false), eq(false));
-
-        // But not when it's already visible
-        reset(mPrimaryBouncer);
-        when(mPrimaryBouncer.isShowing()).thenReturn(true);
-        mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
-        verify(mPrimaryBouncer, never()).show(eq(false), eq(false));
-
-        // Or animating away
-        reset(mPrimaryBouncer);
-        when(mPrimaryBouncer.isAnimatingAway()).thenReturn(true);
-        mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
-        verify(mPrimaryBouncer, never()).show(eq(false), eq(false));
-    }
-
-    @Test
-    public void onPanelExpansionChanged_neverTranslatesBouncerWhenWakeAndUnlock() {
-        when(mBiometricUnlockController.getMode())
-                .thenReturn(BiometricUnlockController.MODE_WAKE_AND_UNLOCK);
-        mStatusBarKeyguardViewManager.onPanelExpansionChanged(
-                expansionEvent(
-                        /* fraction= */ EXPANSION_VISIBLE,
-                        /* expanded= */ true,
-                        /* tracking= */ false));
-        verify(mPrimaryBouncer, never()).setExpansion(anyFloat());
-    }
-
-    @Test
-    public void onPanelExpansionChanged_neverTranslatesBouncerWhenDismissBouncer() {
-        // Since KeyguardBouncer.EXPANSION_VISIBLE = 0 panel expansion, if the unlock is dismissing
-        // the bouncer, there may be an onPanelExpansionChanged(0) call to collapse the panel
-        // which would mistakenly cause the bouncer to show briefly before its visibility
-        // is set to hide. Therefore, we don't want to propagate panelExpansionChanged to the
-        // bouncer if the bouncer is dismissing as a result of a biometric unlock.
-        when(mBiometricUnlockController.getMode())
-                .thenReturn(BiometricUnlockController.MODE_DISMISS_BOUNCER);
-        mStatusBarKeyguardViewManager.onPanelExpansionChanged(
-                expansionEvent(
-                        /* fraction= */ EXPANSION_VISIBLE,
-                        /* expanded= */ true,
-                        /* tracking= */ false));
-        verify(mPrimaryBouncer, never()).setExpansion(anyFloat());
-    }
-
-    @Test
-    public void onPanelExpansionChanged_neverTranslatesBouncerWhenOccluded() {
-        when(mKeyguardStateController.isOccluded()).thenReturn(true);
-        mStatusBarKeyguardViewManager.onPanelExpansionChanged(
-                expansionEvent(
-                        /* fraction= */ EXPANSION_VISIBLE,
-                        /* expanded= */ true,
-                        /* tracking= */ false));
-        verify(mPrimaryBouncer, never()).setExpansion(anyFloat());
-    }
-
-    @Test
-    public void onPanelExpansionChanged_neverTranslatesBouncerWhenShowBouncer() {
-        // Since KeyguardBouncer.EXPANSION_VISIBLE = 0 panel expansion, if the unlock is dismissing
-        // the bouncer, there may be an onPanelExpansionChanged(0) call to collapse the panel
-        // which would mistakenly cause the bouncer to show briefly before its visibility
-        // is set to hide. Therefore, we don't want to propagate panelExpansionChanged to the
-        // bouncer if the bouncer is dismissing as a result of a biometric unlock.
-        when(mBiometricUnlockController.getMode())
-                .thenReturn(BiometricUnlockController.MODE_SHOW_BOUNCER);
-        mStatusBarKeyguardViewManager.onPanelExpansionChanged(
-                expansionEvent(
-                        /* fraction= */ EXPANSION_VISIBLE,
-                        /* expanded= */ true,
-                        /* tracking= */ false));
-        verify(mPrimaryBouncer, never()).setExpansion(anyFloat());
-    }
-
-    @Test
-    public void onPanelExpansionChanged_neverTranslatesBouncerWhenShadeLocked() {
-        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED);
-        mStatusBarKeyguardViewManager.onPanelExpansionChanged(
-                expansionEvent(
-                        /* fraction= */ EXPANSION_VISIBLE,
-                        /* expanded= */ true,
-                        /* tracking= */ false));
-        verify(mPrimaryBouncer, never()).setExpansion(anyFloat());
-    }
-
-    @Test
-    public void setOccluded_animatesPanelExpansion_onlyIfBouncerHidden() {
-        mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, true /* animated */);
-        verify(mCentralSurfaces).animateKeyguardUnoccluding();
-
-        when(mPrimaryBouncer.isShowing()).thenReturn(true);
-        clearInvocations(mCentralSurfaces);
-        mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, true /* animated */);
-        verify(mCentralSurfaces, never()).animateKeyguardUnoccluding();
-    }
-
-    @Test
-    public void setOccluded_onKeyguardOccludedChangedCalled() {
-        clearInvocations(mKeyguardStateController);
-        clearInvocations(mKeyguardUpdateMonitor);
-
-        mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, false /* animated */);
-        verify(mKeyguardStateController).notifyKeyguardState(true, false);
-
-        clearInvocations(mKeyguardUpdateMonitor);
-        clearInvocations(mKeyguardStateController);
-
-        mStatusBarKeyguardViewManager.setOccluded(true /* occluded */, false /* animated */);
-        verify(mKeyguardStateController).notifyKeyguardState(true, true);
-
-        clearInvocations(mKeyguardUpdateMonitor);
-        clearInvocations(mKeyguardStateController);
-
-        mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, false /* animated */);
-        verify(mKeyguardStateController).notifyKeyguardState(true, false);
-    }
-
-    @Test
-    public void setOccluded_isInLaunchTransition_onKeyguardOccludedChangedCalled() {
-        mStatusBarKeyguardViewManager.show(null);
-
-        mStatusBarKeyguardViewManager.setOccluded(true /* occluded */, false /* animated */);
-        verify(mKeyguardStateController).notifyKeyguardState(true, true);
-    }
-
-    @Test
-    public void setOccluded_isLaunchingActivityOverLockscreen_onKeyguardOccludedChangedCalled() {
-        when(mCentralSurfaces.isLaunchingActivityOverLockscreen()).thenReturn(true);
-        mStatusBarKeyguardViewManager.show(null);
-
-        mStatusBarKeyguardViewManager.setOccluded(true /* occluded */, false /* animated */);
-        verify(mKeyguardStateController).notifyKeyguardState(true, true);
-    }
-
-    @Test
-    public void testHiding_cancelsGoneRunnable() {
-        OnDismissAction action = mock(OnDismissAction.class);
-        Runnable cancelAction = mock(Runnable.class);
-        mStatusBarKeyguardViewManager.dismissWithAction(
-                action, cancelAction, true /* afterKeyguardGone */);
-
-        when(mPrimaryBouncer.isShowing()).thenReturn(false);
-        mStatusBarKeyguardViewManager.hideBouncer(true);
-        mStatusBarKeyguardViewManager.hide(0, 30);
-        verify(action, never()).onDismiss();
-        verify(cancelAction).run();
-    }
-
-    @Test
-    public void testHidingBouncer_cancelsGoneRunnable() {
-        OnDismissAction action = mock(OnDismissAction.class);
-        Runnable cancelAction = mock(Runnable.class);
-        mStatusBarKeyguardViewManager.dismissWithAction(
-                action, cancelAction, true /* afterKeyguardGone */);
-
-        when(mPrimaryBouncer.isShowing()).thenReturn(false);
-        mStatusBarKeyguardViewManager.hideBouncer(true);
-
-        verify(action, never()).onDismiss();
-        verify(cancelAction).run();
-    }
-
-    @Test
-    public void testHiding_doesntCancelWhenShowing() {
-        OnDismissAction action = mock(OnDismissAction.class);
-        Runnable cancelAction = mock(Runnable.class);
-        mStatusBarKeyguardViewManager.dismissWithAction(
-                action, cancelAction, true /* afterKeyguardGone */);
-
-        mStatusBarKeyguardViewManager.hide(0, 30);
-        verify(action).onDismiss();
-        verify(cancelAction, never()).run();
-    }
-
-    @Test
-    public void testBouncerIsOrWillBeShowing_whenBouncerIsInTransit() {
-        when(mPrimaryBouncer.isShowing()).thenReturn(false);
-        when(mPrimaryBouncer.inTransit()).thenReturn(true);
-
-        assertTrue(
-                "Is or will be showing should be true when bouncer is in transit",
-                mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing());
-    }
-
-    @Test
-    public void testUpdateResources_delegatesToBouncer() {
-        mStatusBarKeyguardViewManager.updateResources();
-
-        verify(mPrimaryBouncer).updateResources();
-    }
-
-    @Test
-    public void updateKeyguardPosition_delegatesToBouncer() {
-        mStatusBarKeyguardViewManager.updateKeyguardPosition(1.0f);
-
-        verify(mPrimaryBouncer).updateKeyguardPosition(1.0f);
-    }
-
-    @Test
-    public void testIsBouncerInTransit() {
-        when(mPrimaryBouncer.inTransit()).thenReturn(true);
-        Truth.assertThat(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).isTrue();
-        when(mPrimaryBouncer.inTransit()).thenReturn(false);
-        Truth.assertThat(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).isFalse();
-        mPrimaryBouncer = null;
-        Truth.assertThat(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).isFalse();
-    }
-
-    private static ShadeExpansionChangeEvent expansionEvent(
-            float fraction, boolean expanded, boolean tracking) {
-        return new ShadeExpansionChangeEvent(
-                fraction, expanded, tracking, /* dragDownPxAmount= */ 0f);
-    }
-
-    @Test
-    public void testPredictiveBackCallback_registration() {
-        /* verify that a predictive back callback is registered when the bouncer becomes visible */
-        mBouncerExpansionCallback.onVisibilityChanged(true);
-        verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
-                eq(OnBackInvokedDispatcher.PRIORITY_OVERLAY),
-                mOnBackInvokedCallback.capture());
-
-        /* verify that the same callback is unregistered when the bouncer becomes invisible */
-        mBouncerExpansionCallback.onVisibilityChanged(false);
-        verify(mOnBackInvokedDispatcher).unregisterOnBackInvokedCallback(
-                eq(mOnBackInvokedCallback.getValue()));
-    }
-
-    @Test
-    public void testPredictiveBackCallback_invocationHidesBouncer() {
-        mBouncerExpansionCallback.onVisibilityChanged(true);
-        /* capture the predictive back callback during registration */
-        verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
-                eq(OnBackInvokedDispatcher.PRIORITY_OVERLAY),
-                mOnBackInvokedCallback.capture());
-
-        when(mPrimaryBouncer.isShowing()).thenReturn(true);
-        when(mCentralSurfaces.shouldKeyguardHideImmediately()).thenReturn(true);
-        /* invoke the back callback directly */
-        mOnBackInvokedCallback.getValue().onBackInvoked();
-
-        /* verify that the bouncer will be hidden as a result of the invocation */
-        verify(mCentralSurfaces).setBouncerShowing(eq(false));
-    }
-
-    @Test
-    public void testReportBouncerOnDreamWhenVisible() {
-        mBouncerExpansionCallback.onVisibilityChanged(true);
-        verify(mCentralSurfaces).setBouncerShowingOverDream(false);
-        Mockito.clearInvocations(mCentralSurfaces);
-        when(mDreamOverlayStateController.isOverlayActive()).thenReturn(true);
-        mBouncerExpansionCallback.onVisibilityChanged(true);
-        verify(mCentralSurfaces).setBouncerShowingOverDream(true);
-    }
-
-    @Test
-    public void testReportBouncerOnDreamWhenNotVisible() {
-        mBouncerExpansionCallback.onVisibilityChanged(false);
-        verify(mCentralSurfaces).setBouncerShowingOverDream(false);
-        Mockito.clearInvocations(mCentralSurfaces);
-        when(mDreamOverlayStateController.isOverlayActive()).thenReturn(true);
-        mBouncerExpansionCallback.onVisibilityChanged(false);
-        verify(mCentralSurfaces).setBouncerShowingOverDream(false);
-    }
-
-    @Test
-    public void flag_off_DoesNotCallBouncerInteractor() {
-        when(mFeatureFlags.isEnabled(MODERN_BOUNCER)).thenReturn(false);
-        mStatusBarKeyguardViewManager.hideBouncer(false);
-        verify(mPrimaryBouncerInteractor, never()).hide();
-    }
-
-    @Test
-    public void hideAlternateBouncer_beforeCentralSurfacesRegistered() {
-        mStatusBarKeyguardViewManager =
-                new StatusBarKeyguardViewManager(
-                        getContext(),
-                        mViewMediatorCallback,
-                        mLockPatternUtils,
-                        mStatusBarStateController,
-                        mock(ConfigurationController.class),
-                        mKeyguardUpdateMonitor,
-                        mDreamOverlayStateController,
-                        mock(NavigationModeController.class),
-                        mock(DockManager.class),
-                        mock(NotificationShadeWindowController.class),
-                        mKeyguardStateController,
-                        mock(NotificationMediaManager.class),
-                        mKeyguardBouncerFactory,
-                        mKeyguardMessageAreaFactory,
-                        Optional.of(mSysUiUnfoldComponent),
-                        () -> mShadeController,
-                        mLatencyTracker,
-                        mKeyguardSecurityModel,
-                        mFeatureFlags,
-                        mPrimaryBouncerCallbackInteractor,
-                        mPrimaryBouncerInteractor,
-                        mBouncerView,
-                        mAlternateBouncerInteractor) {
-                    @Override
-                    public ViewRootImpl getViewRootImpl() {
-                        return mViewRootImpl;
-                    }
-                };
-
-        // the following call before registering centralSurfaces should NOT throw a NPE:
-        mStatusBarKeyguardViewManager.hideAlternateBouncer(true);
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index eef43bd..5bb25f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -40,8 +40,10 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.settings.FakeDisplayTracker;
 import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.shade.NotificationShadeWindowView;
+import com.android.systemui.shade.QuickSettingsController;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -92,7 +94,7 @@
         mMetricsLogger = new FakeMetricsLogger();
         LockscreenGestureLogger lockscreenGestureLogger = new LockscreenGestureLogger(
                 mMetricsLogger);
-        mCommandQueue = new CommandQueue(mContext);
+        mCommandQueue = new CommandQueue(mContext, new FakeDisplayTracker(mContext));
         mDependency.injectTestDependency(StatusBarStateController.class,
                 mock(SysuiStatusBarStateController.class));
         mDependency.injectTestDependency(ShadeController.class, mShadeController);
@@ -111,6 +113,7 @@
         mStatusBarNotificationPresenter = new StatusBarNotificationPresenter(
                 mContext,
                 mock(NotificationPanelViewController.class),
+                mock(QuickSettingsController.class),
                 mock(HeadsUpManagerPhone.class),
                 notificationShadeWindowView,
                 mock(ActivityStarter.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
index 613238f..929099a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
@@ -32,6 +32,7 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.settings.FakeDisplayTracker;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.statusbar.ActionClickLogger;
 import com.android.systemui.statusbar.CommandQueue;
@@ -78,7 +79,8 @@
         mRemoteInputCallback = spy(new StatusBarRemoteInputCallback(mContext,
                 mock(GroupExpansionManager.class), mNotificationLockscreenUserManager,
                 mKeyguardStateController, mStatusBarStateController, mStatusBarKeyguardViewManager,
-                mActivityStarter, mShadeController, new CommandQueue(mContext),
+                mActivityStarter, mShadeController,
+                new CommandQueue(mContext, new FakeDisplayTracker(mContext)),
                 mock(ActionClickLogger.class), mFakeExecutor));
         mRemoteInputCallback.mChallengeReceiver = mRemoteInputCallback.new ChallengeReceiver();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
index 6c83e9f..14a925a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
@@ -19,8 +19,10 @@
 
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.content.BroadcastReceiver;
 import android.content.Intent;
@@ -32,6 +34,8 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -46,12 +50,15 @@
 public class SystemUIDialogTest extends SysuiTestCase {
 
     @Mock
+    private FeatureFlags mFeatureFlags;
+    @Mock
     private BroadcastDispatcher mBroadcastDispatcher;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
 
+        mDependency.injectTestDependency(FeatureFlags.class, mFeatureFlags);
         mDependency.injectTestDependency(BroadcastDispatcher.class, mBroadcastDispatcher);
     }
 
@@ -86,4 +93,20 @@
         verify(mBroadcastDispatcher, never()).unregisterReceiver(any());
         assertFalse(dialog.isShowing());
     }
+
+    @Test
+    public void usePredictiveBackAnimFlag() {
+        when(mFeatureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_QS_DIALOG_ANIM))
+                .thenReturn(true);
+        final SystemUIDialog dialog = new SystemUIDialog(mContext);
+
+        dialog.show();
+
+        assertTrue(dialog.isShowing());
+        verify(mFeatureFlags, atLeast(1))
+                .isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_QS_DIALOG_ANIM);
+
+        dialog.dismiss();
+        assertFalse(dialog.isShowing());
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index f230b87..07e8d3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -23,10 +23,12 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -45,6 +47,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.R;
 import com.android.systemui.SysuiBaseFragmentTest;
 import com.android.systemui.dump.DumpManager;
@@ -67,6 +70,8 @@
 import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.window.StatusBarWindowStateController;
+import com.android.systemui.statusbar.window.StatusBarWindowStateListener;
 import com.android.systemui.util.CarrierConfigTracker;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.settings.SecureSettings;
@@ -79,6 +84,9 @@
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
+import java.util.List;
+
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
 @SmallTest
@@ -118,6 +126,12 @@
     private StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
     @Mock
     private DumpManager mDumpManager;
+    @Mock
+    private StatusBarWindowStateController mStatusBarWindowStateController;
+    @Mock
+    private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+
+    private List<StatusBarWindowStateListener> mStatusBarWindowStateListeners = new ArrayList<>();
 
     public CollapsedStatusBarFragmentTest() {
         super(CollapsedStatusBarFragment.class);
@@ -127,6 +141,14 @@
     public void setup() {
         injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
         mDependency.injectMockDependency(DarkIconDispatcher.class);
+
+        // Keep the window state listeners so we can dispatch to them to test the status bar
+        // fragment's response.
+        doAnswer(invocation -> {
+            mStatusBarWindowStateListeners.add(invocation.getArgument(0));
+            return null;
+        }).when(mStatusBarWindowStateController).addListener(
+                any(StatusBarWindowStateListener.class));
     }
 
     @Test
@@ -414,6 +436,27 @@
         assertFalse(contains);
     }
 
+    @Test
+    public void testStatusBarIcons_hiddenThroughoutCameraLaunch() {
+        final CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+
+        mockSecureCameraLaunch(fragment, true /* launched */);
+
+        // Status icons should be invisible or gone, but certainly not VISIBLE.
+        assertNotEquals(View.VISIBLE, getEndSideContentView().getVisibility());
+        assertNotEquals(View.VISIBLE, getClockView().getVisibility());
+
+        mockSecureCameraLaunchFinished();
+
+        assertNotEquals(View.VISIBLE, getEndSideContentView().getVisibility());
+        assertNotEquals(View.VISIBLE, getClockView().getVisibility());
+
+        mockSecureCameraLaunch(fragment, false /* launched */);
+
+        assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
+        assertEquals(View.VISIBLE, getClockView().getVisibility());
+    }
+
     @Override
     protected Fragment instantiate(Context context, String className, Bundle arguments) {
         MockitoAnnotations.initMocks(this);
@@ -455,7 +498,9 @@
                 mOperatorNameViewControllerFactory,
                 mSecureSettings,
                 mExecutor,
-                mDumpManager);
+                mDumpManager,
+                mStatusBarWindowStateController,
+                mKeyguardUpdateMonitor);
     }
 
     private void setUpDaggerComponent() {
@@ -478,6 +523,35 @@
                 mNotificationAreaInner);
     }
 
+    /**
+     * Configure mocks to return values consistent with the secure camera animating itself launched
+     * over the keyguard.
+     */
+    private void mockSecureCameraLaunch(CollapsedStatusBarFragment fragment, boolean launched) {
+        when(mKeyguardUpdateMonitor.isSecureCameraLaunchedOverKeyguard()).thenReturn(launched);
+        when(mKeyguardStateController.isOccluded()).thenReturn(launched);
+
+        if (launched) {
+            fragment.onCameraLaunchGestureDetected(0 /* source */);
+        } else {
+            for (StatusBarWindowStateListener listener : mStatusBarWindowStateListeners) {
+                listener.onStatusBarWindowStateChanged(StatusBarManager.WINDOW_STATE_SHOWING);
+            }
+        }
+
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+    }
+
+    /**
+     * Configure mocks to return values consistent with the secure camera showing over the keyguard
+     * with its launch animation finished.
+     */
+    private void mockSecureCameraLaunchFinished() {
+        for (StatusBarWindowStateListener listener : mStatusBarWindowStateListeners) {
+            listener.onStatusBarWindowStateChanged(StatusBarManager.WINDOW_STATE_HIDDEN);
+        }
+    }
+
     private CollapsedStatusBarFragment resumeAndGetFragment() {
         mFragments.dispatchResume();
         processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt
index 5a6bb30..ab888f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt
@@ -18,9 +18,9 @@
 
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
 import com.google.common.truth.Truth.assertThat
@@ -42,7 +42,7 @@
 
     private lateinit var underTest: AirplaneModeViewModelImpl
 
-    @Mock private lateinit var logger: ConnectivityPipelineLogger
+    @Mock private lateinit var logger: TableLogBuffer
     private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
     private lateinit var connectivityRepository: FakeConnectivityRepository
     private lateinit var interactor: AirplaneModeInteractor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModelTest.kt
index f822ba0..45189cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModelTest.kt
@@ -19,7 +19,8 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.log.table.TableRowLogger
-import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel.Companion.COL_ACTIVITY_DIRECTION
+import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel.Companion.COL_ACTIVITY_DIRECTION_IN
+import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel.Companion.COL_ACTIVITY_DIRECTION_OUT
 import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel.Companion.COL_CARRIER_NETWORK_CHANGE
 import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel.Companion.COL_CDMA_LEVEL
 import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel.Companion.COL_CONNECTION_STATE
@@ -54,7 +55,19 @@
         assertThat(logger.changes)
             .contains(Pair(COL_CONNECTION_STATE, connection.dataConnectionState.toString()))
         assertThat(logger.changes)
-            .contains(Pair(COL_ACTIVITY_DIRECTION, connection.dataActivityDirection.toString()))
+            .contains(
+                Pair(
+                    COL_ACTIVITY_DIRECTION_IN,
+                    connection.dataActivityDirection.hasActivityIn.toString(),
+                )
+            )
+        assertThat(logger.changes)
+            .contains(
+                Pair(
+                    COL_ACTIVITY_DIRECTION_OUT,
+                    connection.dataActivityDirection.hasActivityOut.toString(),
+                )
+            )
         assertThat(logger.changes)
             .contains(
                 Pair(COL_CARRIER_NETWORK_CHANGE, connection.carrierNetworkChangeActive.toString())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt
new file mode 100644
index 0000000..63cb30c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt
@@ -0,0 +1,120 @@
+/*
+ * 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.statusbar.pipeline.mobile.data.model
+
+import android.os.PersistableBundle
+import android.telephony.CarrierConfigManager
+import android.telephony.CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL
+import android.telephony.CarrierConfigManager.KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import org.junit.Before
+import org.junit.Test
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+class SystemUiCarrierConfigTest : SysuiTestCase() {
+
+    lateinit var underTest: SystemUiCarrierConfig
+
+    @Before
+    fun setUp() {
+        underTest = SystemUiCarrierConfig(SUB_1_ID, createTestConfig())
+    }
+
+    @Test
+    fun `process new config - reflected by isUsingDefault`() {
+        // Starts out using the defaults
+        assertThat(underTest.isUsingDefault).isTrue()
+
+        // ANY new config means we're no longer tracking defaults
+        underTest.processNewCarrierConfig(createTestConfig())
+
+        assertThat(underTest.isUsingDefault).isFalse()
+    }
+
+    @Test
+    fun `process new config - updates all flows`() {
+        assertThat(underTest.shouldInflateSignalStrength.value).isFalse()
+        assertThat(underTest.showOperatorNameInStatusBar.value).isFalse()
+
+        underTest.processNewCarrierConfig(
+            configWithOverrides(
+                KEY_INFLATE_SIGNAL_STRENGTH_BOOL to true,
+                KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL to true,
+            )
+        )
+
+        assertThat(underTest.shouldInflateSignalStrength.value).isTrue()
+        assertThat(underTest.showOperatorNameInStatusBar.value).isTrue()
+    }
+
+    @Test
+    fun `process new config - defaults to false for config overrides`() {
+        // This case is only apparent when:
+        //   1. The default is true
+        //   2. The override config has no value for a given key
+        // In this case (per the old code) we would use the default value of false, despite there
+        // being no override key present in the override config
+
+        underTest =
+            SystemUiCarrierConfig(
+                SUB_1_ID,
+                configWithOverrides(
+                    KEY_INFLATE_SIGNAL_STRENGTH_BOOL to true,
+                    KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL to true,
+                )
+            )
+
+        assertThat(underTest.isUsingDefault).isTrue()
+        assertThat(underTest.shouldInflateSignalStrength.value).isTrue()
+        assertThat(underTest.showOperatorNameInStatusBar.value).isTrue()
+
+        // Process a new config with no keys
+        underTest.processNewCarrierConfig(PersistableBundle())
+
+        assertThat(underTest.isUsingDefault).isFalse()
+        assertThat(underTest.shouldInflateSignalStrength.value).isFalse()
+        assertThat(underTest.showOperatorNameInStatusBar.value).isFalse()
+    }
+
+    companion object {
+        private const val SUB_1_ID = 1
+
+        /**
+         * In order to keep us from having to update every place that might want to create a config,
+         * make sure to add new keys here
+         */
+        fun createTestConfig() =
+            PersistableBundle().also {
+                it.putBoolean(CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL, false)
+                it.putBoolean(CarrierConfigManager.KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL, false)
+            }
+
+        /** Override the default config with the given (key, value) pair */
+        fun configWithOverride(key: String, override: Boolean): PersistableBundle =
+            createTestConfig().also { it.putBoolean(key, override) }
+
+        /** Override any number of configs from the default */
+        fun configWithOverrides(vararg overrides: Pair<String, Boolean>) =
+            createTestConfig().also { config ->
+                overrides.forEach { (key, value) -> config.putBoolean(key, value) }
+            }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt
new file mode 100644
index 0000000..0145103
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt
@@ -0,0 +1,172 @@
+/*
+ * 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.statusbar.pipeline.mobile.data.repository
+
+import android.content.Intent
+import android.os.PersistableBundle
+import android.telephony.CarrierConfigManager
+import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
+import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfigTest.Companion.createTestConfig
+import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.MockitoSession
+import org.mockito.quality.Strictness
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+class CarrierConfigRepositoryTest : SysuiTestCase() {
+    private val testDispatcher = UnconfinedTestDispatcher()
+    private val testScope = TestScope(testDispatcher)
+
+    private lateinit var underTest: CarrierConfigRepository
+    private lateinit var mockitoSession: MockitoSession
+    private lateinit var carrierConfigCoreStartable: CarrierConfigCoreStartable
+
+    @Mock private lateinit var logger: MobileInputLogger
+    @Mock private lateinit var carrierConfigManager: CarrierConfigManager
+    @Mock private lateinit var dumpManager: DumpManager
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        mockitoSession =
+            mockitoSession()
+                .initMocks(this)
+                .mockStatic(CarrierConfigManager::class.java)
+                .strictness(Strictness.LENIENT)
+                .startMocking()
+
+        whenever(CarrierConfigManager.getDefaultConfig()).thenReturn(DEFAULT_CONFIG)
+
+        whenever(carrierConfigManager.getConfigForSubId(anyInt())).thenAnswer { invocation ->
+            when (invocation.getArgument(0) as Int) {
+                1 -> CONFIG_1
+                2 -> CONFIG_2
+                else -> null
+            }
+        }
+
+        underTest =
+            CarrierConfigRepository(
+                fakeBroadcastDispatcher,
+                carrierConfigManager,
+                dumpManager,
+                logger,
+                testScope.backgroundScope,
+            )
+
+        carrierConfigCoreStartable =
+            CarrierConfigCoreStartable(underTest, testScope.backgroundScope)
+    }
+
+    @After
+    fun tearDown() {
+        mockitoSession.finishMocking()
+    }
+
+    @Test
+    fun `carrier config stream produces int-bundle pairs`() =
+        testScope.runTest {
+            var latest: Pair<Int, PersistableBundle>? = null
+            val job = underTest.carrierConfigStream.onEach { latest = it }.launchIn(this)
+
+            sendConfig(SUB_ID_1)
+            assertThat(latest).isEqualTo(Pair(SUB_ID_1, CONFIG_1))
+
+            sendConfig(SUB_ID_2)
+            assertThat(latest).isEqualTo(Pair(SUB_ID_2, CONFIG_2))
+
+            job.cancel()
+        }
+
+    @Test
+    fun `carrier config stream ignores invalid subscriptions`() =
+        testScope.runTest {
+            var latest: Pair<Int, PersistableBundle>? = null
+            val job = underTest.carrierConfigStream.onEach { latest = it }.launchIn(this)
+
+            sendConfig(INVALID_SUBSCRIPTION_ID)
+
+            assertThat(latest).isNull()
+
+            job.cancel()
+        }
+
+    @Test
+    fun `getOrCreateConfig - uses default config if no override`() {
+        val config = underTest.getOrCreateConfigForSubId(123)
+        assertThat(config.isUsingDefault).isTrue()
+    }
+
+    @Test
+    fun `getOrCreateConfig - uses override if exists`() {
+        val config = underTest.getOrCreateConfigForSubId(SUB_ID_1)
+        assertThat(config.isUsingDefault).isFalse()
+    }
+
+    @Test
+    fun `config - updates while config stream is collected`() =
+        testScope.runTest {
+            CONFIG_1.putBoolean(CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL, false)
+
+            carrierConfigCoreStartable.start()
+
+            val config = underTest.getOrCreateConfigForSubId(SUB_ID_1)
+            assertThat(config.shouldInflateSignalStrength.value).isFalse()
+
+            CONFIG_1.putBoolean(CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL, true)
+            sendConfig(SUB_ID_1)
+
+            assertThat(config.shouldInflateSignalStrength.value).isTrue()
+        }
+
+    private fun sendConfig(subId: Int) {
+        fakeBroadcastDispatcher.registeredReceivers.forEach { receiver ->
+            receiver.onReceive(
+                context,
+                Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)
+                    .putExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, subId)
+            )
+        }
+    }
+
+    companion object {
+        private const val SUB_ID_1 = 1
+        private const val SUB_ID_2 = 2
+
+        private val DEFAULT_CONFIG = createTestConfig()
+        private val CONFIG_1 = createTestConfig()
+        private val CONFIG_2 = createTestConfig()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
index 0add905e..f483e42 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
@@ -55,8 +55,12 @@
     private val _subscriptions = MutableStateFlow<List<SubscriptionModel>>(listOf())
     override val subscriptions = _subscriptions
 
-    private val _activeMobileDataSubscriptionId = MutableStateFlow(INVALID_SUBSCRIPTION_ID)
+    private val _activeMobileDataSubscriptionId = MutableStateFlow<Int?>(null)
     override val activeMobileDataSubscriptionId = _activeMobileDataSubscriptionId
+
+    private val _activeMobileRepository = MutableStateFlow<MobileConnectionRepository?>(null)
+    override val activeMobileDataRepository = _activeMobileRepository
+
     override val activeSubChangedInGroupEvent: MutableSharedFlow<Unit> = MutableSharedFlow()
 
     private val _defaultDataSubId = MutableStateFlow(INVALID_SUBSCRIPTION_ID)
@@ -66,14 +70,12 @@
     override val defaultMobileNetworkConnectivity = _mobileConnectivity
 
     private val subIdRepos = mutableMapOf<Int, MobileConnectionRepository>()
+
     override fun getRepoForSubId(subId: Int): MobileConnectionRepository {
         return subIdRepos[subId]
             ?: FakeMobileConnectionRepository(subId, tableLogBuffer).also { subIdRepos[subId] = it }
     }
 
-    private val _globalMobileDataSettingChangedEvent = MutableStateFlow(Unit)
-    override val globalMobileDataSettingChangedEvent = _globalMobileDataSettingChangedEvent
-
     override val defaultDataSubRatConfig = MutableStateFlow(MobileMappings.Config())
 
     private val _defaultMobileIconMapping = MutableStateFlow(TEST_MAPPING)
@@ -94,12 +96,15 @@
         _mobileConnectivity.value = model
     }
 
-    suspend fun triggerGlobalMobileDataSettingChangedEvent() {
-        _globalMobileDataSettingChangedEvent.emit(Unit)
-    }
-
     fun setActiveMobileDataSubscriptionId(subId: Int) {
-        _activeMobileDataSubscriptionId.value = subId
+        // Simulate the filtering that the repo does
+        if (subId == INVALID_SUBSCRIPTION_ID) {
+            _activeMobileDataSubscriptionId.value = null
+            _activeMobileRepository.value = null
+        } else {
+            _activeMobileDataSubscriptionId.value = subId
+            _activeMobileRepository.value = getRepoForSubId(subId)
+        }
     }
 
     fun setMobileConnectionRepositoryMap(connections: Map<Int, MobileConnectionRepository>) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
index 0859d14..17502f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.demomode.DemoMode
 import com.android.systemui.demomode.DemoModeController
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.log.table.TableLogBufferFactory
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.DemoMobileConnectionsRepository
@@ -32,15 +33,14 @@
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.validMobileEvent
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileConnectionsRepositoryImpl
+import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSource
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.kotlinArgumentCaptor
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.FakeSettings
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.CoroutineScope
@@ -81,11 +81,11 @@
     @Mock private lateinit var connectivityManager: ConnectivityManager
     @Mock private lateinit var subscriptionManager: SubscriptionManager
     @Mock private lateinit var telephonyManager: TelephonyManager
-    @Mock private lateinit var logger: ConnectivityPipelineLogger
+    @Mock private lateinit var logger: MobileInputLogger
+    @Mock private lateinit var summaryLogger: TableLogBuffer
     @Mock private lateinit var demoModeController: DemoModeController
     @Mock private lateinit var dumpManager: DumpManager
 
-    private val globalSettings = FakeSettings()
     private val fakeNetworkEventsFlow = MutableStateFlow<FakeNetworkEventModel?>(null)
     private val mobileMappings = FakeMobileMappingsProxy()
 
@@ -116,9 +116,9 @@
                 subscriptionManager,
                 telephonyManager,
                 logger,
+                summaryLogger,
                 mobileMappings,
                 fakeBroadcastDispatcher,
-                globalSettings,
                 context,
                 IMMEDIATE,
                 scope,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt
index 6989b514..00ce412 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt
@@ -138,7 +138,8 @@
                 assertThat(connectionInfo.carrierNetworkChangeActive)
                     .isEqualTo(model.carrierNetworkChange)
                 assertThat(connectionInfo.isRoaming).isEqualTo(model.roaming)
-                assertThat(conn.networkName.value).isEqualTo(NetworkNameModel.Derived(model.name))
+                assertThat(conn.networkName.value)
+                    .isEqualTo(NetworkNameModel.IntentDerived(model.name))
 
                 // TODO(b/261029387): check these once we start handling them
                 assertThat(connectionInfo.isEmergencyOnly).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
index f12d113..f60d92b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
@@ -539,7 +539,8 @@
                 assertThat(connectionInfo.carrierNetworkChangeActive)
                     .isEqualTo(model.carrierNetworkChange)
                 assertThat(connectionInfo.isRoaming).isEqualTo(model.roaming)
-                assertThat(conn.networkName.value).isEqualTo(NetworkNameModel.Derived(model.name))
+                assertThat(conn.networkName.value)
+                    .isEqualTo(NetworkNameModel.IntentDerived(model.name))
 
                 // TODO(b/261029387) check these once we start handling them
                 assertThat(connectionInfo.isEmergencyOnly).isFalse()
@@ -594,9 +595,11 @@
     subId: Int = 1,
     level: Int = 1,
     numberOfLevels: Int = 4,
+    activity: Int = DATA_ACTIVITY_NONE,
 ): FakeWifiEventModel.CarrierMerged =
     FakeWifiEventModel.CarrierMerged(
         subscriptionId = subId,
         level = level,
         numberOfLevels = numberOfLevels,
+        activity = activity,
     )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt
index ea90150..f0f213b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
 
+import android.telephony.TelephonyManager
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -25,8 +26,9 @@
 import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.launchIn
@@ -49,6 +51,7 @@
 
     private lateinit var wifiRepository: FakeWifiRepository
     @Mock private lateinit var logger: TableLogBuffer
+    @Mock private lateinit var telephonyManager: TelephonyManager
 
     private val testDispatcher = UnconfinedTestDispatcher()
     private val testScope = TestScope(testDispatcher)
@@ -56,13 +59,16 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+        whenever(telephonyManager.subscriptionId).thenReturn(SUB_ID)
+        whenever(telephonyManager.simOperatorName).thenReturn("")
+
         wifiRepository = FakeWifiRepository()
 
         underTest =
             CarrierMergedConnectionRepository(
                 SUB_ID,
                 logger,
-                NetworkNameModel.Default("name"),
+                telephonyManager,
                 testScope.backgroundScope,
                 wifiRepository,
             )
@@ -135,6 +141,44 @@
         }
 
     @Test
+    fun connectionInfo_activity_comesFromWifiActivity() =
+        testScope.runTest {
+            var latest: MobileConnectionModel? = null
+            val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
+
+            wifiRepository.setIsWifiEnabled(true)
+            wifiRepository.setIsWifiDefault(true)
+            wifiRepository.setWifiNetwork(
+                WifiNetworkModel.CarrierMerged(
+                    networkId = NET_ID,
+                    subscriptionId = SUB_ID,
+                    level = 3,
+                )
+            )
+            wifiRepository.setWifiActivity(
+                DataActivityModel(
+                    hasActivityIn = true,
+                    hasActivityOut = false,
+                )
+            )
+
+            assertThat(latest!!.dataActivityDirection.hasActivityIn).isTrue()
+            assertThat(latest!!.dataActivityDirection.hasActivityOut).isFalse()
+
+            wifiRepository.setWifiActivity(
+                DataActivityModel(
+                    hasActivityIn = false,
+                    hasActivityOut = true,
+                )
+            )
+
+            assertThat(latest!!.dataActivityDirection.hasActivityIn).isFalse()
+            assertThat(latest!!.dataActivityDirection.hasActivityOut).isTrue()
+
+            job.cancel()
+        }
+
+    @Test
     fun connectionInfo_carrierMergedWifi_wrongSubId_isDefault() =
         testScope.runTest {
             var latest: MobileConnectionModel? = null
@@ -244,6 +288,43 @@
             job.cancel()
         }
 
+    @Test
+    fun networkName_usesSimOperatorNameAsInitial() =
+        testScope.runTest {
+            whenever(telephonyManager.simOperatorName).thenReturn("Test SIM name")
+
+            var latest: NetworkNameModel? = null
+            val job = underTest.networkName.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isEqualTo(NetworkNameModel.SimDerived("Test SIM name"))
+
+            job.cancel()
+        }
+
+    @Test
+    fun networkName_updatesOnNetworkUpdate() =
+        testScope.runTest {
+            whenever(telephonyManager.simOperatorName).thenReturn("Test SIM name")
+
+            var latest: NetworkNameModel? = null
+            val job = underTest.networkName.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isEqualTo(NetworkNameModel.SimDerived("Test SIM name"))
+
+            whenever(telephonyManager.simOperatorName).thenReturn("New SIM name")
+            wifiRepository.setWifiNetwork(
+                WifiNetworkModel.CarrierMerged(
+                    networkId = NET_ID,
+                    subscriptionId = SUB_ID,
+                    level = 3,
+                )
+            )
+
+            assertThat(latest).isEqualTo(NetworkNameModel.SimDerived("New SIM name"))
+
+            job.cancel()
+        }
+
     private companion object {
         const val SUB_ID = 123
         const val NET_ID = 456
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
index c02a4df..cd4d847 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
@@ -16,24 +16,33 @@
 
 package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
 
+import android.telephony.ServiceState
+import android.telephony.SignalStrength
+import android.telephony.TelephonyCallback
+import android.telephony.TelephonyManager
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.log.table.TableLogBufferFactory
 import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
+import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel.Companion.COL_EMERGENCY
+import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel.Companion.COL_OPERATOR
+import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel.Companion.COL_PRIMARY_LEVEL
 import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
-import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.getTelephonyCallbackForType
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 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
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.StringWriter
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.test.TestScope
@@ -55,24 +64,18 @@
 class FullMobileConnectionRepositoryTest : SysuiTestCase() {
     private lateinit var underTest: FullMobileConnectionRepository
 
+    private val systemClock = FakeSystemClock()
     private val testDispatcher = UnconfinedTestDispatcher()
     private val testScope = TestScope(testDispatcher)
-    private val mobileMappings = FakeMobileMappingsProxy()
-    private val tableLogBuffer = mock<TableLogBuffer>()
+    private val tableLogBuffer = TableLogBuffer(maxSize = 100, name = "TestName", systemClock)
     private val mobileFactory = mock<MobileConnectionRepositoryImpl.Factory>()
     private val carrierMergedFactory = mock<CarrierMergedConnectionRepository.Factory>()
 
-    private lateinit var connectionsRepo: FakeMobileConnectionsRepository
-    private val globalMobileDataSettingChangedEvent: Flow<Unit>
-        get() = connectionsRepo.globalMobileDataSettingChangedEvent
-
     private lateinit var mobileRepo: FakeMobileConnectionRepository
     private lateinit var carrierMergedRepo: FakeMobileConnectionRepository
 
     @Before
     fun setUp() {
-        connectionsRepo = FakeMobileConnectionsRepository(mobileMappings, tableLogBuffer)
-
         mobileRepo = FakeMobileConnectionRepository(SUB_ID, tableLogBuffer)
         carrierMergedRepo = FakeMobileConnectionRepository(SUB_ID, tableLogBuffer)
 
@@ -82,12 +85,10 @@
                     any(),
                     eq(DEFAULT_NAME),
                     eq(SEP),
-                    eq(globalMobileDataSettingChangedEvent),
                 )
             )
             .thenReturn(mobileRepo)
-        whenever(carrierMergedFactory.build(eq(SUB_ID), any(), eq(DEFAULT_NAME)))
-            .thenReturn(carrierMergedRepo)
+        whenever(carrierMergedFactory.build(eq(SUB_ID), any())).thenReturn(carrierMergedRepo)
     }
 
     @Test
@@ -109,7 +110,6 @@
                     tableLogBuffer,
                     DEFAULT_NAME,
                     SEP,
-                    globalMobileDataSettingChangedEvent
                 )
         }
 
@@ -126,7 +126,7 @@
 
             assertThat(underTest.activeRepo.value).isEqualTo(mobileRepo)
             assertThat(underTest.connectionInfo.value).isEqualTo(mobileConnectionInfo)
-            verify(carrierMergedFactory, never()).build(SUB_ID, tableLogBuffer, DEFAULT_NAME)
+            verify(carrierMergedFactory, never()).build(SUB_ID, tableLogBuffer)
         }
 
     @Test
@@ -310,7 +310,6 @@
                     startingIsCarrierMerged = false,
                     DEFAULT_NAME,
                     SEP,
-                    globalMobileDataSettingChangedEvent,
                 )
 
             val connection1Repeat =
@@ -319,7 +318,6 @@
                     startingIsCarrierMerged = false,
                     DEFAULT_NAME,
                     SEP,
-                    globalMobileDataSettingChangedEvent,
                 )
 
             assertThat(connection1.tableLogBuffer)
@@ -345,7 +343,6 @@
                     startingIsCarrierMerged = false,
                     DEFAULT_NAME,
                     SEP,
-                    globalMobileDataSettingChangedEvent,
                 )
 
             // WHEN a connection with the same sub ID but carrierMerged = true is created
@@ -355,7 +352,6 @@
                     startingIsCarrierMerged = true,
                     DEFAULT_NAME,
                     SEP,
-                    globalMobileDataSettingChangedEvent,
                 )
 
             // THEN the same table is re-used
@@ -363,8 +359,219 @@
                 .isSameInstanceAs(connection1Repeat.tableLogBuffer)
         }
 
-    // TODO(b/238425913): Verify that the logging switches correctly (once the carrier merged repo
-    //   implements logging).
+    @Test
+    fun connectionInfo_logging_notCarrierMerged_getsUpdates() =
+        testScope.runTest {
+            // SETUP: Use real repositories to verify the diffing still works. (See b/267501739.)
+            val telephonyManager =
+                mock<TelephonyManager>().apply { whenever(this.simOperatorName).thenReturn("") }
+            createRealMobileRepo(telephonyManager)
+            createRealCarrierMergedRepo(telephonyManager, FakeWifiRepository())
+
+            initializeRepo(startingIsCarrierMerged = false)
+
+            val job = underTest.connectionInfo.launchIn(this)
+
+            // WHEN we set up some mobile connection info
+            val serviceState = ServiceState()
+            serviceState.setOperatorName("longName", "OpTypical", "1")
+            serviceState.isEmergencyOnly = false
+            getTelephonyCallbackForType<TelephonyCallback.ServiceStateListener>(telephonyManager)
+                .onServiceStateChanged(serviceState)
+
+            // THEN it's logged to the buffer
+            assertThat(dumpBuffer()).contains("$COL_OPERATOR${BUFFER_SEPARATOR}OpTypical")
+            assertThat(dumpBuffer()).contains("$COL_EMERGENCY${BUFFER_SEPARATOR}false")
+
+            // WHEN we update mobile connection info
+            val serviceState2 = ServiceState()
+            serviceState2.setOperatorName("longName", "OpDiff", "1")
+            serviceState2.isEmergencyOnly = true
+            getTelephonyCallbackForType<TelephonyCallback.ServiceStateListener>(telephonyManager)
+                .onServiceStateChanged(serviceState2)
+
+            // THEN the updates are logged
+            assertThat(dumpBuffer()).contains("$COL_OPERATOR${BUFFER_SEPARATOR}OpDiff")
+            assertThat(dumpBuffer()).contains("$COL_EMERGENCY${BUFFER_SEPARATOR}true")
+
+            job.cancel()
+        }
+
+    @Test
+    fun connectionInfo_logging_carrierMerged_getsUpdates() =
+        testScope.runTest {
+            // SETUP: Use real repositories to verify the diffing still works. (See b/267501739.)
+            val telephonyManager =
+                mock<TelephonyManager>().apply { whenever(this.simOperatorName).thenReturn("") }
+            createRealMobileRepo(telephonyManager)
+            val wifiRepository = FakeWifiRepository()
+            createRealCarrierMergedRepo(telephonyManager, wifiRepository)
+
+            initializeRepo(startingIsCarrierMerged = true)
+
+            val job = underTest.connectionInfo.launchIn(this)
+
+            // WHEN we set up carrier merged info
+            val networkId = 2
+            wifiRepository.setWifiNetwork(
+                WifiNetworkModel.CarrierMerged(
+                    networkId,
+                    SUB_ID,
+                    level = 3,
+                )
+            )
+
+            // THEN the carrier merged info is logged
+            assertThat(dumpBuffer()).contains("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}3")
+
+            // WHEN we update the info
+            wifiRepository.setWifiNetwork(
+                WifiNetworkModel.CarrierMerged(
+                    networkId,
+                    SUB_ID,
+                    level = 1,
+                )
+            )
+
+            // THEN the updates are logged
+            assertThat(dumpBuffer()).contains("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}1")
+
+            job.cancel()
+        }
+
+    @Test
+    fun connectionInfo_logging_updatesWhenCarrierMergedUpdates() =
+        testScope.runTest {
+            // SETUP: Use real repositories to verify the diffing still works. (See b/267501739.)
+            val telephonyManager =
+                mock<TelephonyManager>().apply { whenever(this.simOperatorName).thenReturn("") }
+            createRealMobileRepo(telephonyManager)
+
+            val wifiRepository = FakeWifiRepository()
+            createRealCarrierMergedRepo(telephonyManager, wifiRepository)
+
+            initializeRepo(startingIsCarrierMerged = false)
+
+            val job = underTest.connectionInfo.launchIn(this)
+
+            // WHEN we set up some mobile connection info
+            val signalStrength = mock<SignalStrength>()
+            whenever(signalStrength.level).thenReturn(1)
+
+            getTelephonyCallbackForType<TelephonyCallback.SignalStrengthsListener>(telephonyManager)
+                .onSignalStrengthsChanged(signalStrength)
+
+            // THEN it's logged to the buffer
+            assertThat(dumpBuffer()).contains("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}1")
+
+            // WHEN isCarrierMerged is set to true
+            val networkId = 2
+            wifiRepository.setWifiNetwork(
+                WifiNetworkModel.CarrierMerged(
+                    networkId,
+                    SUB_ID,
+                    level = 3,
+                )
+            )
+            underTest.setIsCarrierMerged(true)
+
+            // THEN the carrier merged info is logged
+            assertThat(dumpBuffer()).contains("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}3")
+
+            // WHEN the carrier merge network is updated
+            wifiRepository.setWifiNetwork(
+                WifiNetworkModel.CarrierMerged(
+                    networkId,
+                    SUB_ID,
+                    level = 4,
+                )
+            )
+
+            // THEN the new level is logged
+            assertThat(dumpBuffer()).contains("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}4")
+
+            // WHEN isCarrierMerged is set to false
+            underTest.setIsCarrierMerged(false)
+
+            // THEN the typical info is logged
+            // Note: Since our first logs also had the typical info, we need to search the log
+            // contents for after our carrier merged level log.
+            val fullBuffer = dumpBuffer()
+            val carrierMergedContentIndex = fullBuffer.indexOf("${BUFFER_SEPARATOR}4")
+            val bufferAfterCarrierMerged = fullBuffer.substring(carrierMergedContentIndex)
+            assertThat(bufferAfterCarrierMerged).contains("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}1")
+
+            // WHEN the normal network is updated
+            val newMobileInfo =
+                MobileConnectionModel(
+                    operatorAlphaShort = "Mobile Operator 2",
+                    primaryLevel = 0,
+                )
+            mobileRepo.setConnectionInfo(newMobileInfo)
+
+            // THEN the new level is logged
+            assertThat(dumpBuffer()).contains("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}0")
+
+            job.cancel()
+        }
+
+    @Test
+    fun connectionInfo_logging_doesNotLogUpdatesForNotActiveRepo() =
+        testScope.runTest {
+            // SETUP: Use real repositories to verify the diffing still works. (See b/267501739.)
+            val telephonyManager =
+                mock<TelephonyManager>().apply { whenever(this.simOperatorName).thenReturn("") }
+            createRealMobileRepo(telephonyManager)
+
+            val wifiRepository = FakeWifiRepository()
+            createRealCarrierMergedRepo(telephonyManager, wifiRepository)
+
+            // WHEN isCarrierMerged = false
+            initializeRepo(startingIsCarrierMerged = false)
+
+            val job = underTest.connectionInfo.launchIn(this)
+
+            val signalStrength = mock<SignalStrength>()
+            whenever(signalStrength.level).thenReturn(1)
+            getTelephonyCallbackForType<TelephonyCallback.SignalStrengthsListener>(telephonyManager)
+                .onSignalStrengthsChanged(signalStrength)
+
+            // THEN updates to the carrier merged level aren't logged
+            val networkId = 2
+            wifiRepository.setWifiNetwork(
+                WifiNetworkModel.CarrierMerged(
+                    networkId,
+                    SUB_ID,
+                    level = 4,
+                )
+            )
+            assertThat(dumpBuffer()).doesNotContain("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}4")
+
+            wifiRepository.setWifiNetwork(
+                WifiNetworkModel.CarrierMerged(
+                    networkId,
+                    SUB_ID,
+                    level = 3,
+                )
+            )
+            assertThat(dumpBuffer()).doesNotContain("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}3")
+
+            // WHEN isCarrierMerged is set to true
+            underTest.setIsCarrierMerged(true)
+
+            // THEN updates to the normal level aren't logged
+            whenever(signalStrength.level).thenReturn(5)
+            getTelephonyCallbackForType<TelephonyCallback.SignalStrengthsListener>(telephonyManager)
+                .onSignalStrengthsChanged(signalStrength)
+            assertThat(dumpBuffer()).doesNotContain("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}5")
+
+            whenever(signalStrength.level).thenReturn(6)
+            getTelephonyCallbackForType<TelephonyCallback.SignalStrengthsListener>(telephonyManager)
+                .onSignalStrengthsChanged(signalStrength)
+            assertThat(dumpBuffer()).doesNotContain("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}6")
+
+            job.cancel()
+        }
 
     private fun initializeRepo(startingIsCarrierMerged: Boolean) {
         underTest =
@@ -374,16 +581,74 @@
                 tableLogBuffer,
                 DEFAULT_NAME,
                 SEP,
-                globalMobileDataSettingChangedEvent,
                 testScope.backgroundScope,
                 mobileFactory,
                 carrierMergedFactory,
             )
     }
 
+    private fun createRealMobileRepo(
+        telephonyManager: TelephonyManager,
+    ): MobileConnectionRepositoryImpl {
+        whenever(telephonyManager.subscriptionId).thenReturn(SUB_ID)
+
+        val realRepo =
+            MobileConnectionRepositoryImpl(
+                context,
+                SUB_ID,
+                defaultNetworkName = NetworkNameModel.Default("default"),
+                networkNameSeparator = SEP,
+                telephonyManager,
+                systemUiCarrierConfig = mock(),
+                fakeBroadcastDispatcher,
+                mobileMappingsProxy = mock(),
+                testDispatcher,
+                logger = mock(),
+                tableLogBuffer,
+                testScope.backgroundScope,
+            )
+        whenever(
+                mobileFactory.build(
+                    eq(SUB_ID),
+                    any(),
+                    eq(DEFAULT_NAME),
+                    eq(SEP),
+                )
+            )
+            .thenReturn(realRepo)
+
+        return realRepo
+    }
+
+    private fun createRealCarrierMergedRepo(
+        telephonyManager: TelephonyManager,
+        wifiRepository: FakeWifiRepository,
+    ): CarrierMergedConnectionRepository {
+        wifiRepository.setIsWifiEnabled(true)
+        wifiRepository.setIsWifiDefault(true)
+        val realRepo =
+            CarrierMergedConnectionRepository(
+                SUB_ID,
+                tableLogBuffer,
+                telephonyManager,
+                testScope.backgroundScope,
+                wifiRepository,
+            )
+        whenever(carrierMergedFactory.build(eq(SUB_ID), any())).thenReturn(realRepo)
+
+        return realRepo
+    }
+
+    private fun dumpBuffer(): String {
+        val outputWriter = StringWriter()
+        tableLogBuffer.dump(PrintWriter(outputWriter), arrayOf())
+        return outputWriter.toString()
+    }
+
     private companion object {
         const val SUB_ID = 42
         private val DEFAULT_NAME = NetworkNameModel.Default("default name")
         private const val SEP = "-"
+        private const val BUFFER_SEPARATOR = "|"
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
index d6b8c0d..b2577e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
@@ -17,15 +17,13 @@
 package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
 
 import android.content.Intent
-import android.os.UserHandle
-import android.provider.Settings
+import android.telephony.CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL
 import android.telephony.CellSignalStrengthCdma
 import android.telephony.NetworkRegistrationInfo
 import android.telephony.ServiceState
 import android.telephony.ServiceState.STATE_IN_SERVICE
 import android.telephony.ServiceState.STATE_OUT_OF_SERVICE
 import android.telephony.SignalStrength
-import android.telephony.SubscriptionInfo
 import android.telephony.TelephonyCallback
 import android.telephony.TelephonyCallback.DataActivityListener
 import android.telephony.TelephonyCallback.ServiceStateListener
@@ -41,6 +39,8 @@
 import android.telephony.TelephonyManager.DATA_CONNECTING
 import android.telephony.TelephonyManager.DATA_DISCONNECTED
 import android.telephony.TelephonyManager.DATA_DISCONNECTING
+import android.telephony.TelephonyManager.DATA_HANDOVER_IN_PROGRESS
+import android.telephony.TelephonyManager.DATA_SUSPENDED
 import android.telephony.TelephonyManager.DATA_UNKNOWN
 import android.telephony.TelephonyManager.ERI_OFF
 import android.telephony.TelephonyManager.ERI_ON
@@ -59,18 +59,19 @@
 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType
 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.OverrideNetworkType
 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.UnknownNetworkType
+import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
+import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfigTest.Companion.configWithOverride
+import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfigTest.Companion.createTestConfig
 import com.android.systemui.statusbar.pipeline.mobile.data.model.toNetworkNameModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
+import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
 import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.FakeSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
@@ -83,7 +84,6 @@
 import org.junit.Before
 import org.junit.Test
 import org.mockito.Mock
-import org.mockito.Mockito
 import org.mockito.MockitoAnnotations
 
 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@@ -94,17 +94,20 @@
     private lateinit var connectionsRepo: FakeMobileConnectionsRepository
 
     @Mock private lateinit var telephonyManager: TelephonyManager
-    @Mock private lateinit var logger: ConnectivityPipelineLogger
+    @Mock private lateinit var logger: MobileInputLogger
     @Mock private lateinit var tableLogger: TableLogBuffer
 
     private val scope = CoroutineScope(IMMEDIATE)
     private val mobileMappings = FakeMobileMappingsProxy()
-    private val globalSettings = FakeSettings()
+    private val systemUiCarrierConfig =
+        SystemUiCarrierConfig(
+            SUB_1_ID,
+            createTestConfig(),
+        )
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        globalSettings.userId = UserHandle.USER_ALL
         whenever(telephonyManager.subscriptionId).thenReturn(SUB_1_ID)
 
         connectionsRepo = FakeMobileConnectionsRepository(mobileMappings, tableLogger)
@@ -116,9 +119,8 @@
                 DEFAULT_NAME,
                 SEP,
                 telephonyManager,
-                globalSettings,
+                systemUiCarrierConfig,
                 fakeBroadcastDispatcher,
-                connectionsRepo.globalMobileDataSettingChangedEvent,
                 mobileMappings,
                 IMMEDIATE,
                 logger,
@@ -255,6 +257,37 @@
         }
 
     @Test
+    fun testFlowForSubId_dataConnectionState_suspended() =
+        runBlocking(IMMEDIATE) {
+            var latest: MobileConnectionModel? = null
+            val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
+
+            val callback =
+                getTelephonyCallbackForType<TelephonyCallback.DataConnectionStateListener>()
+            callback.onDataConnectionStateChanged(DATA_SUSPENDED, 200 /* unused */)
+
+            assertThat(latest?.dataConnectionState).isEqualTo(DataConnectionState.Suspended)
+
+            job.cancel()
+        }
+
+    @Test
+    fun testFlowForSubId_dataConnectionState_handoverInProgress() =
+        runBlocking(IMMEDIATE) {
+            var latest: MobileConnectionModel? = null
+            val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
+
+            val callback =
+                getTelephonyCallbackForType<TelephonyCallback.DataConnectionStateListener>()
+            callback.onDataConnectionStateChanged(DATA_HANDOVER_IN_PROGRESS, 200 /* unused */)
+
+            assertThat(latest?.dataConnectionState)
+                .isEqualTo(DataConnectionState.HandoverInProgress)
+
+            job.cancel()
+        }
+
+    @Test
     fun testFlowForSubId_dataConnectionState_unknown() =
         runBlocking(IMMEDIATE) {
             var latest: MobileConnectionModel? = null
@@ -270,6 +303,21 @@
         }
 
     @Test
+    fun testFlowForSubId_dataConnectionState_invalid() =
+        runBlocking(IMMEDIATE) {
+            var latest: MobileConnectionModel? = null
+            val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
+
+            val callback =
+                getTelephonyCallbackForType<TelephonyCallback.DataConnectionStateListener>()
+            callback.onDataConnectionStateChanged(45, 200 /* unused */)
+
+            assertThat(latest?.dataConnectionState).isEqualTo(DataConnectionState.Invalid)
+
+            job.cancel()
+        }
+
+    @Test
     fun testFlowForSubId_dataActivity() =
         runBlocking(IMMEDIATE) {
             var latest: MobileConnectionModel? = null
@@ -352,52 +400,26 @@
     @Test
     fun dataEnabled_initial_false() =
         runBlocking(IMMEDIATE) {
-            whenever(telephonyManager.isDataConnectionAllowed).thenReturn(true)
-
-            assertThat(underTest.dataEnabled.value).isFalse()
-        }
-
-    @Test
-    fun dataEnabled_isEnabled_true() =
-        runBlocking(IMMEDIATE) {
-            whenever(telephonyManager.isDataConnectionAllowed).thenReturn(true)
-            val job = underTest.dataEnabled.launchIn(this)
-
-            assertThat(underTest.dataEnabled.value).isTrue()
-
-            job.cancel()
-        }
-
-    @Test
-    fun dataEnabled_isDisabled() =
-        runBlocking(IMMEDIATE) {
             whenever(telephonyManager.isDataConnectionAllowed).thenReturn(false)
-            val job = underTest.dataEnabled.launchIn(this)
 
             assertThat(underTest.dataEnabled.value).isFalse()
-
-            job.cancel()
         }
 
     @Test
-    fun isDataConnectionAllowed_subIdSettingUpdate_valueUpdated() =
+    fun `is data enabled - tracks telephony callback`() =
         runBlocking(IMMEDIATE) {
-            val subIdSettingName = "${Settings.Global.MOBILE_DATA}$SUB_1_ID"
-
             var latest: Boolean? = null
             val job = underTest.dataEnabled.onEach { latest = it }.launchIn(this)
 
-            // We don't read the setting directly, we query telephony when changes happen
             whenever(telephonyManager.isDataConnectionAllowed).thenReturn(false)
-            globalSettings.putInt(subIdSettingName, 0)
-            assertThat(latest).isFalse()
+            assertThat(underTest.dataEnabled.value).isFalse()
 
-            whenever(telephonyManager.isDataConnectionAllowed).thenReturn(true)
-            globalSettings.putInt(subIdSettingName, 1)
+            val callback = getTelephonyCallbackForType<TelephonyCallback.DataEnabledListener>()
+
+            callback.onDataEnabledChanged(true, 1)
             assertThat(latest).isTrue()
 
-            whenever(telephonyManager.isDataConnectionAllowed).thenReturn(false)
-            globalSettings.putInt(subIdSettingName, 0)
+            callback.onDataEnabledChanged(false, 1)
             assertThat(latest).isFalse()
 
             job.cancel()
@@ -418,8 +440,6 @@
     fun `roaming - cdma - queries telephony manager`() =
         runBlocking(IMMEDIATE) {
             var latest: Boolean? = null
-            // Start the telephony collection job so that cdmaRoaming starts updating
-            val telephonyJob = underTest.connectionInfo.launchIn(this)
             val job = underTest.cdmaRoaming.onEach { latest = it }.launchIn(this)
 
             val cb = getTelephonyCallbackForType<ServiceStateListener>()
@@ -439,7 +459,6 @@
 
             assertThat(latest).isTrue()
 
-            telephonyJob.cancel()
             job.cancel()
         }
 
@@ -536,16 +555,51 @@
         }
 
     @Test
-    fun `network name - broadcast not for this sub id - returns default`() =
+    fun `network name - broadcast not for this sub id - keeps old value`() =
         runBlocking(IMMEDIATE) {
             var latest: NetworkNameModel? = null
             val job = underTest.networkName.onEach { latest = it }.launchIn(this)
 
-            val intent = spnIntent(subId = 101)
-
+            val intent = spnIntent()
             fakeBroadcastDispatcher.registeredReceivers.forEach { receiver ->
                 receiver.onReceive(context, intent)
             }
+            assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
+
+            // WHEN an intent with a different subId is sent
+            val wrongSubIntent = spnIntent(subId = 101)
+
+            fakeBroadcastDispatcher.registeredReceivers.forEach { receiver ->
+                receiver.onReceive(context, wrongSubIntent)
+            }
+
+            // THEN the previous intent's name is still used
+            assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
+
+            job.cancel()
+        }
+
+    @Test
+    fun `network name - broadcast has no data - updates to default`() =
+        runBlocking(IMMEDIATE) {
+            var latest: NetworkNameModel? = null
+            val job = underTest.networkName.onEach { latest = it }.launchIn(this)
+
+            val intent = spnIntent()
+            fakeBroadcastDispatcher.registeredReceivers.forEach { receiver ->
+                receiver.onReceive(context, intent)
+            }
+            assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
+
+            val intentWithoutInfo =
+                spnIntent(
+                    showSpn = false,
+                    showPlmn = false,
+                )
+
+            fakeBroadcastDispatcher.registeredReceivers.forEach { receiver ->
+                receiver.onReceive(context, intentWithoutInfo)
+            }
 
             assertThat(latest).isEqualTo(DEFAULT_NAME)
 
@@ -553,7 +607,7 @@
         }
 
     @Test
-    fun `network name - operatorAlphaShort - tracked`() =
+    fun `operatorAlphaShort - tracked`() =
         runBlocking(IMMEDIATE) {
             var latest: String? = null
 
@@ -625,16 +679,31 @@
             job.cancel()
         }
 
-    private fun getTelephonyCallbacks(): List<TelephonyCallback> {
-        val callbackCaptor = argumentCaptor<TelephonyCallback>()
-        Mockito.verify(telephonyManager).registerTelephonyCallback(any(), callbackCaptor.capture())
-        return callbackCaptor.allValues
-    }
+    @Test
+    fun `number of levels - uses carrier config`() =
+        runBlocking(IMMEDIATE) {
+            var latest: Int? = null
+            val job = underTest.numberOfLevels.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isEqualTo(DEFAULT_NUM_LEVELS)
+
+            systemUiCarrierConfig.processNewCarrierConfig(
+                configWithOverride(KEY_INFLATE_SIGNAL_STRENGTH_BOOL, true)
+            )
+
+            assertThat(latest).isEqualTo(DEFAULT_NUM_LEVELS + 1)
+
+            systemUiCarrierConfig.processNewCarrierConfig(
+                configWithOverride(KEY_INFLATE_SIGNAL_STRENGTH_BOOL, false)
+            )
+
+            assertThat(latest).isEqualTo(DEFAULT_NUM_LEVELS)
+
+            job.cancel()
+        }
 
     private inline fun <reified T> getTelephonyCallbackForType(): T {
-        val cbs = getTelephonyCallbacks().filterIsInstance<T>()
-        assertThat(cbs.size).isEqualTo(1)
-        return cbs[0]
+        return MobileTelephonyHelpers.getTelephonyCallbackForType(telephonyManager)
     }
 
     /** Convenience constructor for SignalStrength */
@@ -668,8 +737,6 @@
     companion object {
         private val IMMEDIATE = Dispatchers.Main.immediate
         private const val SUB_1_ID = 1
-        private val SUB_1 =
-            mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_1_ID) }
 
         private val DEFAULT_NAME = NetworkNameModel.Default("default name")
         private const val SEP = "-"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index ae390a0..09b7a66 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -22,10 +22,11 @@
 import android.net.NetworkCapabilities
 import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
 import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
-import android.provider.Settings
+import android.os.ParcelUuid
 import android.telephony.CarrierConfigManager
 import android.telephony.SubscriptionInfo
 import android.telephony.SubscriptionManager
+import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
 import android.telephony.TelephonyCallback
 import android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener
 import android.telephony.TelephonyManager
@@ -38,22 +39,25 @@
 import com.android.systemui.log.table.TableLogBufferFactory
 import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Factory.Companion.tableBufferLogName
+import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.FakeSettings
 import com.google.common.truth.Truth.assertThat
+import java.util.UUID
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.runBlocking
@@ -78,20 +82,22 @@
     private lateinit var carrierMergedFactory: CarrierMergedConnectionRepository.Factory
     private lateinit var fullConnectionFactory: FullMobileConnectionRepository.Factory
     private lateinit var wifiRepository: FakeWifiRepository
+    private lateinit var carrierConfigRepository: CarrierConfigRepository
     @Mock private lateinit var connectivityManager: ConnectivityManager
     @Mock private lateinit var subscriptionManager: SubscriptionManager
     @Mock private lateinit var telephonyManager: TelephonyManager
-    @Mock private lateinit var logger: ConnectivityPipelineLogger
+    @Mock private lateinit var logger: MobileInputLogger
+    @Mock private lateinit var summaryLogger: TableLogBuffer
     @Mock private lateinit var logBufferFactory: TableLogBufferFactory
 
     private val mobileMappings = FakeMobileMappingsProxy()
 
     private val scope = CoroutineScope(IMMEDIATE)
-    private val globalSettings = FakeSettings()
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+        whenever(telephonyManager.simOperatorName).thenReturn("")
 
         // Set up so the individual connection repositories
         whenever(telephonyManager.createForSubscriptionId(anyInt())).thenAnswer { invocation ->
@@ -104,21 +110,42 @@
             mock<TableLogBuffer>()
         }
 
+        // For convenience, set up the subscription info callbacks
+        whenever(subscriptionManager.getActiveSubscriptionInfo(anyInt())).thenAnswer { invocation ->
+            when (invocation.getArgument(0) as Int) {
+                1 -> SUB_1
+                2 -> SUB_2
+                3 -> SUB_3
+                4 -> SUB_4
+                else -> null
+            }
+        }
+
         wifiRepository = FakeWifiRepository()
 
+        carrierConfigRepository =
+            CarrierConfigRepository(
+                fakeBroadcastDispatcher,
+                mock(),
+                mock(),
+                logger,
+                scope,
+            )
+
         connectionFactory =
             MobileConnectionRepositoryImpl.Factory(
                 fakeBroadcastDispatcher,
                 context = context,
                 telephonyManager = telephonyManager,
                 bgDispatcher = IMMEDIATE,
-                globalSettings = globalSettings,
                 logger = logger,
                 mobileMappingsProxy = mobileMappings,
                 scope = scope,
+                carrierConfigRepository = carrierConfigRepository,
             )
         carrierMergedFactory =
             CarrierMergedConnectionRepository.Factory(
+                telephonyManager,
                 scope,
                 wifiRepository,
             )
@@ -136,9 +163,9 @@
                 subscriptionManager,
                 telephonyManager,
                 logger,
+                summaryLogger,
                 mobileMappings,
                 fakeBroadcastDispatcher,
-                globalSettings,
                 context,
                 IMMEDIATE,
                 scope,
@@ -232,10 +259,9 @@
         }
 
     @Test
-    fun testActiveDataSubscriptionId_initialValueIsInvalidId() =
+    fun testActiveDataSubscriptionId_initialValueIsNull() =
         runBlocking(IMMEDIATE) {
-            assertThat(underTest.activeMobileDataSubscriptionId.value)
-                .isEqualTo(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+            assertThat(underTest.activeMobileDataSubscriptionId.value).isEqualTo(null)
         }
 
     @Test
@@ -254,6 +280,140 @@
         }
 
     @Test
+    fun activeSubId_nullIfInvalidSubIdIsReceived() =
+        runBlocking(IMMEDIATE) {
+            var latest: Int? = null
+
+            val job = underTest.activeMobileDataSubscriptionId.onEach { latest = it }.launchIn(this)
+
+            getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
+                .onActiveDataSubscriptionIdChanged(SUB_2_ID)
+
+            assertThat(latest).isNotNull()
+
+            getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
+                .onActiveDataSubscriptionIdChanged(INVALID_SUBSCRIPTION_ID)
+
+            assertThat(latest).isNull()
+
+            job.cancel()
+        }
+
+    @Test
+    fun activeRepo_initiallyNull() {
+        assertThat(underTest.activeMobileDataRepository.value).isNull()
+    }
+
+    @Test
+    fun activeRepo_updatesWithActiveDataId() =
+        runBlocking(IMMEDIATE) {
+            var latest: MobileConnectionRepository? = null
+            val job = underTest.activeMobileDataRepository.onEach { latest = it }.launchIn(this)
+
+            getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
+                .onActiveDataSubscriptionIdChanged(SUB_2_ID)
+
+            assertThat(latest?.subId).isEqualTo(SUB_2_ID)
+
+            job.cancel()
+        }
+
+    @Test
+    fun activeRepo_nullIfActiveDataSubIdBecomesInvalid() =
+        runBlocking(IMMEDIATE) {
+            var latest: MobileConnectionRepository? = null
+            val job = underTest.activeMobileDataRepository.onEach { latest = it }.launchIn(this)
+
+            getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
+                .onActiveDataSubscriptionIdChanged(SUB_2_ID)
+
+            assertThat(latest).isNotNull()
+
+            getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
+                .onActiveDataSubscriptionIdChanged(INVALID_SUBSCRIPTION_ID)
+
+            assertThat(latest).isNull()
+
+            job.cancel()
+        }
+
+    @Test
+    /** Regression test for b/268146648. */
+    fun activeSubIdIsSetBeforeSubscriptionsAreUpdated_doesNotThrow() =
+        runBlocking(IMMEDIATE) {
+            var activeRepo: MobileConnectionRepository? = null
+            var subscriptions: List<SubscriptionModel>? = null
+
+            val activeRepoJob =
+                underTest.activeMobileDataRepository.onEach { activeRepo = it }.launchIn(this)
+            val subscriptionsJob =
+                underTest.subscriptions.onEach { subscriptions = it }.launchIn(this)
+
+            getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
+                .onActiveDataSubscriptionIdChanged(SUB_2_ID)
+
+            assertThat(subscriptions).isEmpty()
+            assertThat(activeRepo).isNotNull()
+
+            activeRepoJob.cancel()
+            subscriptionsJob.cancel()
+        }
+
+    @Test
+    fun getRepoForSubId_activeDataSubIdIsRequestedBeforeSubscriptionsUpdate() =
+        runBlocking(IMMEDIATE) {
+            var latest: MobileConnectionRepository? = null
+            var subscriptions: List<SubscriptionModel>? = null
+            val activeSubIdJob =
+                underTest.activeMobileDataSubscriptionId
+                    .filterNotNull()
+                    .onEach { latest = underTest.getRepoForSubId(it) }
+                    .launchIn(this)
+            val subscriptionsJob =
+                underTest.subscriptions.onEach { subscriptions = it }.launchIn(this)
+
+            // Active data subscription id is sent, but no subscription change has been posted yet
+            getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
+                .onActiveDataSubscriptionIdChanged(SUB_2_ID)
+
+            // Subscriptions list is empty
+            assertThat(subscriptions).isEmpty()
+            // getRepoForSubId does not throw
+            assertThat(latest).isNotNull()
+
+            activeSubIdJob.cancel()
+            subscriptionsJob.cancel()
+        }
+
+    @Test
+    fun activeDataSentBeforeSubscriptionList_subscriptionReusesActiveDataRepo() =
+        runBlocking(IMMEDIATE) {
+            var activeRepo: MobileConnectionRepository? = null
+            val job = underTest.activeMobileDataRepository.onEach { activeRepo = it }.launchIn(this)
+            val subscriptionsJob = underTest.subscriptions.launchIn(this)
+
+            // GIVEN active repo is updated before the subscription list updates
+            getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
+                .onActiveDataSubscriptionIdChanged(SUB_2_ID)
+
+            assertThat(activeRepo).isNotNull()
+
+            // GIVEN the subscription list is then updated which includes the active data sub id
+            whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+                .thenReturn(listOf(SUB_2))
+            getSubscriptionCallback().onSubscriptionsChanged()
+
+            // WHEN requesting a connection repository for the subscription
+            val newRepo = underTest.getRepoForSubId(SUB_2_ID)
+
+            // THEN the newly request repo has been cached and reused
+            assertThat(activeRepo).isSameInstanceAs(newRepo)
+
+            job.cancel()
+            subscriptionsJob.cancel()
+        }
+
+    @Test
     fun testConnectionRepository_validSubId_isCached() =
         runBlocking(IMMEDIATE) {
             val job = underTest.subscriptions.launchIn(this)
@@ -453,7 +613,7 @@
         }
 
     @Test
-    fun `connection repository - log buffer contains sub id in its name`() =
+    fun connectionRepository_logBufferContainsSubIdInItsName() =
         runBlocking(IMMEDIATE) {
             val job = underTest.subscriptions.launchIn(this)
 
@@ -531,24 +691,6 @@
         }
 
     @Test
-    fun globalMobileDataSettingsChangedEvent_producesOnSettingChange() =
-        runBlocking(IMMEDIATE) {
-            var produced = false
-            val job =
-                underTest.globalMobileDataSettingChangedEvent
-                    .onEach { produced = true }
-                    .launchIn(this)
-
-            assertThat(produced).isFalse()
-
-            globalSettings.putInt(Settings.Global.MOBILE_DATA, 0)
-
-            assertThat(produced).isTrue()
-
-            job.cancel()
-        }
-
-    @Test
     fun mobileConnectivity_isConnected_isNotValidated() =
         runBlocking(IMMEDIATE) {
             val caps = createCapabilities(connected = true, validated = false)
@@ -614,9 +756,9 @@
                     subscriptionManager,
                     telephonyManager,
                     logger,
+                    summaryLogger,
                     mobileMappings,
                     fakeBroadcastDispatcher,
-                    globalSettings,
                     context,
                     IMMEDIATE,
                     scope,
@@ -686,6 +828,38 @@
             job.cancel()
         }
 
+    @Test
+    fun activeDataChange_inSameGroup_emitsUnit() =
+        runBlocking(IMMEDIATE) {
+            var latest: Unit? = null
+            val job = underTest.activeSubChangedInGroupEvent.onEach { latest = it }.launchIn(this)
+
+            getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
+                .onActiveDataSubscriptionIdChanged(SUB_3_ID_GROUPED)
+            getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
+                .onActiveDataSubscriptionIdChanged(SUB_4_ID_GROUPED)
+
+            assertThat(latest).isEqualTo(Unit)
+
+            job.cancel()
+        }
+
+    @Test
+    fun activeDataChange_notInSameGroup_doesNotEmit() =
+        runBlocking(IMMEDIATE) {
+            var latest: Unit? = null
+            val job = underTest.activeSubChangedInGroupEvent.onEach { latest = it }.launchIn(this)
+
+            getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
+                .onActiveDataSubscriptionIdChanged(SUB_3_ID_GROUPED)
+            getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
+                .onActiveDataSubscriptionIdChanged(SUB_1_ID)
+
+            assertThat(latest).isEqualTo(null)
+
+            job.cancel()
+        }
+
     private fun createCapabilities(connected: Boolean, validated: Boolean): NetworkCapabilities =
         mock<NetworkCapabilities>().also {
             whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(connected)
@@ -719,19 +893,60 @@
 
     companion object {
         private val IMMEDIATE = Dispatchers.Main.immediate
-        private const val SUB_1_ID = 1
-        private val SUB_1 =
-            mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_1_ID) }
-        private val MODEL_1 = SubscriptionModel(subscriptionId = SUB_1_ID)
 
+        // Subscription 1
+        private const val SUB_1_ID = 1
+        private val GROUP_1 = ParcelUuid(UUID.randomUUID())
+        private val SUB_1 =
+            mock<SubscriptionInfo>().also {
+                whenever(it.subscriptionId).thenReturn(SUB_1_ID)
+                whenever(it.groupUuid).thenReturn(GROUP_1)
+            }
+        private val MODEL_1 =
+            SubscriptionModel(
+                subscriptionId = SUB_1_ID,
+                groupUuid = GROUP_1,
+            )
+
+        // Subscription 2
         private const val SUB_2_ID = 2
+        private val GROUP_2 = ParcelUuid(UUID.randomUUID())
         private val SUB_2 =
-            mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_2_ID) }
-        private val MODEL_2 = SubscriptionModel(subscriptionId = SUB_2_ID)
+            mock<SubscriptionInfo>().also {
+                whenever(it.subscriptionId).thenReturn(SUB_2_ID)
+                whenever(it.groupUuid).thenReturn(GROUP_2)
+            }
+        private val MODEL_2 =
+            SubscriptionModel(
+                subscriptionId = SUB_2_ID,
+                groupUuid = GROUP_2,
+            )
+
+        // Subs 3 and 4 are considered to be in the same group ------------------------------------
+        private val GROUP_ID_3_4 = ParcelUuid(UUID.randomUUID())
+
+        // Subscription 3
+        private const val SUB_3_ID_GROUPED = 3
+        private val SUB_3 =
+            mock<SubscriptionInfo>().also {
+                whenever(it.subscriptionId).thenReturn(SUB_3_ID_GROUPED)
+                whenever(it.groupUuid).thenReturn(GROUP_ID_3_4)
+            }
+
+        // Subscription 4
+        private const val SUB_4_ID_GROUPED = 4
+        private val SUB_4 =
+            mock<SubscriptionInfo>().also {
+                whenever(it.subscriptionId).thenReturn(SUB_4_ID_GROUPED)
+                whenever(it.groupUuid).thenReturn(GROUP_ID_3_4)
+            }
+
+        // Subs 3 and 4 are considered to be in the same group ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
         private const val NET_ID = 123
         private val NETWORK = mock<Network>().apply { whenever(getNetId()).thenReturn(NET_ID) }
 
+        // Carrier merged subscription
         private const val SUB_CM_ID = 5
         private val SUB_CM =
             mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_CM_ID) }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileTelephonyHelpers.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileTelephonyHelpers.kt
new file mode 100644
index 0000000..621f793
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileTelephonyHelpers.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.statusbar.pipeline.mobile.data.repository.prod
+
+import android.telephony.TelephonyCallback
+import android.telephony.TelephonyManager
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
+import com.google.common.truth.Truth.assertThat
+import org.mockito.Mockito.verify
+
+/** Helper methods for telephony-related callbacks for mobile tests. */
+object MobileTelephonyHelpers {
+    fun getTelephonyCallbacks(mockTelephonyManager: TelephonyManager): List<TelephonyCallback> {
+        val callbackCaptor = argumentCaptor<TelephonyCallback>()
+        verify(mockTelephonyManager).registerTelephonyCallback(any(), callbackCaptor.capture())
+        return callbackCaptor.allValues
+    }
+
+    inline fun <reified T> getTelephonyCallbackForType(mockTelephonyManager: TelephonyManager): T {
+        val cbs = getTelephonyCallbacks(mockTelephonyManager).filterIsInstance<T>()
+        assertThat(cbs.size).isEqualTo(1)
+        return cbs[0]
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
index 7aeaa48..b645e66 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
@@ -45,7 +45,7 @@
     private val _iconGroup = MutableStateFlow<SignalIcon.MobileIconGroup>(TelephonyIcons.THREE_G)
     override val networkTypeIconGroup = _iconGroup
 
-    override val networkName = MutableStateFlow(NetworkNameModel.Derived("demo mode"))
+    override val networkName = MutableStateFlow(NetworkNameModel.IntentDerived("demo mode"))
 
     private val _isEmergencyOnly = MutableStateFlow(false)
     override val isEmergencyOnly = _isEmergencyOnly
@@ -71,6 +71,8 @@
     private val _numberOfLevels = MutableStateFlow(DEFAULT_NUM_LEVELS)
     override val numberOfLevels = _numberOfLevels
 
+    override val isForceHidden = MutableStateFlow(false)
+
     fun setIconGroup(group: SignalIcon.MobileIconGroup) {
         _iconGroup.value = group
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
index 172755c..2699316 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
@@ -73,6 +73,8 @@
     private val _isUserSetup = MutableStateFlow(true)
     override val isUserSetup = _isUserSetup
 
+    override val isForceHidden = MutableStateFlow(false)
+
     /** Always returns a new fake interactor */
     override fun createMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor {
         return FakeMobileIconInteractor(tableLogBuffer)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
index c42aba5..fa072fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
@@ -66,6 +66,7 @@
                 mobileIconsInteractor.defaultMobileIconGroup,
                 mobileIconsInteractor.defaultDataSubId,
                 mobileIconsInteractor.isDefaultConnectionFailed,
+                mobileIconsInteractor.isForceHidden,
                 connectionRepository,
             )
     }
@@ -530,7 +531,7 @@
             )
             yield()
 
-            assertThat(latest).isEqualTo(NetworkNameModel.Derived(testOperatorName))
+            assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived(testOperatorName))
 
             // Default network name, operator name is null, uses the default
             connectionRepository.setConnectionInfo(MobileConnectionModel(operatorAlphaShort = null))
@@ -550,6 +551,21 @@
             job.cancel()
         }
 
+    @Test
+    fun isForceHidden_matchesParent() =
+        runBlocking(IMMEDIATE) {
+            var latest: Boolean? = null
+            val job = underTest.isForceHidden.onEach { latest = it }.launchIn(this)
+
+            mobileIconsInteractor.isForceHidden.value = true
+            assertThat(latest).isTrue()
+
+            mobileIconsInteractor.isForceHidden.value = false
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
     companion object {
         private val IMMEDIATE = Dispatchers.Main.immediate
 
@@ -559,6 +575,6 @@
         private const val SUB_1_ID = 1
 
         private val DEFAULT_NAME = NetworkNameModel.Default("test default name")
-        private val DERIVED_NAME = NetworkNameModel.Derived("test derived name")
+        private val DERIVED_NAME = NetworkNameModel.IntentDerived("test derived name")
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
index 1b62d5c..c51dbf1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.pipeline.mobile.domain.interactor
 
+import android.os.ParcelUuid
 import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
 import androidx.test.filters.SmallTest
 import com.android.settingslib.mobile.MobileMappings
@@ -27,10 +28,14 @@
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
+import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
+import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
 import com.android.systemui.util.CarrierConfigTracker
+import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
+import java.util.UUID
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
@@ -49,6 +54,7 @@
 @SmallTest
 class MobileIconsInteractorTest : SysuiTestCase() {
     private lateinit var underTest: MobileIconsInteractor
+    private lateinit var connectivityRepository: FakeConnectivityRepository
     private lateinit var connectionsRepository: FakeMobileConnectionsRepository
     private val userSetupRepository = FakeUserSetupRepository()
     private val mobileMappingsProxy = FakeMobileMappingsProxy()
@@ -62,6 +68,8 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
+        connectivityRepository = FakeConnectivityRepository()
+
         connectionsRepository = FakeMobileConnectionsRepository(mobileMappingsProxy, tableLogBuffer)
         connectionsRepository.setMobileConnectionRepositoryMap(
             mapOf(
@@ -77,6 +85,8 @@
             MobileIconsInteractorImpl(
                 connectionsRepository,
                 carrierConfigTracker,
+                tableLogger = mock(),
+                connectivityRepository,
                 userSetupRepository,
                 testScope.backgroundScope,
             )
@@ -95,6 +105,21 @@
             job.cancel()
         }
 
+    // Based on the logic from the old pipeline, we'll never filter subs when there are more than 2
+    @Test
+    fun filteredSubscriptions_moreThanTwo_doesNotFilter() =
+        testScope.runTest {
+            connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_3_OPP, SUB_4_OPP))
+            connectionsRepository.setActiveMobileDataSubscriptionId(SUB_4_ID)
+
+            var latest: List<SubscriptionModel>? = null
+            val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isEqualTo(listOf(SUB_1, SUB_3_OPP, SUB_4_OPP))
+
+            job.cancel()
+        }
+
     @Test
     fun filteredSubscriptions_nonOpportunistic_updatesWithMultipleSubs() =
         testScope.runTest {
@@ -109,10 +134,50 @@
         }
 
     @Test
-    fun filteredSubscriptions_bothOpportunistic_configFalse_showsActive_3() =
+    fun filteredSubscriptions_opportunistic_differentGroups_doesNotFilter() =
         testScope.runTest {
             connectionsRepository.setSubscriptions(listOf(SUB_3_OPP, SUB_4_OPP))
             connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
+
+            var latest: List<SubscriptionModel>? = null
+            val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isEqualTo(listOf(SUB_3_OPP, SUB_4_OPP))
+
+            job.cancel()
+        }
+
+    @Test
+    fun filteredSubscriptions_opportunistic_nonGrouped_doesNotFilter() =
+        testScope.runTest {
+            val (sub1, sub2) =
+                createSubscriptionPair(
+                    subscriptionIds = Pair(SUB_1_ID, SUB_2_ID),
+                    opportunistic = Pair(true, true),
+                    grouped = false,
+                )
+            connectionsRepository.setSubscriptions(listOf(sub1, sub2))
+            connectionsRepository.setActiveMobileDataSubscriptionId(SUB_1_ID)
+
+            var latest: List<SubscriptionModel>? = null
+            val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isEqualTo(listOf(sub1, sub2))
+
+            job.cancel()
+        }
+
+    @Test
+    fun filteredSubscriptions_opportunistic_grouped_configFalse_showsActive_3() =
+        testScope.runTest {
+            val (sub3, sub4) =
+                createSubscriptionPair(
+                    subscriptionIds = Pair(SUB_3_ID, SUB_4_ID),
+                    opportunistic = Pair(true, true),
+                    grouped = true,
+                )
+            connectionsRepository.setSubscriptions(listOf(sub3, sub4))
+            connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
             whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
                 .thenReturn(false)
 
@@ -120,15 +185,21 @@
             val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
 
             // Filtered subscriptions should show the active one when the config is false
-            assertThat(latest).isEqualTo(listOf(SUB_3_OPP))
+            assertThat(latest).isEqualTo(listOf(sub3))
 
             job.cancel()
         }
 
     @Test
-    fun filteredSubscriptions_bothOpportunistic_configFalse_showsActive_4() =
+    fun filteredSubscriptions_opportunistic_grouped_configFalse_showsActive_4() =
         testScope.runTest {
-            connectionsRepository.setSubscriptions(listOf(SUB_3_OPP, SUB_4_OPP))
+            val (sub3, sub4) =
+                createSubscriptionPair(
+                    subscriptionIds = Pair(SUB_3_ID, SUB_4_ID),
+                    opportunistic = Pair(true, true),
+                    grouped = true,
+                )
+            connectionsRepository.setSubscriptions(listOf(sub3, sub4))
             connectionsRepository.setActiveMobileDataSubscriptionId(SUB_4_ID)
             whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
                 .thenReturn(false)
@@ -137,15 +208,21 @@
             val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
 
             // Filtered subscriptions should show the active one when the config is false
-            assertThat(latest).isEqualTo(listOf(SUB_4_OPP))
+            assertThat(latest).isEqualTo(listOf(sub4))
 
             job.cancel()
         }
 
     @Test
-    fun filteredSubscriptions_oneOpportunistic_configTrue_showsPrimary_active_1() =
+    fun filteredSubscriptions_oneOpportunistic_grouped_configTrue_showsPrimary_active_1() =
         testScope.runTest {
-            connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_3_OPP))
+            val (sub1, sub3) =
+                createSubscriptionPair(
+                    subscriptionIds = Pair(SUB_1_ID, SUB_3_ID),
+                    opportunistic = Pair(false, true),
+                    grouped = true,
+                )
+            connectionsRepository.setSubscriptions(listOf(sub1, sub3))
             connectionsRepository.setActiveMobileDataSubscriptionId(SUB_1_ID)
             whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
                 .thenReturn(true)
@@ -155,15 +232,21 @@
 
             // Filtered subscriptions should show the primary (non-opportunistic) if the config is
             // true
-            assertThat(latest).isEqualTo(listOf(SUB_1))
+            assertThat(latest).isEqualTo(listOf(sub1))
 
             job.cancel()
         }
 
     @Test
-    fun filteredSubscriptions_oneOpportunistic_configTrue_showsPrimary_nonActive_1() =
+    fun filteredSubscriptions_oneOpportunistic_grouped_configTrue_showsPrimary_nonActive_1() =
         testScope.runTest {
-            connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_3_OPP))
+            val (sub1, sub3) =
+                createSubscriptionPair(
+                    subscriptionIds = Pair(SUB_1_ID, SUB_3_ID),
+                    opportunistic = Pair(false, true),
+                    grouped = true,
+                )
+            connectionsRepository.setSubscriptions(listOf(sub1, sub3))
             connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
             whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
                 .thenReturn(true)
@@ -173,7 +256,7 @@
 
             // Filtered subscriptions should show the primary (non-opportunistic) if the config is
             // true
-            assertThat(latest).isEqualTo(listOf(SUB_1))
+            assertThat(latest).isEqualTo(listOf(sub1))
 
             job.cancel()
         }
@@ -607,6 +690,59 @@
             job.cancel()
         }
 
+    @Test
+    fun isForceHidden_repoHasMobileHidden_true() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job = underTest.isForceHidden.onEach { latest = it }.launchIn(this)
+
+            connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.MOBILE))
+
+            assertThat(latest).isTrue()
+
+            job.cancel()
+        }
+
+    @Test
+    fun isForceHidden_repoDoesNotHaveMobileHidden_false() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job = underTest.isForceHidden.onEach { latest = it }.launchIn(this)
+
+            connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.WIFI))
+
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
+    /**
+     * Convenience method for creating a pair of subscriptions to test the filteredSubscriptions
+     * flow.
+     */
+    private fun createSubscriptionPair(
+        subscriptionIds: Pair<Int, Int>,
+        opportunistic: Pair<Boolean, Boolean> = Pair(false, false),
+        grouped: Boolean = false,
+    ): Pair<SubscriptionModel, SubscriptionModel> {
+        val groupUuid = if (grouped) ParcelUuid(UUID.randomUUID()) else null
+        val sub1 =
+            SubscriptionModel(
+                subscriptionId = subscriptionIds.first,
+                isOpportunistic = opportunistic.first,
+                groupUuid = groupUuid,
+            )
+
+        val sub2 =
+            SubscriptionModel(
+                subscriptionId = subscriptionIds.second,
+                isOpportunistic = opportunistic.second,
+                groupUuid = groupUuid,
+            )
+
+        return Pair(sub1, sub2)
+    }
+
     companion object {
         private val tableLogBuffer =
             TableLogBuffer(8, "MobileIconsInteractorTest", FakeSystemClock())
@@ -620,11 +756,21 @@
         private val CONNECTION_2 = FakeMobileConnectionRepository(SUB_2_ID, tableLogBuffer)
 
         private const val SUB_3_ID = 3
-        private val SUB_3_OPP = SubscriptionModel(subscriptionId = SUB_3_ID, isOpportunistic = true)
+        private val SUB_3_OPP =
+            SubscriptionModel(
+                subscriptionId = SUB_3_ID,
+                isOpportunistic = true,
+                groupUuid = ParcelUuid(UUID.randomUUID()),
+            )
         private val CONNECTION_3 = FakeMobileConnectionRepository(SUB_3_ID, tableLogBuffer)
 
         private const val SUB_4_ID = 4
-        private val SUB_4_OPP = SubscriptionModel(subscriptionId = SUB_4_ID, isOpportunistic = true)
+        private val SUB_4_OPP =
+            SubscriptionModel(
+                subscriptionId = SUB_4_ID,
+                isOpportunistic = true,
+                groupUuid = ParcelUuid(UUID.randomUUID()),
+            )
         private val CONNECTION_4 = FakeMobileConnectionRepository(SUB_4_ID, tableLogBuffer)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/shared/MobileInputLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/shared/MobileInputLoggerTest.kt
new file mode 100644
index 0000000..86529dc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/shared/MobileInputLoggerTest.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2022 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.statusbar.pipeline.mobile.shared
+
+import android.net.Network
+import android.net.NetworkCapabilities
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.log.LogBufferFactory
+import com.android.systemui.plugins.log.LogcatEchoTracker
+import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.StringWriter
+import org.junit.Test
+import org.mockito.Mockito
+import org.mockito.Mockito.mock
+
+@SmallTest
+class MobileInputLoggerTest : SysuiTestCase() {
+    private val buffer =
+        LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java)).create("buffer", 10)
+    private val logger = MobileInputLogger(buffer)
+
+    @Test
+    fun testLogNetworkCapsChange_bufferHasInfo() {
+        logger.logOnCapabilitiesChanged(NET_1, NET_1_CAPS, isDefaultNetworkCallback = true)
+
+        val stringWriter = StringWriter()
+        buffer.dump(PrintWriter(stringWriter), tailLength = 0)
+        val actualString = stringWriter.toString()
+
+        val expectedNetId = NET_1_ID.toString()
+        val expectedCaps = NET_1_CAPS.toString()
+
+        assertThat(actualString).contains("true")
+        assertThat(actualString).contains(expectedNetId)
+        assertThat(actualString).contains(expectedCaps)
+    }
+
+    @Test
+    fun testLogOnLost_bufferHasNetIdOfLostNetwork() {
+        logger.logOnLost(NET_1)
+
+        val stringWriter = StringWriter()
+        buffer.dump(PrintWriter(stringWriter), tailLength = 0)
+        val actualString = stringWriter.toString()
+
+        val expectedNetId = NET_1_ID.toString()
+
+        assertThat(actualString).contains(expectedNetId)
+    }
+
+    companion object {
+        private const val NET_1_ID = 100
+        private val NET_1 =
+            com.android.systemui.util.mockito.mock<Network>().also {
+                Mockito.`when`(it.getNetId()).thenReturn(NET_1_ID)
+            }
+        private val NET_1_CAPS =
+            NetworkCapabilities.Builder()
+                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
+                .build()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
index a2c1209..e68a397 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
@@ -29,12 +29,14 @@
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.statusbar.StatusBarIconView
 import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
+import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
+import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconInteractor
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.LocationBasedMobileViewModel
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconViewModel
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.QsMobileIconViewModel
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
+import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -58,31 +60,37 @@
 
     @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
     @Mock private lateinit var tableLogBuffer: TableLogBuffer
-    @Mock private lateinit var logger: ConnectivityPipelineLogger
     @Mock private lateinit var constants: ConnectivityConstants
+    private lateinit var interactor: FakeMobileIconInteractor
+    private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
+    private lateinit var airplaneModeInteractor: AirplaneModeInteractor
 
+    private lateinit var viewModelCommon: MobileIconViewModel
     private lateinit var viewModel: LocationBasedMobileViewModel
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+        // This line was necessary to make the onDarkChanged and setStaticDrawableColor tests pass.
+        // But, it maybe *shouldn't* be necessary.
+        whenever(constants.hasDataCapabilities).thenReturn(true)
+
         testableLooper = TestableLooper.get(this)
 
-        val interactor = FakeMobileIconInteractor(tableLogBuffer)
-
-        val viewModelCommon =
-            MobileIconViewModel(
-                subscriptionId = 1,
-                interactor,
-                logger,
-                constants,
-                testScope.backgroundScope,
+        airplaneModeRepository = FakeAirplaneModeRepository()
+        airplaneModeInteractor =
+            AirplaneModeInteractor(
+                airplaneModeRepository,
+                FakeConnectivityRepository(),
             )
-        viewModel = QsMobileIconViewModel(viewModelCommon, statusBarPipelineFlags)
+
+        interactor = FakeMobileIconInteractor(tableLogBuffer)
+        createViewModel()
     }
 
     // Note: The following tests are more like integration tests, since they stand up a full
-    // [WifiViewModel] and test the interactions between the view, view-binder, and view-model.
+    // [MobileIconViewModel] and test the interactions between the view, view-binder, and
+    // view-model.
 
     @Test
     fun setVisibleState_icon_iconShownDotHidden() {
@@ -130,7 +138,25 @@
     }
 
     @Test
-    fun isIconVisible_alwaysTrue() {
+    fun isIconVisible_noData_outputsFalse() {
+        whenever(constants.hasDataCapabilities).thenReturn(false)
+        createViewModel()
+
+        val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel)
+
+        ViewUtils.attachView(view)
+        testableLooper.processAllMessages()
+
+        assertThat(view.isIconVisible).isFalse()
+
+        ViewUtils.detachView(view)
+    }
+
+    @Test
+    fun isIconVisible_hasData_outputsTrue() {
+        whenever(constants.hasDataCapabilities).thenReturn(true)
+        createViewModel()
+
         val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel)
 
         ViewUtils.attachView(view)
@@ -142,6 +168,34 @@
     }
 
     @Test
+    fun isIconVisible_notAirplaneMode_outputsTrue() {
+        airplaneModeRepository.setIsAirplaneMode(false)
+
+        val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel)
+
+        ViewUtils.attachView(view)
+        testableLooper.processAllMessages()
+
+        assertThat(view.isIconVisible).isTrue()
+
+        ViewUtils.detachView(view)
+    }
+
+    @Test
+    fun isIconVisible_airplaneMode_outputsTrue() {
+        airplaneModeRepository.setIsAirplaneMode(true)
+
+        val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel)
+
+        ViewUtils.attachView(view)
+        testableLooper.processAllMessages()
+
+        assertThat(view.isIconVisible).isFalse()
+
+        ViewUtils.detachView(view)
+    }
+
+    @Test
     fun onDarkChanged_iconHasNewColor() {
         whenever(statusBarPipelineFlags.useDebugColoring()).thenReturn(false)
         val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel)
@@ -184,6 +238,18 @@
     private fun View.getDotView(): View {
         return this.requireViewById(R.id.status_bar_dot)
     }
+
+    private fun createViewModel() {
+        viewModelCommon =
+            MobileIconViewModel(
+                subscriptionId = 1,
+                interactor,
+                airplaneModeInteractor,
+                constants,
+                testScope.backgroundScope,
+            )
+        viewModel = QsMobileIconViewModel(viewModelCommon, statusBarPipelineFlags)
+    }
 }
 
 private const val SLOT_NAME = "TestSlotName"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
index c960a06..f983030 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
@@ -21,10 +21,13 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
+import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
+import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconInteractor
+import com.android.systemui.statusbar.pipeline.mobile.ui.model.SignalIconModel
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconViewModelTest.Companion.defaultSignal
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
+import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.launchIn
@@ -46,8 +49,8 @@
     private lateinit var qsIcon: QsMobileIconViewModel
     private lateinit var keyguardIcon: KeyguardMobileIconViewModel
     private lateinit var interactor: FakeMobileIconInteractor
+    private lateinit var airplaneModeInteractor: AirplaneModeInteractor
     @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
-    @Mock private lateinit var logger: ConnectivityPipelineLogger
     @Mock private lateinit var constants: ConnectivityConstants
     @Mock private lateinit var tableLogBuffer: TableLogBuffer
 
@@ -57,6 +60,11 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+        airplaneModeInteractor =
+            AirplaneModeInteractor(
+                FakeAirplaneModeRepository(),
+                FakeConnectivityRepository(),
+            )
         interactor = FakeMobileIconInteractor(tableLogBuffer)
         interactor.apply {
             setLevel(1)
@@ -68,7 +76,13 @@
             isDataConnected.value = true
         }
         commonImpl =
-            MobileIconViewModel(SUB_1_ID, interactor, logger, constants, testScope.backgroundScope)
+            MobileIconViewModel(
+                SUB_1_ID,
+                interactor,
+                airplaneModeInteractor,
+                constants,
+                testScope.backgroundScope,
+            )
 
         homeIcon = HomeMobileIconViewModel(commonImpl, statusBarPipelineFlags)
         qsIcon = QsMobileIconViewModel(commonImpl, statusBarPipelineFlags)
@@ -78,14 +92,14 @@
     @Test
     fun `location based view models receive same icon id when common impl updates`() =
         testScope.runTest {
-            var latestHome: Int? = null
-            val homeJob = homeIcon.iconId.onEach { latestHome = it }.launchIn(this)
+            var latestHome: SignalIconModel? = null
+            val homeJob = homeIcon.icon.onEach { latestHome = it }.launchIn(this)
 
-            var latestQs: Int? = null
-            val qsJob = qsIcon.iconId.onEach { latestQs = it }.launchIn(this)
+            var latestQs: SignalIconModel? = null
+            val qsJob = qsIcon.icon.onEach { latestQs = it }.launchIn(this)
 
-            var latestKeyguard: Int? = null
-            val keyguardJob = keyguardIcon.iconId.onEach { latestKeyguard = it }.launchIn(this)
+            var latestKeyguard: SignalIconModel? = null
+            val keyguardJob = keyguardIcon.icon.onEach { latestKeyguard = it }.launchIn(this)
 
             var expected = defaultSignal(level = 1)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
index a24e29ae..bec276a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
@@ -17,16 +17,20 @@
 package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
 
 import androidx.test.filters.SmallTest
-import com.android.settingslib.graph.SignalDrawable
+import com.android.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH
+import com.android.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH_NONE
 import com.android.settingslib.mobile.TelephonyIcons.THREE_G
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
+import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconInteractor
+import com.android.systemui.statusbar.pipeline.mobile.ui.model.SignalIconModel
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
+import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -47,7 +51,8 @@
 class MobileIconViewModelTest : SysuiTestCase() {
     private lateinit var underTest: MobileIconViewModel
     private lateinit var interactor: FakeMobileIconInteractor
-    @Mock private lateinit var logger: ConnectivityPipelineLogger
+    private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
+    private lateinit var airplaneModeInteractor: AirplaneModeInteractor
     @Mock private lateinit var constants: ConnectivityConstants
     @Mock private lateinit var tableLogBuffer: TableLogBuffer
 
@@ -57,6 +62,15 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+        whenever(constants.hasDataCapabilities).thenReturn(true)
+
+        airplaneModeRepository = FakeAirplaneModeRepository()
+        airplaneModeInteractor =
+            AirplaneModeInteractor(
+                airplaneModeRepository,
+                FakeConnectivityRepository(),
+            )
+
         interactor = FakeMobileIconInteractor(tableLogBuffer)
         interactor.apply {
             setLevel(1)
@@ -67,15 +81,94 @@
             setNumberOfLevels(4)
             isDataConnected.value = true
         }
-        underTest =
-            MobileIconViewModel(SUB_1_ID, interactor, logger, constants, testScope.backgroundScope)
+        createAndSetViewModel()
     }
 
     @Test
+    fun isVisible_notDataCapable_alwaysFalse() =
+        testScope.runTest {
+            // Create a new view model here so the constants are properly read
+            whenever(constants.hasDataCapabilities).thenReturn(false)
+            createAndSetViewModel()
+
+            var latest: Boolean? = null
+            val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
+    @Test
+    fun isVisible_notAirplane_notForceHidden_true() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
+
+            airplaneModeRepository.setIsAirplaneMode(false)
+            interactor.isForceHidden.value = false
+
+            assertThat(latest).isTrue()
+
+            job.cancel()
+        }
+
+    @Test
+    fun isVisible_airplane_false() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
+
+            airplaneModeRepository.setIsAirplaneMode(true)
+            interactor.isForceHidden.value = false
+
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
+    @Test
+    fun isVisible_forceHidden_false() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
+
+            airplaneModeRepository.setIsAirplaneMode(false)
+            interactor.isForceHidden.value = true
+
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
+    @Test
+    fun isVisible_respondsToUpdates() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
+
+            airplaneModeRepository.setIsAirplaneMode(false)
+            interactor.isForceHidden.value = false
+
+            assertThat(latest).isTrue()
+
+            airplaneModeRepository.setIsAirplaneMode(true)
+            assertThat(latest).isFalse()
+
+            airplaneModeRepository.setIsAirplaneMode(false)
+            assertThat(latest).isTrue()
+
+            interactor.isForceHidden.value = true
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
+    @Test
     fun iconId_correctLevel_notCutout() =
         testScope.runTest {
-            var latest: Int? = null
-            val job = underTest.iconId.onEach { latest = it }.launchIn(this)
+            var latest: SignalIconModel? = null
+            val job = underTest.icon.onEach { latest = it }.launchIn(this)
             val expected = defaultSignal()
 
             assertThat(latest).isEqualTo(expected)
@@ -88,8 +181,8 @@
         testScope.runTest {
             interactor.setIsDefaultDataEnabled(false)
 
-            var latest: Int? = null
-            val job = underTest.iconId.onEach { latest = it }.launchIn(this)
+            var latest: SignalIconModel? = null
+            val job = underTest.icon.onEach { latest = it }.launchIn(this)
             val expected = defaultSignal(level = 1, connected = false)
 
             assertThat(latest).isEqualTo(expected)
@@ -100,8 +193,8 @@
     @Test
     fun `icon - uses empty state - when not in service`() =
         testScope.runTest {
-            var latest: Int? = null
-            val job = underTest.iconId.onEach { latest = it }.launchIn(this)
+            var latest: SignalIconModel? = null
+            val job = underTest.icon.onEach { latest = it }.launchIn(this)
 
             interactor.isInService.value = false
 
@@ -122,6 +215,39 @@
         }
 
     @Test
+    fun contentDescription_notInService_usesNoPhone() =
+        testScope.runTest {
+            var latest: ContentDescription? = null
+            val job = underTest.contentDescription.onEach { latest = it }.launchIn(this)
+
+            interactor.isInService.value = false
+
+            assertThat((latest as ContentDescription.Resource).res)
+                .isEqualTo(PHONE_SIGNAL_STRENGTH_NONE)
+
+            job.cancel()
+        }
+
+    @Test
+    fun contentDescription_inService_usesLevel() =
+        testScope.runTest {
+            var latest: ContentDescription? = null
+            val job = underTest.contentDescription.onEach { latest = it }.launchIn(this)
+
+            interactor.isInService.value = true
+
+            interactor.level.value = 2
+            assertThat((latest as ContentDescription.Resource).res)
+                .isEqualTo(PHONE_SIGNAL_STRENGTH[2])
+
+            interactor.level.value = 0
+            assertThat((latest as ContentDescription.Resource).res)
+                .isEqualTo(PHONE_SIGNAL_STRENGTH[0])
+
+            job.cancel()
+        }
+
+    @Test
     fun networkType_dataEnabled_groupIsRepresented() =
         testScope.runTest {
             val expected =
@@ -329,14 +455,7 @@
         testScope.runTest {
             // Create a new view model here so the constants are properly read
             whenever(constants.shouldShowActivityConfig).thenReturn(false)
-            underTest =
-                MobileIconViewModel(
-                    SUB_1_ID,
-                    interactor,
-                    logger,
-                    constants,
-                    testScope.backgroundScope,
-                )
+            createAndSetViewModel()
 
             var inVisible: Boolean? = null
             val inJob = underTest.activityInVisible.onEach { inVisible = it }.launchIn(this)
@@ -368,14 +487,7 @@
         testScope.runTest {
             // Create a new view model here so the constants are properly read
             whenever(constants.shouldShowActivityConfig).thenReturn(true)
-            underTest =
-                MobileIconViewModel(
-                    SUB_1_ID,
-                    interactor,
-                    logger,
-                    constants,
-                    testScope.backgroundScope,
-                )
+            createAndSetViewModel()
 
             var inVisible: Boolean? = null
             val inJob = underTest.activityInVisible.onEach { inVisible = it }.launchIn(this)
@@ -424,6 +536,16 @@
             containerJob.cancel()
         }
 
+    private fun createAndSetViewModel() {
+        underTest = MobileIconViewModel(
+            SUB_1_ID,
+            interactor,
+            airplaneModeInteractor,
+            constants,
+            testScope.backgroundScope,
+        )
+    }
+
     companion object {
         private const val SUB_1_ID = 1
 
@@ -431,10 +553,11 @@
         fun defaultSignal(
             level: Int = 1,
             connected: Boolean = true,
-        ): Int {
-            return SignalDrawable.getState(level, /* numLevels */ 4, !connected)
+        ): SignalIconModel {
+            return SignalIconModel(level, numberOfLevels = 4, showExclamationMark = !connected)
         }
 
-        fun emptySignal(): Int = SignalDrawable.getEmptyState(4)
+        fun emptySignal(): SignalIconModel =
+            SignalIconModel(level = 0, numberOfLevels = 4, showExclamationMark = true)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
index 58b50c7..4628f84 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
@@ -20,11 +20,13 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.statusbar.phone.StatusBarLocation
 import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
+import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
+import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
+import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -46,8 +48,8 @@
     private lateinit var underTest: MobileIconsViewModel
     private val interactor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
 
+    private lateinit var airplaneModeInteractor: AirplaneModeInteractor
     @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
-    @Mock private lateinit var logger: ConnectivityPipelineLogger
     @Mock private lateinit var constants: ConnectivityConstants
 
     private val testDispatcher = UnconfinedTestDispatcher()
@@ -57,6 +59,12 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
+        airplaneModeInteractor =
+            AirplaneModeInteractor(
+                FakeAirplaneModeRepository(),
+                FakeConnectivityRepository(),
+            )
+
         val subscriptionIdsFlow =
             interactor.filteredSubscriptions
                 .map { subs -> subs.map { it.subscriptionId } }
@@ -66,7 +74,7 @@
             MobileIconsViewModel(
                 subscriptionIdsFlow,
                 interactor,
-                logger,
+                airplaneModeInteractor,
                 constants,
                 testScope.backgroundScope,
                 statusBarPipelineFlags,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLoggerTest.kt
deleted file mode 100644
index b32058f..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLoggerTest.kt
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2022 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.statusbar.pipeline.shared
-
-import android.net.Network
-import android.net.NetworkCapabilities
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.log.LogBufferFactory
-import com.android.systemui.plugins.log.LogcatEchoTracker
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logInputChange
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logOutputChange
-import com.google.common.truth.Truth.assertThat
-import java.io.PrintWriter
-import java.io.StringWriter
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.runBlocking
-import org.junit.Test
-import org.mockito.Mockito
-import org.mockito.Mockito.mock
-
-@SmallTest
-class ConnectivityPipelineLoggerTest : SysuiTestCase() {
-    private val buffer = LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
-        .create("buffer", 10)
-    private val logger = ConnectivityPipelineLogger(buffer)
-
-    @Test
-    fun testLogNetworkCapsChange_bufferHasInfo() {
-        logger.logOnCapabilitiesChanged(NET_1, NET_1_CAPS)
-
-        val stringWriter = StringWriter()
-        buffer.dump(PrintWriter(stringWriter), tailLength = 0)
-        val actualString = stringWriter.toString()
-
-        val expectedNetId = NET_1_ID.toString()
-        val expectedCaps = NET_1_CAPS.toString()
-
-        assertThat(actualString).contains(expectedNetId)
-        assertThat(actualString).contains(expectedCaps)
-    }
-
-    @Test
-    fun testLogOnLost_bufferHasNetIdOfLostNetwork() {
-        logger.logOnLost(NET_1)
-
-        val stringWriter = StringWriter()
-        buffer.dump(PrintWriter(stringWriter), tailLength = 0)
-        val actualString = stringWriter.toString()
-
-        val expectedNetId = NET_1_ID.toString()
-
-        assertThat(actualString).contains(expectedNetId)
-    }
-
-    @Test
-    fun logOutputChange_printsValuesAndNulls() = runBlocking(IMMEDIATE) {
-        val flow: Flow<Int?> = flowOf(1, null, 3)
-
-        val job = flow
-            .logOutputChange(logger, "testInts")
-            .launchIn(this)
-
-        val stringWriter = StringWriter()
-        buffer.dump(PrintWriter(stringWriter), tailLength = 0)
-        val actualString = stringWriter.toString()
-
-        assertThat(actualString).contains("1")
-        assertThat(actualString).contains("null")
-        assertThat(actualString).contains("3")
-
-        job.cancel()
-    }
-
-    @Test
-    fun logInputChange_unit_printsInputName() = runBlocking(IMMEDIATE) {
-        val flow: Flow<Unit> = flowOf(Unit, Unit)
-
-        val job = flow
-            .logInputChange(logger, "testInputs")
-            .launchIn(this)
-
-        val stringWriter = StringWriter()
-        buffer.dump(PrintWriter(stringWriter), tailLength = 0)
-        val actualString = stringWriter.toString()
-
-        assertThat(actualString).contains("testInputs")
-
-        job.cancel()
-    }
-
-    @Test
-    fun logInputChange_any_printsValuesAndNulls() = runBlocking(IMMEDIATE) {
-        val flow: Flow<Any?> = flowOf(null, 2, "threeString")
-
-        val job = flow
-            .logInputChange(logger, "testInputs")
-            .launchIn(this)
-
-        val stringWriter = StringWriter()
-        buffer.dump(PrintWriter(stringWriter), tailLength = 0)
-        val actualString = stringWriter.toString()
-
-        assertThat(actualString).contains("null")
-        assertThat(actualString).contains("2")
-        assertThat(actualString).contains("threeString")
-
-        job.cancel()
-    }
-
-    companion object {
-        private const val NET_1_ID = 100
-        private val NET_1 = com.android.systemui.util.mockito.mock<Network>().also {
-            Mockito.`when`(it.getNetId()).thenReturn(NET_1_ID)
-        }
-        private val NET_1_CAPS = NetworkCapabilities.Builder()
-            .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
-            .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
-            .build()
-        private val IMMEDIATE = Dispatchers.Main.immediate
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt
index 6dbee2f..496f090 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt
@@ -19,7 +19,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
+import com.android.systemui.statusbar.pipeline.shared.ConnectivityInputLogger
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlots
 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.DEFAULT_HIDDEN_ICONS_RESOURCE
@@ -52,7 +52,7 @@
 
     @Mock private lateinit var connectivitySlots: ConnectivitySlots
     @Mock private lateinit var dumpManager: DumpManager
-    @Mock private lateinit var logger: ConnectivityPipelineLogger
+    @Mock private lateinit var logger: ConnectivityInputLogger
     private lateinit var scope: CoroutineScope
     @Mock private lateinit var tunerService: TunerService
 
@@ -61,14 +61,15 @@
         MockitoAnnotations.initMocks(this)
         scope = CoroutineScope(IMMEDIATE)
 
-        underTest = ConnectivityRepositoryImpl(
-            connectivitySlots,
-            context,
-            dumpManager,
-            logger,
-            scope,
-            tunerService,
-        )
+        underTest =
+            ConnectivityRepositoryImpl(
+                connectivitySlots,
+                context,
+                dumpManager,
+                logger,
+                scope,
+                tunerService,
+            )
     }
 
     @After
@@ -77,199 +78,179 @@
     }
 
     @Test
-    fun forceHiddenSlots_initiallyGetsDefault() = runBlocking(IMMEDIATE) {
-        setUpEthernetWifiMobileSlotNames()
-        context.getOrCreateTestableResources().addOverride(
-            DEFAULT_HIDDEN_ICONS_RESOURCE,
-            arrayOf(SLOT_WIFI, SLOT_ETHERNET)
-        )
-        // Re-create our [ConnectivityRepositoryImpl], since it fetches
-        // config_statusBarIconsToExclude when it's first constructed
-        underTest = ConnectivityRepositoryImpl(
-            connectivitySlots,
-            context,
-            dumpManager,
-            logger,
-            scope,
-            tunerService,
-        )
+    fun forceHiddenSlots_initiallyGetsDefault() =
+        runBlocking(IMMEDIATE) {
+            setUpEthernetWifiMobileSlotNames()
+            context
+                .getOrCreateTestableResources()
+                .addOverride(DEFAULT_HIDDEN_ICONS_RESOURCE, arrayOf(SLOT_WIFI, SLOT_ETHERNET))
+            // Re-create our [ConnectivityRepositoryImpl], since it fetches
+            // config_statusBarIconsToExclude when it's first constructed
+            underTest =
+                ConnectivityRepositoryImpl(
+                    connectivitySlots,
+                    context,
+                    dumpManager,
+                    logger,
+                    scope,
+                    tunerService,
+                )
 
-        var latest: Set<ConnectivitySlot>? = null
-        val job = underTest
-            .forceHiddenSlots
-            .onEach { latest = it }
-            .launchIn(this)
+            var latest: Set<ConnectivitySlot>? = null
+            val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
 
-        assertThat(latest).containsExactly(ConnectivitySlot.ETHERNET, ConnectivitySlot.WIFI)
+            assertThat(latest).containsExactly(ConnectivitySlot.ETHERNET, ConnectivitySlot.WIFI)
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun forceHiddenSlots_slotNamesAdded_flowHasSlots() = runBlocking(IMMEDIATE) {
-        setUpEthernetWifiMobileSlotNames()
+    fun forceHiddenSlots_slotNamesAdded_flowHasSlots() =
+        runBlocking(IMMEDIATE) {
+            setUpEthernetWifiMobileSlotNames()
 
-        var latest: Set<ConnectivitySlot>? = null
-        val job = underTest
-            .forceHiddenSlots
-            .onEach { latest = it }
-            .launchIn(this)
+            var latest: Set<ConnectivitySlot>? = null
+            val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
 
-        getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
+            getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
 
-        assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
+            assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun forceHiddenSlots_wrongKey_doesNotUpdate() = runBlocking(IMMEDIATE) {
-        setUpEthernetWifiMobileSlotNames()
+    fun forceHiddenSlots_wrongKey_doesNotUpdate() =
+        runBlocking(IMMEDIATE) {
+            setUpEthernetWifiMobileSlotNames()
 
-        var latest: Set<ConnectivitySlot>? = null
-        val job = underTest
-            .forceHiddenSlots
-            .onEach { latest = it }
-            .launchIn(this)
+            var latest: Set<ConnectivitySlot>? = null
+            val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
 
-        getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
+            getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
 
-        // WHEN onTuningChanged with the wrong key
-        getTunable().onTuningChanged("wrongKey", SLOT_WIFI)
-        yield()
+            // WHEN onTuningChanged with the wrong key
+            getTunable().onTuningChanged("wrongKey", SLOT_WIFI)
+            yield()
 
-        // THEN we didn't update our value and still have the old one
-        assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
+            // THEN we didn't update our value and still have the old one
+            assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun forceHiddenSlots_slotNamesAddedThenNull_flowHasDefault() = runBlocking(IMMEDIATE) {
-        setUpEthernetWifiMobileSlotNames()
-        context.getOrCreateTestableResources().addOverride(
-            DEFAULT_HIDDEN_ICONS_RESOURCE,
-            arrayOf(SLOT_WIFI, SLOT_ETHERNET)
-        )
-        // Re-create our [ConnectivityRepositoryImpl], since it fetches
-        // config_statusBarIconsToExclude when it's first constructed
-        underTest = ConnectivityRepositoryImpl(
-            connectivitySlots,
-            context,
-            dumpManager,
-            logger,
-            scope,
-            tunerService,
-        )
+    fun forceHiddenSlots_slotNamesAddedThenNull_flowHasDefault() =
+        runBlocking(IMMEDIATE) {
+            setUpEthernetWifiMobileSlotNames()
+            context
+                .getOrCreateTestableResources()
+                .addOverride(DEFAULT_HIDDEN_ICONS_RESOURCE, arrayOf(SLOT_WIFI, SLOT_ETHERNET))
+            // Re-create our [ConnectivityRepositoryImpl], since it fetches
+            // config_statusBarIconsToExclude when it's first constructed
+            underTest =
+                ConnectivityRepositoryImpl(
+                    connectivitySlots,
+                    context,
+                    dumpManager,
+                    logger,
+                    scope,
+                    tunerService,
+                )
 
-        var latest: Set<ConnectivitySlot>? = null
-        val job = underTest
-            .forceHiddenSlots
-            .onEach { latest = it }
-            .launchIn(this)
+            var latest: Set<ConnectivitySlot>? = null
+            val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
 
-        // First, update the slots
-        getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
-        assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
+            // First, update the slots
+            getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
+            assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
 
-        // WHEN we update to a null value
-        getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, null)
-        yield()
+            // WHEN we update to a null value
+            getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, null)
+            yield()
 
-        // THEN we go back to our default value
-        assertThat(latest).containsExactly(ConnectivitySlot.ETHERNET, ConnectivitySlot.WIFI)
+            // THEN we go back to our default value
+            assertThat(latest).containsExactly(ConnectivitySlot.ETHERNET, ConnectivitySlot.WIFI)
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun forceHiddenSlots_someInvalidSlotNames_flowHasValidSlotsOnly() = runBlocking(IMMEDIATE) {
-        var latest: Set<ConnectivitySlot>? = null
-        val job = underTest
-            .forceHiddenSlots
-            .onEach { latest = it }
-            .launchIn(this)
+    fun forceHiddenSlots_someInvalidSlotNames_flowHasValidSlotsOnly() =
+        runBlocking(IMMEDIATE) {
+            var latest: Set<ConnectivitySlot>? = null
+            val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
 
-        whenever(connectivitySlots.getSlotFromName(SLOT_WIFI))
-            .thenReturn(ConnectivitySlot.WIFI)
-        whenever(connectivitySlots.getSlotFromName(SLOT_MOBILE)).thenReturn(null)
+            whenever(connectivitySlots.getSlotFromName(SLOT_WIFI)).thenReturn(ConnectivitySlot.WIFI)
+            whenever(connectivitySlots.getSlotFromName(SLOT_MOBILE)).thenReturn(null)
 
-        getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_WIFI,$SLOT_MOBILE")
+            getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_WIFI,$SLOT_MOBILE")
 
-        assertThat(latest).containsExactly(ConnectivitySlot.WIFI)
+            assertThat(latest).containsExactly(ConnectivitySlot.WIFI)
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun forceHiddenSlots_someEmptySlotNames_flowHasValidSlotsOnly() = runBlocking(IMMEDIATE) {
-        setUpEthernetWifiMobileSlotNames()
+    fun forceHiddenSlots_someEmptySlotNames_flowHasValidSlotsOnly() =
+        runBlocking(IMMEDIATE) {
+            setUpEthernetWifiMobileSlotNames()
 
-        var latest: Set<ConnectivitySlot>? = null
-        val job = underTest
-            .forceHiddenSlots
-            .onEach { latest = it }
-            .launchIn(this)
+            var latest: Set<ConnectivitySlot>? = null
+            val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
 
-        // WHEN there's empty and blank slot names
-        getTunable().onTuningChanged(
-            HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_MOBILE,  ,,$SLOT_WIFI"
-        )
+            // WHEN there's empty and blank slot names
+            getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_MOBILE,  ,,$SLOT_WIFI")
 
-        // THEN we skip that slot but still process the other ones
-        assertThat(latest).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.MOBILE)
+            // THEN we skip that slot but still process the other ones
+            assertThat(latest).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.MOBILE)
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun forceHiddenSlots_allInvalidOrEmptySlotNames_flowHasEmpty() = runBlocking(IMMEDIATE) {
-        var latest: Set<ConnectivitySlot>? = null
-        val job = underTest
-            .forceHiddenSlots
-            .onEach { latest = it }
-            .launchIn(this)
+    fun forceHiddenSlots_allInvalidOrEmptySlotNames_flowHasEmpty() =
+        runBlocking(IMMEDIATE) {
+            var latest: Set<ConnectivitySlot>? = null
+            val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
 
-        whenever(connectivitySlots.getSlotFromName(SLOT_WIFI)).thenReturn(null)
-        whenever(connectivitySlots.getSlotFromName(SLOT_ETHERNET)).thenReturn(null)
-        whenever(connectivitySlots.getSlotFromName(SLOT_MOBILE)).thenReturn(null)
+            whenever(connectivitySlots.getSlotFromName(SLOT_WIFI)).thenReturn(null)
+            whenever(connectivitySlots.getSlotFromName(SLOT_ETHERNET)).thenReturn(null)
+            whenever(connectivitySlots.getSlotFromName(SLOT_MOBILE)).thenReturn(null)
 
-        getTunable().onTuningChanged(
-            HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_MOBILE,,$SLOT_WIFI,$SLOT_ETHERNET,,,"
-        )
+            getTunable()
+                .onTuningChanged(
+                    HIDDEN_ICONS_TUNABLE_KEY,
+                    "$SLOT_MOBILE,,$SLOT_WIFI,$SLOT_ETHERNET,,,"
+                )
 
-        assertThat(latest).isEmpty()
+            assertThat(latest).isEmpty()
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun forceHiddenSlots_newSubscriberGetsCurrentValue() = runBlocking(IMMEDIATE) {
-        setUpEthernetWifiMobileSlotNames()
+    fun forceHiddenSlots_newSubscriberGetsCurrentValue() =
+        runBlocking(IMMEDIATE) {
+            setUpEthernetWifiMobileSlotNames()
 
-        var latest1: Set<ConnectivitySlot>? = null
-        val job1 = underTest
-            .forceHiddenSlots
-            .onEach { latest1 = it }
-            .launchIn(this)
+            var latest1: Set<ConnectivitySlot>? = null
+            val job1 = underTest.forceHiddenSlots.onEach { latest1 = it }.launchIn(this)
 
-        getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_WIFI,$SLOT_ETHERNET")
+            getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_WIFI,$SLOT_ETHERNET")
 
-        assertThat(latest1).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.ETHERNET)
+            assertThat(latest1).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.ETHERNET)
 
-        // WHEN we add a second subscriber after having already emitted a value
-        var latest2: Set<ConnectivitySlot>? = null
-        val job2 = underTest
-            .forceHiddenSlots
-            .onEach { latest2 = it }
-            .launchIn(this)
+            // WHEN we add a second subscriber after having already emitted a value
+            var latest2: Set<ConnectivitySlot>? = null
+            val job2 = underTest.forceHiddenSlots.onEach { latest2 = it }.launchIn(this)
 
-        // THEN the second subscribe receives the already-emitted value
-        assertThat(latest2).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.ETHERNET)
+            // THEN the second subscribe receives the already-emitted value
+            assertThat(latest2).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.ETHERNET)
 
-        job1.cancel()
-        job2.cancel()
-    }
+            job1.cancel()
+            job2.cancel()
+        }
 
     private fun getTunable(): TunerService.Tunable {
         val callbackCaptor = argumentCaptor<TunerService.Tunable>()
@@ -280,10 +261,8 @@
     private fun setUpEthernetWifiMobileSlotNames() {
         whenever(connectivitySlots.getSlotFromName(SLOT_ETHERNET))
             .thenReturn(ConnectivitySlot.ETHERNET)
-        whenever(connectivitySlots.getSlotFromName(SLOT_WIFI))
-            .thenReturn(ConnectivitySlot.WIFI)
-        whenever(connectivitySlots.getSlotFromName(SLOT_MOBILE))
-            .thenReturn(ConnectivitySlot.MOBILE)
+        whenever(connectivitySlots.getSlotFromName(SLOT_WIFI)).thenReturn(ConnectivitySlot.WIFI)
+        whenever(connectivitySlots.getSlotFromName(SLOT_MOBILE)).thenReturn(ConnectivitySlot.MOBILE)
     }
 
     companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarViewTest.kt
index 3fe6983..e4c8fd0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarViewTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.pipeline.shared.ui.view
 
+import android.graphics.Rect
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
 import androidx.test.filters.SmallTest
@@ -118,6 +119,22 @@
         assertThat(view.isIconVisible).isEqualTo(false)
     }
 
+    @Test
+    fun getDrawingRect_takesTranslationIntoAccount() {
+        val view = createAndInitView()
+
+        view.translationX = 50f
+        view.translationY = 60f
+
+        val drawingRect = Rect()
+        view.getDrawingRect(drawingRect)
+
+        assertThat(drawingRect.left).isEqualTo(view.left + 50)
+        assertThat(drawingRect.right).isEqualTo(view.right + 50)
+        assertThat(drawingRect.top).isEqualTo(view.top + 60)
+        assertThat(drawingRect.bottom).isEqualTo(view.bottom + 60)
+    }
+
     private fun createAndInitView(): ModernStatusBarView {
         val view = ModernStatusBarView(context, null)
         binding = TestBinding()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
index f5837d6..1bf431b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
@@ -17,8 +17,8 @@
 package com.android.systemui.statusbar.pipeline.wifi.data.repository
 
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl.Companion.ACTIVITY_DEFAULT
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
index 1085c2b..25678b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
@@ -23,11 +23,11 @@
 import com.android.systemui.demomode.DemoMode
 import com.android.systemui.demomode.DemoModeController
 import com.android.systemui.log.table.TableLogBuffer
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSource
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoWifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl
+import com.android.systemui.statusbar.pipeline.wifi.shared.WifiInputLogger
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.kotlinArgumentCaptor
 import com.android.systemui.util.mockito.whenever
@@ -47,6 +47,7 @@
 import org.mockito.MockitoAnnotations
 
 @OptIn(ExperimentalCoroutinesApi::class)
+@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
 @SmallTest
 class WifiRepositorySwitcherTest : SysuiTestCase() {
     private lateinit var underTest: WifiRepositorySwitcher
@@ -54,7 +55,7 @@
     private lateinit var demoImpl: DemoWifiRepository
 
     @Mock private lateinit var demoModeController: DemoModeController
-    @Mock private lateinit var logger: ConnectivityPipelineLogger
+    @Mock private lateinit var logger: WifiInputLogger
     @Mock private lateinit var tableLogger: TableLogBuffer
     @Mock private lateinit var connectivityManager: ConnectivityManager
     @Mock private lateinit var wifiManager: WifiManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt
index 3c4e85b..9cf08c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt
@@ -19,7 +19,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
index 87ce8fa..c7b31bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
@@ -21,7 +21,10 @@
 import android.net.NetworkCapabilities
 import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
 import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
+import android.net.NetworkCapabilities.TRANSPORT_VPN
 import android.net.NetworkCapabilities.TRANSPORT_WIFI
+import android.net.TransportInfo
+import android.net.VpnTransportInfo
 import android.net.vcn.VcnTransportInfo
 import android.net.wifi.WifiInfo
 import android.net.wifi.WifiManager
@@ -31,10 +34,10 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.log.table.TableLogBuffer
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl.Companion.WIFI_NETWORK_DEFAULT
+import com.android.systemui.statusbar.pipeline.wifi.shared.WifiInputLogger
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
@@ -69,7 +72,7 @@
     private lateinit var underTest: WifiRepositoryImpl
 
     @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
-    @Mock private lateinit var logger: ConnectivityPipelineLogger
+    @Mock private lateinit var logger: WifiInputLogger
     @Mock private lateinit var tableLogger: TableLogBuffer
     @Mock private lateinit var connectivityManager: ConnectivityManager
     @Mock private lateinit var wifiManager: WifiManager
@@ -80,13 +83,14 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         whenever(
-            broadcastDispatcher.broadcastFlow(
-                any(),
-                nullable(),
-                anyInt(),
-                nullable(),
+                broadcastDispatcher.broadcastFlow(
+                    any(),
+                    nullable(),
+                    anyInt(),
+                    nullable(),
+                )
             )
-        ).thenReturn(flowOf(Unit))
+            .thenReturn(flowOf(Unit))
         executor = FakeExecutor(FakeSystemClock())
         scope = CoroutineScope(IMMEDIATE)
         underTest = createRepo()
@@ -98,279 +102,352 @@
     }
 
     @Test
-    fun isWifiEnabled_initiallyGetsWifiManagerValue() = runBlocking(IMMEDIATE) {
-        whenever(wifiManager.isWifiEnabled).thenReturn(true)
+    fun isWifiEnabled_initiallyGetsWifiManagerValue() =
+        runBlocking(IMMEDIATE) {
+            whenever(wifiManager.isWifiEnabled).thenReturn(true)
 
-        underTest = createRepo()
+            underTest = createRepo()
 
-        assertThat(underTest.isWifiEnabled.value).isTrue()
-    }
-
-    @Test
-    fun isWifiEnabled_networkCapabilitiesChanged_valueUpdated() = runBlocking(IMMEDIATE) {
-        // We need to call launch on the flows so that they start updating
-        val networkJob = underTest.wifiNetwork.launchIn(this)
-        val enabledJob = underTest.isWifiEnabled.launchIn(this)
-
-        whenever(wifiManager.isWifiEnabled).thenReturn(true)
-        getNetworkCallback().onCapabilitiesChanged(
-            NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO)
-        )
-
-        assertThat(underTest.isWifiEnabled.value).isTrue()
-
-        whenever(wifiManager.isWifiEnabled).thenReturn(false)
-        getNetworkCallback().onCapabilitiesChanged(
-            NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO)
-        )
-
-        assertThat(underTest.isWifiEnabled.value).isFalse()
-
-        networkJob.cancel()
-        enabledJob.cancel()
-    }
-
-    @Test
-    fun isWifiEnabled_networkLost_valueUpdated() = runBlocking(IMMEDIATE) {
-        // We need to call launch on the flows so that they start updating
-        val networkJob = underTest.wifiNetwork.launchIn(this)
-        val enabledJob = underTest.isWifiEnabled.launchIn(this)
-
-        whenever(wifiManager.isWifiEnabled).thenReturn(true)
-        getNetworkCallback().onLost(NETWORK)
-
-        assertThat(underTest.isWifiEnabled.value).isTrue()
-
-        whenever(wifiManager.isWifiEnabled).thenReturn(false)
-        getNetworkCallback().onLost(NETWORK)
-
-        assertThat(underTest.isWifiEnabled.value).isFalse()
-
-        networkJob.cancel()
-        enabledJob.cancel()
-    }
-
-    @Test
-    fun isWifiEnabled_intentsReceived_valueUpdated() = runBlocking(IMMEDIATE) {
-        val intentFlow = MutableSharedFlow<Unit>()
-        whenever(
-            broadcastDispatcher.broadcastFlow(
-                any(),
-                nullable(),
-                anyInt(),
-                nullable(),
-            )
-        ).thenReturn(intentFlow)
-        underTest = createRepo()
-
-        val job = underTest.isWifiEnabled.launchIn(this)
-
-        whenever(wifiManager.isWifiEnabled).thenReturn(true)
-        intentFlow.emit(Unit)
-
-        assertThat(underTest.isWifiEnabled.value).isTrue()
-
-        whenever(wifiManager.isWifiEnabled).thenReturn(false)
-        intentFlow.emit(Unit)
-
-        assertThat(underTest.isWifiEnabled.value).isFalse()
-
-        job.cancel()
-    }
-
-    @Test
-    fun isWifiEnabled_bothIntentAndNetworkUpdates_valueAlwaysUpdated() = runBlocking(IMMEDIATE) {
-        val intentFlow = MutableSharedFlow<Unit>()
-        whenever(
-            broadcastDispatcher.broadcastFlow(
-                any(),
-                nullable(),
-                anyInt(),
-                nullable(),
-            )
-        ).thenReturn(intentFlow)
-        underTest = createRepo()
-
-        val networkJob = underTest.wifiNetwork.launchIn(this)
-        val enabledJob = underTest.isWifiEnabled.launchIn(this)
-
-        whenever(wifiManager.isWifiEnabled).thenReturn(false)
-        intentFlow.emit(Unit)
-        assertThat(underTest.isWifiEnabled.value).isFalse()
-
-        whenever(wifiManager.isWifiEnabled).thenReturn(true)
-        getNetworkCallback().onLost(NETWORK)
-        assertThat(underTest.isWifiEnabled.value).isTrue()
-
-        whenever(wifiManager.isWifiEnabled).thenReturn(false)
-        getNetworkCallback().onCapabilitiesChanged(
-            NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO)
-        )
-        assertThat(underTest.isWifiEnabled.value).isFalse()
-
-        whenever(wifiManager.isWifiEnabled).thenReturn(true)
-        intentFlow.emit(Unit)
-        assertThat(underTest.isWifiEnabled.value).isTrue()
-
-        networkJob.cancel()
-        enabledJob.cancel()
-    }
-
-    @Test
-    fun isWifiDefault_initiallyGetsDefault() = runBlocking(IMMEDIATE) {
-        val job = underTest.isWifiDefault.launchIn(this)
-
-        assertThat(underTest.isWifiDefault.value).isFalse()
-
-        job.cancel()
-    }
-
-    @Test
-    fun isWifiDefault_wifiNetwork_isTrue() = runBlocking(IMMEDIATE) {
-        val job = underTest.isWifiDefault.launchIn(this)
-
-        val wifiInfo = mock<WifiInfo>().apply {
-            whenever(this.ssid).thenReturn(SSID)
+            assertThat(underTest.isWifiEnabled.value).isTrue()
         }
 
-        getDefaultNetworkCallback().onCapabilitiesChanged(
-            NETWORK,
-            createWifiNetworkCapabilities(wifiInfo)
-        )
-
-        assertThat(underTest.isWifiDefault.value).isTrue()
-
-        job.cancel()
-    }
-
     @Test
-    fun isWifiDefault_cellularVcnNetwork_isTrue() = runBlocking(IMMEDIATE) {
-        val job = underTest.isWifiDefault.launchIn(this)
+    fun isWifiEnabled_networkCapabilitiesChanged_valueUpdated() =
+        runBlocking(IMMEDIATE) {
+            // We need to call launch on the flows so that they start updating
+            val networkJob = underTest.wifiNetwork.launchIn(this)
+            val enabledJob = underTest.isWifiEnabled.launchIn(this)
 
-        val capabilities = mock<NetworkCapabilities>().apply {
-            whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
-            whenever(this.transportInfo).thenReturn(VcnTransportInfo(PRIMARY_WIFI_INFO))
+            whenever(wifiManager.isWifiEnabled).thenReturn(true)
+            getNetworkCallback()
+                .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
+
+            assertThat(underTest.isWifiEnabled.value).isTrue()
+
+            whenever(wifiManager.isWifiEnabled).thenReturn(false)
+            getNetworkCallback()
+                .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
+
+            assertThat(underTest.isWifiEnabled.value).isFalse()
+
+            networkJob.cancel()
+            enabledJob.cancel()
         }
 
-        getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
-
-        assertThat(underTest.isWifiDefault.value).isTrue()
-
-        job.cancel()
-    }
-
     @Test
-    fun isWifiDefault_cellularNotVcnNetwork_isFalse() = runBlocking(IMMEDIATE) {
-        val job = underTest.isWifiDefault.launchIn(this)
+    fun isWifiEnabled_networkLost_valueUpdated() =
+        runBlocking(IMMEDIATE) {
+            // We need to call launch on the flows so that they start updating
+            val networkJob = underTest.wifiNetwork.launchIn(this)
+            val enabledJob = underTest.isWifiEnabled.launchIn(this)
 
-        val capabilities = mock<NetworkCapabilities>().apply {
-            whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
-            whenever(this.transportInfo).thenReturn(mock())
+            whenever(wifiManager.isWifiEnabled).thenReturn(true)
+            getNetworkCallback().onLost(NETWORK)
+
+            assertThat(underTest.isWifiEnabled.value).isTrue()
+
+            whenever(wifiManager.isWifiEnabled).thenReturn(false)
+            getNetworkCallback().onLost(NETWORK)
+
+            assertThat(underTest.isWifiEnabled.value).isFalse()
+
+            networkJob.cancel()
+            enabledJob.cancel()
         }
 
-        getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
-
-        assertThat(underTest.isWifiDefault.value).isFalse()
-
-        job.cancel()
-    }
-
     @Test
-    fun isWifiDefault_wifiNetworkLost_isFalse() = runBlocking(IMMEDIATE) {
-        val job = underTest.isWifiDefault.launchIn(this)
+    fun isWifiEnabled_intentsReceived_valueUpdated() =
+        runBlocking(IMMEDIATE) {
+            val intentFlow = MutableSharedFlow<Unit>()
+            whenever(
+                    broadcastDispatcher.broadcastFlow(
+                        any(),
+                        nullable(),
+                        anyInt(),
+                        nullable(),
+                    )
+                )
+                .thenReturn(intentFlow)
+            underTest = createRepo()
 
-        // First, add a network
-        getDefaultNetworkCallback()
-            .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
-        assertThat(underTest.isWifiDefault.value).isTrue()
+            val job = underTest.isWifiEnabled.launchIn(this)
 
-        // WHEN the network is lost
-        getDefaultNetworkCallback().onLost(NETWORK)
+            whenever(wifiManager.isWifiEnabled).thenReturn(true)
+            intentFlow.emit(Unit)
 
-        // THEN we update to false
-        assertThat(underTest.isWifiDefault.value).isFalse()
+            assertThat(underTest.isWifiEnabled.value).isTrue()
 
-        job.cancel()
-    }
+            whenever(wifiManager.isWifiEnabled).thenReturn(false)
+            intentFlow.emit(Unit)
 
-    @Test
-    fun wifiNetwork_initiallyGetsDefault() = runBlocking(IMMEDIATE) {
-        var latest: WifiNetworkModel? = null
-        val job = underTest
-            .wifiNetwork
-            .onEach { latest = it }
-            .launchIn(this)
+            assertThat(underTest.isWifiEnabled.value).isFalse()
 
-        assertThat(latest).isEqualTo(WIFI_NETWORK_DEFAULT)
-
-        job.cancel()
-    }
-
-    @Test
-    fun wifiNetwork_primaryWifiNetworkAdded_flowHasNetwork() = runBlocking(IMMEDIATE) {
-        var latest: WifiNetworkModel? = null
-        val job = underTest
-            .wifiNetwork
-            .onEach { latest = it }
-            .launchIn(this)
-
-        val wifiInfo = mock<WifiInfo>().apply {
-            whenever(this.ssid).thenReturn(SSID)
-            whenever(this.isPrimary).thenReturn(true)
-        }
-        val network = mock<Network>().apply {
-            whenever(this.getNetId()).thenReturn(NETWORK_ID)
+            job.cancel()
         }
 
-        getNetworkCallback().onCapabilitiesChanged(network, createWifiNetworkCapabilities(wifiInfo))
-
-        assertThat(latest is WifiNetworkModel.Active).isTrue()
-        val latestActive = latest as WifiNetworkModel.Active
-        assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
-        assertThat(latestActive.ssid).isEqualTo(SSID)
-
-        job.cancel()
-    }
-
     @Test
-    fun wifiNetwork_isCarrierMerged_flowHasCarrierMerged() = runBlocking(IMMEDIATE) {
-        var latest: WifiNetworkModel? = null
-        val job = underTest
-            .wifiNetwork
-            .onEach { latest = it }
-            .launchIn(this)
+    fun isWifiEnabled_bothIntentAndNetworkUpdates_valueAlwaysUpdated() =
+        runBlocking(IMMEDIATE) {
+            val intentFlow = MutableSharedFlow<Unit>()
+            whenever(
+                    broadcastDispatcher.broadcastFlow(
+                        any(),
+                        nullable(),
+                        anyInt(),
+                        nullable(),
+                    )
+                )
+                .thenReturn(intentFlow)
+            underTest = createRepo()
 
-        val wifiInfo = mock<WifiInfo>().apply {
-            whenever(this.isPrimary).thenReturn(true)
-            whenever(this.isCarrierMerged).thenReturn(true)
+            val networkJob = underTest.wifiNetwork.launchIn(this)
+            val enabledJob = underTest.isWifiEnabled.launchIn(this)
+
+            whenever(wifiManager.isWifiEnabled).thenReturn(false)
+            intentFlow.emit(Unit)
+            assertThat(underTest.isWifiEnabled.value).isFalse()
+
+            whenever(wifiManager.isWifiEnabled).thenReturn(true)
+            getNetworkCallback().onLost(NETWORK)
+            assertThat(underTest.isWifiEnabled.value).isTrue()
+
+            whenever(wifiManager.isWifiEnabled).thenReturn(false)
+            getNetworkCallback()
+                .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
+            assertThat(underTest.isWifiEnabled.value).isFalse()
+
+            whenever(wifiManager.isWifiEnabled).thenReturn(true)
+            intentFlow.emit(Unit)
+            assertThat(underTest.isWifiEnabled.value).isTrue()
+
+            networkJob.cancel()
+            enabledJob.cancel()
         }
 
-        getNetworkCallback().onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(wifiInfo))
+    @Test
+    fun isWifiDefault_initiallyGetsDefault() =
+        runBlocking(IMMEDIATE) {
+            val job = underTest.isWifiDefault.launchIn(this)
 
-        assertThat(latest is WifiNetworkModel.CarrierMerged).isTrue()
+            assertThat(underTest.isWifiDefault.value).isFalse()
 
-        job.cancel()
-    }
+            job.cancel()
+        }
+
+    @Test
+    fun isWifiDefault_wifiNetwork_isTrue() =
+        runBlocking(IMMEDIATE) {
+            val job = underTest.isWifiDefault.launchIn(this)
+
+            val wifiInfo = mock<WifiInfo>().apply { whenever(this.ssid).thenReturn(SSID) }
+
+            getDefaultNetworkCallback()
+                .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(wifiInfo))
+
+            assertThat(underTest.isWifiDefault.value).isTrue()
+
+            job.cancel()
+        }
+
+    /** Regression test for b/266628069. */
+    @Test
+    fun isWifiDefault_transportInfoIsNotWifi_andNoWifiTransport_false() =
+        runBlocking(IMMEDIATE) {
+            val job = underTest.isWifiDefault.launchIn(this)
+
+            val transportInfo =
+                VpnTransportInfo(
+                    /* type= */ 0,
+                    /* sessionId= */ "sessionId",
+                )
+            val networkCapabilities =
+                mock<NetworkCapabilities>().also {
+                    whenever(it.hasTransport(TRANSPORT_VPN)).thenReturn(true)
+                    whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(false)
+                    whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(false)
+                    whenever(it.transportInfo).thenReturn(transportInfo)
+                }
+
+            getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, networkCapabilities)
+
+            assertThat(underTest.isWifiDefault.value).isFalse()
+
+            job.cancel()
+        }
+
+    /** Regression test for b/266628069. */
+    @Test
+    fun isWifiDefault_transportInfoIsNotWifi_butHasWifiTransport_true() =
+        runBlocking(IMMEDIATE) {
+            val job = underTest.isWifiDefault.launchIn(this)
+
+            val transportInfo =
+                VpnTransportInfo(
+                    /* type= */ 0,
+                    /* sessionId= */ "sessionId",
+                )
+            val networkCapabilities =
+                mock<NetworkCapabilities>().also {
+                    whenever(it.hasTransport(TRANSPORT_VPN)).thenReturn(true)
+                    whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
+                    whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(false)
+                    whenever(it.transportInfo).thenReturn(transportInfo)
+                }
+
+            getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, networkCapabilities)
+
+            assertThat(underTest.isWifiDefault.value).isTrue()
+
+            job.cancel()
+        }
+
+    @Test
+    fun isWifiDefault_cellularVcnNetwork_isTrue() =
+        runBlocking(IMMEDIATE) {
+            val job = underTest.isWifiDefault.launchIn(this)
+
+            val capabilities =
+                mock<NetworkCapabilities>().apply {
+                    whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
+                    whenever(this.transportInfo).thenReturn(VcnTransportInfo(PRIMARY_WIFI_INFO))
+                }
+
+            getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
+
+            assertThat(underTest.isWifiDefault.value).isTrue()
+
+            job.cancel()
+        }
+
+    @Test
+    fun wifiNetwork_cellularAndWifiTransports_usesCellular_isTrue() =
+        runBlocking(IMMEDIATE) {
+            val job = underTest.isWifiDefault.launchIn(this)
+
+            val capabilities =
+                mock<NetworkCapabilities>().apply {
+                    whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
+                    whenever(this.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
+                    whenever(this.transportInfo).thenReturn(VcnTransportInfo(PRIMARY_WIFI_INFO))
+                }
+
+            getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
+
+            assertThat(underTest.isWifiDefault.value).isTrue()
+
+            job.cancel()
+        }
+
+    @Test
+    fun isWifiDefault_cellularNotVcnNetwork_isFalse() =
+        runBlocking(IMMEDIATE) {
+            val job = underTest.isWifiDefault.launchIn(this)
+
+            val capabilities =
+                mock<NetworkCapabilities>().apply {
+                    whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
+                    whenever(this.transportInfo).thenReturn(mock())
+                }
+
+            getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
+
+            assertThat(underTest.isWifiDefault.value).isFalse()
+
+            job.cancel()
+        }
+
+    @Test
+    fun isWifiDefault_wifiNetworkLost_isFalse() =
+        runBlocking(IMMEDIATE) {
+            val job = underTest.isWifiDefault.launchIn(this)
+
+            // First, add a network
+            getDefaultNetworkCallback()
+                .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
+            assertThat(underTest.isWifiDefault.value).isTrue()
+
+            // WHEN the network is lost
+            getDefaultNetworkCallback().onLost(NETWORK)
+
+            // THEN we update to false
+            assertThat(underTest.isWifiDefault.value).isFalse()
+
+            job.cancel()
+        }
+
+    @Test
+    fun wifiNetwork_initiallyGetsDefault() =
+        runBlocking(IMMEDIATE) {
+            var latest: WifiNetworkModel? = null
+            val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isEqualTo(WIFI_NETWORK_DEFAULT)
+
+            job.cancel()
+        }
+
+    @Test
+    fun wifiNetwork_primaryWifiNetworkAdded_flowHasNetwork() =
+        runBlocking(IMMEDIATE) {
+            var latest: WifiNetworkModel? = null
+            val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
+
+            val wifiInfo =
+                mock<WifiInfo>().apply {
+                    whenever(this.ssid).thenReturn(SSID)
+                    whenever(this.isPrimary).thenReturn(true)
+                }
+            val network = mock<Network>().apply { whenever(this.getNetId()).thenReturn(NETWORK_ID) }
+
+            getNetworkCallback()
+                .onCapabilitiesChanged(network, createWifiNetworkCapabilities(wifiInfo))
+
+            assertThat(latest is WifiNetworkModel.Active).isTrue()
+            val latestActive = latest as WifiNetworkModel.Active
+            assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
+            assertThat(latestActive.ssid).isEqualTo(SSID)
+
+            job.cancel()
+        }
+
+    @Test
+    fun wifiNetwork_isCarrierMerged_flowHasCarrierMerged() =
+        runBlocking(IMMEDIATE) {
+            var latest: WifiNetworkModel? = null
+            val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
+
+            val wifiInfo =
+                mock<WifiInfo>().apply {
+                    whenever(this.isPrimary).thenReturn(true)
+                    whenever(this.isCarrierMerged).thenReturn(true)
+                }
+
+            getNetworkCallback()
+                .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(wifiInfo))
+
+            assertThat(latest is WifiNetworkModel.CarrierMerged).isTrue()
+
+            job.cancel()
+        }
 
     @Test
     fun wifiNetwork_carrierMergedButInvalidSubId_flowHasInvalid() =
         runBlocking(IMMEDIATE) {
             var latest: WifiNetworkModel? = null
-            val job = underTest
-                .wifiNetwork
-                .onEach { latest = it }
-                .launchIn(this)
+            val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
 
-            val wifiInfo = mock<WifiInfo>().apply {
-                whenever(this.isPrimary).thenReturn(true)
-                whenever(this.isCarrierMerged).thenReturn(true)
-                whenever(this.subscriptionId).thenReturn(INVALID_SUBSCRIPTION_ID)
-            }
+            val wifiInfo =
+                mock<WifiInfo>().apply {
+                    whenever(this.isPrimary).thenReturn(true)
+                    whenever(this.isCarrierMerged).thenReturn(true)
+                    whenever(this.subscriptionId).thenReturn(INVALID_SUBSCRIPTION_ID)
+                }
 
-            getNetworkCallback().onCapabilitiesChanged(
-                NETWORK,
-                createWifiNetworkCapabilities(wifiInfo),
-            )
+            getNetworkCallback()
+                .onCapabilitiesChanged(
+                    NETWORK,
+                    createWifiNetworkCapabilities(wifiInfo),
+                )
 
             assertThat(latest).isInstanceOf(WifiNetworkModel.Invalid::class.java)
 
@@ -381,26 +458,25 @@
     fun wifiNetwork_isCarrierMerged_getsCorrectValues() =
         runBlocking(IMMEDIATE) {
             var latest: WifiNetworkModel? = null
-            val job = underTest
-                .wifiNetwork
-                .onEach { latest = it }
-                .launchIn(this)
+            val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
 
             val rssi = -57
-            val wifiInfo = mock<WifiInfo>().apply {
-                whenever(this.isPrimary).thenReturn(true)
-                whenever(this.isCarrierMerged).thenReturn(true)
-                whenever(this.rssi).thenReturn(rssi)
-                whenever(this.subscriptionId).thenReturn(567)
-            }
+            val wifiInfo =
+                mock<WifiInfo>().apply {
+                    whenever(this.isPrimary).thenReturn(true)
+                    whenever(this.isCarrierMerged).thenReturn(true)
+                    whenever(this.rssi).thenReturn(rssi)
+                    whenever(this.subscriptionId).thenReturn(567)
+                }
 
             whenever(wifiManager.calculateSignalLevel(rssi)).thenReturn(2)
             whenever(wifiManager.maxSignalLevel).thenReturn(5)
 
-            getNetworkCallback().onCapabilitiesChanged(
-                NETWORK,
-                createWifiNetworkCapabilities(wifiInfo),
-            )
+            getNetworkCallback()
+                .onCapabilitiesChanged(
+                    NETWORK,
+                    createWifiNetworkCapabilities(wifiInfo),
+                )
 
             assertThat(latest is WifiNetworkModel.CarrierMerged).isTrue()
             val latestCarrierMerged = latest as WifiNetworkModel.CarrierMerged
@@ -414,430 +490,442 @@
         }
 
     @Test
-    fun wifiNetwork_notValidated_networkNotValidated() = runBlocking(IMMEDIATE) {
-        var latest: WifiNetworkModel? = null
-        val job = underTest
-            .wifiNetwork
-            .onEach { latest = it }
-            .launchIn(this)
+    fun wifiNetwork_notValidated_networkNotValidated() =
+        runBlocking(IMMEDIATE) {
+            var latest: WifiNetworkModel? = null
+            val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
 
-        getNetworkCallback().onCapabilitiesChanged(
-            NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO, isValidated = false)
-        )
+            getNetworkCallback()
+                .onCapabilitiesChanged(
+                    NETWORK,
+                    createWifiNetworkCapabilities(PRIMARY_WIFI_INFO, isValidated = false)
+                )
 
-        assertThat((latest as WifiNetworkModel.Active).isValidated).isFalse()
+            assertThat((latest as WifiNetworkModel.Active).isValidated).isFalse()
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun wifiNetwork_validated_networkValidated() = runBlocking(IMMEDIATE) {
-        var latest: WifiNetworkModel? = null
-        val job = underTest
-            .wifiNetwork
-            .onEach { latest = it }
-            .launchIn(this)
+    fun wifiNetwork_validated_networkValidated() =
+        runBlocking(IMMEDIATE) {
+            var latest: WifiNetworkModel? = null
+            val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
 
-        getNetworkCallback().onCapabilitiesChanged(
-            NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO, isValidated = true)
-        )
+            getNetworkCallback()
+                .onCapabilitiesChanged(
+                    NETWORK,
+                    createWifiNetworkCapabilities(PRIMARY_WIFI_INFO, isValidated = true)
+                )
 
-        assertThat((latest as WifiNetworkModel.Active).isValidated).isTrue()
+            assertThat((latest as WifiNetworkModel.Active).isValidated).isTrue()
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun wifiNetwork_nonPrimaryWifiNetworkAdded_flowHasNoNetwork() = runBlocking(IMMEDIATE) {
-        var latest: WifiNetworkModel? = null
-        val job = underTest
-            .wifiNetwork
-            .onEach { latest = it }
-            .launchIn(this)
+    fun wifiNetwork_nonPrimaryWifiNetworkAdded_flowHasNoNetwork() =
+        runBlocking(IMMEDIATE) {
+            var latest: WifiNetworkModel? = null
+            val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
 
-        val wifiInfo = mock<WifiInfo>().apply {
-            whenever(this.ssid).thenReturn(SSID)
-            whenever(this.isPrimary).thenReturn(false)
+            val wifiInfo =
+                mock<WifiInfo>().apply {
+                    whenever(this.ssid).thenReturn(SSID)
+                    whenever(this.isPrimary).thenReturn(false)
+                }
+
+            getNetworkCallback()
+                .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(wifiInfo))
+
+            assertThat(latest is WifiNetworkModel.Inactive).isTrue()
+
+            job.cancel()
         }
 
-        getNetworkCallback().onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(wifiInfo))
+    /** Regression test for b/266628069. */
+    @Test
+    fun wifiNetwork_transportInfoIsNotWifi_flowHasNoNetwork() =
+        runBlocking(IMMEDIATE) {
+            var latest: WifiNetworkModel? = null
+            val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
 
-        assertThat(latest is WifiNetworkModel.Inactive).isTrue()
+            val transportInfo =
+                VpnTransportInfo(
+                    /* type= */ 0,
+                    /* sessionId= */ "sessionId",
+                )
+            getNetworkCallback()
+                .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(transportInfo))
 
-        job.cancel()
-    }
+            assertThat(latest is WifiNetworkModel.Inactive).isTrue()
+
+            job.cancel()
+        }
 
     @Test
-    fun wifiNetwork_cellularVcnNetworkAdded_flowHasNetwork() = runBlocking(IMMEDIATE) {
-        var latest: WifiNetworkModel? = null
-        val job = underTest
-            .wifiNetwork
-            .onEach { latest = it }
-            .launchIn(this)
+    fun wifiNetwork_cellularVcnNetworkAdded_flowHasNetwork() =
+        runBlocking(IMMEDIATE) {
+            var latest: WifiNetworkModel? = null
+            val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
 
-        val capabilities = mock<NetworkCapabilities>().apply {
-            whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
-            whenever(this.transportInfo).thenReturn(VcnTransportInfo(PRIMARY_WIFI_INFO))
+            val capabilities =
+                mock<NetworkCapabilities>().apply {
+                    whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
+                    whenever(this.transportInfo).thenReturn(VcnTransportInfo(PRIMARY_WIFI_INFO))
+                }
+
+            getNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
+
+            assertThat(latest is WifiNetworkModel.Active).isTrue()
+            val latestActive = latest as WifiNetworkModel.Active
+            assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
+            assertThat(latestActive.ssid).isEqualTo(SSID)
+
+            job.cancel()
         }
 
-        getNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
-
-        assertThat(latest is WifiNetworkModel.Active).isTrue()
-        val latestActive = latest as WifiNetworkModel.Active
-        assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
-        assertThat(latestActive.ssid).isEqualTo(SSID)
-
-        job.cancel()
-    }
-
     @Test
-    fun wifiNetwork_nonPrimaryCellularVcnNetworkAdded_flowHasNoNetwork() = runBlocking(IMMEDIATE) {
-        var latest: WifiNetworkModel? = null
-        val job = underTest
-            .wifiNetwork
-            .onEach { latest = it }
-            .launchIn(this)
+    fun wifiNetwork_nonPrimaryCellularVcnNetworkAdded_flowHasNoNetwork() =
+        runBlocking(IMMEDIATE) {
+            var latest: WifiNetworkModel? = null
+            val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
 
-        val wifiInfo = mock<WifiInfo>().apply {
-            whenever(this.ssid).thenReturn(SSID)
-            whenever(this.isPrimary).thenReturn(false)
+            val wifiInfo =
+                mock<WifiInfo>().apply {
+                    whenever(this.ssid).thenReturn(SSID)
+                    whenever(this.isPrimary).thenReturn(false)
+                }
+            val capabilities =
+                mock<NetworkCapabilities>().apply {
+                    whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
+                    whenever(this.transportInfo).thenReturn(VcnTransportInfo(wifiInfo))
+                }
+
+            getNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
+
+            assertThat(latest is WifiNetworkModel.Inactive).isTrue()
+
+            job.cancel()
         }
-        val capabilities = mock<NetworkCapabilities>().apply {
-            whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
-            whenever(this.transportInfo).thenReturn(VcnTransportInfo(wifiInfo))
-        }
-
-        getNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
-
-        assertThat(latest is WifiNetworkModel.Inactive).isTrue()
-
-        job.cancel()
-    }
 
     @Test
-    fun wifiNetwork_cellularNotVcnNetworkAdded_flowHasNoNetwork() = runBlocking(IMMEDIATE) {
-        var latest: WifiNetworkModel? = null
-        val job = underTest
-            .wifiNetwork
-            .onEach { latest = it }
-            .launchIn(this)
+    fun wifiNetwork_cellularNotVcnNetworkAdded_flowHasNoNetwork() =
+        runBlocking(IMMEDIATE) {
+            var latest: WifiNetworkModel? = null
+            val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
 
-        val capabilities = mock<NetworkCapabilities>().apply {
-            whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
-            whenever(this.transportInfo).thenReturn(mock())
+            val capabilities =
+                mock<NetworkCapabilities>().apply {
+                    whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
+                    whenever(this.transportInfo).thenReturn(mock())
+                }
+
+            getNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
+
+            assertThat(latest is WifiNetworkModel.Inactive).isTrue()
+
+            job.cancel()
         }
 
-        getNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
-
-        assertThat(latest is WifiNetworkModel.Inactive).isTrue()
-
-        job.cancel()
-    }
-
     @Test
-    fun wifiNetwork_newPrimaryWifiNetwork_flowHasNewNetwork() = runBlocking(IMMEDIATE) {
-        var latest: WifiNetworkModel? = null
-        val job = underTest
-            .wifiNetwork
-            .onEach { latest = it }
-            .launchIn(this)
+    fun wifiNetwork_cellularAndWifiTransports_usesCellular() =
+        runBlocking(IMMEDIATE) {
+            var latest: WifiNetworkModel? = null
+            val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
 
-        // Start with the original network
-        getNetworkCallback()
-            .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
+            val capabilities =
+                mock<NetworkCapabilities>().apply {
+                    whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
+                    whenever(this.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
+                    whenever(this.transportInfo).thenReturn(VcnTransportInfo(PRIMARY_WIFI_INFO))
+                }
 
-        // WHEN we update to a new primary network
-        val newNetworkId = 456
-        val newNetwork = mock<Network>().apply {
-            whenever(this.getNetId()).thenReturn(newNetworkId)
+            getNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
+
+            assertThat(latest is WifiNetworkModel.Active).isTrue()
+            val latestActive = latest as WifiNetworkModel.Active
+            assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
+            assertThat(latestActive.ssid).isEqualTo(SSID)
+
+            job.cancel()
         }
-        val newSsid = "CD"
-        val newWifiInfo = mock<WifiInfo>().apply {
-            whenever(this.ssid).thenReturn(newSsid)
-            whenever(this.isPrimary).thenReturn(true)
-        }
-
-        getNetworkCallback().onCapabilitiesChanged(
-            newNetwork, createWifiNetworkCapabilities(newWifiInfo)
-        )
-
-        // THEN we use the new network
-        assertThat(latest is WifiNetworkModel.Active).isTrue()
-        val latestActive = latest as WifiNetworkModel.Active
-        assertThat(latestActive.networkId).isEqualTo(newNetworkId)
-        assertThat(latestActive.ssid).isEqualTo(newSsid)
-
-        job.cancel()
-    }
 
     @Test
-    fun wifiNetwork_newNonPrimaryWifiNetwork_flowHasOldNetwork() = runBlocking(IMMEDIATE) {
-        var latest: WifiNetworkModel? = null
-        val job = underTest
-            .wifiNetwork
-            .onEach { latest = it }
-            .launchIn(this)
+    fun wifiNetwork_newPrimaryWifiNetwork_flowHasNewNetwork() =
+        runBlocking(IMMEDIATE) {
+            var latest: WifiNetworkModel? = null
+            val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
 
-        // Start with the original network
-        getNetworkCallback()
-            .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
+            // Start with the original network
+            getNetworkCallback()
+                .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
 
-        // WHEN we notify of a new but non-primary network
-        val newNetworkId = 456
-        val newNetwork = mock<Network>().apply {
-            whenever(this.getNetId()).thenReturn(newNetworkId)
+            // WHEN we update to a new primary network
+            val newNetworkId = 456
+            val newNetwork =
+                mock<Network>().apply { whenever(this.getNetId()).thenReturn(newNetworkId) }
+            val newSsid = "CD"
+            val newWifiInfo =
+                mock<WifiInfo>().apply {
+                    whenever(this.ssid).thenReturn(newSsid)
+                    whenever(this.isPrimary).thenReturn(true)
+                }
+
+            getNetworkCallback()
+                .onCapabilitiesChanged(newNetwork, createWifiNetworkCapabilities(newWifiInfo))
+
+            // THEN we use the new network
+            assertThat(latest is WifiNetworkModel.Active).isTrue()
+            val latestActive = latest as WifiNetworkModel.Active
+            assertThat(latestActive.networkId).isEqualTo(newNetworkId)
+            assertThat(latestActive.ssid).isEqualTo(newSsid)
+
+            job.cancel()
         }
-        val newSsid = "EF"
-        val newWifiInfo = mock<WifiInfo>().apply {
-            whenever(this.ssid).thenReturn(newSsid)
-            whenever(this.isPrimary).thenReturn(false)
-        }
-
-        getNetworkCallback().onCapabilitiesChanged(
-            newNetwork, createWifiNetworkCapabilities(newWifiInfo)
-        )
-
-        // THEN we still use the original network
-        assertThat(latest is WifiNetworkModel.Active).isTrue()
-        val latestActive = latest as WifiNetworkModel.Active
-        assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
-        assertThat(latestActive.ssid).isEqualTo(SSID)
-
-        job.cancel()
-    }
 
     @Test
-    fun wifiNetwork_newNetworkCapabilities_flowHasNewData() = runBlocking(IMMEDIATE) {
-        var latest: WifiNetworkModel? = null
-        val job = underTest
-            .wifiNetwork
-            .onEach { latest = it }
-            .launchIn(this)
+    fun wifiNetwork_newNonPrimaryWifiNetwork_flowHasOldNetwork() =
+        runBlocking(IMMEDIATE) {
+            var latest: WifiNetworkModel? = null
+            val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
 
-        val wifiInfo = mock<WifiInfo>().apply {
-            whenever(this.ssid).thenReturn(SSID)
-            whenever(this.isPrimary).thenReturn(true)
+            // Start with the original network
+            getNetworkCallback()
+                .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
+
+            // WHEN we notify of a new but non-primary network
+            val newNetworkId = 456
+            val newNetwork =
+                mock<Network>().apply { whenever(this.getNetId()).thenReturn(newNetworkId) }
+            val newSsid = "EF"
+            val newWifiInfo =
+                mock<WifiInfo>().apply {
+                    whenever(this.ssid).thenReturn(newSsid)
+                    whenever(this.isPrimary).thenReturn(false)
+                }
+
+            getNetworkCallback()
+                .onCapabilitiesChanged(newNetwork, createWifiNetworkCapabilities(newWifiInfo))
+
+            // THEN we still use the original network
+            assertThat(latest is WifiNetworkModel.Active).isTrue()
+            val latestActive = latest as WifiNetworkModel.Active
+            assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
+            assertThat(latestActive.ssid).isEqualTo(SSID)
+
+            job.cancel()
         }
 
-        // Start with the original network
-        getNetworkCallback().onCapabilitiesChanged(
-            NETWORK, createWifiNetworkCapabilities(wifiInfo, isValidated = true)
-        )
+    @Test
+    fun wifiNetwork_newNetworkCapabilities_flowHasNewData() =
+        runBlocking(IMMEDIATE) {
+            var latest: WifiNetworkModel? = null
+            val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
 
-        // WHEN we keep the same network ID but change the SSID
-        val newSsid = "CD"
-        val newWifiInfo = mock<WifiInfo>().apply {
-            whenever(this.ssid).thenReturn(newSsid)
-            whenever(this.isPrimary).thenReturn(true)
+            val wifiInfo =
+                mock<WifiInfo>().apply {
+                    whenever(this.ssid).thenReturn(SSID)
+                    whenever(this.isPrimary).thenReturn(true)
+                }
+
+            // Start with the original network
+            getNetworkCallback()
+                .onCapabilitiesChanged(
+                    NETWORK,
+                    createWifiNetworkCapabilities(wifiInfo, isValidated = true)
+                )
+
+            // WHEN we keep the same network ID but change the SSID
+            val newSsid = "CD"
+            val newWifiInfo =
+                mock<WifiInfo>().apply {
+                    whenever(this.ssid).thenReturn(newSsid)
+                    whenever(this.isPrimary).thenReturn(true)
+                }
+
+            getNetworkCallback()
+                .onCapabilitiesChanged(
+                    NETWORK,
+                    createWifiNetworkCapabilities(newWifiInfo, isValidated = false)
+                )
+
+            // THEN we've updated to the new SSID
+            assertThat(latest is WifiNetworkModel.Active).isTrue()
+            val latestActive = latest as WifiNetworkModel.Active
+            assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
+            assertThat(latestActive.ssid).isEqualTo(newSsid)
+            assertThat(latestActive.isValidated).isFalse()
+
+            job.cancel()
         }
 
-        getNetworkCallback().onCapabilitiesChanged(
-            NETWORK, createWifiNetworkCapabilities(newWifiInfo, isValidated = false)
-        )
-
-        // THEN we've updated to the new SSID
-        assertThat(latest is WifiNetworkModel.Active).isTrue()
-        val latestActive = latest as WifiNetworkModel.Active
-        assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
-        assertThat(latestActive.ssid).isEqualTo(newSsid)
-        assertThat(latestActive.isValidated).isFalse()
-
-        job.cancel()
-    }
-
     @Test
-    fun wifiNetwork_noCurrentNetwork_networkLost_flowHasNoNetwork() = runBlocking(IMMEDIATE) {
-        var latest: WifiNetworkModel? = null
-        val job = underTest
-            .wifiNetwork
-            .onEach { latest = it }
-            .launchIn(this)
+    fun wifiNetwork_noCurrentNetwork_networkLost_flowHasNoNetwork() =
+        runBlocking(IMMEDIATE) {
+            var latest: WifiNetworkModel? = null
+            val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
 
-        // WHEN we receive #onLost without any #onCapabilitiesChanged beforehand
-        getNetworkCallback().onLost(NETWORK)
+            // WHEN we receive #onLost without any #onCapabilitiesChanged beforehand
+            getNetworkCallback().onLost(NETWORK)
 
-        // THEN there's no crash and we still have no network
-        assertThat(latest is WifiNetworkModel.Inactive).isTrue()
+            // THEN there's no crash and we still have no network
+            assertThat(latest is WifiNetworkModel.Inactive).isTrue()
 
-        job.cancel()
-    }
-
-    @Test
-    fun wifiNetwork_currentNetworkLost_flowHasNoNetwork() = runBlocking(IMMEDIATE) {
-        var latest: WifiNetworkModel? = null
-        val job = underTest
-            .wifiNetwork
-            .onEach { latest = it }
-            .launchIn(this)
-
-        getNetworkCallback()
-            .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
-        assertThat((latest as WifiNetworkModel.Active).networkId).isEqualTo(NETWORK_ID)
-
-        // WHEN we lose our current network
-        getNetworkCallback().onLost(NETWORK)
-
-        // THEN we update to no network
-        assertThat(latest is WifiNetworkModel.Inactive).isTrue()
-
-        job.cancel()
-    }
-
-    @Test
-    fun wifiNetwork_unknownNetworkLost_flowHasPreviousNetwork() = runBlocking(IMMEDIATE) {
-        var latest: WifiNetworkModel? = null
-        val job = underTest
-            .wifiNetwork
-            .onEach { latest = it }
-            .launchIn(this)
-
-        getNetworkCallback()
-            .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
-        assertThat((latest as WifiNetworkModel.Active).networkId).isEqualTo(NETWORK_ID)
-
-        // WHEN we lose an unknown network
-        val unknownNetwork = mock<Network>().apply {
-            whenever(this.getNetId()).thenReturn(543)
+            job.cancel()
         }
-        getNetworkCallback().onLost(unknownNetwork)
-
-        // THEN we still have our previous network
-        assertThat(latest is WifiNetworkModel.Active).isTrue()
-        val latestActive = latest as WifiNetworkModel.Active
-        assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
-        assertThat(latestActive.ssid).isEqualTo(SSID)
-
-        job.cancel()
-    }
 
     @Test
-    fun wifiNetwork_notCurrentNetworkLost_flowHasCurrentNetwork() = runBlocking(IMMEDIATE) {
-        var latest: WifiNetworkModel? = null
-        val job = underTest
-            .wifiNetwork
-            .onEach { latest = it }
-            .launchIn(this)
+    fun wifiNetwork_currentNetworkLost_flowHasNoNetwork() =
+        runBlocking(IMMEDIATE) {
+            var latest: WifiNetworkModel? = null
+            val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
 
-        getNetworkCallback()
-            .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
-        assertThat((latest as WifiNetworkModel.Active).networkId).isEqualTo(NETWORK_ID)
+            getNetworkCallback()
+                .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
+            assertThat((latest as WifiNetworkModel.Active).networkId).isEqualTo(NETWORK_ID)
 
-        // WHEN we update to a new network...
-        val newNetworkId = 89
-        val newNetwork = mock<Network>().apply {
-            whenever(this.getNetId()).thenReturn(newNetworkId)
+            // WHEN we lose our current network
+            getNetworkCallback().onLost(NETWORK)
+
+            // THEN we update to no network
+            assertThat(latest is WifiNetworkModel.Inactive).isTrue()
+
+            job.cancel()
         }
-        getNetworkCallback().onCapabilitiesChanged(
-            newNetwork, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO)
-        )
-        // ...and lose the old network
-        getNetworkCallback().onLost(NETWORK)
 
-        // THEN we still have the new network
-        assertThat((latest as WifiNetworkModel.Active).networkId).isEqualTo(newNetworkId)
+    @Test
+    fun wifiNetwork_unknownNetworkLost_flowHasPreviousNetwork() =
+        runBlocking(IMMEDIATE) {
+            var latest: WifiNetworkModel? = null
+            val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
 
-        job.cancel()
-    }
+            getNetworkCallback()
+                .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
+            assertThat((latest as WifiNetworkModel.Active).networkId).isEqualTo(NETWORK_ID)
+
+            // WHEN we lose an unknown network
+            val unknownNetwork = mock<Network>().apply { whenever(this.getNetId()).thenReturn(543) }
+            getNetworkCallback().onLost(unknownNetwork)
+
+            // THEN we still have our previous network
+            assertThat(latest is WifiNetworkModel.Active).isTrue()
+            val latestActive = latest as WifiNetworkModel.Active
+            assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
+            assertThat(latestActive.ssid).isEqualTo(SSID)
+
+            job.cancel()
+        }
+
+    @Test
+    fun wifiNetwork_notCurrentNetworkLost_flowHasCurrentNetwork() =
+        runBlocking(IMMEDIATE) {
+            var latest: WifiNetworkModel? = null
+            val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
+
+            getNetworkCallback()
+                .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
+            assertThat((latest as WifiNetworkModel.Active).networkId).isEqualTo(NETWORK_ID)
+
+            // WHEN we update to a new network...
+            val newNetworkId = 89
+            val newNetwork =
+                mock<Network>().apply { whenever(this.getNetId()).thenReturn(newNetworkId) }
+            getNetworkCallback()
+                .onCapabilitiesChanged(newNetwork, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
+            // ...and lose the old network
+            getNetworkCallback().onLost(NETWORK)
+
+            // THEN we still have the new network
+            assertThat((latest as WifiNetworkModel.Active).networkId).isEqualTo(newNetworkId)
+
+            job.cancel()
+        }
 
     /** Regression test for b/244173280. */
     @Test
-    fun wifiNetwork_multipleSubscribers_newSubscribersGetCurrentValue() = runBlocking(IMMEDIATE) {
-        var latest1: WifiNetworkModel? = null
-        val job1 = underTest
-            .wifiNetwork
-            .onEach { latest1 = it }
-            .launchIn(this)
+    fun wifiNetwork_multipleSubscribers_newSubscribersGetCurrentValue() =
+        runBlocking(IMMEDIATE) {
+            var latest1: WifiNetworkModel? = null
+            val job1 = underTest.wifiNetwork.onEach { latest1 = it }.launchIn(this)
 
-        getNetworkCallback()
-            .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
+            getNetworkCallback()
+                .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
 
-        assertThat(latest1 is WifiNetworkModel.Active).isTrue()
-        val latest1Active = latest1 as WifiNetworkModel.Active
-        assertThat(latest1Active.networkId).isEqualTo(NETWORK_ID)
-        assertThat(latest1Active.ssid).isEqualTo(SSID)
+            assertThat(latest1 is WifiNetworkModel.Active).isTrue()
+            val latest1Active = latest1 as WifiNetworkModel.Active
+            assertThat(latest1Active.networkId).isEqualTo(NETWORK_ID)
+            assertThat(latest1Active.ssid).isEqualTo(SSID)
 
-        // WHEN we add a second subscriber after having already emitted a value
-        var latest2: WifiNetworkModel? = null
-        val job2 = underTest
-            .wifiNetwork
-            .onEach { latest2 = it }
-            .launchIn(this)
+            // WHEN we add a second subscriber after having already emitted a value
+            var latest2: WifiNetworkModel? = null
+            val job2 = underTest.wifiNetwork.onEach { latest2 = it }.launchIn(this)
 
-        // THEN the second subscribe receives the already-emitted value
-        assertThat(latest2 is WifiNetworkModel.Active).isTrue()
-        val latest2Active = latest2 as WifiNetworkModel.Active
-        assertThat(latest2Active.networkId).isEqualTo(NETWORK_ID)
-        assertThat(latest2Active.ssid).isEqualTo(SSID)
+            // THEN the second subscribe receives the already-emitted value
+            assertThat(latest2 is WifiNetworkModel.Active).isTrue()
+            val latest2Active = latest2 as WifiNetworkModel.Active
+            assertThat(latest2Active.networkId).isEqualTo(NETWORK_ID)
+            assertThat(latest2Active.ssid).isEqualTo(SSID)
 
-        job1.cancel()
-        job2.cancel()
-    }
+            job1.cancel()
+            job2.cancel()
+        }
 
     @Test
-    fun wifiActivity_callbackGivesNone_activityFlowHasNone() = runBlocking(IMMEDIATE) {
-        var latest: DataActivityModel? = null
-        val job = underTest
-                .wifiActivity
-                .onEach { latest = it }
-                .launchIn(this)
+    fun wifiActivity_callbackGivesNone_activityFlowHasNone() =
+        runBlocking(IMMEDIATE) {
+            var latest: DataActivityModel? = null
+            val job = underTest.wifiActivity.onEach { latest = it }.launchIn(this)
 
-        getTrafficStateCallback().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_NONE)
+            getTrafficStateCallback().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_NONE)
 
-        assertThat(latest).isEqualTo(
-            DataActivityModel(hasActivityIn = false, hasActivityOut = false)
-        )
+            assertThat(latest)
+                .isEqualTo(DataActivityModel(hasActivityIn = false, hasActivityOut = false))
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun wifiActivity_callbackGivesIn_activityFlowHasIn() = runBlocking(IMMEDIATE) {
-        var latest: DataActivityModel? = null
-        val job = underTest
-                .wifiActivity
-                .onEach { latest = it }
-                .launchIn(this)
+    fun wifiActivity_callbackGivesIn_activityFlowHasIn() =
+        runBlocking(IMMEDIATE) {
+            var latest: DataActivityModel? = null
+            val job = underTest.wifiActivity.onEach { latest = it }.launchIn(this)
 
-        getTrafficStateCallback().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_IN)
+            getTrafficStateCallback().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_IN)
 
-        assertThat(latest).isEqualTo(
-            DataActivityModel(hasActivityIn = true, hasActivityOut = false)
-        )
+            assertThat(latest)
+                .isEqualTo(DataActivityModel(hasActivityIn = true, hasActivityOut = false))
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun wifiActivity_callbackGivesOut_activityFlowHasOut() = runBlocking(IMMEDIATE) {
-        var latest: DataActivityModel? = null
-        val job = underTest
-                .wifiActivity
-                .onEach { latest = it }
-                .launchIn(this)
+    fun wifiActivity_callbackGivesOut_activityFlowHasOut() =
+        runBlocking(IMMEDIATE) {
+            var latest: DataActivityModel? = null
+            val job = underTest.wifiActivity.onEach { latest = it }.launchIn(this)
 
-        getTrafficStateCallback().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_OUT)
+            getTrafficStateCallback().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_OUT)
 
-        assertThat(latest).isEqualTo(
-            DataActivityModel(hasActivityIn = false, hasActivityOut = true)
-        )
+            assertThat(latest)
+                .isEqualTo(DataActivityModel(hasActivityIn = false, hasActivityOut = true))
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun wifiActivity_callbackGivesInout_activityFlowHasInAndOut() = runBlocking(IMMEDIATE) {
-        var latest: DataActivityModel? = null
-        val job = underTest
-                .wifiActivity
-                .onEach { latest = it }
-                .launchIn(this)
+    fun wifiActivity_callbackGivesInout_activityFlowHasInAndOut() =
+        runBlocking(IMMEDIATE) {
+            var latest: DataActivityModel? = null
+            val job = underTest.wifiActivity.onEach { latest = it }.launchIn(this)
 
-        getTrafficStateCallback().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_INOUT)
+            getTrafficStateCallback().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_INOUT)
 
-        assertThat(latest).isEqualTo(DataActivityModel(hasActivityIn = true, hasActivityOut = true))
+            assertThat(latest)
+                .isEqualTo(DataActivityModel(hasActivityIn = true, hasActivityOut = true))
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     private fun createRepo(): WifiRepositoryImpl {
         return WifiRepositoryImpl(
@@ -870,26 +958,25 @@
     }
 
     private fun createWifiNetworkCapabilities(
-        wifiInfo: WifiInfo,
+        transportInfo: TransportInfo,
         isValidated: Boolean = true,
     ): NetworkCapabilities {
         return mock<NetworkCapabilities>().also {
             whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
-            whenever(it.transportInfo).thenReturn(wifiInfo)
+            whenever(it.transportInfo).thenReturn(transportInfo)
             whenever(it.hasCapability(NET_CAPABILITY_VALIDATED)).thenReturn(isValidated)
         }
     }
 
     private companion object {
         const val NETWORK_ID = 45
-        val NETWORK = mock<Network>().apply {
-            whenever(this.getNetId()).thenReturn(NETWORK_ID)
-        }
+        val NETWORK = mock<Network>().apply { whenever(this.getNetId()).thenReturn(NETWORK_ID) }
         const val SSID = "AB"
-        val PRIMARY_WIFI_INFO: WifiInfo = mock<WifiInfo>().apply {
-            whenever(this.ssid).thenReturn(SSID)
-            whenever(this.isPrimary).thenReturn(true)
-        }
+        val PRIMARY_WIFI_INFO: WifiInfo =
+            mock<WifiInfo>().apply {
+                whenever(this.ssid).thenReturn(SSID)
+                whenever(this.isPrimary).thenReturn(true)
+            }
     }
 }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
index 089a170..fc2277b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
@@ -22,8 +22,8 @@
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -57,10 +57,7 @@
             wifiRepository.setWifiNetwork(WifiNetworkModel.Unavailable)
 
             var latest: String? = "default"
-            val job = underTest
-                .ssid
-                .onEach { latest = it }
-                .launchIn(this)
+            val job = underTest.ssid.onEach { latest = it }.launchIn(this)
 
             assertThat(latest).isNull()
 
@@ -68,238 +65,223 @@
         }
 
     @Test
-    fun ssid_inactiveNetwork_outputsNull() = runBlocking(IMMEDIATE) {
-        wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive)
+    fun ssid_inactiveNetwork_outputsNull() =
+        runBlocking(IMMEDIATE) {
+            wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive)
 
-        var latest: String? = "default"
-        val job = underTest
-            .ssid
-            .onEach { latest = it }
-            .launchIn(this)
+            var latest: String? = "default"
+            val job = underTest.ssid.onEach { latest = it }.launchIn(this)
 
-        assertThat(latest).isNull()
+            assertThat(latest).isNull()
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun ssid_carrierMergedNetwork_outputsNull() = runBlocking(IMMEDIATE) {
-        wifiRepository.setWifiNetwork(
-            WifiNetworkModel.CarrierMerged(networkId = 1, subscriptionId = 2, level = 1)
-        )
+    fun ssid_carrierMergedNetwork_outputsNull() =
+        runBlocking(IMMEDIATE) {
+            wifiRepository.setWifiNetwork(
+                WifiNetworkModel.CarrierMerged(networkId = 1, subscriptionId = 2, level = 1)
+            )
 
-        var latest: String? = "default"
-        val job = underTest
-            .ssid
-            .onEach { latest = it }
-            .launchIn(this)
+            var latest: String? = "default"
+            val job = underTest.ssid.onEach { latest = it }.launchIn(this)
 
-        assertThat(latest).isNull()
+            assertThat(latest).isNull()
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun ssid_isPasspointAccessPoint_outputsPasspointName() = runBlocking(IMMEDIATE) {
-        wifiRepository.setWifiNetwork(WifiNetworkModel.Active(
-            networkId = 1,
-            level = 1,
-            isPasspointAccessPoint = true,
-            passpointProviderFriendlyName = "friendly",
-        ))
+    fun ssid_isPasspointAccessPoint_outputsPasspointName() =
+        runBlocking(IMMEDIATE) {
+            wifiRepository.setWifiNetwork(
+                WifiNetworkModel.Active(
+                    networkId = 1,
+                    level = 1,
+                    isPasspointAccessPoint = true,
+                    passpointProviderFriendlyName = "friendly",
+                )
+            )
 
-        var latest: String? = null
-        val job = underTest
-            .ssid
-            .onEach { latest = it }
-            .launchIn(this)
+            var latest: String? = null
+            val job = underTest.ssid.onEach { latest = it }.launchIn(this)
 
-        assertThat(latest).isEqualTo("friendly")
+            assertThat(latest).isEqualTo("friendly")
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun ssid_isOnlineSignUpForPasspoint_outputsPasspointName() = runBlocking(IMMEDIATE) {
-        wifiRepository.setWifiNetwork(WifiNetworkModel.Active(
-            networkId = 1,
-            level = 1,
-            isOnlineSignUpForPasspointAccessPoint = true,
-            passpointProviderFriendlyName = "friendly",
-        ))
+    fun ssid_isOnlineSignUpForPasspoint_outputsPasspointName() =
+        runBlocking(IMMEDIATE) {
+            wifiRepository.setWifiNetwork(
+                WifiNetworkModel.Active(
+                    networkId = 1,
+                    level = 1,
+                    isOnlineSignUpForPasspointAccessPoint = true,
+                    passpointProviderFriendlyName = "friendly",
+                )
+            )
 
-        var latest: String? = null
-        val job = underTest
-            .ssid
-            .onEach { latest = it }
-            .launchIn(this)
+            var latest: String? = null
+            val job = underTest.ssid.onEach { latest = it }.launchIn(this)
 
-        assertThat(latest).isEqualTo("friendly")
+            assertThat(latest).isEqualTo("friendly")
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun ssid_unknownSsid_outputsNull() = runBlocking(IMMEDIATE) {
-        wifiRepository.setWifiNetwork(WifiNetworkModel.Active(
-            networkId = 1,
-            level = 1,
-            ssid = WifiManager.UNKNOWN_SSID,
-        ))
+    fun ssid_unknownSsid_outputsNull() =
+        runBlocking(IMMEDIATE) {
+            wifiRepository.setWifiNetwork(
+                WifiNetworkModel.Active(
+                    networkId = 1,
+                    level = 1,
+                    ssid = WifiManager.UNKNOWN_SSID,
+                )
+            )
 
-        var latest: String? = "default"
-        val job = underTest
-            .ssid
-            .onEach { latest = it }
-            .launchIn(this)
+            var latest: String? = "default"
+            val job = underTest.ssid.onEach { latest = it }.launchIn(this)
 
-        assertThat(latest).isNull()
+            assertThat(latest).isNull()
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun ssid_validSsid_outputsSsid() = runBlocking(IMMEDIATE) {
-        wifiRepository.setWifiNetwork(WifiNetworkModel.Active(
-            networkId = 1,
-            level = 1,
-            ssid = "MyAwesomeWifiNetwork",
-        ))
+    fun ssid_validSsid_outputsSsid() =
+        runBlocking(IMMEDIATE) {
+            wifiRepository.setWifiNetwork(
+                WifiNetworkModel.Active(
+                    networkId = 1,
+                    level = 1,
+                    ssid = "MyAwesomeWifiNetwork",
+                )
+            )
 
-        var latest: String? = null
-        val job = underTest
-            .ssid
-            .onEach { latest = it }
-            .launchIn(this)
+            var latest: String? = null
+            val job = underTest.ssid.onEach { latest = it }.launchIn(this)
 
-        assertThat(latest).isEqualTo("MyAwesomeWifiNetwork")
+            assertThat(latest).isEqualTo("MyAwesomeWifiNetwork")
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun isEnabled_matchesRepoIsEnabled() = runBlocking(IMMEDIATE) {
-        var latest: Boolean? = null
-        val job = underTest
-            .isEnabled
-            .onEach { latest = it }
-            .launchIn(this)
+    fun isEnabled_matchesRepoIsEnabled() =
+        runBlocking(IMMEDIATE) {
+            var latest: Boolean? = null
+            val job = underTest.isEnabled.onEach { latest = it }.launchIn(this)
 
-        wifiRepository.setIsWifiEnabled(true)
-        yield()
-        assertThat(latest).isTrue()
+            wifiRepository.setIsWifiEnabled(true)
+            yield()
+            assertThat(latest).isTrue()
 
-        wifiRepository.setIsWifiEnabled(false)
-        yield()
-        assertThat(latest).isFalse()
+            wifiRepository.setIsWifiEnabled(false)
+            yield()
+            assertThat(latest).isFalse()
 
-        wifiRepository.setIsWifiEnabled(true)
-        yield()
-        assertThat(latest).isTrue()
+            wifiRepository.setIsWifiEnabled(true)
+            yield()
+            assertThat(latest).isTrue()
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun isDefault_matchesRepoIsDefault() = runBlocking(IMMEDIATE) {
-        var latest: Boolean? = null
-        val job = underTest
-            .isDefault
-            .onEach { latest = it }
-            .launchIn(this)
+    fun isDefault_matchesRepoIsDefault() =
+        runBlocking(IMMEDIATE) {
+            var latest: Boolean? = null
+            val job = underTest.isDefault.onEach { latest = it }.launchIn(this)
 
-        wifiRepository.setIsWifiDefault(true)
-        yield()
-        assertThat(latest).isTrue()
+            wifiRepository.setIsWifiDefault(true)
+            yield()
+            assertThat(latest).isTrue()
 
-        wifiRepository.setIsWifiDefault(false)
-        yield()
-        assertThat(latest).isFalse()
+            wifiRepository.setIsWifiDefault(false)
+            yield()
+            assertThat(latest).isFalse()
 
-        wifiRepository.setIsWifiDefault(true)
-        yield()
-        assertThat(latest).isTrue()
+            wifiRepository.setIsWifiDefault(true)
+            yield()
+            assertThat(latest).isTrue()
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun wifiNetwork_matchesRepoWifiNetwork() = runBlocking(IMMEDIATE) {
-        val wifiNetwork = WifiNetworkModel.Active(
-            networkId = 45,
-            isValidated = true,
-            level = 3,
-            ssid = "AB",
-            passpointProviderFriendlyName = "friendly"
-        )
-        wifiRepository.setWifiNetwork(wifiNetwork)
+    fun wifiNetwork_matchesRepoWifiNetwork() =
+        runBlocking(IMMEDIATE) {
+            val wifiNetwork =
+                WifiNetworkModel.Active(
+                    networkId = 45,
+                    isValidated = true,
+                    level = 3,
+                    ssid = "AB",
+                    passpointProviderFriendlyName = "friendly"
+                )
+            wifiRepository.setWifiNetwork(wifiNetwork)
 
-        var latest: WifiNetworkModel? = null
-        val job = underTest
-            .wifiNetwork
-            .onEach { latest = it }
-            .launchIn(this)
+            var latest: WifiNetworkModel? = null
+            val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
 
-        assertThat(latest).isEqualTo(wifiNetwork)
+            assertThat(latest).isEqualTo(wifiNetwork)
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun activity_matchesRepoWifiActivity() = runBlocking(IMMEDIATE) {
-        var latest: DataActivityModel? = null
-        val job = underTest
-            .activity
-            .onEach { latest = it }
-            .launchIn(this)
+    fun activity_matchesRepoWifiActivity() =
+        runBlocking(IMMEDIATE) {
+            var latest: DataActivityModel? = null
+            val job = underTest.activity.onEach { latest = it }.launchIn(this)
 
-        val activity1 = DataActivityModel(hasActivityIn = true, hasActivityOut = true)
-        wifiRepository.setWifiActivity(activity1)
-        yield()
-        assertThat(latest).isEqualTo(activity1)
+            val activity1 = DataActivityModel(hasActivityIn = true, hasActivityOut = true)
+            wifiRepository.setWifiActivity(activity1)
+            yield()
+            assertThat(latest).isEqualTo(activity1)
 
-        val activity2 = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
-        wifiRepository.setWifiActivity(activity2)
-        yield()
-        assertThat(latest).isEqualTo(activity2)
+            val activity2 = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
+            wifiRepository.setWifiActivity(activity2)
+            yield()
+            assertThat(latest).isEqualTo(activity2)
 
-        val activity3 = DataActivityModel(hasActivityIn = true, hasActivityOut = false)
-        wifiRepository.setWifiActivity(activity3)
-        yield()
-        assertThat(latest).isEqualTo(activity3)
+            val activity3 = DataActivityModel(hasActivityIn = true, hasActivityOut = false)
+            wifiRepository.setWifiActivity(activity3)
+            yield()
+            assertThat(latest).isEqualTo(activity3)
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun isForceHidden_repoHasWifiHidden_outputsTrue() = runBlocking(IMMEDIATE) {
-        connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.WIFI))
+    fun isForceHidden_repoHasWifiHidden_outputsTrue() =
+        runBlocking(IMMEDIATE) {
+            connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.WIFI))
 
-        var latest: Boolean? = null
-        val job = underTest
-            .isForceHidden
-            .onEach { latest = it }
-            .launchIn(this)
+            var latest: Boolean? = null
+            val job = underTest.isForceHidden.onEach { latest = it }.launchIn(this)
 
-        assertThat(latest).isTrue()
+            assertThat(latest).isTrue()
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun isForceHidden_repoDoesNotHaveWifiHidden_outputsFalse() = runBlocking(IMMEDIATE) {
-        connectivityRepository.setForceHiddenIcons(setOf())
+    fun isForceHidden_repoDoesNotHaveWifiHidden_outputsFalse() =
+        runBlocking(IMMEDIATE) {
+            connectivityRepository.setForceHiddenIcons(setOf())
 
-        var latest: Boolean? = null
-        val job = underTest
-            .isForceHidden
-            .onEach { latest = it }
-            .launchIn(this)
+            var latest: Boolean? = null
+            val job = underTest.isForceHidden.onEach { latest = it }.launchIn(this)
 
-        assertThat(latest).isFalse()
+            assertThat(latest).isFalse()
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 }
 
 private val IMMEDIATE = Dispatchers.Main.immediate
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModelTest.kt
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModelTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModelTest.kt
index 824cebd..ab4e93c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModelTest.kt
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.pipeline.wifi.data.model
+package com.android.systemui.statusbar.pipeline.wifi.shared.model
 
 import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.log.table.TableRowLogger
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel.Active.Companion.MAX_VALID_LEVEL
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel.Companion.MIN_VALID_LEVEL
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel.Active.Companion.MAX_VALID_LEVEL
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel.Companion.MIN_VALID_LEVEL
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
index b8ace2f..64810d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
@@ -36,13 +36,12 @@
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
 import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
 import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel
 import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel
 import com.android.systemui.util.mockito.whenever
@@ -62,16 +61,10 @@
 
     private lateinit var testableLooper: TestableLooper
 
-    @Mock
-    private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
-    @Mock
-    private lateinit var logger: ConnectivityPipelineLogger
-    @Mock
-    private lateinit var tableLogBuffer: TableLogBuffer
-    @Mock
-    private lateinit var connectivityConstants: ConnectivityConstants
-    @Mock
-    private lateinit var wifiConstants: WifiConstants
+    @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
+    @Mock private lateinit var tableLogBuffer: TableLogBuffer
+    @Mock private lateinit var connectivityConstants: ConnectivityConstants
+    @Mock private lateinit var wifiConstants: WifiConstants
     private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
     private lateinit var connectivityRepository: FakeConnectivityRepository
     private lateinit var wifiRepository: FakeWifiRepository
@@ -91,25 +84,27 @@
         wifiRepository.setIsWifiEnabled(true)
         interactor = WifiInteractorImpl(connectivityRepository, wifiRepository)
         scope = CoroutineScope(Dispatchers.Unconfined)
-        airplaneModeViewModel = AirplaneModeViewModelImpl(
-            AirplaneModeInteractor(
-                airplaneModeRepository,
-                connectivityRepository,
-            ),
-            logger,
-            scope,
-        )
-        viewModel = WifiViewModel(
-            airplaneModeViewModel,
-            connectivityConstants,
-            context,
-            logger,
-            tableLogBuffer,
-            interactor,
-            scope,
-            statusBarPipelineFlags,
-            wifiConstants,
-        ).home
+        airplaneModeViewModel =
+            AirplaneModeViewModelImpl(
+                AirplaneModeInteractor(
+                    airplaneModeRepository,
+                    connectivityRepository,
+                ),
+                tableLogBuffer,
+                scope,
+            )
+        viewModel =
+            WifiViewModel(
+                    airplaneModeViewModel,
+                    connectivityConstants,
+                    context,
+                    tableLogBuffer,
+                    interactor,
+                    scope,
+                    statusBarPipelineFlags,
+                    wifiConstants,
+                )
+                .home
     }
 
     // Note: The following tests are more like integration tests, since they stand up a full
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
index b932837..12b1664 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
@@ -33,14 +33,13 @@
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
 import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
 import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
 import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel.Companion.NO_INTERNET
 import com.google.common.truth.Truth.assertThat
@@ -68,7 +67,6 @@
     private lateinit var underTest: WifiViewModel
 
     @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
-    @Mock private lateinit var logger: ConnectivityPipelineLogger
     @Mock private lateinit var tableLogBuffer: TableLogBuffer
     @Mock private lateinit var connectivityConstants: ConnectivityConstants
     @Mock private lateinit var wifiConstants: WifiConstants
@@ -94,7 +92,7 @@
                     airplaneModeRepository,
                     connectivityRepository,
                 ),
-                logger,
+                tableLogBuffer,
                 scope,
             )
     }
@@ -125,7 +123,6 @@
                     airplaneModeViewModel,
                     connectivityConstants,
                     context,
-                    logger,
                     tableLogBuffer,
                     interactor,
                     scope,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
index e5cfec9..7a62cb8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
@@ -25,15 +25,14 @@
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
 import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
 import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.CoroutineScope
@@ -59,7 +58,6 @@
     private lateinit var underTest: WifiViewModel
 
     @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
-    @Mock private lateinit var logger: ConnectivityPipelineLogger
     @Mock private lateinit var tableLogBuffer: TableLogBuffer
     @Mock private lateinit var connectivityConstants: ConnectivityConstants
     @Mock private lateinit var wifiConstants: WifiConstants
@@ -79,14 +77,15 @@
         wifiRepository.setIsWifiEnabled(true)
         interactor = WifiInteractorImpl(connectivityRepository, wifiRepository)
         scope = CoroutineScope(IMMEDIATE)
-        airplaneModeViewModel = AirplaneModeViewModelImpl(
-            AirplaneModeInteractor(
-                airplaneModeRepository,
-                connectivityRepository,
-            ),
-            logger,
-            scope,
-        )
+        airplaneModeViewModel =
+            AirplaneModeViewModelImpl(
+                AirplaneModeInteractor(
+                    airplaneModeRepository,
+                    connectivityRepository,
+                ),
+                tableLogBuffer,
+                scope,
+            )
 
         createAndSetViewModel()
     }
@@ -104,451 +103,385 @@
     // instances. There are also some tests that verify all 3 instances received the same data.
 
     @Test
-    fun wifiIcon_allLocationViewModelsReceiveSameData() = runBlocking(IMMEDIATE) {
-        var latestHome: WifiIcon? = null
-        val jobHome = underTest
-            .home
-            .wifiIcon
-            .onEach { latestHome = it }
-            .launchIn(this)
+    fun wifiIcon_allLocationViewModelsReceiveSameData() =
+        runBlocking(IMMEDIATE) {
+            var latestHome: WifiIcon? = null
+            val jobHome = underTest.home.wifiIcon.onEach { latestHome = it }.launchIn(this)
 
-        var latestKeyguard: WifiIcon? = null
-        val jobKeyguard = underTest
-            .keyguard
-            .wifiIcon
-            .onEach { latestKeyguard = it }
-            .launchIn(this)
+            var latestKeyguard: WifiIcon? = null
+            val jobKeyguard =
+                underTest.keyguard.wifiIcon.onEach { latestKeyguard = it }.launchIn(this)
 
-        var latestQs: WifiIcon? = null
-        val jobQs = underTest
-            .qs
-            .wifiIcon
-            .onEach { latestQs = it }
-            .launchIn(this)
+            var latestQs: WifiIcon? = null
+            val jobQs = underTest.qs.wifiIcon.onEach { latestQs = it }.launchIn(this)
 
-        wifiRepository.setWifiNetwork(
-            WifiNetworkModel.Active(
-                NETWORK_ID,
-                isValidated = true,
-                level = 1
+            wifiRepository.setWifiNetwork(
+                WifiNetworkModel.Active(NETWORK_ID, isValidated = true, level = 1)
             )
-        )
-        yield()
+            yield()
 
-        assertThat(latestHome).isInstanceOf(WifiIcon.Visible::class.java)
-        assertThat(latestHome).isEqualTo(latestKeyguard)
-        assertThat(latestKeyguard).isEqualTo(latestQs)
+            assertThat(latestHome).isInstanceOf(WifiIcon.Visible::class.java)
+            assertThat(latestHome).isEqualTo(latestKeyguard)
+            assertThat(latestKeyguard).isEqualTo(latestQs)
 
-        jobHome.cancel()
-        jobKeyguard.cancel()
-        jobQs.cancel()
-    }
+            jobHome.cancel()
+            jobKeyguard.cancel()
+            jobQs.cancel()
+        }
 
     @Test
-    fun activity_showActivityConfigFalse_outputsFalse() = runBlocking(IMMEDIATE) {
-        whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(false)
-        createAndSetViewModel()
-        wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
+    fun activity_showActivityConfigFalse_outputsFalse() =
+        runBlocking(IMMEDIATE) {
+            whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(false)
+            createAndSetViewModel()
+            wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
 
-        var activityIn: Boolean? = null
-        val activityInJob = underTest
-            .home
-            .isActivityInViewVisible
-            .onEach { activityIn = it }
-            .launchIn(this)
+            var activityIn: Boolean? = null
+            val activityInJob =
+                underTest.home.isActivityInViewVisible.onEach { activityIn = it }.launchIn(this)
 
-        var activityOut: Boolean? = null
-        val activityOutJob = underTest
-            .home
-            .isActivityOutViewVisible
-            .onEach { activityOut = it }
-            .launchIn(this)
+            var activityOut: Boolean? = null
+            val activityOutJob =
+                underTest.home.isActivityOutViewVisible.onEach { activityOut = it }.launchIn(this)
 
-        var activityContainer: Boolean? = null
-        val activityContainerJob = underTest
-            .home
-            .isActivityContainerVisible
-            .onEach { activityContainer = it }
-            .launchIn(this)
+            var activityContainer: Boolean? = null
+            val activityContainerJob =
+                underTest.home.isActivityContainerVisible
+                    .onEach { activityContainer = it }
+                    .launchIn(this)
 
-        // Verify that on launch, we receive false.
-        assertThat(activityIn).isFalse()
-        assertThat(activityOut).isFalse()
-        assertThat(activityContainer).isFalse()
+            // Verify that on launch, we receive false.
+            assertThat(activityIn).isFalse()
+            assertThat(activityOut).isFalse()
+            assertThat(activityContainer).isFalse()
 
-        activityInJob.cancel()
-        activityOutJob.cancel()
-        activityContainerJob.cancel()
-    }
+            activityInJob.cancel()
+            activityOutJob.cancel()
+            activityContainerJob.cancel()
+        }
 
     @Test
-    fun activity_showActivityConfigFalse_noUpdatesReceived() = runBlocking(IMMEDIATE) {
-        whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(false)
-        createAndSetViewModel()
-        wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
+    fun activity_showActivityConfigFalse_noUpdatesReceived() =
+        runBlocking(IMMEDIATE) {
+            whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(false)
+            createAndSetViewModel()
+            wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
 
-        var activityIn: Boolean? = null
-        val activityInJob = underTest
-            .home
-            .isActivityInViewVisible
-            .onEach { activityIn = it }
-            .launchIn(this)
+            var activityIn: Boolean? = null
+            val activityInJob =
+                underTest.home.isActivityInViewVisible.onEach { activityIn = it }.launchIn(this)
 
-        var activityOut: Boolean? = null
-        val activityOutJob = underTest
-            .home
-            .isActivityOutViewVisible
-            .onEach { activityOut = it }
-            .launchIn(this)
+            var activityOut: Boolean? = null
+            val activityOutJob =
+                underTest.home.isActivityOutViewVisible.onEach { activityOut = it }.launchIn(this)
 
-        var activityContainer: Boolean? = null
-        val activityContainerJob = underTest
-            .home
-            .isActivityContainerVisible
-            .onEach { activityContainer = it }
-            .launchIn(this)
+            var activityContainer: Boolean? = null
+            val activityContainerJob =
+                underTest.home.isActivityContainerVisible
+                    .onEach { activityContainer = it }
+                    .launchIn(this)
 
-        // WHEN we update the repo to have activity
-        val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = true)
-        wifiRepository.setWifiActivity(activity)
-        yield()
+            // WHEN we update the repo to have activity
+            val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = true)
+            wifiRepository.setWifiActivity(activity)
+            yield()
 
-        // THEN we didn't update to the new activity (because our config is false)
-        assertThat(activityIn).isFalse()
-        assertThat(activityOut).isFalse()
-        assertThat(activityContainer).isFalse()
+            // THEN we didn't update to the new activity (because our config is false)
+            assertThat(activityIn).isFalse()
+            assertThat(activityOut).isFalse()
+            assertThat(activityContainer).isFalse()
 
-        activityInJob.cancel()
-        activityOutJob.cancel()
-        activityContainerJob.cancel()
-    }
+            activityInJob.cancel()
+            activityOutJob.cancel()
+            activityContainerJob.cancel()
+        }
 
     @Test
-    fun activity_nullSsid_outputsFalse() = runBlocking(IMMEDIATE) {
-        whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
-        createAndSetViewModel()
+    fun activity_nullSsid_outputsFalse() =
+        runBlocking(IMMEDIATE) {
+            whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
+            createAndSetViewModel()
 
-        wifiRepository.setWifiNetwork(WifiNetworkModel.Active(NETWORK_ID, ssid = null, level = 1))
+            wifiRepository.setWifiNetwork(
+                WifiNetworkModel.Active(NETWORK_ID, ssid = null, level = 1)
+            )
 
-        var activityIn: Boolean? = null
-        val activityInJob = underTest
-            .home
-            .isActivityInViewVisible
-            .onEach { activityIn = it }
-            .launchIn(this)
+            var activityIn: Boolean? = null
+            val activityInJob =
+                underTest.home.isActivityInViewVisible.onEach { activityIn = it }.launchIn(this)
 
-        var activityOut: Boolean? = null
-        val activityOutJob = underTest
-            .home
-            .isActivityOutViewVisible
-            .onEach { activityOut = it }
-            .launchIn(this)
+            var activityOut: Boolean? = null
+            val activityOutJob =
+                underTest.home.isActivityOutViewVisible.onEach { activityOut = it }.launchIn(this)
 
-        var activityContainer: Boolean? = null
-        val activityContainerJob = underTest
-            .home
-            .isActivityContainerVisible
-            .onEach { activityContainer = it }
-            .launchIn(this)
+            var activityContainer: Boolean? = null
+            val activityContainerJob =
+                underTest.home.isActivityContainerVisible
+                    .onEach { activityContainer = it }
+                    .launchIn(this)
 
-        // WHEN we update the repo to have activity
-        val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = true)
-        wifiRepository.setWifiActivity(activity)
-        yield()
+            // WHEN we update the repo to have activity
+            val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = true)
+            wifiRepository.setWifiActivity(activity)
+            yield()
 
-        // THEN we still output false because our network's SSID is null
-        assertThat(activityIn).isFalse()
-        assertThat(activityOut).isFalse()
-        assertThat(activityContainer).isFalse()
+            // THEN we still output false because our network's SSID is null
+            assertThat(activityIn).isFalse()
+            assertThat(activityOut).isFalse()
+            assertThat(activityContainer).isFalse()
 
-        activityInJob.cancel()
-        activityOutJob.cancel()
-        activityContainerJob.cancel()
-    }
+            activityInJob.cancel()
+            activityOutJob.cancel()
+            activityContainerJob.cancel()
+        }
 
     @Test
-    fun activity_allLocationViewModelsReceiveSameData() = runBlocking(IMMEDIATE) {
-        whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
-        createAndSetViewModel()
-        wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
+    fun activity_allLocationViewModelsReceiveSameData() =
+        runBlocking(IMMEDIATE) {
+            whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
+            createAndSetViewModel()
+            wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
 
-        var latestHome: Boolean? = null
-        val jobHome = underTest
-            .home
-            .isActivityInViewVisible
-            .onEach { latestHome = it }
-            .launchIn(this)
+            var latestHome: Boolean? = null
+            val jobHome =
+                underTest.home.isActivityInViewVisible.onEach { latestHome = it }.launchIn(this)
 
-        var latestKeyguard: Boolean? = null
-        val jobKeyguard = underTest
-            .keyguard
-            .isActivityInViewVisible
-            .onEach { latestKeyguard = it }
-            .launchIn(this)
+            var latestKeyguard: Boolean? = null
+            val jobKeyguard =
+                underTest.keyguard.isActivityInViewVisible
+                    .onEach { latestKeyguard = it }
+                    .launchIn(this)
 
-        var latestQs: Boolean? = null
-        val jobQs = underTest
-            .qs
-            .isActivityInViewVisible
-            .onEach { latestQs = it }
-            .launchIn(this)
+            var latestQs: Boolean? = null
+            val jobQs = underTest.qs.isActivityInViewVisible.onEach { latestQs = it }.launchIn(this)
 
-        val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = true)
-        wifiRepository.setWifiActivity(activity)
-        yield()
+            val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = true)
+            wifiRepository.setWifiActivity(activity)
+            yield()
 
-        assertThat(latestHome).isTrue()
-        assertThat(latestKeyguard).isTrue()
-        assertThat(latestQs).isTrue()
+            assertThat(latestHome).isTrue()
+            assertThat(latestKeyguard).isTrue()
+            assertThat(latestQs).isTrue()
 
-        jobHome.cancel()
-        jobKeyguard.cancel()
-        jobQs.cancel()
-    }
+            jobHome.cancel()
+            jobKeyguard.cancel()
+            jobQs.cancel()
+        }
 
     @Test
-    fun activityIn_hasActivityInTrue_outputsTrue() = runBlocking(IMMEDIATE) {
-        whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
-        createAndSetViewModel()
-        wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
+    fun activityIn_hasActivityInTrue_outputsTrue() =
+        runBlocking(IMMEDIATE) {
+            whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
+            createAndSetViewModel()
+            wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
 
-        var latest: Boolean? = null
-        val job = underTest
-            .home
-            .isActivityInViewVisible
-            .onEach { latest = it }
-            .launchIn(this)
+            var latest: Boolean? = null
+            val job = underTest.home.isActivityInViewVisible.onEach { latest = it }.launchIn(this)
 
-        val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = false)
-        wifiRepository.setWifiActivity(activity)
-        yield()
+            val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = false)
+            wifiRepository.setWifiActivity(activity)
+            yield()
 
-        assertThat(latest).isTrue()
+            assertThat(latest).isTrue()
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun activityIn_hasActivityInFalse_outputsFalse() = runBlocking(IMMEDIATE) {
-        whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
-        createAndSetViewModel()
-        wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
+    fun activityIn_hasActivityInFalse_outputsFalse() =
+        runBlocking(IMMEDIATE) {
+            whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
+            createAndSetViewModel()
+            wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
 
-        var latest: Boolean? = null
-        val job = underTest
-            .home
-            .isActivityInViewVisible
-            .onEach { latest = it }
-            .launchIn(this)
+            var latest: Boolean? = null
+            val job = underTest.home.isActivityInViewVisible.onEach { latest = it }.launchIn(this)
 
-        val activity = DataActivityModel(hasActivityIn = false, hasActivityOut = true)
-        wifiRepository.setWifiActivity(activity)
-        yield()
+            val activity = DataActivityModel(hasActivityIn = false, hasActivityOut = true)
+            wifiRepository.setWifiActivity(activity)
+            yield()
 
-        assertThat(latest).isFalse()
+            assertThat(latest).isFalse()
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun activityOut_hasActivityOutTrue_outputsTrue() = runBlocking(IMMEDIATE) {
-        whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
-        createAndSetViewModel()
-        wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
+    fun activityOut_hasActivityOutTrue_outputsTrue() =
+        runBlocking(IMMEDIATE) {
+            whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
+            createAndSetViewModel()
+            wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
 
-        var latest: Boolean? = null
-        val job = underTest
-            .home
-            .isActivityOutViewVisible
-            .onEach { latest = it }
-            .launchIn(this)
+            var latest: Boolean? = null
+            val job = underTest.home.isActivityOutViewVisible.onEach { latest = it }.launchIn(this)
 
-        val activity = DataActivityModel(hasActivityIn = false, hasActivityOut = true)
-        wifiRepository.setWifiActivity(activity)
-        yield()
+            val activity = DataActivityModel(hasActivityIn = false, hasActivityOut = true)
+            wifiRepository.setWifiActivity(activity)
+            yield()
 
-        assertThat(latest).isTrue()
+            assertThat(latest).isTrue()
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun activityOut_hasActivityOutFalse_outputsFalse() = runBlocking(IMMEDIATE) {
-        whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
-        createAndSetViewModel()
-        wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
+    fun activityOut_hasActivityOutFalse_outputsFalse() =
+        runBlocking(IMMEDIATE) {
+            whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
+            createAndSetViewModel()
+            wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
 
-        var latest: Boolean? = null
-        val job = underTest
-            .home
-            .isActivityOutViewVisible
-            .onEach { latest = it }
-            .launchIn(this)
+            var latest: Boolean? = null
+            val job = underTest.home.isActivityOutViewVisible.onEach { latest = it }.launchIn(this)
 
-        val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = false)
-        wifiRepository.setWifiActivity(activity)
-        yield()
+            val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = false)
+            wifiRepository.setWifiActivity(activity)
+            yield()
 
-        assertThat(latest).isFalse()
+            assertThat(latest).isFalse()
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun activityContainer_hasActivityInTrue_outputsTrue() = runBlocking(IMMEDIATE) {
-        whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
-        createAndSetViewModel()
-        wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
+    fun activityContainer_hasActivityInTrue_outputsTrue() =
+        runBlocking(IMMEDIATE) {
+            whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
+            createAndSetViewModel()
+            wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
 
-        var latest: Boolean? = null
-        val job = underTest
-            .home
-            .isActivityContainerVisible
-            .onEach { latest = it }
-            .launchIn(this)
+            var latest: Boolean? = null
+            val job =
+                underTest.home.isActivityContainerVisible.onEach { latest = it }.launchIn(this)
 
-        val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = false)
-        wifiRepository.setWifiActivity(activity)
-        yield()
+            val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = false)
+            wifiRepository.setWifiActivity(activity)
+            yield()
 
-        assertThat(latest).isTrue()
+            assertThat(latest).isTrue()
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun activityContainer_hasActivityOutTrue_outputsTrue() = runBlocking(IMMEDIATE) {
-        whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
-        createAndSetViewModel()
-        wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
+    fun activityContainer_hasActivityOutTrue_outputsTrue() =
+        runBlocking(IMMEDIATE) {
+            whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
+            createAndSetViewModel()
+            wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
 
-        var latest: Boolean? = null
-        val job = underTest
-            .home
-            .isActivityContainerVisible
-            .onEach { latest = it }
-            .launchIn(this)
+            var latest: Boolean? = null
+            val job =
+                underTest.home.isActivityContainerVisible.onEach { latest = it }.launchIn(this)
 
-        val activity = DataActivityModel(hasActivityIn = false, hasActivityOut = true)
-        wifiRepository.setWifiActivity(activity)
-        yield()
+            val activity = DataActivityModel(hasActivityIn = false, hasActivityOut = true)
+            wifiRepository.setWifiActivity(activity)
+            yield()
 
-        assertThat(latest).isTrue()
+            assertThat(latest).isTrue()
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun activityContainer_inAndOutTrue_outputsTrue() = runBlocking(IMMEDIATE) {
-        whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
-        createAndSetViewModel()
-        wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
+    fun activityContainer_inAndOutTrue_outputsTrue() =
+        runBlocking(IMMEDIATE) {
+            whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
+            createAndSetViewModel()
+            wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
 
-        var latest: Boolean? = null
-        val job = underTest
-            .home
-            .isActivityContainerVisible
-            .onEach { latest = it }
-            .launchIn(this)
+            var latest: Boolean? = null
+            val job =
+                underTest.home.isActivityContainerVisible.onEach { latest = it }.launchIn(this)
 
-        val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = true)
-        wifiRepository.setWifiActivity(activity)
-        yield()
+            val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = true)
+            wifiRepository.setWifiActivity(activity)
+            yield()
 
-        assertThat(latest).isTrue()
+            assertThat(latest).isTrue()
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun activityContainer_inAndOutFalse_outputsFalse() = runBlocking(IMMEDIATE) {
-        whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
-        createAndSetViewModel()
-        wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
+    fun activityContainer_inAndOutFalse_outputsFalse() =
+        runBlocking(IMMEDIATE) {
+            whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
+            createAndSetViewModel()
+            wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
 
-        var latest: Boolean? = null
-        val job = underTest
-            .home
-            .isActivityContainerVisible
-            .onEach { latest = it }
-            .launchIn(this)
+            var latest: Boolean? = null
+            val job =
+                underTest.home.isActivityContainerVisible.onEach { latest = it }.launchIn(this)
 
-        val activity = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
-        wifiRepository.setWifiActivity(activity)
-        yield()
+            val activity = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
+            wifiRepository.setWifiActivity(activity)
+            yield()
 
-        assertThat(latest).isFalse()
+            assertThat(latest).isFalse()
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun airplaneSpacer_notAirplaneMode_outputsFalse() = runBlocking(IMMEDIATE) {
-        var latest: Boolean? = null
-        val job = underTest
-            .qs
-            .isAirplaneSpacerVisible
-            .onEach { latest = it }
-            .launchIn(this)
+    fun airplaneSpacer_notAirplaneMode_outputsFalse() =
+        runBlocking(IMMEDIATE) {
+            var latest: Boolean? = null
+            val job = underTest.qs.isAirplaneSpacerVisible.onEach { latest = it }.launchIn(this)
 
-        airplaneModeRepository.setIsAirplaneMode(false)
-        yield()
+            airplaneModeRepository.setIsAirplaneMode(false)
+            yield()
 
-        assertThat(latest).isFalse()
+            assertThat(latest).isFalse()
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun airplaneSpacer_airplaneForceHidden_outputsFalse() = runBlocking(IMMEDIATE) {
-        var latest: Boolean? = null
-        val job = underTest
-            .qs
-            .isAirplaneSpacerVisible
-            .onEach { latest = it }
-            .launchIn(this)
+    fun airplaneSpacer_airplaneForceHidden_outputsFalse() =
+        runBlocking(IMMEDIATE) {
+            var latest: Boolean? = null
+            val job = underTest.qs.isAirplaneSpacerVisible.onEach { latest = it }.launchIn(this)
 
-        airplaneModeRepository.setIsAirplaneMode(true)
-        connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.AIRPLANE))
-        yield()
+            airplaneModeRepository.setIsAirplaneMode(true)
+            connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.AIRPLANE))
+            yield()
 
-        assertThat(latest).isFalse()
+            assertThat(latest).isFalse()
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     @Test
-    fun airplaneSpacer_airplaneIconVisible_outputsTrue() = runBlocking(IMMEDIATE) {
-        var latest: Boolean? = null
-        val job = underTest
-            .qs
-            .isAirplaneSpacerVisible
-            .onEach { latest = it }
-            .launchIn(this)
+    fun airplaneSpacer_airplaneIconVisible_outputsTrue() =
+        runBlocking(IMMEDIATE) {
+            var latest: Boolean? = null
+            val job = underTest.qs.isAirplaneSpacerVisible.onEach { latest = it }.launchIn(this)
 
-        airplaneModeRepository.setIsAirplaneMode(true)
-        yield()
+            airplaneModeRepository.setIsAirplaneMode(true)
+            yield()
 
-        assertThat(latest).isTrue()
+            assertThat(latest).isTrue()
 
-        job.cancel()
-    }
+            job.cancel()
+        }
 
     private fun createAndSetViewModel() {
         // [WifiViewModel] creates its flows as soon as it's instantiated, and some of those flow
         // creations rely on certain config values that we mock out in individual tests. This method
         // allows tests to create the view model only after those configs are correctly set up.
-        underTest = WifiViewModel(
-            airplaneModeViewModel,
-            connectivityConstants,
-            context,
-            logger,
-            tableLogBuffer,
-            interactor,
-            scope,
-            statusBarPipelineFlags,
-            wifiConstants,
-        )
+        underTest =
+            WifiViewModel(
+                airplaneModeViewModel,
+                connectivityConstants,
+                context,
+                tableLogBuffer,
+                interactor,
+                scope,
+                statusBarPipelineFlags,
+                wifiConstants,
+            )
     }
 
     companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt
index 64a93cf..6557754 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt
@@ -38,6 +38,7 @@
 import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl.Companion.PREFS_CONTROLS_SEEDING_COMPLETED
 import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl.Companion.QS_DEFAULT_POSITION
 import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl.Companion.QS_PRIORITY_POSITION
+import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.settings.SecureSettings
 
 import java.util.Optional
@@ -102,6 +103,8 @@
         `when`(controlsComponent.getControlsListingController())
                 .thenReturn(Optional.of(controlsListingController))
 
+        `when`(controlsComponent.isEnabled()).thenReturn(true)
+
         controller = DeviceControlsControllerImpl(
             mContext,
             controlsComponent,
@@ -168,4 +171,15 @@
         seedCallback.value.accept(SeedResponse(TEST_PKG, true))
         verify(callback).onControlsUpdate(QS_DEFAULT_POSITION)
     }
+
+    @Test
+    fun testControlsDisabledRemoveFromAutoTracker() {
+        `when`(controlsComponent.isEnabled()).thenReturn(false)
+        val callback: DeviceControlsController.Callback = mock()
+
+        controller.setCallback(callback)
+
+        verify(callback).removeControlsAutoTracker()
+        verify(callback, never()).onControlsUpdate(anyInt())
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt
index 1cccd65c8..cc6be5e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt
@@ -92,6 +92,20 @@
     }
 
     @Test
+    fun onStylusAdded_internal_updatesNotificationSuppression() {
+        startable.onStylusAdded(STYLUS_DEVICE_ID)
+
+        verify(stylusUsiPowerUi, times(1)).updateSuppression(false)
+    }
+
+    @Test
+    fun onStylusAdded_external_noop() {
+        startable.onStylusAdded(EXTERNAL_DEVICE_ID)
+
+        verifyZeroInteractions(stylusUsiPowerUi)
+    }
+
+    @Test
     fun onStylusBluetoothConnected_refreshesNotification() {
         startable.onStylusBluetoothConnected(STYLUS_DEVICE_ID, "ANY")
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationTest.kt
index 756397a..74ed7fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationTest.kt
@@ -42,13 +42,35 @@
                 pixelDensity = 2f,
                 color = Color.RED,
                 opacity = 30,
-                shouldFillRipple = true,
+                baseRingFadeParams =
+                    RippleShader.FadeParams(
+                        fadeInStart = 0f,
+                        fadeInEnd = 0.3f,
+                        fadeOutStart = 0.5f,
+                        fadeOutEnd = 1f
+                    ),
+                sparkleRingFadeParams =
+                    RippleShader.FadeParams(
+                        fadeInStart = 0.1f,
+                        fadeInEnd = 0.2f,
+                        fadeOutStart = 0.7f,
+                        fadeOutEnd = 0.9f
+                    ),
+                centerFillFadeParams =
+                    RippleShader.FadeParams(
+                        fadeInStart = 0f,
+                        fadeInEnd = 0.1f,
+                        fadeOutStart = 0.2f,
+                        fadeOutEnd = 0.3f
+                    ),
                 sparkleStrength = 0.3f
             )
         val rippleAnimation = RippleAnimation(config)
 
         with(rippleAnimation.rippleShader) {
-            assertThat(rippleFill).isEqualTo(config.shouldFillRipple)
+            assertThat(baseRingFadeParams).isEqualTo(config.baseRingFadeParams)
+            assertThat(sparkleRingFadeParams).isEqualTo(config.sparkleRingFadeParams)
+            assertThat(centerFillFadeParams).isEqualTo(config.centerFillFadeParams)
             assertThat(pixelDensity).isEqualTo(config.pixelDensity)
             assertThat(color).isEqualTo(ColorUtils.setAlphaComponent(config.color, config.opacity))
             assertThat(sparkleStrength).isEqualTo(config.sparkleStrength)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
index 45f7df3..c7c6b94 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
@@ -1172,7 +1172,7 @@
 
     inner class Listener : TemporaryViewDisplayController.Listener {
         val permanentlyRemovedIds = mutableListOf<String>()
-        override fun onInfoPermanentlyRemoved(id: String) {
+        override fun onInfoPermanentlyRemoved(id: String, reason: String) {
             permanentlyRemovedIds.add(id)
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
index 45eb1f9..586bdc6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
@@ -27,6 +27,7 @@
 import android.view.accessibility.AccessibilityManager
 import android.widget.ImageView
 import android.widget.TextView
+import androidx.core.animation.doOnCancel
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.systemui.R
@@ -66,7 +67,7 @@
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper
 class ChipbarCoordinatorTest : SysuiTestCase() {
-    private lateinit var underTest: FakeChipbarCoordinator
+    private lateinit var underTest: ChipbarCoordinator
 
     @Mock private lateinit var logger: ChipbarLogger
     @Mock private lateinit var accessibilityManager: AccessibilityManager
@@ -79,6 +80,7 @@
     @Mock private lateinit var viewUtil: ViewUtil
     @Mock private lateinit var vibratorHelper: VibratorHelper
     @Mock private lateinit var swipeGestureHandler: SwipeChipbarAwayGestureHandler
+    private lateinit var chipbarAnimator: TestChipbarAnimator
     private lateinit var fakeWakeLockBuilder: WakeLockFake.Builder
     private lateinit var fakeWakeLock: WakeLockFake
     private lateinit var fakeClock: FakeSystemClock
@@ -98,9 +100,10 @@
         fakeWakeLockBuilder.setWakeLock(fakeWakeLock)
 
         uiEventLoggerFake = UiEventLoggerFake()
+        chipbarAnimator = TestChipbarAnimator()
 
         underTest =
-            FakeChipbarCoordinator(
+            ChipbarCoordinator(
                 context,
                 logger,
                 windowManager,
@@ -109,6 +112,7 @@
                 configurationController,
                 dumpManager,
                 powerManager,
+                chipbarAnimator,
                 falsingManager,
                 falsingCollector,
                 swipeGestureHandler,
@@ -358,6 +362,105 @@
     }
 
     @Test
+    fun displayView_loading_animationStarted() {
+        underTest.displayView(
+            createChipbarInfo(
+                Icon.Resource(R.id.check_box, null),
+                Text.Loaded("text"),
+                endItem = ChipbarEndItem.Loading,
+            )
+        )
+
+        assertThat(underTest.loadingDetails!!.animator.isStarted).isTrue()
+    }
+
+    @Test
+    fun displayView_notLoading_noAnimation() {
+        underTest.displayView(
+            createChipbarInfo(
+                Icon.Resource(R.id.check_box, null),
+                Text.Loaded("text"),
+                endItem = ChipbarEndItem.Error,
+            )
+        )
+
+        assertThat(underTest.loadingDetails).isNull()
+    }
+
+    @Test
+    fun displayView_loadingThenNotLoading_animationStopped() {
+        underTest.displayView(
+            createChipbarInfo(
+                Icon.Resource(R.id.check_box, null),
+                Text.Loaded("text"),
+                endItem = ChipbarEndItem.Loading,
+            )
+        )
+
+        val animator = underTest.loadingDetails!!.animator
+        var cancelled = false
+        animator.doOnCancel { cancelled = true }
+
+        underTest.displayView(
+            createChipbarInfo(
+                Icon.Resource(R.id.check_box, null),
+                Text.Loaded("text"),
+                endItem = ChipbarEndItem.Button(Text.Loaded("button")) {},
+            )
+        )
+
+        assertThat(cancelled).isTrue()
+        assertThat(underTest.loadingDetails).isNull()
+    }
+
+    @Test
+    fun displayView_loadingThenHideView_animationStopped() {
+        underTest.displayView(
+            createChipbarInfo(
+                Icon.Resource(R.id.check_box, null),
+                Text.Loaded("text"),
+                endItem = ChipbarEndItem.Loading,
+            )
+        )
+
+        val animator = underTest.loadingDetails!!.animator
+        var cancelled = false
+        animator.doOnCancel { cancelled = true }
+
+        underTest.removeView(DEVICE_ID, "TestReason")
+
+        assertThat(cancelled).isTrue()
+        assertThat(underTest.loadingDetails).isNull()
+    }
+
+    @Test
+    fun displayView_loadingThenNewLoading_animationStaysTheSame() {
+        underTest.displayView(
+            createChipbarInfo(
+                Icon.Resource(R.id.check_box, null),
+                Text.Loaded("text"),
+                endItem = ChipbarEndItem.Loading,
+            )
+        )
+
+        val animator = underTest.loadingDetails!!.animator
+        var cancelled = false
+        animator.doOnCancel { cancelled = true }
+
+        underTest.displayView(
+            createChipbarInfo(
+                Icon.Resource(R.id.check_box, null),
+                Text.Loaded("new text"),
+                endItem = ChipbarEndItem.Loading,
+            )
+        )
+
+        assertThat(underTest.loadingDetails!!.animator).isEqualTo(animator)
+        assertThat(underTest.loadingDetails!!.animator.isStarted).isTrue()
+        assertThat(cancelled).isFalse()
+    }
+
+    @Test
     fun displayView_vibrationEffect_doubleClickEffect() {
         underTest.displayView(
             createChipbarInfo(
@@ -371,6 +474,26 @@
         verify(vibratorHelper).vibrate(VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK))
     }
 
+    /** Regression test for b/266119467. */
+    @Test
+    fun displayView_animationFailure_viewsStillBecomeVisible() {
+        chipbarAnimator.allowAnimation = false
+
+        underTest.displayView(
+            createChipbarInfo(
+                Icon.Resource(R.id.check_box, null),
+                Text.Loaded("text"),
+                endItem = ChipbarEndItem.Loading,
+            )
+        )
+
+        val view = getChipbarView()
+        assertThat(view.getInnerView().alpha).isEqualTo(1f)
+        assertThat(view.getStartIconView().alpha).isEqualTo(1f)
+        assertThat(view.getLoadingIcon().alpha).isEqualTo(1f)
+        assertThat(view.getChipTextView().alpha).isEqualTo(1f)
+    }
+
     @Test
     fun updateView_viewUpdated() {
         // First, display a view
@@ -436,6 +559,42 @@
         verify(logger).logViewUpdate(eq(WINDOW_TITLE), eq("new title text"), any())
     }
 
+    /** Regression test for b/266209420. */
+    @Test
+    fun displayViewThenImmediateRemoval_viewStillRemoved() {
+        underTest.displayView(
+            createChipbarInfo(
+                Icon.Resource(R.drawable.ic_cake, contentDescription = null),
+                Text.Loaded("title text"),
+                endItem = ChipbarEndItem.Error,
+            ),
+        )
+        val chipbarView = getChipbarView()
+
+        underTest.removeView(DEVICE_ID, "test reason")
+
+        verify(windowManager).removeView(chipbarView)
+    }
+
+    /** Regression test for b/266209420. */
+    @Test
+    fun removeView_animationFailure_viewStillRemoved() {
+        chipbarAnimator.allowAnimation = false
+
+        underTest.displayView(
+            createChipbarInfo(
+                Icon.Resource(R.drawable.ic_cake, contentDescription = null),
+                Text.Loaded("title text"),
+                endItem = ChipbarEndItem.Error,
+            ),
+        )
+        val chipbarView = getChipbarView()
+
+        underTest.removeView(DEVICE_ID, "test reason")
+
+        verify(windowManager).removeView(chipbarView)
+    }
+
     @Test
     fun swipeToDismiss_false_neverListensForGesture() {
         underTest.displayView(
@@ -543,8 +702,9 @@
 
     private fun ViewGroup.getStartIconView() = this.requireViewById<ImageView>(R.id.start_icon)
 
-    private fun ViewGroup.getChipText(): String =
-        (this.requireViewById<TextView>(R.id.text)).text as String
+    private fun ViewGroup.getChipTextView() = this.requireViewById<TextView>(R.id.text)
+
+    private fun ViewGroup.getChipText(): String = this.getChipTextView().text as String
 
     private fun ViewGroup.getLoadingIcon(): View = this.requireViewById(R.id.loading)
 
@@ -557,6 +717,25 @@
         verify(windowManager).addView(viewCaptor.capture(), any())
         return viewCaptor.value as ViewGroup
     }
+
+    /** Test class that lets us disallow animations. */
+    inner class TestChipbarAnimator : ChipbarAnimator() {
+        var allowAnimation: Boolean = true
+
+        override fun animateViewIn(innerView: ViewGroup, onAnimationEnd: Runnable): Boolean {
+            if (!allowAnimation) {
+                return false
+            }
+            return super.animateViewIn(innerView, onAnimationEnd)
+        }
+
+        override fun animateViewOut(innerView: ViewGroup, onAnimationEnd: Runnable): Boolean {
+            if (!allowAnimation) {
+                return false
+            }
+            return super.animateViewOut(innerView, onAnimationEnd)
+        }
+    }
 }
 
 private const val TIMEOUT = 10000
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt
deleted file mode 100644
index ffac8f6..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2022 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.temporarydisplay.chipbar
-
-import android.content.Context
-import android.os.PowerManager
-import android.view.ViewGroup
-import android.view.WindowManager
-import android.view.accessibility.AccessibilityManager
-import com.android.systemui.classifier.FalsingCollector
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.plugins.FalsingManager
-import com.android.systemui.statusbar.VibratorHelper
-import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.util.concurrency.DelayableExecutor
-import com.android.systemui.util.time.SystemClock
-import com.android.systemui.util.view.ViewUtil
-import com.android.systemui.util.wakelock.WakeLock
-
-/** A fake implementation of [ChipbarCoordinator] for testing. */
-class FakeChipbarCoordinator(
-    context: Context,
-    logger: ChipbarLogger,
-    windowManager: WindowManager,
-    mainExecutor: DelayableExecutor,
-    accessibilityManager: AccessibilityManager,
-    configurationController: ConfigurationController,
-    dumpManager: DumpManager,
-    powerManager: PowerManager,
-    falsingManager: FalsingManager,
-    falsingCollector: FalsingCollector,
-    swipeChipbarAwayGestureHandler: SwipeChipbarAwayGestureHandler,
-    viewUtil: ViewUtil,
-    vibratorHelper: VibratorHelper,
-    wakeLockBuilder: WakeLock.Builder,
-    systemClock: SystemClock,
-) :
-    ChipbarCoordinator(
-        context,
-        logger,
-        windowManager,
-        mainExecutor,
-        accessibilityManager,
-        configurationController,
-        dumpManager,
-        powerManager,
-        falsingManager,
-        falsingCollector,
-        swipeChipbarAwayGestureHandler,
-        viewUtil,
-        vibratorHelper,
-        wakeLockBuilder,
-        systemClock,
-    ) {
-    override fun animateViewOut(view: ViewGroup, removalReason: String?, onAnimationEnd: Runnable) {
-        // Just bypass the animation in tests
-        onAnimationEnd.run()
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandlerTest.kt
index a87a950..c539246 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandlerTest.kt
@@ -22,6 +22,7 @@
 import androidx.test.filters.SmallTest
 import com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.FakeDisplayTracker
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
@@ -36,7 +37,7 @@
 
     @Before
     fun setUp() {
-        underTest = SwipeChipbarAwayGestureHandler(context, mock())
+        underTest = SwipeChipbarAwayGestureHandler(context, FakeDisplayTracker(mContext), mock())
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
index f4226bc..a87e61a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
@@ -25,7 +25,10 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.util.LatencyTracker
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
 import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.shade.NotificationPanelViewController
@@ -105,8 +108,14 @@
             }
 
         keyguardRepository = FakeKeyguardRepository()
+        val featureFlags = FakeFeatureFlags().apply { set(FACE_AUTH_REFACTOR, true) }
         val keyguardInteractor =
-            KeyguardInteractor(repository = keyguardRepository, commandQueue = commandQueue)
+            KeyguardInteractor(
+                repository = keyguardRepository,
+                commandQueue = commandQueue,
+                featureFlags = featureFlags,
+                bouncerRepository = FakeKeyguardBouncerRepository(),
+            )
 
         // Needs to be run on the main thread
         runBlocking(IMMEDIATE) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt
index abbdab0..5288608 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt
@@ -20,17 +20,13 @@
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider
-import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
-import com.android.systemui.unfold.updates.FOLD_UPDATE_START_OPENING
-import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE
+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_FINISH_CLOSED
+import com.android.systemui.unfold.updates.FOLD_UPDATE_START_OPENING
+import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE
 import com.android.systemui.unfold.util.TestFoldStateProvider
-import com.android.systemui.util.leak.ReferenceTestUtils.waitForCondition
-import com.google.common.truth.Truth.assertThat
-import com.google.common.truth.Truth.assertWithMessage
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -45,9 +41,7 @@
 
     @Before
     fun setUp() {
-        progressProvider = PhysicsBasedUnfoldTransitionProgressProvider(
-            foldStateProvider
-        )
+        progressProvider = PhysicsBasedUnfoldTransitionProgressProvider(foldStateProvider)
         progressProvider.addCallback(listener)
     }
 
@@ -79,9 +73,7 @@
             { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) },
         )
 
-        with(listener.ensureTransitionFinished()) {
-            assertHasSingleFinishingEvent()
-        }
+        with(listener.ensureTransitionFinished()) { assertHasSingleFinishingEvent() }
     }
 
     @Test
@@ -150,106 +142,12 @@
             { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_FINISH_CLOSED) },
         )
 
-        with(listener.ensureTransitionFinished()) {
-            assertHasFoldAnimationAtTheEnd()
-        }
-    }
-
-    private class TestUnfoldProgressListener : TransitionProgressListener {
-
-        private val recordings: MutableList<UnfoldTransitionRecording> = arrayListOf()
-        private var currentRecording: UnfoldTransitionRecording? = null
-
-        override fun onTransitionStarted() {
-            assertWithMessage("Trying to start a transition when it is already in progress")
-                .that(currentRecording).isNull()
-
-            currentRecording = UnfoldTransitionRecording()
-        }
-
-        override fun onTransitionProgress(progress: Float) {
-            assertWithMessage("Received transition progress event when it's not started")
-                .that(currentRecording).isNotNull()
-            currentRecording!!.addProgress(progress)
-        }
-
-        override fun onTransitionFinishing() {
-            assertWithMessage("Received transition finishing event when it's not started")
-                    .that(currentRecording).isNotNull()
-            currentRecording!!.onFinishing()
-        }
-
-        override fun onTransitionFinished() {
-            assertWithMessage("Received transition finish event when it's not started")
-                .that(currentRecording).isNotNull()
-            recordings += currentRecording!!
-            currentRecording = null
-        }
-
-        fun ensureTransitionFinished(): UnfoldTransitionRecording {
-            waitForCondition { recordings.size == 1 }
-            return recordings.first()
-        }
-
-        class UnfoldTransitionRecording {
-            private val progressHistory: MutableList<Float> = arrayListOf()
-            private var finishingInvocations: Int = 0
-
-            fun addProgress(progress: Float) {
-                assertThat(progress).isAtMost(1.0f)
-                assertThat(progress).isAtLeast(0.0f)
-
-                progressHistory += progress
-            }
-
-            fun onFinishing() {
-                finishingInvocations++
-            }
-
-            fun assertIncreasingProgress() {
-                assertThat(progressHistory.size).isGreaterThan(MIN_ANIMATION_EVENTS)
-                assertThat(progressHistory).isInOrder()
-            }
-
-            fun assertDecreasingProgress() {
-                assertThat(progressHistory.size).isGreaterThan(MIN_ANIMATION_EVENTS)
-                assertThat(progressHistory).isInOrder(Comparator.reverseOrder<Float>())
-            }
-
-            fun assertFinishedWithUnfold() {
-                assertThat(progressHistory).isNotEmpty()
-                assertThat(progressHistory.last()).isEqualTo(1.0f)
-            }
-
-            fun assertFinishedWithFold() {
-                assertThat(progressHistory).isNotEmpty()
-                assertThat(progressHistory.last()).isEqualTo(0.0f)
-            }
-
-            fun assertHasFoldAnimationAtTheEnd() {
-                // Check that there are at least a few decreasing events at the end
-                assertThat(progressHistory.size).isGreaterThan(MIN_ANIMATION_EVENTS)
-                assertThat(progressHistory.takeLast(MIN_ANIMATION_EVENTS))
-                    .isInOrder(Comparator.reverseOrder<Float>())
-                assertThat(progressHistory.last()).isEqualTo(0.0f)
-            }
-
-            fun assertHasSingleFinishingEvent() {
-                assertWithMessage("onTransitionFinishing callback should be invoked exactly " +
-                        "one time").that(finishingInvocations).isEqualTo(1)
-            }
-        }
-
-        private companion object {
-            private const val MIN_ANIMATION_EVENTS = 5
-        }
+        with(listener.ensureTransitionFinished()) { assertHasFoldAnimationAtTheEnd() }
     }
 
     private fun runOnMainThreadWithInterval(vararg blocks: () -> Unit, intervalMillis: Long = 60) {
         blocks.forEach {
-            InstrumentationRegistry.getInstrumentation().runOnMainSync {
-                it()
-            }
+            InstrumentationRegistry.getInstrumentation().runOnMainSync { it() }
             Thread.sleep(intervalMillis)
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/RemoteUnfoldTransitionReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/RemoteUnfoldTransitionReceiverTest.kt
new file mode 100644
index 0000000..0e7e039
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/RemoteUnfoldTransitionReceiverTest.kt
@@ -0,0 +1,72 @@
+/*
+ * 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.progress
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class RemoteUnfoldTransitionReceiverTest : SysuiTestCase() {
+
+    private val progressProvider = RemoteUnfoldTransitionReceiver { it.run() }
+    private val listener = TestUnfoldProgressListener()
+
+    @Before
+    fun setUp() {
+        progressProvider.addCallback(listener)
+    }
+
+    @Test
+    fun onTransitionStarted_propagated() {
+        progressProvider.onTransitionStarted()
+
+        listener.assertStarted()
+    }
+
+    @Test
+    fun onTransitionProgress_propagated() {
+        progressProvider.onTransitionStarted()
+
+        progressProvider.onTransitionProgress(0.5f)
+
+        listener.assertLastProgress(0.5f)
+    }
+
+    @Test
+    fun onTransitionEnded_propagated() {
+        progressProvider.onTransitionStarted()
+        progressProvider.onTransitionProgress(0.5f)
+
+        progressProvider.onTransitionFinished()
+
+        listener.ensureTransitionFinished()
+    }
+
+    @Test
+    fun onTransitionStarted_afterCallbackRemoved_notPropagated() {
+        progressProvider.removeCallback(listener)
+
+        progressProvider.onTransitionStarted()
+
+        listener.assertNotStarted()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt
new file mode 100644
index 0000000..f653207
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt
@@ -0,0 +1,136 @@
+/*
+ * 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.progress
+
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.util.leak.ReferenceTestUtils.waitForCondition
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+
+/** Listener usable by tests with some handy assertions. */
+class TestUnfoldProgressListener : UnfoldTransitionProgressProvider.TransitionProgressListener {
+
+    private val recordings: MutableList<UnfoldTransitionRecording> = arrayListOf()
+    private var currentRecording: UnfoldTransitionRecording? = null
+
+    override fun onTransitionStarted() {
+        assertWithMessage("Trying to start a transition when it is already in progress")
+            .that(currentRecording)
+            .isNull()
+
+        currentRecording = UnfoldTransitionRecording()
+    }
+
+    override fun onTransitionProgress(progress: Float) {
+        assertWithMessage("Received transition progress event when it's not started")
+            .that(currentRecording)
+            .isNotNull()
+        currentRecording!!.addProgress(progress)
+    }
+
+    override fun onTransitionFinishing() {
+        assertWithMessage("Received transition finishing event when it's not started")
+            .that(currentRecording)
+            .isNotNull()
+        currentRecording!!.onFinishing()
+    }
+
+    override fun onTransitionFinished() {
+        assertWithMessage("Received transition finish event when it's not started")
+            .that(currentRecording)
+            .isNotNull()
+        recordings += currentRecording!!
+        currentRecording = null
+    }
+
+    fun ensureTransitionFinished(): UnfoldTransitionRecording {
+        waitForCondition { recordings.size == 1 }
+        return recordings.first()
+    }
+
+    fun assertStarted() {
+        assertWithMessage("Transition didn't start").that(currentRecording).isNotNull()
+    }
+
+    fun assertNotStarted() {
+        assertWithMessage("Transition started").that(currentRecording).isNull()
+    }
+
+    fun assertLastProgress(progress: Float) {
+        currentRecording?.assertLastProgress(progress) ?: error("unfold not in progress.")
+    }
+
+    class UnfoldTransitionRecording {
+        private val progressHistory: MutableList<Float> = arrayListOf()
+        private var finishingInvocations: Int = 0
+
+        fun addProgress(progress: Float) {
+            assertThat(progress).isAtMost(1.0f)
+            assertThat(progress).isAtLeast(0.0f)
+
+            progressHistory += progress
+        }
+
+        fun onFinishing() {
+            finishingInvocations++
+        }
+
+        fun assertIncreasingProgress() {
+            assertThat(progressHistory.size).isGreaterThan(MIN_ANIMATION_EVENTS)
+            assertThat(progressHistory).isInOrder()
+        }
+
+        fun assertDecreasingProgress() {
+            assertThat(progressHistory.size).isGreaterThan(MIN_ANIMATION_EVENTS)
+            assertThat(progressHistory).isInOrder(Comparator.reverseOrder<Float>())
+        }
+
+        fun assertFinishedWithUnfold() {
+            assertThat(progressHistory).isNotEmpty()
+            assertThat(progressHistory.last()).isEqualTo(1.0f)
+        }
+
+        fun assertFinishedWithFold() {
+            assertThat(progressHistory).isNotEmpty()
+            assertThat(progressHistory.last()).isEqualTo(0.0f)
+        }
+
+        fun assertHasFoldAnimationAtTheEnd() {
+            // Check that there are at least a few decreasing events at the end
+            assertThat(progressHistory.size).isGreaterThan(MIN_ANIMATION_EVENTS)
+            assertThat(progressHistory.takeLast(MIN_ANIMATION_EVENTS))
+                .isInOrder(Comparator.reverseOrder<Float>())
+            assertThat(progressHistory.last()).isEqualTo(0.0f)
+        }
+
+        fun assertHasSingleFinishingEvent() {
+            assertWithMessage(
+                    "onTransitionFinishing callback should be invoked exactly " + "one time"
+                )
+                .that(finishingInvocations)
+                .isEqualTo(1)
+        }
+
+        fun assertLastProgress(progress: Float) {
+            assertThat(progressHistory.last()).isEqualTo(progress)
+        }
+    }
+
+    private companion object {
+        private const val MIN_ANIMATION_EVENTS = 5
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
index 034c618..ccf378a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
@@ -17,12 +17,17 @@
 
 package com.android.systemui.user.data.repository
 
+import android.app.IActivityManager
+import android.app.UserSwitchObserver
 import android.content.pm.UserInfo
+import android.os.IRemoteCallback
 import android.os.UserHandle
 import android.os.UserManager
 import android.provider.Settings
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
 import com.android.systemui.settings.FakeUserTracker
 import com.android.systemui.user.data.model.UserSwitcherSettingsModel
 import com.android.systemui.util.settings.FakeSettings
@@ -39,7 +44,14 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
 import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
 
@@ -48,6 +60,8 @@
 class UserRepositoryImplTest : SysuiTestCase() {
 
     @Mock private lateinit var manager: UserManager
+    @Mock private lateinit var activityManager: IActivityManager
+    @Captor private lateinit var userSwitchObserver: ArgumentCaptor<UserSwitchObserver>
 
     private lateinit var underTest: UserRepositoryImpl
 
@@ -214,6 +228,34 @@
         assertThat(selectedUserInfo?.id).isEqualTo(1)
     }
 
+    @Test
+    fun userSwitchingInProgress_registersOnlyOneUserSwitchObserver() = runSelfCancelingTest {
+        underTest = create(this)
+
+        underTest.userSwitchingInProgress.launchIn(this)
+        underTest.userSwitchingInProgress.launchIn(this)
+        underTest.userSwitchingInProgress.launchIn(this)
+
+        verify(activityManager, times(1)).registerUserSwitchObserver(any(), anyString())
+    }
+
+    @Test
+    fun userSwitchingInProgress_propagatesStateFromActivityManager() = runSelfCancelingTest {
+        underTest = create(this)
+        verify(activityManager)
+            .registerUserSwitchObserver(userSwitchObserver.capture(), anyString())
+
+        userSwitchObserver.value.onUserSwitching(0, mock(IRemoteCallback::class.java))
+
+        var mostRecentSwitchingValue = false
+        underTest.userSwitchingInProgress.onEach { mostRecentSwitchingValue = it }.launchIn(this)
+
+        assertThat(mostRecentSwitchingValue).isTrue()
+
+        userSwitchObserver.value.onUserSwitchComplete(0)
+        assertThat(mostRecentSwitchingValue).isFalse()
+    }
+
     private fun createUserInfo(
         id: Int,
         isGuest: Boolean,
@@ -280,6 +322,8 @@
         }
 
     private fun create(scope: CoroutineScope = TestCoroutineScope()): UserRepositoryImpl {
+        val featureFlags = FakeFeatureFlags()
+        featureFlags.set(FACE_AUTH_REFACTOR, true)
         return UserRepositoryImpl(
             appContext = context,
             manager = manager,
@@ -288,6 +332,8 @@
             backgroundDispatcher = IMMEDIATE,
             globalSettings = globalSettings,
             tracker = tracker,
+            activityManager = activityManager,
+            featureFlags = featureFlags,
         )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
index 9bb52be..d00acb8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
@@ -28,7 +28,6 @@
 import android.os.UserManager
 import android.provider.Settings
 import androidx.test.filters.SmallTest
-import com.android.internal.R.drawable.ic_account_circle
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.GuestResetOrExitSessionReceiver
 import com.android.systemui.GuestResumeSessionReceiver
@@ -39,6 +38,7 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.plugins.ActivityStarter
@@ -87,6 +87,7 @@
 
     @Mock private lateinit var activityStarter: ActivityStarter
     @Mock private lateinit var manager: UserManager
+    @Mock private lateinit var headlessSystemUserMode: HeadlessSystemUserMode
     @Mock private lateinit var activityManager: ActivityManager
     @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
     @Mock private lateinit var devicePolicyManager: DevicePolicyManager
@@ -117,8 +118,11 @@
             SUPERVISED_USER_CREATION_APP_PACKAGE,
         )
 
-        featureFlags = FakeFeatureFlags()
-        featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+        featureFlags =
+            FakeFeatureFlags().apply {
+                set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+                set(Flags.FACE_AUTH_REFACTOR, true)
+            }
         userRepository = FakeUserRepository()
         keyguardRepository = FakeKeyguardRepository()
         telephonyRepository = FakeTelephonyRepository()
@@ -139,8 +143,11 @@
                     KeyguardInteractor(
                         repository = keyguardRepository,
                         commandQueue = commandQueue,
+                        featureFlags = featureFlags,
+                        bouncerRepository = FakeKeyguardBouncerRepository(),
                     ),
                 manager = manager,
+                headlessSystemUserMode = headlessSystemUserMode,
                 applicationScope = testScope.backgroundScope,
                 telephonyInteractor =
                     TelephonyInteractor(
@@ -844,6 +851,50 @@
             assertThat(selectedUser()).isNotNull()
         }
 
+    @Test
+    fun userRecords_isActionAndNoUsersUnlocked_actionIsDisabled() =
+        testScope.runTest {
+            keyguardRepository.setKeyguardShowing(true)
+            whenever(manager.getUserSwitchability(any()))
+                .thenReturn(UserManager.SWITCHABILITY_STATUS_SYSTEM_USER_LOCKED)
+            val userInfos = createUserInfos(count = 3, includeGuest = false).toMutableList()
+            userRepository.setUserInfos(userInfos)
+            userRepository.setSelectedUserInfo(userInfos[1])
+            userRepository.setSettings(
+                UserSwitcherSettingsModel(
+                    isUserSwitcherEnabled = true,
+                    isAddUsersFromLockscreen = true
+                )
+            )
+
+            runCurrent()
+            underTest.userRecords.value
+                .filter { it.info == null }
+                .forEach { action -> assertThat(action.isSwitchToEnabled).isFalse() }
+        }
+
+    @Test
+    fun userRecords_isActionAndNoUsersUnlocked_actionIsDisabled_HeadlessMode() =
+        testScope.runTest {
+            keyguardRepository.setKeyguardShowing(true)
+            whenever(headlessSystemUserMode.isHeadlessSystemUserMode()).thenReturn(true)
+            whenever(manager.isUserUnlocked(anyInt())).thenReturn(false)
+            val userInfos = createUserInfos(count = 3, includeGuest = false).toMutableList()
+            userRepository.setUserInfos(userInfos)
+            userRepository.setSelectedUserInfo(userInfos[1])
+            userRepository.setSettings(
+                UserSwitcherSettingsModel(
+                    isUserSwitcherEnabled = true,
+                    isAddUsersFromLockscreen = true
+                )
+            )
+
+            runCurrent()
+            underTest.userRecords.value
+                .filter { it.info == null }
+                .forEach { action -> assertThat(action.isSwitchToEnabled).isFalse() }
+        }
+
     private fun assertUsers(
         models: List<UserModel>?,
         count: Int,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
index 9a4ca56..22fc32a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
@@ -31,6 +31,7 @@
 import com.android.systemui.common.shared.model.Text
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.plugins.ActivityStarter
@@ -41,6 +42,7 @@
 import com.android.systemui.user.data.model.UserSwitcherSettingsModel
 import com.android.systemui.user.data.repository.FakeUserRepository
 import com.android.systemui.user.domain.interactor.GuestUserInteractor
+import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode
 import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
 import com.android.systemui.user.domain.interactor.UserInteractor
 import com.android.systemui.util.mockito.mock
@@ -71,6 +73,7 @@
     @Mock private lateinit var activityStarter: ActivityStarter
     @Mock private lateinit var activityManager: ActivityManager
     @Mock private lateinit var manager: UserManager
+    @Mock private lateinit var headlessSystemUserMode: HeadlessSystemUserMode
     @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
     @Mock private lateinit var devicePolicyManager: DevicePolicyManager
     @Mock private lateinit var uiEventLogger: UiEventLogger
@@ -82,7 +85,6 @@
 
     private val userRepository = FakeUserRepository()
     private val keyguardRepository = FakeKeyguardRepository()
-    private val featureFlags = FakeFeatureFlags()
     private lateinit var guestUserInteractor: GuestUserInteractor
     private lateinit var refreshUsersScheduler: RefreshUsersScheduler
 
@@ -233,6 +235,11 @@
         }
 
     private fun viewModel(): StatusBarUserChipViewModel {
+        val featureFlags =
+            FakeFeatureFlags().apply {
+                set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+                set(Flags.FACE_AUTH_REFACTOR, true)
+            }
         return StatusBarUserChipViewModel(
             context = context,
             interactor =
@@ -244,10 +251,12 @@
                         KeyguardInteractor(
                             repository = keyguardRepository,
                             commandQueue = commandQueue,
+                            featureFlags = featureFlags,
+                            bouncerRepository = FakeKeyguardBouncerRepository(),
                         ),
-                    featureFlags =
-                        FakeFeatureFlags().apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) },
+                    featureFlags = featureFlags,
                     manager = manager,
+                    headlessSystemUserMode = headlessSystemUserMode,
                     applicationScope = testScope.backgroundScope,
                     telephonyInteractor =
                         TelephonyInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
index 3d4bbdb..a2bd8d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.common.shared.model.Text
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.plugins.ActivityStarter
@@ -41,6 +42,7 @@
 import com.android.systemui.user.data.model.UserSwitcherSettingsModel
 import com.android.systemui.user.data.repository.FakeUserRepository
 import com.android.systemui.user.domain.interactor.GuestUserInteractor
+import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode
 import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
 import com.android.systemui.user.domain.interactor.UserInteractor
 import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
@@ -72,6 +74,7 @@
     @Mock private lateinit var activityStarter: ActivityStarter
     @Mock private lateinit var activityManager: ActivityManager
     @Mock private lateinit var manager: UserManager
+    @Mock private lateinit var headlessSystemUserMode: HeadlessSystemUserMode
     @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
     @Mock private lateinit var devicePolicyManager: DevicePolicyManager
     @Mock private lateinit var uiEventLogger: UiEventLogger
@@ -134,6 +137,11 @@
                 resetOrExitSessionReceiver = resetOrExitSessionReceiver,
             )
 
+        val featureFlags =
+            FakeFeatureFlags().apply {
+                set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+                set(Flags.FACE_AUTH_REFACTOR, true)
+            }
         underTest =
             UserSwitcherViewModel.Factory(
                     userInteractor =
@@ -145,12 +153,12 @@
                                 KeyguardInteractor(
                                     repository = keyguardRepository,
                                     commandQueue = commandQueue,
+                                    featureFlags = featureFlags,
+                                    bouncerRepository = FakeKeyguardBouncerRepository(),
                                 ),
-                            featureFlags =
-                                FakeFeatureFlags().apply {
-                                    set(Flags.FULL_SCREEN_USER_SWITCHER, false)
-                                },
+                            featureFlags = featureFlags,
                             manager = manager,
+                            headlessSystemUserMode = headlessSystemUserMode,
                             applicationScope = testScope.backgroundScope,
                             telephonyInteractor =
                                 TelephonyInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java
new file mode 100644
index 0000000..b367a60
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java
@@ -0,0 +1,185 @@
+/*
+ * 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.condition;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.CoreStartable;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.shared.condition.Condition;
+import com.android.systemui.shared.condition.Monitor;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class ConditionalCoreStartableTest extends SysuiTestCase {
+    public static class FakeConditionalCoreStartable extends ConditionalCoreStartable {
+        interface Callback {
+            void onStart();
+            void bootCompleted();
+        }
+
+        private final Callback mCallback;
+
+        public FakeConditionalCoreStartable(Monitor monitor, Set<Condition> conditions,
+                Callback callback) {
+            super(monitor, conditions);
+            mCallback = callback;
+        }
+
+        public FakeConditionalCoreStartable(Monitor monitor, Callback callback) {
+            super(monitor);
+            mCallback = callback;
+        }
+
+        @Override
+        protected void onStart() {
+            mCallback.onStart();
+        }
+
+        @Override
+        protected void bootCompleted() {
+            mCallback.bootCompleted();
+        }
+    }
+
+
+    final Set<Condition> mConditions = new HashSet<>();
+
+    @Mock
+    Condition mCondition;
+
+    @Mock
+    Monitor mMonitor;
+
+    @Mock
+    FakeConditionalCoreStartable.Callback mCallback;
+
+    @Mock
+    Monitor.Subscription.Token mSubscriptionToken;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mConditions.clear();
+    }
+
+    /**
+     * Verifies that {@link ConditionalCoreStartable#onStart()} is predicated on conditions being
+     * met.
+     */
+    @Test
+    public void testOnStartCallback() {
+        final CoreStartable coreStartable =
+                new FakeConditionalCoreStartable(mMonitor,
+                        new HashSet<>(Arrays.asList(mCondition)),
+                        mCallback);
+
+        when(mMonitor.addSubscription(any())).thenReturn(mSubscriptionToken);
+        coreStartable.start();
+
+        final ArgumentCaptor<Monitor.Subscription> subscriptionCaptor = ArgumentCaptor.forClass(
+                Monitor.Subscription.class);
+        verify(mMonitor).addSubscription(subscriptionCaptor.capture());
+
+        final Monitor.Subscription subscription = subscriptionCaptor.getValue();
+
+        assertThat(subscription.getConditions()).containsExactly(mCondition);
+
+        verify(mCallback, never()).onStart();
+
+        subscription.getCallback().onConditionsChanged(true);
+
+        verify(mCallback).onStart();
+        verify(mMonitor).removeSubscription(mSubscriptionToken);
+    }
+
+    @Test
+    public void testOnStartCallbackWithNoConditions() {
+        final CoreStartable coreStartable =
+                new FakeConditionalCoreStartable(mMonitor,
+                        mCallback);
+
+        when(mMonitor.addSubscription(any())).thenReturn(mSubscriptionToken);
+        coreStartable.start();
+
+        final ArgumentCaptor<Monitor.Subscription> subscriptionCaptor = ArgumentCaptor.forClass(
+                Monitor.Subscription.class);
+        verify(mMonitor).addSubscription(subscriptionCaptor.capture());
+
+        final Monitor.Subscription subscription = subscriptionCaptor.getValue();
+
+        assertThat(subscription.getConditions()).isEmpty();
+
+        verify(mCallback, never()).onStart();
+
+        subscription.getCallback().onConditionsChanged(true);
+
+        verify(mCallback).onStart();
+        verify(mMonitor).removeSubscription(mSubscriptionToken);
+    }
+
+
+    /**
+     * Verifies that {@link ConditionalCoreStartable#bootCompleted()} ()} is predicated on
+     * conditions being met.
+     */
+    @Test
+    public void testBootCompleted() {
+        final CoreStartable coreStartable =
+                new FakeConditionalCoreStartable(mMonitor,
+                        new HashSet<>(Arrays.asList(mCondition)),
+                        mCallback);
+
+        when(mMonitor.addSubscription(any())).thenReturn(mSubscriptionToken);
+        coreStartable.onBootCompleted();
+
+        final ArgumentCaptor<Monitor.Subscription> subscriptionCaptor = ArgumentCaptor.forClass(
+                Monitor.Subscription.class);
+        verify(mMonitor).addSubscription(subscriptionCaptor.capture());
+
+        final Monitor.Subscription subscription = subscriptionCaptor.getValue();
+
+        assertThat(subscription.getConditions()).containsExactly(mCondition);
+
+        verify(mCallback, never()).bootCompleted();
+
+        subscription.getCallback().onConditionsChanged(true);
+
+        verify(mCallback).bootCompleted();
+        verify(mMonitor).removeSubscription(mSubscriptionToken);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 68ccc30..bc33439 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -91,6 +91,7 @@
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.settings.FakeDisplayTracker;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shade.NotificationShadeWindowControllerImpl;
 import com.android.systemui.shade.NotificationShadeWindowView;
@@ -287,6 +288,8 @@
 
     private TestableLooper mTestableLooper;
 
+    private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -333,7 +336,7 @@
         mZenModeConfig.suppressedVisualEffects = 0;
         when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
 
-        mSysUiState = new SysUiState();
+        mSysUiState = new SysUiState(mDisplayTracker);
         mSysUiState.addCallback(sysUiFlags -> {
             mSysUiStateBubblesManageMenuExpanded =
                     (sysUiFlags
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index 7ae47b4..45489d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -29,6 +29,7 @@
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.notetask.NoteTaskInitializer;
+import com.android.systemui.settings.FakeDisplayTracker;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -63,6 +64,7 @@
 @RunWith(AndroidJUnit4.class)
 public class WMShellTest extends SysuiTestCase {
     WMShell mWMShell;
+    FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
 
     @Mock ShellInterface mShellInterface;
     @Mock CommandQueue mCommandQueue;
@@ -100,6 +102,7 @@
                 mProtoTracer,
                 mWakefulnessLifecycle,
                 mUserTracker,
+                mDisplayTracker,
                 mNoteTaskInitializer,
                 mSysUiMainExecutor
         );
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/condition/SelfExecutingMonitor.java b/packages/SystemUI/tests/utils/src/com/android/systemui/condition/SelfExecutingMonitor.java
new file mode 100644
index 0000000..7ee05d0
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/condition/SelfExecutingMonitor.java
@@ -0,0 +1,62 @@
+/*
+ * 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.condition;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+/**
+ * {@link SelfExecutingMonitor} creates a monitor that independently executes its logic through
+ * a {@link FakeExecutor}, which is ran at when a subscription is added and removed.
+ */
+public class SelfExecutingMonitor extends Monitor {
+    private final FakeExecutor mExecutor;
+
+    /**
+     * Default constructor that allows specifying the FakeExecutor to use.
+     */
+    public SelfExecutingMonitor(FakeExecutor executor) {
+        super(executor);
+        mExecutor = executor;
+    }
+
+    @Override
+    public Subscription.Token addSubscription(@NonNull Subscription subscription) {
+        final Subscription.Token result = super.addSubscription(subscription);
+        mExecutor.runAllReady();
+        return result;
+    }
+
+    @Override
+    public void removeSubscription(@NonNull Subscription.Token token) {
+        super.removeSubscription(token);
+        mExecutor.runNextReady();
+    }
+
+    /**
+     * Creates a {@link SelfExecutingMonitor} with a self-managed {@link FakeExecutor}. Use only
+     * for cases where condition state only will be set at when a subscription is added.
+     */
+    public static SelfExecutingMonitor createInstance() {
+        final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
+        final FakeExecutor mExecutor = new FakeExecutor(mFakeSystemClock);
+        return new SelfExecutingMonitor(mExecutor);
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt
index b7a8d2e..9b4f496 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt
@@ -18,6 +18,8 @@
 
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.EmptyCoroutineContext
+import kotlin.properties.ReadOnlyProperty
+import kotlin.reflect.KProperty
 import kotlinx.coroutines.CoroutineStart
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.Flow
@@ -25,16 +27,35 @@
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 
-/** Collect [flow] in a new [Job] and return a getter for the last collected value. */
+/**
+ * Collect [flow] in a new [Job] and return a getter for the last collected value.
+ * ```
+ * fun myTest() = runTest {
+ *   // ...
+ *   val actual by collectLastValue(underTest.flow)
+ *   assertThat(actual).isEqualTo(expected)
+ * }
+ * ```
+ */
 fun <T> TestScope.collectLastValue(
     flow: Flow<T>,
     context: CoroutineContext = EmptyCoroutineContext,
     start: CoroutineStart = CoroutineStart.DEFAULT,
-): () -> T? {
+): FlowValue<T?> {
     var lastValue: T? = null
     backgroundScope.launch(context, start) { flow.collect { lastValue = it } }
-    return {
+    return FlowValueImpl {
         runCurrent()
         lastValue
     }
 }
+
+/** @see collectLastValue */
+interface FlowValue<T> : ReadOnlyProperty<Any?, T?> {
+    operator fun invoke(): T?
+}
+
+private class FlowValueImpl<T>(private val block: () -> T?) : FlowValue<T> {
+    override operator fun invoke(): T? = block()
+    override fun getValue(thisRef: Any?, property: KProperty<*>): T? = invoke()
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/TestCoroutineSchedulerUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/TestCoroutineSchedulerUtils.kt
new file mode 100644
index 0000000..84e2a5c
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/TestCoroutineSchedulerUtils.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.
+ */
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.coroutines
+
+import kotlin.time.Duration
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestCoroutineScheduler
+
+/**
+ * Moves the virtual clock of this dispatcher forward by the specified [Duration].
+ *
+ * @see [TestCoroutineScheduler.advanceTimeBy]
+ */
+fun TestCoroutineScheduler.advanceTimeBy(duration: Duration) {
+    advanceTimeBy(duration.inWholeMilliseconds)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
index 6c82cef..b94f816e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
@@ -38,12 +38,6 @@
         }
     }
 
-    fun set(flag: DeviceConfigBooleanFlag, value: Boolean) {
-        if (booleanFlags.put(flag.id, value)?.let { value != it } != false) {
-            notifyFlagChanged(flag)
-        }
-    }
-
     fun set(flag: ResourceBooleanFlag, value: Boolean) {
         if (booleanFlags.put(flag.id, value)?.let { value != it } != false) {
             notifyFlagChanged(flag)
@@ -73,7 +67,7 @@
             listeners.forEach { listener ->
                 listener.onFlagChanged(
                     object : FlagListenable.FlagEvent {
-                        override val flagId = flag.id
+                        override val flagName = flag.name
                         override fun requestNoRestart() {}
                     }
                 )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
similarity index 74%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricRepository.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
index f3e52de..01dac36 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
@@ -17,15 +17,24 @@
 
 package com.android.systemui.keyguard.data.repository
 
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
 
-class FakeBiometricRepository : BiometricRepository {
+class FakeBiometricSettingsRepository : BiometricSettingsRepository {
 
     private val _isFingerprintEnrolled = MutableStateFlow<Boolean>(false)
     override val isFingerprintEnrolled: StateFlow<Boolean> = _isFingerprintEnrolled.asStateFlow()
 
+    private val _isFaceEnrolled = MutableStateFlow(false)
+    override val isFaceEnrolled: Flow<Boolean>
+        get() = _isFaceEnrolled
+
+    private val _isFaceAuthEnabled = MutableStateFlow(false)
+    override val isFaceAuthenticationEnabled: Flow<Boolean>
+        get() = _isFaceAuthEnabled
+
     private val _isStrongBiometricAllowed = MutableStateFlow(false)
     override val isStrongBiometricAllowed = _isStrongBiometricAllowed.asStateFlow()
 
@@ -44,4 +53,12 @@
     fun setFingerprintEnabledByDevicePolicy(isFingerprintEnabledByDevicePolicy: Boolean) {
         _isFingerprintEnabledByDevicePolicy.value = isFingerprintEnabledByDevicePolicy
     }
+
+    fun setFaceEnrolled(isFaceEnrolled: Boolean) {
+        _isFaceEnrolled.value = isFaceEnrolled
+    }
+
+    fun setIsFaceAuthEnabled(enabled: Boolean) {
+        _isFaceAuthEnabled.value = enabled
+    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
new file mode 100644
index 0000000..5641832
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.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.keyguard.data.repository
+
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class FakeDeviceEntryFingerprintAuthRepository : DeviceEntryFingerprintAuthRepository {
+    private val _isLockedOut = MutableStateFlow<Boolean>(false)
+    override val isLockedOut: StateFlow<Boolean> = _isLockedOut.asStateFlow()
+
+    fun setLockedOut(lockedOut: Boolean) {
+        _isLockedOut.value = lockedOut
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardBouncerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardBouncerRepository.kt
new file mode 100644
index 0000000..3374219
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardBouncerRepository.kt
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN
+import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
+import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/** Fake implementation of [KeyguardRepository] */
+class FakeKeyguardBouncerRepository : KeyguardBouncerRepository {
+    private val _primaryBouncerVisible = MutableStateFlow(false)
+    override val primaryBouncerVisible = _primaryBouncerVisible.asStateFlow()
+    private val _primaryBouncerShow = MutableStateFlow<KeyguardBouncerModel?>(null)
+    override val primaryBouncerShow = _primaryBouncerShow.asStateFlow()
+    private val _primaryBouncerShowingSoon = MutableStateFlow(false)
+    override val primaryBouncerShowingSoon = _primaryBouncerShowingSoon.asStateFlow()
+    private val _primaryBouncerHide = MutableStateFlow(false)
+    override val primaryBouncerHide = _primaryBouncerHide.asStateFlow()
+    private val _primaryBouncerStartingToHide = MutableStateFlow(false)
+    override val primaryBouncerStartingToHide = _primaryBouncerStartingToHide.asStateFlow()
+    private val _primaryBouncerDisappearAnimation = MutableStateFlow<Runnable?>(null)
+    override val primaryBouncerStartingDisappearAnimation =
+        _primaryBouncerDisappearAnimation.asStateFlow()
+    private val _primaryBouncerScrimmed = MutableStateFlow(false)
+    override val primaryBouncerScrimmed = _primaryBouncerScrimmed.asStateFlow()
+    private val _panelExpansionAmount = MutableStateFlow(EXPANSION_HIDDEN)
+    override val panelExpansionAmount = _panelExpansionAmount.asStateFlow()
+    private val _keyguardPosition = MutableStateFlow(0f)
+    override val keyguardPosition = _keyguardPosition.asStateFlow()
+    private val _onScreenTurnedOff = MutableStateFlow(false)
+    override val onScreenTurnedOff = _onScreenTurnedOff.asStateFlow()
+    private val _isBackButtonEnabled = MutableStateFlow<Boolean?>(null)
+    override val isBackButtonEnabled = _isBackButtonEnabled.asStateFlow()
+    private val _keyguardAuthenticated = MutableStateFlow<Boolean?>(null)
+    override val keyguardAuthenticated = _keyguardAuthenticated.asStateFlow()
+    private val _showMessage = MutableStateFlow<BouncerShowMessageModel?>(null)
+    override val showMessage = _showMessage.asStateFlow()
+    private val _resourceUpdateRequests = MutableStateFlow(false)
+    override val resourceUpdateRequests = _resourceUpdateRequests.asStateFlow()
+    override val bouncerPromptReason = 0
+    override val bouncerErrorMessage: CharSequence? = null
+    private val _isAlternateBouncerVisible = MutableStateFlow(false)
+    override val alternateBouncerVisible = _isAlternateBouncerVisible.asStateFlow()
+    override var lastAlternateBouncerVisibleTime: Long = 0L
+    private val _isAlternateBouncerUIAvailable = MutableStateFlow<Boolean>(false)
+    override val alternateBouncerUIAvailable = _isAlternateBouncerUIAvailable.asStateFlow()
+
+    override fun setPrimaryScrimmed(isScrimmed: Boolean) {
+        _primaryBouncerScrimmed.value = isScrimmed
+    }
+
+    override fun setPrimaryVisible(isVisible: Boolean) {
+        _primaryBouncerVisible.value = isVisible
+    }
+
+    override fun setAlternateVisible(isVisible: Boolean) {
+        _isAlternateBouncerVisible.value = isVisible
+    }
+
+    override fun setAlternateBouncerUIAvailable(isAvailable: Boolean) {
+        _isAlternateBouncerUIAvailable.value = isAvailable
+    }
+
+    override fun setPrimaryShow(keyguardBouncerModel: KeyguardBouncerModel?) {
+        _primaryBouncerShow.value = keyguardBouncerModel
+    }
+
+    override fun setPrimaryShowingSoon(showingSoon: Boolean) {
+        _primaryBouncerShowingSoon.value = showingSoon
+    }
+
+    override fun setPrimaryHide(hide: Boolean) {
+        _primaryBouncerHide.value = hide
+    }
+
+    override fun setPrimaryStartingToHide(startingToHide: Boolean) {
+        _primaryBouncerStartingToHide.value = startingToHide
+    }
+
+    override fun setPrimaryStartDisappearAnimation(runnable: Runnable?) {
+        _primaryBouncerDisappearAnimation.value = runnable
+    }
+
+    override fun setPanelExpansion(panelExpansion: Float) {
+        _panelExpansionAmount.value = panelExpansion
+    }
+
+    override fun setKeyguardPosition(keyguardPosition: Float) {
+        _keyguardPosition.value = keyguardPosition
+    }
+
+    override fun setResourceUpdateRequests(willUpdateResources: Boolean) {
+        _resourceUpdateRequests.value = willUpdateResources
+    }
+
+    override fun setShowMessage(bouncerShowMessageModel: BouncerShowMessageModel?) {
+        _showMessage.value = bouncerShowMessageModel
+    }
+
+    override fun setKeyguardAuthenticated(keyguardAuthenticated: Boolean?) {
+        _keyguardAuthenticated.value = keyguardAuthenticated
+    }
+
+    override fun setIsBackButtonEnabled(isBackButtonEnabled: Boolean) {
+        _isBackButtonEnabled.value = isBackButtonEnabled
+    }
+
+    override fun setOnScreenTurnedOff(onScreenTurnedOff: Boolean) {
+        _onScreenTurnedOff.value = onScreenTurnedOff
+    }
+}
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 15b4736..1a371c7 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
@@ -29,6 +29,7 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
 
 /** Fake implementation of [KeyguardRepository] */
 class FakeKeyguardRepository : KeyguardRepository {
@@ -83,9 +84,6 @@
 
     private val _isUdfpsSupported = MutableStateFlow(false)
 
-    private val _isBouncerShowing = MutableStateFlow(false)
-    override val isBouncerShowing: Flow<Boolean> = _isBouncerShowing
-
     private val _isKeyguardGoingAway = MutableStateFlow(false)
     override val isKeyguardGoingAway: Flow<Boolean> = _isKeyguardGoingAway
 
@@ -101,6 +99,13 @@
     private val _biometricUnlockSource = MutableStateFlow<BiometricUnlockSource?>(null)
     override val biometricUnlockSource: Flow<BiometricUnlockSource?> = _biometricUnlockSource
 
+    private val _isQuickSettingsVisible = MutableStateFlow(false)
+    override val isQuickSettingsVisible: Flow<Boolean> = _isQuickSettingsVisible.asStateFlow()
+
+    override fun setQuickSettingsVisible(isVisible: Boolean) {
+        _isQuickSettingsVisible.value = isVisible
+    }
+
     override fun isKeyguardShowing(): Boolean {
         return _isKeyguardShowing.value
     }
@@ -145,10 +150,6 @@
         _wakefulnessModel.value = model
     }
 
-    fun setBouncerShowing(isShowing: Boolean) {
-        _isBouncerShowing.value = isShowing
-    }
-
     fun setBiometricUnlockState(state: BiometricUnlockModel) {
         _biometricUnlockState.tryEmit(state)
     }
@@ -169,6 +170,10 @@
         _dozeTransitionModel.value = model
     }
 
+    fun setStatusBarState(state: StatusBarState) {
+        _statusBarState.value = state
+    }
+
     override fun isUdfpsSupported(): Boolean {
         return _isUdfpsSupported.value
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
index 6c44244..eac1bd1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
@@ -22,13 +22,15 @@
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import java.util.UUID
+import kotlinx.coroutines.channels.BufferOverflow
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.SharedFlow
 
 /** Fake implementation of [KeyguardTransitionRepository] */
 class FakeKeyguardTransitionRepository : KeyguardTransitionRepository {
 
-    private val _transitions = MutableSharedFlow<TransitionStep>()
+    private val _transitions =
+        MutableSharedFlow<TransitionStep>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
     override val transitions: SharedFlow<TransitionStep> = _transitions
 
     suspend fun sendTransitionStep(step: TransitionStep) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeDisplayTracker.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeDisplayTracker.kt
new file mode 100644
index 0000000..1403cea
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeDisplayTracker.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.settings
+
+import android.content.Context
+import android.hardware.display.DisplayManager
+import android.view.Display
+import java.util.concurrent.Executor
+
+class FakeDisplayTracker constructor(val context: Context) : DisplayTracker {
+    val displayManager: DisplayManager = context.getSystemService(DisplayManager::class.java)!!
+    override var defaultDisplayId: Int = Display.DEFAULT_DISPLAY
+    override var allDisplays: Array<Display> = displayManager.displays
+
+    private val displayCallbacks: MutableList<DisplayTracker.Callback> = ArrayList()
+    private val brightnessCallbacks: MutableList<DisplayTracker.Callback> = ArrayList()
+    override fun addDisplayChangeCallback(callback: DisplayTracker.Callback, executor: Executor) {
+        displayCallbacks.add(callback)
+    }
+    override fun addBrightnessChangeCallback(
+        callback: DisplayTracker.Callback,
+        executor: Executor
+    ) {
+        brightnessCallbacks.add(callback)
+    }
+
+    override fun removeCallback(callback: DisplayTracker.Callback) {
+        displayCallbacks.remove(callback)
+        brightnessCallbacks.remove(callback)
+    }
+
+    fun setDefaultDisplay(displayId: Int) {
+        defaultDisplayId = displayId
+    }
+
+    fun setDisplays(displays: Array<Display>) {
+        allDisplays = displays
+    }
+
+    fun triggerOnDisplayAdded(displayId: Int) {
+        notifyCallbacks({ onDisplayAdded(displayId) }, displayCallbacks)
+    }
+
+    fun triggerOnDisplayRemoved(displayId: Int) {
+        notifyCallbacks({ onDisplayRemoved(displayId) }, displayCallbacks)
+    }
+
+    fun triggerOnDisplayChanged(displayId: Int) {
+        notifyCallbacks({ onDisplayChanged(displayId) }, displayCallbacks)
+    }
+
+    fun triggerOnDisplayBrightnessChanged(displayId: Int) {
+        notifyCallbacks({ onDisplayChanged(displayId) }, brightnessCallbacks)
+    }
+
+    private inline fun notifyCallbacks(
+        crossinline action: DisplayTracker.Callback.() -> Unit,
+        list: List<DisplayTracker.Callback>
+    ) {
+        list.forEach { it.action() }
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
index 0dd1fc7..251014f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
@@ -67,7 +67,10 @@
         _userHandle = UserHandle.of(_userId)
 
         val copy = callbacks.toList()
-        copy.forEach { it.onUserChanged(_userId, userContext) }
+        copy.forEach {
+            it.onUserChanging(_userId, userContext)
+            it.onUserChanged(_userId, userContext)
+        }
     }
 
     fun onProfileChanged() {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
index ea5a302..1a8e244 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
@@ -39,6 +39,10 @@
     private val _selectedUserInfo = MutableStateFlow<UserInfo?>(null)
     override val selectedUserInfo: Flow<UserInfo> = _selectedUserInfo.filterNotNull()
 
+    private val _userSwitchingInProgress = MutableStateFlow(false)
+    override val userSwitchingInProgress: Flow<Boolean>
+        get() = _userSwitchingInProgress
+
     override var lastSelectedNonGuestUserId: Int = UserHandle.USER_SYSTEM
 
     private var _isGuestUserAutoCreated: Boolean = false
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSharedPreferences.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSharedPreferences.kt
index 4a881a7..fd1b8e9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSharedPreferences.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSharedPreferences.kt
@@ -41,7 +41,7 @@
     }
 
     override fun getStringSet(key: String, defValues: MutableSet<String>?): MutableSet<String>? {
-        return data.getOrDefault(key, defValues) as? MutableSet<String>?
+        return (data.getOrDefault(key, defValues) as? Set<String>?)?.toMutableSet()
     }
 
     override fun getInt(key: String, defValue: Int): Int {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java
index 95b62a1..bdf1aff 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java
@@ -49,7 +49,7 @@
     }
 
     @Override
-    public boolean isBouncerShowing() {
+    public boolean isPrimaryBouncerShowing() {
         return false;
     }
 
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldRemoteModule.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldRemoteModule.kt
new file mode 100644
index 0000000..b395d9c
--- /dev/null
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldRemoteModule.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.progress.RemoteUnfoldTransitionReceiver
+import com.android.systemui.unfold.util.ATraceLoggerTransitionProgressListener
+import dagger.Module
+import dagger.Provides
+import java.util.Optional
+import javax.inject.Provider
+import javax.inject.Singleton
+
+/** Binds classes needed to provide unfold transition progresses to another process. */
+@Module
+class UnfoldRemoteModule {
+    @Provides
+    @Singleton
+    fun provideTransitionProvider(
+        config: UnfoldTransitionConfig,
+        traceListener: ATraceLoggerTransitionProgressListener,
+        remoteReceiverProvider: Provider<RemoteUnfoldTransitionReceiver>,
+    ): Optional<RemoteUnfoldTransitionReceiver> {
+        if (!config.isEnabled) {
+            return Optional.empty()
+        }
+        val remoteReceiver = remoteReceiverProvider.get()
+        remoteReceiver.addCallback(traceListener)
+        return Optional.of(remoteReceiver)
+    }
+}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
index cfb959e..068347c 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.unfold.config.UnfoldTransitionConfig
 import com.android.systemui.unfold.dagger.UnfoldMain
 import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg
+import com.android.systemui.unfold.progress.RemoteUnfoldTransitionReceiver
 import com.android.systemui.unfold.updates.FoldProvider
 import com.android.systemui.unfold.updates.RotationChangeProvider
 import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
@@ -68,3 +69,38 @@
     val unfoldTransitionProvider: Optional<UnfoldTransitionProgressProvider>
     val rotationChangeProvider: RotationChangeProvider
 }
+
+/**
+ * Generates a [RemoteTransitionProgress] usable to receive unfold transition progress from another
+ * process.
+ */
+@Singleton
+@Component(modules = [UnfoldRemoteModule::class])
+interface RemoteUnfoldSharedComponent {
+
+    @Component.Factory
+    interface Factory {
+        fun create(
+            @BindsInstance context: Context,
+            @BindsInstance config: UnfoldTransitionConfig,
+            @BindsInstance @UnfoldMain executor: Executor,
+            @BindsInstance @UnfoldSingleThreadBg singleThreadBgExecutor: Executor,
+            @BindsInstance windowManager: IWindowManager,
+            @BindsInstance @UnfoldTransitionATracePrefix tracingTagPrefix: String,
+        ): RemoteUnfoldSharedComponent
+    }
+
+    val remoteTransitionProgress: Optional<RemoteUnfoldTransitionReceiver>
+    val rotationChangeProvider: RotationChangeProvider
+}
+
+/**
+ * Usable to receive and propagate unfold transition progresses
+ *
+ * All unfold events received by [remoteReceiver] will be propagated to [localProvider].
+ * [remoteReceiver] is meant to receive events from a remote process (E.g. from a binder service).
+ */
+data class RemoteTransitionProgress(
+    val localProvider: UnfoldTransitionProgressProvider,
+    val remoteReceiver: UnfoldTransitionProgressProvider.TransitionProgressListener
+)
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 31616fa..5ffc094 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
@@ -19,6 +19,7 @@
 import com.android.systemui.unfold.config.UnfoldTransitionConfig
 import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider
 import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider
+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.hinge.EmptyHingeAngleProvider
@@ -102,4 +103,16 @@
             EmptyHingeAngleProvider
         }
     }
+
+    @Provides
+    @Singleton
+    fun provideProgressForwarder(
+            config: UnfoldTransitionConfig,
+            progressForwarder: Provider<UnfoldTransitionProgressForwarder>
+    ): Optional<UnfoldTransitionProgressForwarder> {
+        if (!config.isEnabled) {
+            return Optional.empty()
+        }
+        return Optional.of(progressForwarder.get())
+    }
 }
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
index aa93c629..8eb79df 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
@@ -63,3 +63,26 @@
                         tracingTagPrefix,
                         windowManager,
                 )
+
+/**
+ * Factory for [RemoteUnfoldSharedComponent].
+ *
+ * Wraps [DaggerRemoteUnfoldSharedComponent] (that is autogenerated), for better discoverability.
+ */
+fun createRemoteUnfoldSharedComponent(
+        context: Context,
+        config: UnfoldTransitionConfig,
+        mainExecutor: Executor,
+        singleThreadBgExecutor: Executor,
+        tracingTagPrefix: String,
+        windowManager: IWindowManager,
+        ): RemoteUnfoldSharedComponent =
+        DaggerRemoteUnfoldSharedComponent.factory()
+                .create(
+                        context,
+                        config,
+                        mainExecutor,
+                        singleThreadBgExecutor,
+                        windowManager,
+                        tracingTagPrefix,
+                )
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/IUnfoldAnimation.aidl b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/IUnfoldAnimation.aidl
new file mode 100644
index 0000000..07a1db4
--- /dev/null
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/IUnfoldAnimation.aidl
@@ -0,0 +1,34 @@
+/*
+ * 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.progress;
+
+
+import com.android.systemui.unfold.progress.IUnfoldTransitionListener;
+
+
+/**
+ * Interface exposed by System UI to allow remote process to register for unfold animation events.
+ */
+oneway interface IUnfoldAnimation {
+
+    /**
+     * Sets a listener for the animation.
+     *
+     * Only one listener is supported. If there are multiple, the earlier one will be overridden.
+     */
+    void setListener(in IUnfoldTransitionListener listener);
+}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/IUnfoldTransitionListener.aidl b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/IUnfoldTransitionListener.aidl
new file mode 100644
index 0000000..8f46b1b
--- /dev/null
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/IUnfoldTransitionListener.aidl
@@ -0,0 +1,38 @@
+/*
+ * 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.progress;
+
+
+/**
+ * Implemented by remote processes to receive unfold animation events from System UI.
+ */
+oneway interface IUnfoldTransitionListener {
+    /**
+    * Sent when unfold animation started.
+    */
+    void onTransitionStarted() = 1;
+
+    /**
+    * Sent when unfold animation progress changes.
+    */
+    void onTransitionProgress(float progress) = 2;
+
+    /**
+    * Sent when unfold animation finished.
+    */
+    void onTransitionFinished() = 3;
+}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/RemoteUnfoldTransitionReceiver.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/RemoteUnfoldTransitionReceiver.kt
new file mode 100644
index 0000000..5e4bcc9
--- /dev/null
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/RemoteUnfoldTransitionReceiver.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.progress
+
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.unfold.dagger.UnfoldMain
+import java.util.concurrent.Executor
+import javax.inject.Inject
+
+/**
+ * Receives unfold events from remote senders (System UI).
+ *
+ * A binder to an instance to this class (created with [RemoteUnfoldTransitionReceiver.asBinder])
+ * should be sent to the remote process providing events.
+ */
+class RemoteUnfoldTransitionReceiver
+@Inject
+constructor(@UnfoldMain private val executor: Executor) :
+    UnfoldTransitionProgressProvider, IUnfoldTransitionListener.Stub() {
+
+    private val listeners: MutableSet<TransitionProgressListener> = mutableSetOf()
+
+    override fun onTransitionStarted() {
+        executor.execute { listeners.forEach { it.onTransitionStarted() } }
+    }
+
+    override fun onTransitionProgress(progress: Float) {
+        executor.execute { listeners.forEach { it.onTransitionProgress(progress) } }
+    }
+
+    override fun onTransitionFinished() {
+        executor.execute { listeners.forEach { it.onTransitionFinished() } }
+    }
+
+    override fun addCallback(listener: TransitionProgressListener) {
+        listeners += listener
+    }
+
+    override fun removeCallback(listener: TransitionProgressListener) {
+        listeners -= listener
+    }
+
+    override fun destroy() {
+        listeners.clear()
+    }
+}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldTransitionProgressForwarder.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldTransitionProgressForwarder.kt
new file mode 100644
index 0000000..b654521
--- /dev/null
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldTransitionProgressForwarder.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.progress
+
+import android.os.RemoteException
+import android.util.Log
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import javax.inject.Inject
+
+/** Forwards received unfold events to [remoteListener], when present. */
+class UnfoldTransitionProgressForwarder @Inject constructor() :
+    TransitionProgressListener, IUnfoldAnimation.Stub() {
+
+    private var remoteListener: IUnfoldTransitionListener? = null
+
+    override fun onTransitionStarted() {
+        try {
+            Log.d(TAG, "onTransitionStarted")
+            remoteListener?.onTransitionStarted()
+        } catch (e: RemoteException) {
+            Log.e(TAG, "Failed call onTransitionStarted", e)
+        }
+    }
+
+    override fun onTransitionFinished() {
+        try {
+            Log.d(TAG, "onTransitionFinished")
+            remoteListener?.onTransitionFinished()
+        } catch (e: RemoteException) {
+            Log.e(TAG, "Failed call onTransitionFinished", e)
+        }
+    }
+
+    override fun onTransitionProgress(progress: Float) {
+        try {
+            remoteListener?.onTransitionProgress(progress)
+        } catch (e: RemoteException) {
+            Log.e(TAG, "Failed call onTransitionProgress", e)
+        }
+    }
+
+    override fun setListener(listener: IUnfoldTransitionListener?) {
+        remoteListener = listener
+    }
+
+    companion object {
+        private val TAG = UnfoldTransitionProgressForwarder::class.java.simpleName
+    }
+}
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 8c2c964..677871f 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -357,7 +357,6 @@
         params.width = WindowManager.LayoutParams.MATCH_PARENT;
         params.accessibilityTitle = context.getString(R.string.autofill_save_accessibility_title);
         params.windowAnimations = R.style.AutofillSaveAnimation;
-        params.setTrustedOverlay();
 
         show();
     }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5d4dc39..d78fe86 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -12764,8 +12764,10 @@
         // restored. This distinction is important for system-process packages that live in the
         // system user's process but backup/restore data for non-system users.
         // TODO (b/123688746): Handle all system-process packages with singleton check.
-        final int instantiatedUserId =
-                PLATFORM_PACKAGE_NAME.equals(packageName) ? UserHandle.USER_SYSTEM : targetUserId;
+        boolean useSystemUser = PLATFORM_PACKAGE_NAME.equals(packageName)
+                || getPackageManagerInternal().getSystemUiServiceComponent().getPackageName()
+                        .equals(packageName);
+        final int instantiatedUserId = useSystemUser ? UserHandle.USER_SYSTEM : targetUserId;
 
         IPackageManager pm = AppGlobals.getPackageManager();
         ApplicationInfo app = null;
@@ -14655,6 +14657,17 @@
                     throw new SecurityException(msg);
                 }
             }
+            if (!Build.IS_DEBUGGABLE && callingUid != ROOT_UID && callingUid != SHELL_UID
+                    && callingUid != SYSTEM_UID && !hasActiveInstrumentationLocked(callingPid)) {
+                // If it's not debug build and not called from root/shell/system uid, reject it.
+                final String msg = "Permission Denial: instrumentation test "
+                        + className + " from pid=" + callingPid + ", uid=" + callingUid
+                        + ", pkgName=" + getPackageNameByPid(callingPid)
+                        + " not allowed because it's not started from SHELL";
+                Slog.wtfQuiet(TAG, msg);
+                reportStartInstrumentationFailureLocked(watcher, className, msg);
+                throw new SecurityException(msg);
+            }
 
             boolean disableHiddenApiChecks = ai.usesNonSdkApi()
                     || (flags & INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0;
@@ -14877,6 +14890,29 @@
         }
     }
 
+    @GuardedBy("this")
+    private boolean hasActiveInstrumentationLocked(int pid) {
+        if (pid == 0) {
+            return false;
+        }
+        synchronized (mPidsSelfLocked) {
+            ProcessRecord process = mPidsSelfLocked.get(pid);
+            return process != null && process.getActiveInstrumentation() != null;
+        }
+    }
+
+    private String getPackageNameByPid(int pid) {
+        synchronized (mPidsSelfLocked) {
+            final ProcessRecord app = mPidsSelfLocked.get(pid);
+
+            if (app != null && app.info != null) {
+                return app.info.packageName;
+            }
+
+            return null;
+        }
+    }
+
     private boolean isCallerShell() {
         final int callingUid = Binder.getCallingUid();
         return callingUid == SHELL_UID || callingUid == ROOT_UID;
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 3584f16..ac78228 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -51,6 +51,7 @@
 import android.app.GameModeInfo;
 import android.app.GameState;
 import android.app.IGameManagerService;
+import android.app.IUidObserver;
 import android.app.compat.PackageOverride;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -118,6 +119,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Service to manage game related features.
@@ -171,40 +173,19 @@
     private final ArrayMap<String, GamePackageConfiguration> mOverrideConfigs = new ArrayMap<>();
     @Nullable
     private final GameServiceController mGameServiceController;
+    private final Object mUidObserverLock = new Object();
+    @VisibleForTesting
+    @Nullable
+    final UidObserver mUidObserver;
+    @GuardedBy("mUidObserverLock")
+    private final Set<Integer> mForegroundGameUids = new HashSet<>();
 
     public GameManagerService(Context context) {
         this(context, createServiceThread().getLooper());
     }
 
     GameManagerService(Context context, Looper looper) {
-        mContext = context;
-        mHandler = new SettingsHandler(looper);
-        mPackageManager = mContext.getPackageManager();
-        mUserManager = mContext.getSystemService(UserManager.class);
-        mPlatformCompat = IPlatformCompat.Stub.asInterface(
-                ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
-        mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
-        mSystemDir = new File(Environment.getDataDirectory(), "system");
-        mSystemDir.mkdirs();
-        FileUtils.setPermissions(mSystemDir.toString(),
-                FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IROTH | FileUtils.S_IXOTH,
-                -1, -1);
-        mGameModeInterventionListFile = new AtomicFile(new File(mSystemDir,
-                                                     GAME_MODE_INTERVENTION_LIST_FILE_NAME));
-        FileUtils.setPermissions(mGameModeInterventionListFile.getBaseFile().getAbsolutePath(),
-                FileUtils.S_IRUSR | FileUtils.S_IWUSR
-                        | FileUtils.S_IRGRP | FileUtils.S_IWGRP,
-                -1, -1);
-        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_GAME_SERVICE)) {
-            mGameServiceController = new GameServiceController(
-                    context, BackgroundThread.getExecutor(),
-                    new GameServiceProviderSelectorImpl(
-                            context.getResources(),
-                            context.getPackageManager()),
-                    new GameServiceProviderInstanceFactoryImpl(context));
-        } else {
-            mGameServiceController = null;
-        }
+        this(context, looper, Environment.getDataDirectory());
     }
 
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
@@ -237,6 +218,14 @@
         } else {
             mGameServiceController = null;
         }
+        mUidObserver = new UidObserver();
+        try {
+            ActivityManager.getService().registerUidObserver(mUidObserver,
+                    ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE,
+                    ActivityManager.PROCESS_STATE_UNKNOWN, null);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Could not register UidObserver");
+        }
     }
 
     @Override
@@ -1874,4 +1863,66 @@
      * load dynamic library for frame rate overriding JNI calls
      */
     private static native void nativeSetOverrideFrameRate(int uid, float frameRate);
+
+    final class UidObserver extends IUidObserver.Stub {
+        @Override
+        public void onUidIdle(int uid, boolean disabled) {}
+
+        @Override
+        public void onUidGone(int uid, boolean disabled) {
+            synchronized (mUidObserverLock) {
+                disableGameMode(uid);
+            }
+        }
+
+        @Override
+        public void onUidActive(int uid) {}
+
+        @Override
+        public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
+            synchronized (mUidObserverLock) {
+                if (ActivityManager.isProcStateBackground(procState)) {
+                    disableGameMode(uid);
+                    return;
+                }
+
+                final String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
+                if (packages == null || packages.length == 0) {
+                    return;
+                }
+
+                final int userId = mContext.getUserId();
+                if (!Arrays.stream(packages).anyMatch(p -> isPackageGame(p, userId))) {
+                    return;
+                }
+
+                if (mForegroundGameUids.isEmpty()) {
+                    Slog.v(TAG, "Game power mode ON (process state was changed to foreground)");
+                    mPowerManagerInternal.setPowerMode(Mode.GAME, true);
+                }
+                mForegroundGameUids.add(uid);
+            }
+        }
+
+        private void disableGameMode(int uid) {
+            synchronized (mUidObserverLock) {
+                if (!mForegroundGameUids.contains(uid)) {
+                    return;
+                }
+                mForegroundGameUids.remove(uid);
+                if (!mForegroundGameUids.isEmpty()) {
+                    return;
+                }
+                Slog.v(TAG,
+                        "Game power mode OFF (process remomved or state changed to background)");
+                mPowerManagerInternal.setPowerMode(Mode.GAME, false);
+            }
+        }
+
+        @Override
+        public void onUidCachedChanged(int uid, boolean cached) {}
+
+        @Override
+        public void onUidProcAdjChanged(int uid) {}
+    }
 }
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 278c98f..6410142 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -56,6 +56,7 @@
 import com.android.internal.annotations.GuardedBy;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.LinkedList;
@@ -434,6 +435,48 @@
         return device;
     }
 
+    private static final int[] VALID_COMMUNICATION_DEVICE_TYPES = {
+            AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
+            AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
+            AudioDeviceInfo.TYPE_WIRED_HEADSET,
+            AudioDeviceInfo.TYPE_USB_HEADSET,
+            AudioDeviceInfo.TYPE_BUILTIN_EARPIECE,
+            AudioDeviceInfo.TYPE_WIRED_HEADPHONES,
+            AudioDeviceInfo.TYPE_HEARING_AID,
+            AudioDeviceInfo.TYPE_BLE_HEADSET,
+            AudioDeviceInfo.TYPE_USB_DEVICE,
+            AudioDeviceInfo.TYPE_BLE_SPEAKER,
+            AudioDeviceInfo.TYPE_LINE_ANALOG,
+            AudioDeviceInfo.TYPE_HDMI,
+            AudioDeviceInfo.TYPE_AUX_LINE
+    };
+
+    /*package */ static boolean isValidCommunicationDevice(AudioDeviceInfo device) {
+        for (int type : VALID_COMMUNICATION_DEVICE_TYPES) {
+            if (device.getType() == type) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /* package */ static List<AudioDeviceInfo> getAvailableCommunicationDevices() {
+        ArrayList<AudioDeviceInfo> commDevices = new ArrayList<>();
+        AudioDeviceInfo[] allDevices =
+                AudioManager.getDevicesStatic(AudioManager.GET_DEVICES_OUTPUTS);
+        for (AudioDeviceInfo device : allDevices) {
+            if (isValidCommunicationDevice(device)) {
+                commDevices.add(device);
+            }
+        }
+        return commDevices;
+    }
+
+    private @Nullable AudioDeviceInfo getCommunicationDeviceOfType(int type) {
+        return getAvailableCommunicationDevices().stream().filter(d -> d.getType() == type)
+                .findFirst().orElse(null);
+    }
+
     /**
      * Returns the device currently requested for communication use case.
      * @return AudioDeviceInfo the requested device for communication.
@@ -441,7 +484,29 @@
     /* package */ AudioDeviceInfo getCommunicationDevice() {
         synchronized (mDeviceStateLock) {
             updateActiveCommunicationDevice();
-            return mActiveCommunicationDevice;
+            AudioDeviceInfo device = mActiveCommunicationDevice;
+            // make sure we return a valid communication device (i.e. a device that is allowed by
+            // setCommunicationDevice()) for consistency.
+            if (device != null) {
+                // a digital dock is used instead of the speaker in speakerphone mode and should
+                // be reflected as such
+                if (device.getType() == AudioDeviceInfo.TYPE_DOCK) {
+                    device = getCommunicationDeviceOfType(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
+                }
+            }
+            // Try to default to earpiece when current communication device is not valid. This can
+            // happen for instance if no call is active. If no earpiece device is available take the
+            // first valid communication device
+            if (device == null || !AudioDeviceBroker.isValidCommunicationDevice(device)) {
+                device = getCommunicationDeviceOfType(AudioDeviceInfo.TYPE_BUILTIN_EARPIECE);
+                if (device == null) {
+                    List<AudioDeviceInfo> commDevices = getAvailableCommunicationDevices();
+                    if (!commDevices.isEmpty()) {
+                        device = commDevices.get(0);
+                    }
+                }
+            }
+            return device;
         }
     }
 
@@ -918,8 +983,8 @@
 
     @GuardedBy("mDeviceStateLock")
     private void dispatchCommunicationDevice() {
-        int portId = (mActiveCommunicationDevice == null) ? 0
-                : mActiveCommunicationDevice.getId();
+        AudioDeviceInfo device = getCommunicationDevice();
+        int portId = device != null ? device.getId() : 0;
         if (portId == mCurCommunicationPortId) {
             return;
         }
@@ -936,6 +1001,7 @@
         mCommDevDispatchers.finishBroadcast();
     }
 
+
     //---------------------------------------------------------------------
     // Communication with (to) AudioService
     //TODO check whether the AudioService methods are candidates to move here
@@ -1152,6 +1218,10 @@
         sendILMsg(MSG_IL_BTA2DP_TIMEOUT, SENDMSG_QUEUE, a2dpCodec, address, delayMs);
     }
 
+    /*package*/ void setLeAudioTimeout(String address, int device, int delayMs) {
+        sendILMsg(MSG_IL_BTLEAUDIO_TIMEOUT, SENDMSG_QUEUE, device, address, delayMs);
+    }
+
     /*package*/ void setAvrcpAbsoluteVolumeSupported(boolean supported) {
         synchronized (mDeviceStateLock) {
             mBtHelper.setAvrcpAbsoluteVolumeSupported(supported);
@@ -1356,6 +1426,13 @@
                         mDeviceInventory.onMakeA2dpDeviceUnavailableNow((String) msg.obj, msg.arg1);
                     }
                     break;
+                case MSG_IL_BTLEAUDIO_TIMEOUT:
+                    // msg.obj  == address of LE Audio device
+                    synchronized (mDeviceStateLock) {
+                        mDeviceInventory.onMakeLeAudioDeviceUnavailableNow(
+                                (String) msg.obj, msg.arg1);
+                    }
+                    break;
                 case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
                     final BluetoothDevice btDevice = (BluetoothDevice) msg.obj;
                     synchronized (mDeviceStateLock) {
@@ -1446,6 +1523,7 @@
                 case MSG_I_BT_SERVICE_DISCONNECTED_PROFILE:
                     if (msg.arg1 != BluetoothProfile.HEADSET) {
                         synchronized (mDeviceStateLock) {
+                            mBtHelper.onBtProfileDisconnected(msg.arg1);
                             mDeviceInventory.onBtProfileDisconnected(msg.arg1);
                         }
                     } else {
@@ -1582,11 +1660,14 @@
     // process set volume for Le Audio, obj is BleVolumeInfo
     private static final int MSG_II_SET_LE_AUDIO_OUT_VOLUME = 46;
 
+    private static final int MSG_IL_BTLEAUDIO_TIMEOUT = 49;
+
     private static boolean isMessageHandledUnderWakelock(int msgId) {
         switch(msgId) {
             case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
             case MSG_L_SET_BT_ACTIVE_DEVICE:
             case MSG_IL_BTA2DP_TIMEOUT:
+            case MSG_IL_BTLEAUDIO_TIMEOUT:
             case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
             case MSG_TOGGLE_HDMI:
             case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT:
@@ -1677,6 +1758,7 @@
                 case MSG_L_SET_BT_ACTIVE_DEVICE:
                 case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
                 case MSG_IL_BTA2DP_TIMEOUT:
+                case MSG_IL_BTLEAUDIO_TIMEOUT:
                 case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
                     if (sLastDeviceConnectMsgTime >= time) {
                         // add a little delay to make sure messages are ordered as expected
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 35da73e..a74f4154 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -374,7 +374,7 @@
                 case BluetoothProfile.LE_AUDIO:
                 case BluetoothProfile.LE_AUDIO_BROADCAST:
                     if (switchToUnavailable) {
-                        makeLeAudioDeviceUnavailable(address, btInfo.mAudioSystemDevice);
+                        makeLeAudioDeviceUnavailableNow(address, btInfo.mAudioSystemDevice);
                     } else if (switchToAvailable) {
                         makeLeAudioDeviceAvailable(address, BtHelper.getName(btInfo.mDevice),
                                 streamType, btInfo.mVolume == -1 ? -1 : btInfo.mVolume * 10,
@@ -486,6 +486,12 @@
         }
     }
 
+    /*package*/ void onMakeLeAudioDeviceUnavailableNow(String address, int device) {
+        synchronized (mDevicesLock) {
+            makeLeAudioDeviceUnavailableNow(address, device);
+        }
+    }
+
     /*package*/ void onReportNewRoutes() {
         int n = mRoutesObservers.beginBroadcast();
         if (n > 0) {
@@ -883,10 +889,11 @@
             new MediaMetrics.Item(mMetricsId + "disconnectLeAudio")
                     .record();
             if (toRemove.size() > 0) {
-                final int delay = checkSendBecomingNoisyIntentInt(device, 0,
+                final int delay = checkSendBecomingNoisyIntentInt(device,
+                        AudioService.CONNECTION_STATE_DISCONNECTED,
                         AudioSystem.DEVICE_NONE);
                 toRemove.stream().forEach(deviceAddress ->
-                        makeLeAudioDeviceUnavailable(deviceAddress, device)
+                        makeLeAudioDeviceUnavailableLater(deviceAddress, device, delay)
                 );
             }
         }
@@ -1187,9 +1194,21 @@
              */
             mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, eventSource);
 
-            AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(device, address, name),
+            final int res = AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
+                    device, address, name),
                     AudioSystem.DEVICE_STATE_AVAILABLE,
                     AudioSystem.AUDIO_FORMAT_DEFAULT);
+            if (res != AudioSystem.AUDIO_STATUS_OK) {
+                AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
+                        "APM failed to make available LE Audio device addr=" + address
+                                + " error=" + res).printLog(TAG));
+                // TODO: connection failed, stop here
+                // TODO: return;
+            } else {
+                AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
+                        "LE Audio device addr=" + address + " now available").printLog(TAG));
+            }
+
             mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address),
                     new DeviceInfo(device, name, address, AudioSystem.AUDIO_FORMAT_DEFAULT));
             mDeviceBroker.postAccessoryPlugMediaUnmute(device);
@@ -1210,11 +1229,23 @@
     }
 
     @GuardedBy("mDevicesLock")
-    private void makeLeAudioDeviceUnavailable(String address, int device) {
+    private void makeLeAudioDeviceUnavailableNow(String address, int device) {
         if (device != AudioSystem.DEVICE_NONE) {
-            AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(device, address),
+            final int res = AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
+                    device, address),
                     AudioSystem.DEVICE_STATE_UNAVAILABLE,
                     AudioSystem.AUDIO_FORMAT_DEFAULT);
+
+            if (res != AudioSystem.AUDIO_STATUS_OK) {
+                AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
+                        "APM failed to make unavailable LE Audio device addr=" + address
+                                + " error=" + res).printLog(TAG));
+                // TODO:  failed to disconnect, stop here
+                // TODO: return;
+            } else {
+                AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
+                        "LE Audio device addr=" + address + " made unavailable")).printLog(TAG));
+            }
             mConnectedDevices.remove(DeviceInfo.makeDeviceListKey(device, address));
         }
 
@@ -1222,6 +1253,14 @@
     }
 
     @GuardedBy("mDevicesLock")
+    private void makeLeAudioDeviceUnavailableLater(String address, int device, int delayMs) {
+        // the device will be made unavailable later, so consider it disconnected right away
+        mConnectedDevices.remove(DeviceInfo.makeDeviceListKey(device, address));
+        // send the delayed message to make the device unavailable later
+        mDeviceBroker.setLeAudioTimeout(address, device, delayMs);
+    }
+
+    @GuardedBy("mDevicesLock")
     private void setCurrentAudioRouteNameIfPossible(String name, boolean fromA2dp) {
         synchronized (mCurAudioRoutes) {
             if (TextUtils.equals(mCurAudioRoutes.bluetoothName, name)) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 1bd8f1e..9f15128 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1253,6 +1253,20 @@
                 0 /* arg1 */, 0 /* arg2 */, null /* obj */, 0 /* delay */);
     }
 
+    private void initVolumeStreamStates() {
+        int numStreamTypes = AudioSystem.getNumStreamTypes();
+        synchronized (VolumeStreamState.class) {
+            for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
+                VolumeStreamState streamState = mStreamStates[streamType];
+                int groupId = getVolumeGroupForStreamType(streamType);
+                if (groupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP
+                        && sVolumeGroupStates.indexOfKey(groupId) >= 0) {
+                    streamState.setVolumeGroupState(sVolumeGroupStates.get(groupId));
+                }
+            }
+        }
+    }
+
     /**
      * Separating notification volume from ring is NOT of aliasing the corresponding streams
      * @param properties
@@ -1282,6 +1296,8 @@
         // mSafeUsbMediaVolumeIndex must be initialized after createStreamStates() because it
         // relies on audio policy having correct ranges for volume indexes.
         mSafeUsbMediaVolumeIndex = getSafeUsbMediaVolumeIndex();
+        // Link VGS on VSS
+        initVolumeStreamStates();
 
         // Call setRingerModeInt() to apply correct mute
         // state on streams affected by ringer mode.
@@ -2178,19 +2194,19 @@
                 AudioSystem.STREAM_ASSISTANT : AudioSystem.STREAM_MUSIC;
 
         if (mIsSingleVolume) {
-            mStreamVolumeAlias = STREAM_VOLUME_ALIAS_TELEVISION;
+            mStreamVolumeAlias = STREAM_VOLUME_ALIAS_TELEVISION.clone();
             dtmfStreamAlias = AudioSystem.STREAM_MUSIC;
         } else if (mUseVolumeGroupAliases) {
-            mStreamVolumeAlias = STREAM_VOLUME_ALIAS_NONE;
+            mStreamVolumeAlias = STREAM_VOLUME_ALIAS_NONE.clone();
             dtmfStreamAlias = AudioSystem.STREAM_DTMF;
         } else {
             switch (mPlatformType) {
                 case AudioSystem.PLATFORM_VOICE:
-                    mStreamVolumeAlias = STREAM_VOLUME_ALIAS_VOICE;
+                    mStreamVolumeAlias = STREAM_VOLUME_ALIAS_VOICE.clone();
                     dtmfStreamAlias = AudioSystem.STREAM_RING;
                     break;
                 default:
-                    mStreamVolumeAlias = STREAM_VOLUME_ALIAS_DEFAULT;
+                    mStreamVolumeAlias = STREAM_VOLUME_ALIAS_DEFAULT.clone();
                     dtmfStreamAlias = AudioSystem.STREAM_MUSIC;
             }
             if (!mNotifAliasRing) {
@@ -3340,15 +3356,7 @@
                 } else {
                     state = direction == AudioManager.ADJUST_MUTE;
                 }
-                for (int stream = 0; stream < mStreamStates.length; stream++) {
-                    if (streamTypeAlias == mStreamVolumeAlias[stream]) {
-                        if (!(readCameraSoundForced()
-                                    && (mStreamStates[stream].getStreamType()
-                                        == AudioSystem.STREAM_SYSTEM_ENFORCED))) {
-                            mStreamStates[stream].mute(state);
-                        }
-                    }
-                }
+                muteAliasStreams(streamTypeAlias, state);
             } else if ((direction == AudioManager.ADJUST_RAISE) &&
                     !checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) {
                 Log.e(TAG, "adjustStreamVolume() safe volume index = " + oldIndex);
@@ -3363,7 +3371,7 @@
                     // Unmute the stream if it was previously muted
                     if (direction == AudioManager.ADJUST_RAISE) {
                         // unmute immediately for volume up
-                        streamState.mute(false);
+                        muteAliasStreams(streamTypeAlias, false);
                     } else if (direction == AudioManager.ADJUST_LOWER) {
                         if (mIsSingleVolume) {
                             sendMsg(mAudioHandler, MSG_UNMUTE_STREAM, SENDMSG_QUEUE,
@@ -3489,6 +3497,43 @@
         sendVolumeUpdate(streamType, oldIndex, newIndex, flags, device);
     }
 
+    /**
+     * Loops on aliasted stream, update the mute cache attribute of each
+     * {@see AudioService#VolumeStreamState}, and then apply the change.
+     * It prevents to unnecessary {@see AudioSystem#setStreamVolume} done for each stream
+     * and aliases before mute change changed and after.
+     */
+    private void muteAliasStreams(int streamAlias, boolean state) {
+        synchronized (VolumeStreamState.class) {
+            List<Integer> streamsToMute = new ArrayList<>();
+            for (int stream = 0; stream < mStreamStates.length; stream++) {
+                VolumeStreamState vss = mStreamStates[stream];
+                if (streamAlias == mStreamVolumeAlias[stream] && vss.isMutable()) {
+                    if (!(readCameraSoundForced()
+                            && (vss.getStreamType()
+                                    == AudioSystem.STREAM_SYSTEM_ENFORCED))) {
+                        boolean changed = vss.mute(state, /* apply= */ false);
+                        if (changed) {
+                            streamsToMute.add(stream);
+                        }
+                    }
+                }
+            }
+            streamsToMute.forEach(streamToMute -> {
+                mStreamStates[streamToMute].doMute();
+                broadcastMuteSetting(streamToMute, state);
+            });
+        }
+    }
+
+    private void broadcastMuteSetting(int streamType, boolean isMuted) {
+        // Stream mute changed, fire the intent.
+        Intent intent = new Intent(AudioManager.STREAM_MUTE_CHANGED_ACTION);
+        intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
+        intent.putExtra(AudioManager.EXTRA_STREAM_VOLUME_MUTED, isMuted);
+        sendBroadcastToAll(intent);
+    }
+
     // Called after a delay when volume down is pressed while muted
     private void onUnmuteStream(int stream, int flags) {
         boolean wasMuted;
@@ -3593,8 +3638,19 @@
         return false;
     }
 
+    /**
+     * Update stream volume, ringer mode and mute status after a volume index change
+     * @param streamType
+     * @param index
+     * @param flags
+     * @param device the device for which the volume is changed
+     * @param caller
+     * @param hasModifyAudioSettings
+     * @param canChangeMute true if the origin of this event is one where the mute state should be
+     *                      updated following the change in volume index
+     */
     private void onSetStreamVolume(int streamType, int index, int flags, int device,
-            String caller, boolean hasModifyAudioSettings) {
+            String caller, boolean hasModifyAudioSettings, boolean canChangeMute) {
         final int stream = mStreamVolumeAlias[streamType];
         setStreamVolumeInt(stream, index, device, false, caller, hasModifyAudioSettings);
         // setting volume on ui sounds stream type also controls silent mode
@@ -3604,11 +3660,10 @@
                     TAG + ".onSetStreamVolume", false /*external*/);
         }
         // setting non-zero volume for a muted stream unmutes the stream and vice versa
-        // (only when changing volume for the current device),
         // except for BT SCO stream where only explicit mute is allowed to comply to BT requirements
-        if ((streamType != AudioSystem.STREAM_BLUETOOTH_SCO)
-                && (getDeviceForStream(stream) == device)) {
-            mStreamStates[stream].mute(index == 0);
+        if ((streamType != AudioSystem.STREAM_BLUETOOTH_SCO) && canChangeMute) {
+            // As adjustStreamVolume with muteAdjust flags mute/unmutes stream and aliased streams.
+            muteAliasStreams(stream, index == 0);
         }
     }
 
@@ -3664,29 +3719,27 @@
     }
 
 
-    /** @see AudioManager#setVolumeIndexForAttributes(attr, int, int) */
-    public void setVolumeIndexForAttributes(@NonNull AudioAttributes attr, int index, int flags,
+    /** @see AudioManager#setVolumeGroupVolumeIndex(int, int, int) */
+    public void setVolumeGroupVolumeIndex(int groupId, int index, int flags,
             String callingPackage, String attributionTag) {
         enforceModifyAudioRoutingPermission();
-        Objects.requireNonNull(attr, "attr must not be null");
-        final int volumeGroup = getVolumeGroupIdForAttributes(attr);
-        if (sVolumeGroupStates.indexOfKey(volumeGroup) < 0) {
-            Log.e(TAG, ": no volume group found for attributes " + attr.toString());
+        if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
+            Log.e(TAG, ": no volume group found for id " + groupId);
             return;
         }
-        final VolumeGroupState vgs = sVolumeGroupStates.get(volumeGroup);
+        VolumeGroupState vgs = sVolumeGroupStates.get(groupId);
 
-        sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_SET_GROUP_VOL, attr, vgs.name(),
-                index/*val1*/, flags/*val2*/, callingPackage));
+        sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_SET_GROUP_VOL, vgs.name(),
+                index, flags, callingPackage + ", user " + getCurrentUserId()));
 
         vgs.setVolumeIndex(index, flags);
 
         // For legacy reason, propagate to all streams associated to this volume group
-        for (final int groupedStream : vgs.getLegacyStreamTypes()) {
+        for (int groupedStream : vgs.getLegacyStreamTypes()) {
             try {
                 ensureValidStreamType(groupedStream);
             } catch (IllegalArgumentException e) {
-                Log.d(TAG, "volume group " + volumeGroup + " has internal streams (" + groupedStream
+                Log.d(TAG, "volume group " + groupId + " has internal streams (" + groupedStream
                         + "), do not change associated stream volume");
                 continue;
             }
@@ -3698,7 +3751,7 @@
 
     @Nullable
     private AudioVolumeGroup getAudioVolumeGroupById(int volumeGroupId) {
-        for (final AudioVolumeGroup avg : AudioVolumeGroup.getAudioVolumeGroups()) {
+        for (AudioVolumeGroup avg : AudioVolumeGroup.getAudioVolumeGroups()) {
             if (avg.getId() == volumeGroupId) {
                 return avg;
             }
@@ -3708,30 +3761,42 @@
         return null;
     }
 
-    /** @see AudioManager#getVolumeIndexForAttributes(attr) */
-    public int getVolumeIndexForAttributes(@NonNull AudioAttributes attr) {
+    /** @see AudioManager#getVolumeGroupVolumeIndex(int) */
+    public int getVolumeGroupVolumeIndex(int groupId) {
         enforceModifyAudioRoutingPermission();
-        Objects.requireNonNull(attr, "attr must not be null");
-        final int volumeGroup = getVolumeGroupIdForAttributes(attr);
-        if (sVolumeGroupStates.indexOfKey(volumeGroup) < 0) {
-            throw new IllegalArgumentException("No volume group for attributes " + attr);
+        synchronized (VolumeStreamState.class) {
+            if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
+                throw new IllegalArgumentException("No volume group for id " + groupId);
+            }
+            VolumeGroupState vgs = sVolumeGroupStates.get(groupId);
+            // Return 0 when muted, not min index since for e.g. Voice Call, it has a non zero
+            // min but it mutable on permission condition.
+            return vgs.isMuted() ? 0 : vgs.getVolumeIndex();
         }
-        final VolumeGroupState vgs = sVolumeGroupStates.get(volumeGroup);
-        return vgs.getVolumeIndex();
     }
 
-    /** @see AudioManager#getMaxVolumeIndexForAttributes(attr) */
-    public int getMaxVolumeIndexForAttributes(@NonNull AudioAttributes attr) {
+    /** @see AudioManager#getVolumeGroupMaxVolumeIndex(int) */
+    public int getVolumeGroupMaxVolumeIndex(int groupId) {
         enforceModifyAudioRoutingPermission();
-        Objects.requireNonNull(attr, "attr must not be null");
-        return AudioSystem.getMaxVolumeIndexForAttributes(attr);
+        synchronized (VolumeStreamState.class) {
+            if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
+                throw new IllegalArgumentException("No volume group for id " + groupId);
+            }
+            VolumeGroupState vgs = sVolumeGroupStates.get(groupId);
+            return vgs.getMaxIndex();
+        }
     }
 
-    /** @see AudioManager#getMinVolumeIndexForAttributes(attr) */
-    public int getMinVolumeIndexForAttributes(@NonNull AudioAttributes attr) {
+    /** @see AudioManager#getVolumeGroupMinVolumeIndex(int) */
+    public int getVolumeGroupMinVolumeIndex(int groupId) {
         enforceModifyAudioRoutingPermission();
-        Objects.requireNonNull(attr, "attr must not be null");
-        return AudioSystem.getMinVolumeIndexForAttributes(attr);
+        synchronized (VolumeStreamState.class) {
+            if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
+                throw new IllegalArgumentException("No volume group for id " + groupId);
+            }
+            VolumeGroupState vgs = sVolumeGroupStates.get(groupId);
+            return vgs.getMinIndex();
+        }
     }
 
     /** @see AudioDeviceVolumeManager#setDeviceVolume(VolumeInfo, AudioDeviceAttributes)
@@ -3766,8 +3831,15 @@
         // VOLUME_CHANGED_ACTION intent to see if the current device is the one being modified
         final int currDev = getDeviceForStream(vi.getStreamType());
 
+        final boolean skipping = (currDev == ada.getInternalType());
+
         AudioService.sVolumeLogger.log(new DeviceVolumeEvent(vi.getStreamType(), index, ada,
-                currDev, callingPackage));
+                currDev, callingPackage, skipping));
+
+        if (skipping) {
+            // setDeviceVolume was called on a device currently being used
+            return;
+        }
 
         // TODO handle unmuting of current audio device
         // if a stream is not muted but the VolumeInfo is for muting, set the volume index
@@ -3808,6 +3880,70 @@
                 callingPackage, /*attributionTag*/ null);
     }
 
+    /** @see AudioManager#adjustVolumeGroupVolume(int, int, int) */
+    public void adjustVolumeGroupVolume(int groupId, int direction, int flags,
+                                        String callingPackage) {
+        ensureValidDirection(direction);
+        if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
+            Log.e(TAG, ": no volume group found for id " + groupId);
+            return;
+        }
+        VolumeGroupState vgs = sVolumeGroupStates.get(groupId);
+        // For compatibility reason, use stream API if group linked to a valid stream
+        boolean fallbackOnStream = false;
+        for (int stream : vgs.getLegacyStreamTypes()) {
+            try {
+                ensureValidStreamType(stream);
+            } catch (IllegalArgumentException e) {
+                Log.d(TAG, "volume group " + groupId + " has internal streams (" + stream
+                        + "), do not change associated stream volume");
+                continue;
+            }
+            // Note: Group and Stream does not share same convention, 0 is mute for stream,
+            // min index is acting as mute for Groups
+            if (vgs.isVssMuteBijective(stream)) {
+                adjustStreamVolume(stream, direction, flags, callingPackage);
+                if (isMuteAdjust(direction)) {
+                    // will be propagated to all aliased streams
+                    return;
+                }
+                fallbackOnStream = true;
+            }
+        }
+        if (fallbackOnStream) {
+            // Handled by at least one stream, will be propagated to group, bailing out.
+            return;
+        }
+        sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_GROUP_VOL, vgs.name(),
+                direction, flags, callingPackage));
+        vgs.adjustVolume(direction, flags);
+    }
+
+    /** @see AudioManager#getLastAudibleVolumeGroupVolume(int) */
+    public int getLastAudibleVolumeGroupVolume(int groupId) {
+        enforceQueryStatePermission();
+        synchronized (VolumeStreamState.class) {
+            if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
+                Log.e(TAG, ": no volume group found for id " + groupId);
+                return 0;
+            }
+            VolumeGroupState vgs = sVolumeGroupStates.get(groupId);
+            return vgs.getVolumeIndex();
+        }
+    }
+
+    /** @see AudioManager#isVolumeGroupMuted(int) */
+    public boolean isVolumeGroupMuted(int groupId) {
+        synchronized (VolumeStreamState.class) {
+            if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
+                Log.e(TAG, ": no volume group found for id " + groupId);
+                return false;
+            }
+            VolumeGroupState vgs = sVolumeGroupStates.get(groupId);
+            return vgs.isMuted();
+        }
+    }
+
     /** @see AudioManager#setStreamVolume(int, int, int)
      * Part of service interface, check permissions here */
     public void setStreamVolumeWithAttribution(int streamType, int index, int flags,
@@ -4247,7 +4383,10 @@
                 mPendingVolumeCommand = new StreamVolumeCommand(
                                                     streamType, index, flags, device);
             } else {
-                onSetStreamVolume(streamType, index, flags, device, caller, hasModifyAudioSettings);
+                onSetStreamVolume(streamType, index, flags, device, caller, hasModifyAudioSettings,
+                        // ada is non-null when called from setDeviceVolume,
+                        // which shouldn't update the mute state
+                        ada == null /*canChangeMute*/);
                 index = mStreamStates[streamType].getIndex(device);
             }
         }
@@ -4264,31 +4403,6 @@
         }
     }
 
-
-
-    private int getVolumeGroupIdForAttributes(@NonNull AudioAttributes attributes) {
-        Objects.requireNonNull(attributes, "attributes must not be null");
-        int volumeGroupId = getVolumeGroupIdForAttributesInt(attributes);
-        if (volumeGroupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP) {
-            return volumeGroupId;
-        }
-        // The default volume group is the one hosted by default product strategy, i.e.
-        // supporting Default Attributes
-        return getVolumeGroupIdForAttributesInt(AudioProductStrategy.getDefaultAttributes());
-    }
-
-    private int getVolumeGroupIdForAttributesInt(@NonNull AudioAttributes attributes) {
-        Objects.requireNonNull(attributes, "attributes must not be null");
-        for (final AudioProductStrategy productStrategy :
-                AudioProductStrategy.getAudioProductStrategies()) {
-            int volumeGroupId = productStrategy.getVolumeGroupIdForAudioAttributes(attributes);
-            if (volumeGroupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP) {
-                return volumeGroupId;
-            }
-        }
-        return AudioVolumeGroup.DEFAULT_VOLUME_GROUP;
-    }
-
     private void dispatchAbsoluteVolumeChanged(int streamType, AbsoluteVolumeDeviceInfo deviceInfo,
             int index) {
         VolumeInfo volumeInfo = deviceInfo.getMatchingVolumeInfoForStream(streamType);
@@ -4321,7 +4435,6 @@
         }
     }
 
-
     // No ringer or zen muted stream volumes can be changed unless it'll exit dnd
     private boolean volumeAdjustmentAllowedByDnd(int streamTypeAlias, int flags) {
         switch (mNm.getZenMode()) {
@@ -5013,7 +5126,7 @@
     }
 
     private void setRingerMode(int ringerMode, String caller, boolean external) {
-        if (mUseFixedVolume || mIsSingleVolume) {
+        if (mUseFixedVolume || mIsSingleVolume || mUseVolumeGroupAliases) {
             return;
         }
         if (caller == null || caller.length() == 0) {
@@ -5096,7 +5209,8 @@
             if (!shouldMute) {
                 // unmute
                 // ring and notifications volume should never be 0 when not silenced
-                if (mStreamVolumeAlias[streamType] == AudioSystem.STREAM_RING) {
+                if (mStreamVolumeAlias[streamType] == AudioSystem.STREAM_RING
+                        || mStreamVolumeAlias[streamType] == AudioSystem.STREAM_NOTIFICATION) {
                     synchronized (VolumeStreamState.class) {
                         final VolumeStreamState vss = mStreamStates[streamType];
                         for (int i = 0; i < vss.mIndexMap.size(); i++) {
@@ -5821,6 +5935,8 @@
             }
         }
 
+        readVolumeGroupsSettings(userSwitch);
+
         // apply new ringer mode before checking volume for alias streams so that streams
         // muted by ringer mode have the correct volume
         setRingerModeInt(getRingerModeInternal(), false);
@@ -5838,54 +5954,22 @@
             }
         }
 
-        readVolumeGroupsSettings();
-
         if (DEBUG_VOL) {
             Log.d(TAG, "Restoring device volume behavior");
         }
         restoreDeviceVolumeBehavior();
     }
 
-    private static final int[] VALID_COMMUNICATION_DEVICE_TYPES = {
-        AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
-        AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
-        AudioDeviceInfo.TYPE_WIRED_HEADSET,
-        AudioDeviceInfo.TYPE_USB_HEADSET,
-        AudioDeviceInfo.TYPE_BUILTIN_EARPIECE,
-        AudioDeviceInfo.TYPE_WIRED_HEADPHONES,
-        AudioDeviceInfo.TYPE_HEARING_AID,
-        AudioDeviceInfo.TYPE_BLE_HEADSET,
-        AudioDeviceInfo.TYPE_USB_DEVICE,
-        AudioDeviceInfo.TYPE_BLE_SPEAKER,
-        AudioDeviceInfo.TYPE_LINE_ANALOG,
-        AudioDeviceInfo.TYPE_HDMI,
-        AudioDeviceInfo.TYPE_AUX_LINE
-    };
-
-    private boolean isValidCommunicationDevice(AudioDeviceInfo device) {
-        for (int type : VALID_COMMUNICATION_DEVICE_TYPES) {
-            if (device.getType() == type) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     /** @see AudioManager#getAvailableCommunicationDevices(int) */
     public int[] getAvailableCommunicationDeviceIds() {
-        ArrayList<Integer> deviceIds = new ArrayList<>();
-        AudioDeviceInfo[] devices = AudioManager.getDevicesStatic(AudioManager.GET_DEVICES_OUTPUTS);
-        for (AudioDeviceInfo device : devices) {
-            if (isValidCommunicationDevice(device)) {
-                deviceIds.add(device.getId());
-            }
-        }
-        return deviceIds.stream().mapToInt(Integer::intValue).toArray();
+        List<AudioDeviceInfo> commDevices = AudioDeviceBroker.getAvailableCommunicationDevices();
+        return commDevices.stream().mapToInt(AudioDeviceInfo::getId).toArray();
     }
-        /**
-         * @see AudioManager#setCommunicationDevice(int)
-         * @see AudioManager#clearCommunicationDevice()
-         */
+
+    /**
+     * @see AudioManager#setCommunicationDevice(int)
+     * @see AudioManager#clearCommunicationDevice()
+     */
     public boolean setCommunicationDevice(IBinder cb, int portId) {
         final int uid = Binder.getCallingUid();
         final int pid = Binder.getCallingPid();
@@ -5894,9 +5978,10 @@
         if (portId != 0) {
             device = AudioManager.getDeviceForPortId(portId, AudioManager.GET_DEVICES_OUTPUTS);
             if (device == null) {
-                throw new IllegalArgumentException("invalid portID " + portId);
+                Log.w(TAG, "setCommunicationDevice: invalid portID " + portId);
+                return false;
             }
-            if (!isValidCommunicationDevice(device)) {
+            if (!AudioDeviceBroker.isValidCommunicationDevice(device)) {
                 throw new IllegalArgumentException("invalid device type " + device.getType());
             }
         }
@@ -5939,13 +6024,15 @@
 
     /** @see AudioManager#getCommunicationDevice() */
     public int getCommunicationDevice() {
+        int deviceId = 0;
         final long ident = Binder.clearCallingIdentity();
-        AudioDeviceInfo device = mDeviceBroker.getCommunicationDevice();
-        Binder.restoreCallingIdentity(ident);
-        if (device == null) {
-            return 0;
+        try {
+            AudioDeviceInfo device = mDeviceBroker.getCommunicationDevice();
+            deviceId = device != null ? device.getId() : 0;
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
-        return device.getId();
+        return deviceId;
     }
 
     /** @see AudioManager#addOnCommunicationDeviceChangedListener(
@@ -7313,6 +7400,7 @@
             try {
                 // if no valid attributes, this volume group is not controllable, throw exception
                 ensureValidAttributes(avg);
+                sVolumeGroupStates.append(avg.getId(), new VolumeGroupState(avg));
             } catch (IllegalArgumentException e) {
                 // Volume Groups without attributes are not controllable through set/get volume
                 // using attributes. Do not append them.
@@ -7321,11 +7409,10 @@
                 }
                 continue;
             }
-            sVolumeGroupStates.append(avg.getId(), new VolumeGroupState(avg));
         }
         for (int i = 0; i < sVolumeGroupStates.size(); i++) {
             final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i);
-            vgs.applyAllVolumes();
+            vgs.applyAllVolumes(/* userSwitch= */ false);
         }
     }
 
@@ -7338,14 +7425,22 @@
         }
     }
 
-    private void readVolumeGroupsSettings() {
-        if (DEBUG_VOL) {
-            Log.v(TAG, "readVolumeGroupsSettings");
-        }
-        for (int i = 0; i < sVolumeGroupStates.size(); i++) {
-            final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i);
-            vgs.readSettings();
-            vgs.applyAllVolumes();
+    private void readVolumeGroupsSettings(boolean userSwitch) {
+        synchronized (mSettingsLock) {
+            synchronized (VolumeStreamState.class) {
+                if (DEBUG_VOL) {
+                    Log.d(TAG, "readVolumeGroupsSettings userSwitch=" + userSwitch);
+                }
+                for (int i = 0; i < sVolumeGroupStates.size(); i++) {
+                    VolumeGroupState vgs = sVolumeGroupStates.valueAt(i);
+                    // as for STREAM_MUSIC, preserve volume from one user to the next.
+                    if (!(userSwitch && vgs.isMusic())) {
+                        vgs.clearIndexCache();
+                        vgs.readSettings();
+                    }
+                    vgs.applyAllVolumes(userSwitch);
+                }
+            }
         }
     }
 
@@ -7356,7 +7451,7 @@
         }
         for (int i = 0; i < sVolumeGroupStates.size(); i++) {
             final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i);
-            vgs.applyAllVolumes();
+            vgs.applyAllVolumes(false/*userSwitch*/);
         }
     }
 
@@ -7369,17 +7464,34 @@
         }
     }
 
+    private static boolean isCallStream(int stream) {
+        return stream == AudioSystem.STREAM_VOICE_CALL
+                || stream == AudioSystem.STREAM_BLUETOOTH_SCO;
+    }
+
+    private static int getVolumeGroupForStreamType(int stream) {
+        AudioAttributes attributes =
+                AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType(stream);
+        if (attributes.equals(new AudioAttributes.Builder().build())) {
+            return AudioVolumeGroup.DEFAULT_VOLUME_GROUP;
+        }
+        return AudioProductStrategy.getVolumeGroupIdForAudioAttributes(
+                attributes, /* fallbackOnDefault= */ false);
+    }
+
     // NOTE: Locking order for synchronized objects related to volume management:
     //  1     mSettingsLock
-    //  2       VolumeGroupState.class
+    //  2       VolumeStreamState.class
     private class VolumeGroupState {
         private final AudioVolumeGroup mAudioVolumeGroup;
         private final SparseIntArray mIndexMap = new SparseIntArray(8);
         private int mIndexMin;
         private int mIndexMax;
-        private int mLegacyStreamType = AudioSystem.STREAM_DEFAULT;
+        private boolean mHasValidStreamType = false;
         private int mPublicStreamType = AudioSystem.STREAM_MUSIC;
         private AudioAttributes mAudioAttributes = AudioProductStrategy.getDefaultAttributes();
+        private boolean mIsMuted = false;
+        private final String mSettingName;
 
         // No API in AudioSystem to get a device from strategy or from attributes.
         // Need a valid public stream type to use current API getDeviceForStream
@@ -7393,20 +7505,22 @@
                 Log.v(TAG, "VolumeGroupState for " + avg.toString());
             }
             // mAudioAttributes is the default at this point
-            for (final AudioAttributes aa : avg.getAudioAttributes()) {
+            for (AudioAttributes aa : avg.getAudioAttributes()) {
                 if (!aa.equals(mAudioAttributes)) {
                     mAudioAttributes = aa;
                     break;
                 }
             }
-            final int[] streamTypes = mAudioVolumeGroup.getLegacyStreamTypes();
+            int[] streamTypes = mAudioVolumeGroup.getLegacyStreamTypes();
+            String streamSettingName = "";
             if (streamTypes.length != 0) {
                 // Uses already initialized MIN / MAX if a stream type is attached to group
-                mLegacyStreamType = streamTypes[0];
-                for (final int streamType : streamTypes) {
+                for (int streamType : streamTypes) {
                     if (streamType != AudioSystem.STREAM_DEFAULT
                             && streamType < AudioSystem.getNumStreamTypes()) {
                         mPublicStreamType = streamType;
+                        mHasValidStreamType = true;
+                        streamSettingName = System.VOLUME_SETTINGS_INT[mPublicStreamType];
                         break;
                     }
                 }
@@ -7416,10 +7530,10 @@
                 mIndexMin = AudioSystem.getMinVolumeIndexForAttributes(mAudioAttributes);
                 mIndexMax = AudioSystem.getMaxVolumeIndexForAttributes(mAudioAttributes);
             } else {
-                Log.e(TAG, "volume group: " + mAudioVolumeGroup.name()
+                throw new IllegalArgumentException("volume group: " + mAudioVolumeGroup.name()
                         + " has neither valid attributes nor valid stream types assigned");
-                return;
             }
+            mSettingName = !streamSettingName.isEmpty() ? streamSettingName : ("volume_" + name());
             // Load volume indexes from data base
             readSettings();
         }
@@ -7432,40 +7546,149 @@
             return mAudioVolumeGroup.name();
         }
 
+        /**
+         * Volume group with non null minimum index are considered as non mutable, thus
+         * bijectivity is broken with potential associated stream type.
+         * VOICE_CALL stream has minVolumeIndex > 0  but can be muted directly by an
+         * app that has MODIFY_PHONE_STATE permission.
+         */
+        private boolean isVssMuteBijective(int stream) {
+            return isStreamAffectedByMute(stream)
+                    && (getMinIndex() == (mStreamStates[stream].mIndexMin + 5) / 10)
+                    && (getMinIndex() == 0 || isCallStream(stream));
+        }
+
+        private boolean isMutable() {
+            return mIndexMin == 0 || (mHasValidStreamType && isVssMuteBijective(mPublicStreamType));
+        }
+        /**
+         * Mute/unmute the volume group
+         * @param muted the new mute state
+         */
+        @GuardedBy("AudioService.VolumeStreamState.class")
+        public boolean mute(boolean muted) {
+            if (!isMutable()) {
+                // Non mutable volume group
+                if (DEBUG_VOL) {
+                    Log.d(TAG, "invalid mute on unmutable volume group " + name());
+                }
+                return false;
+            }
+            boolean changed = (mIsMuted != muted);
+            // As for VSS, mute shall apply minIndex to all devices found in IndexMap and default.
+            if (changed) {
+                mIsMuted = muted;
+                applyAllVolumes(false /*userSwitch*/);
+            }
+            return changed;
+        }
+
+        public boolean isMuted() {
+            return mIsMuted;
+        }
+
+        public void adjustVolume(int direction, int flags) {
+            synchronized (VolumeStreamState.class) {
+                int device = getDeviceForVolume();
+                int previousIndex = getIndex(device);
+                if (isMuteAdjust(direction) && !isMutable()) {
+                    // Non mutable volume group
+                    if (DEBUG_VOL) {
+                        Log.d(TAG, "invalid mute on unmutable volume group " + name());
+                    }
+                    return;
+                }
+                switch (direction) {
+                    case AudioManager.ADJUST_TOGGLE_MUTE: {
+                        // Note: If muted by volume 0, unmute will restore volume 0.
+                        mute(!mIsMuted);
+                        break;
+                    }
+                    case AudioManager.ADJUST_UNMUTE:
+                        // Note: If muted by volume 0, unmute will restore volume 0.
+                        mute(false);
+                        break;
+                    case AudioManager.ADJUST_MUTE:
+                        // May be already muted by setvolume 0, prevent from setting same value
+                        if (previousIndex != 0) {
+                            // bypass persist
+                            mute(true);
+                        }
+                        mIsMuted = true;
+                        break;
+                    case AudioManager.ADJUST_RAISE:
+                        // As for stream, RAISE during mute will increment the index
+                        setVolumeIndex(Math.min(previousIndex + 1, mIndexMax),  device, flags);
+                        break;
+                    case AudioManager.ADJUST_LOWER:
+                        // For stream, ADJUST_LOWER on a muted VSS is a no-op
+                        // If we decide to unmute on ADJUST_LOWER, cannot fallback on
+                        // adjustStreamVolume for group associated to legacy stream type
+                        if (isMuted() && previousIndex != 0) {
+                            mute(false);
+                        } else {
+                            int newIndex = Math.max(previousIndex - 1, mIndexMin);
+                            setVolumeIndex(newIndex, device, flags);
+                        }
+                        break;
+                }
+            }
+        }
+
         public int getVolumeIndex() {
-            return getIndex(getDeviceForVolume());
+            synchronized (VolumeStreamState.class) {
+                return getIndex(getDeviceForVolume());
+            }
         }
 
         public void setVolumeIndex(int index, int flags) {
-            if (mUseFixedVolume) {
-                return;
+            synchronized (VolumeStreamState.class) {
+                if (mUseFixedVolume) {
+                    return;
+                }
+                setVolumeIndex(index, getDeviceForVolume(), flags);
             }
-            setVolumeIndex(index, getDeviceForVolume(), flags);
         }
 
+        @GuardedBy("AudioService.VolumeStreamState.class")
         private void setVolumeIndex(int index, int device, int flags) {
-            // Set the volume index
-            setVolumeIndexInt(index, device, flags);
-
-            // Update local cache
-            mIndexMap.put(device, index);
-
-            // update data base - post a persist volume group msg
-            sendMsg(mAudioHandler,
-                    MSG_PERSIST_VOLUME_GROUP,
-                    SENDMSG_QUEUE,
-                    device,
-                    0,
-                    this,
-                    PERSIST_DELAY);
+            // Update cache & persist (muted by volume 0 shall be persisted)
+            updateVolumeIndex(index, device);
+            // setting non-zero volume for a muted stream unmutes the stream and vice versa,
+            boolean changed = mute(index == 0);
+            if (!changed) {
+                // Set the volume index only if mute operation is a no-op
+                index = getValidIndex(index);
+                setVolumeIndexInt(index, device, flags);
+            }
         }
 
+        @GuardedBy("AudioService.VolumeStreamState.class")
+        public void updateVolumeIndex(int index, int device) {
+            // Filter persistency if already exist and the index has not changed
+            if (mIndexMap.indexOfKey(device) < 0 || mIndexMap.get(device) != index) {
+                // Update local cache
+                mIndexMap.put(device, getValidIndex(index));
+
+                // update data base - post a persist volume group msg
+                sendMsg(mAudioHandler,
+                        MSG_PERSIST_VOLUME_GROUP,
+                        SENDMSG_QUEUE,
+                        device,
+                        0,
+                        this,
+                        PERSIST_DELAY);
+            }
+        }
+
+        @GuardedBy("AudioService.VolumeStreamState.class")
         private void setVolumeIndexInt(int index, int device, int flags) {
             // Reflect mute state of corresponding stream by forcing index to 0 if muted
             // Only set audio policy BT SCO stream volume to 0 when the stream is actually muted.
             // This allows RX path muting by the audio HAL only when explicitly muted but not when
             // index is just set to 0 to repect BT requirements
-            if (mStreamStates[mPublicStreamType].isFullyMuted()) {
+            if (mHasValidStreamType && isVssMuteBijective(mPublicStreamType)
+                    && mStreamStates[mPublicStreamType].isFullyMuted()) {
                 index = 0;
             } else if (mPublicStreamType == AudioSystem.STREAM_BLUETOOTH_SCO && index == 0) {
                 index = 1;
@@ -7474,18 +7697,16 @@
             AudioSystem.setVolumeIndexForAttributes(mAudioAttributes, index, device);
         }
 
-        public int getIndex(int device) {
-            synchronized (VolumeGroupState.class) {
-                int index = mIndexMap.get(device, -1);
-                // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT
-                return (index != -1) ? index : mIndexMap.get(AudioSystem.DEVICE_OUT_DEFAULT);
-            }
+        @GuardedBy("AudioService.VolumeStreamState.class")
+        private int getIndex(int device) {
+            int index = mIndexMap.get(device, -1);
+            // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT
+            return (index != -1) ? index : mIndexMap.get(AudioSystem.DEVICE_OUT_DEFAULT);
         }
 
-        public boolean hasIndexForDevice(int device) {
-            synchronized (VolumeGroupState.class) {
-                return (mIndexMap.get(device, -1) != -1);
-            }
+        @GuardedBy("AudioService.VolumeStreamState.class")
+        private boolean hasIndexForDevice(int device) {
+            return (mIndexMap.get(device, -1) != -1);
         }
 
         public int getMaxIndex() {
@@ -7496,55 +7717,108 @@
             return mIndexMin;
         }
 
-        private boolean isValidLegacyStreamType() {
-            return (mLegacyStreamType != AudioSystem.STREAM_DEFAULT)
-                    && (mLegacyStreamType < mStreamStates.length);
+        private boolean isValidStream(int stream) {
+            return (stream != AudioSystem.STREAM_DEFAULT) && (stream < mStreamStates.length);
         }
 
-        public void applyAllVolumes() {
-            synchronized (VolumeGroupState.class) {
-                int deviceForStream = AudioSystem.DEVICE_NONE;
-                int volumeIndexForStream = 0;
-                if (isValidLegacyStreamType()) {
-                    // Prevent to apply settings twice when group is associated to public stream
-                    deviceForStream = getDeviceForStream(mLegacyStreamType);
-                    volumeIndexForStream = getStreamVolume(mLegacyStreamType);
-                }
+        public boolean isMusic() {
+            return mHasValidStreamType && mPublicStreamType == AudioSystem.STREAM_MUSIC;
+        }
+
+        public void applyAllVolumes(boolean userSwitch) {
+            String caller = "from vgs";
+            synchronized (VolumeStreamState.class) {
                 // apply device specific volumes first
-                int index;
                 for (int i = 0; i < mIndexMap.size(); i++) {
-                    final int device = mIndexMap.keyAt(i);
+                    int device = mIndexMap.keyAt(i);
+                    int index = mIndexMap.valueAt(i);
+                    boolean synced = false;
                     if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
-                        index = mIndexMap.valueAt(i);
-                        if (device == deviceForStream && volumeIndexForStream == index) {
-                            continue;
+                        for (int stream : getLegacyStreamTypes()) {
+                            if (isValidStream(stream)) {
+                                boolean streamMuted = mStreamStates[stream].mIsMuted;
+                                int deviceForStream = getDeviceForStream(stream);
+                                int indexForStream =
+                                        (mStreamStates[stream].getIndex(deviceForStream) + 5) / 10;
+                                if (device == deviceForStream) {
+                                    if (indexForStream == index && (isMuted() == streamMuted)
+                                            && isVssMuteBijective(stream)) {
+                                        synced = true;
+                                        continue;
+                                    }
+                                    if (indexForStream != index) {
+                                        mStreamStates[stream].setIndex(index * 10, device, caller,
+                                                true /*hasModifyAudioSettings*/);
+                                    }
+                                    if ((isMuted() != streamMuted) && isVssMuteBijective(stream)) {
+                                        mStreamStates[stream].mute(isMuted());
+                                    }
+                                }
+                            }
                         }
-                        if (DEBUG_VOL) {
-                            Log.v(TAG, "applyAllVolumes: restore index " + index + " for group "
-                                    + mAudioVolumeGroup.name() + " and device "
-                                    + AudioSystem.getOutputDeviceName(device));
+                        if (!synced) {
+                            if (DEBUG_VOL) {
+                                Log.d(TAG, "applyAllVolumes: apply index " + index + ", group "
+                                        + mAudioVolumeGroup.name() + " and device "
+                                        + AudioSystem.getOutputDeviceName(device));
+                            }
+                            setVolumeIndexInt(isMuted() ? 0 : index, device, 0 /*flags*/);
                         }
-                        setVolumeIndexInt(index, device, 0 /*flags*/);
                     }
                 }
                 // apply default volume last: by convention , default device volume will be used
                 // by audio policy manager if no explicit volume is present for a given device type
-                index = getIndex(AudioSystem.DEVICE_OUT_DEFAULT);
-                if (DEBUG_VOL) {
-                    Log.v(TAG, "applyAllVolumes: restore default device index " + index
-                            + " for group " + mAudioVolumeGroup.name());
-                }
-                if (isValidLegacyStreamType()) {
-                    int defaultStreamIndex = (mStreamStates[mLegacyStreamType]
-                            .getIndex(AudioSystem.DEVICE_OUT_DEFAULT) + 5) / 10;
-                    if (defaultStreamIndex == index) {
-                        return;
+                int index = getIndex(AudioSystem.DEVICE_OUT_DEFAULT);
+                boolean synced = false;
+                int deviceForVolume = getDeviceForVolume();
+                boolean forceDeviceSync = userSwitch && (mIndexMap.indexOfKey(deviceForVolume) < 0);
+                for (int stream : getLegacyStreamTypes()) {
+                    if (isValidStream(stream)) {
+                        boolean streamMuted = mStreamStates[stream].mIsMuted;
+                        int defaultStreamIndex = (mStreamStates[stream].getIndex(
+                                        AudioSystem.DEVICE_OUT_DEFAULT) + 5) / 10;
+                        if (forceDeviceSync) {
+                            mStreamStates[stream].setIndex(index * 10, deviceForVolume, caller,
+                                    true /*hasModifyAudioSettings*/);
+                        }
+                        if (defaultStreamIndex == index && (isMuted() == streamMuted)
+                                && isVssMuteBijective(stream)) {
+                            synced = true;
+                            continue;
+                        }
+                        if (defaultStreamIndex != index) {
+                            mStreamStates[stream].setIndex(
+                                    index * 10, AudioSystem.DEVICE_OUT_DEFAULT, caller,
+                                    true /*hasModifyAudioSettings*/);
+                        }
+                        if ((isMuted() != streamMuted) && isVssMuteBijective(stream)) {
+                            mStreamStates[stream].mute(isMuted());
+                        }
                     }
                 }
-                setVolumeIndexInt(index, AudioSystem.DEVICE_OUT_DEFAULT, 0 /*flags*/);
+                if (!synced) {
+                    if (DEBUG_VOL) {
+                        Log.d(TAG, "applyAllVolumes: apply default device index " + index
+                                + ", group " + mAudioVolumeGroup.name());
+                    }
+                    setVolumeIndexInt(
+                            isMuted() ? 0 : index, AudioSystem.DEVICE_OUT_DEFAULT, 0 /*flags*/);
+                }
+                if (forceDeviceSync) {
+                    if (DEBUG_VOL) {
+                        Log.d(TAG, "applyAllVolumes: forceDeviceSync index " + index
+                                + ", device " + AudioSystem.getOutputDeviceName(deviceForVolume)
+                                + ", group " + mAudioVolumeGroup.name());
+                    }
+                    setVolumeIndexInt(isMuted() ? 0 : index, deviceForVolume, 0);
+                }
             }
         }
 
+        public void clearIndexCache() {
+            mIndexMap.clear();
+        }
+
         private void persistVolumeGroup(int device) {
             if (mUseFixedVolume) {
                 return;
@@ -7553,21 +7827,19 @@
                 Log.v(TAG, "persistVolumeGroup: storing index " + getIndex(device) + " for group "
                         + mAudioVolumeGroup.name()
                         + ", device " + AudioSystem.getOutputDeviceName(device)
-                        + " and User=" + ActivityManager.getCurrentUser());
+                        + " and User=" + getCurrentUserId());
             }
             boolean success = mSettings.putSystemIntForUser(mContentResolver,
                     getSettingNameForDevice(device),
                     getIndex(device),
-                    UserHandle.USER_CURRENT);
+                    isMusic() ? UserHandle.USER_SYSTEM : UserHandle.USER_CURRENT);
             if (!success) {
                 Log.e(TAG, "persistVolumeGroup failed for group " +  mAudioVolumeGroup.name());
             }
         }
 
         public void readSettings() {
-            synchronized (VolumeGroupState.class) {
-                // First clear previously loaded (previous user?) settings
-                mIndexMap.clear();
+            synchronized (VolumeStreamState.class) {
                 // force maximum volume on all streams if fixed volume property is set
                 if (mUseFixedVolume) {
                     mIndexMap.put(AudioSystem.DEVICE_OUT_DEFAULT, mIndexMax);
@@ -7582,7 +7854,8 @@
                     int index;
                     String name = getSettingNameForDevice(device);
                     index = mSettings.getSystemIntForUser(
-                            mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT);
+                            mContentResolver, name, defaultIndex,
+                            isMusic() ? UserHandle.USER_SYSTEM : UserHandle.USER_CURRENT);
                     if (index == -1) {
                         continue;
                     }
@@ -7593,13 +7866,14 @@
                     if (DEBUG_VOL) {
                         Log.v(TAG, "readSettings: found stored index " + getValidIndex(index)
                                  + " for group " + mAudioVolumeGroup.name() + ", device: " + name
-                                 + ", User=" + ActivityManager.getCurrentUser());
+                                 + ", User=" + getCurrentUserId());
                     }
                     mIndexMap.put(device, getValidIndex(index));
                 }
             }
         }
 
+        @GuardedBy("AudioService.VolumeStreamState.class")
         private int getValidIndex(int index) {
             if (index < mIndexMin) {
                 return mIndexMin;
@@ -7610,15 +7884,17 @@
         }
 
         public @NonNull String getSettingNameForDevice(int device) {
-            final String suffix = AudioSystem.getOutputDeviceName(device);
+            String suffix = AudioSystem.getOutputDeviceName(device);
             if (suffix.isEmpty()) {
-                return mAudioVolumeGroup.name();
+                return mSettingName;
             }
-            return mAudioVolumeGroup.name() + "_" + AudioSystem.getOutputDeviceName(device);
+            return mSettingName + "_" + AudioSystem.getOutputDeviceName(device);
         }
 
         private void dump(PrintWriter pw) {
             pw.println("- VOLUME GROUP " + mAudioVolumeGroup.name() + ":");
+            pw.print("   Muted: ");
+            pw.println(mIsMuted);
             pw.print("   Min: ");
             pw.println(mIndexMin);
             pw.print("   Max: ");
@@ -7628,9 +7904,9 @@
                 if (i > 0) {
                     pw.print(", ");
                 }
-                final int device = mIndexMap.keyAt(i);
+                int device = mIndexMap.keyAt(i);
                 pw.print(Integer.toHexString(device));
-                final String deviceName = device == AudioSystem.DEVICE_OUT_DEFAULT ? "default"
+                String deviceName = device == AudioSystem.DEVICE_OUT_DEFAULT ? "default"
                         : AudioSystem.getOutputDeviceName(device);
                 if (!deviceName.isEmpty()) {
                     pw.print(" (");
@@ -7643,7 +7919,7 @@
             pw.println();
             pw.print("   Devices: ");
             int n = 0;
-            final int devices = getDeviceForVolume();
+            int devices = getDeviceForVolume();
             for (int device : AudioSystem.DEVICE_OUT_ALL_SET) {
                 if ((devices & device) == device) {
                     if (n++ > 0) {
@@ -7652,6 +7928,10 @@
                     pw.print(AudioSystem.getOutputDeviceName(device));
                 }
             }
+            pw.println();
+            pw.print("   Streams: ");
+            Arrays.stream(getLegacyStreamTypes())
+                    .forEach(stream -> pw.print(AudioSystem.streamToString(stream) + " "));
         }
     }
 
@@ -7663,13 +7943,14 @@
     //  4       VolumeStreamState.class
     private class VolumeStreamState {
         private final int mStreamType;
+        private VolumeGroupState mVolumeGroupState = null;
         private int mIndexMin;
         // min index when user doesn't have permission to change audio settings
         private int mIndexMinNoPerm;
         private int mIndexMax;
 
-        private boolean mIsMuted;
-        private boolean mIsMutedInternally;
+        private boolean mIsMuted = false;
+        private boolean mIsMutedInternally = false;
         private String mVolumeIndexSettingName;
         @NonNull private Set<Integer> mObservedDeviceSet = new TreeSet<>();
 
@@ -7727,6 +8008,15 @@
         }
 
         /**
+         * Associate a {@link volumeGroupState} on the {@link VolumeStreamState}.
+         * <p> It helps to synchronize the index, mute attributes on the maching
+         * {@link volumeGroupState}
+         * @param volumeGroupState matching the {@link VolumeStreamState}
+         */
+        public void setVolumeGroupState(VolumeGroupState volumeGroupState) {
+            mVolumeGroupState = volumeGroupState;
+        }
+        /**
          * Update the minimum index that can be used without MODIFY_AUDIO_SETTINGS permission
          * @param index minimum index expressed in "UI units", i.e. no 10x factor
          */
@@ -7984,6 +8274,9 @@
                 }
             }
             if (changed) {
+                // If associated to volume group, update group cache
+                updateVolumeGroupIndex(device, /* forceMuteState= */ false);
+
                 oldIndex = (oldIndex + 5) / 10;
                 index = (index + 5) / 10;
                 // log base stream changes to the event log
@@ -8086,6 +8379,29 @@
             }
         }
 
+        // If associated to volume group, update group cache
+        private void updateVolumeGroupIndex(int device, boolean forceMuteState) {
+            synchronized (VolumeStreamState.class) {
+                if (mVolumeGroupState != null) {
+                    int groupIndex = (getIndex(device) + 5) / 10;
+                    if (DEBUG_VOL) {
+                        Log.d(TAG, "updateVolumeGroupIndex for stream " + mStreamType
+                                + ", muted=" + mIsMuted + ", device=" + device + ", index="
+                                + getIndex(device) + ", group " + mVolumeGroupState.name()
+                                + " Muted=" + mVolumeGroupState.isMuted() + ", Index=" + groupIndex
+                                + ", forceMuteState=" + forceMuteState);
+                    }
+                    mVolumeGroupState.updateVolumeIndex(groupIndex, device);
+                    // Only propage mute of stream when applicable
+                    if (isMutable()) {
+                        // For call stream, align mute only when muted, not when index is set to 0
+                        mVolumeGroupState.mute(
+                                forceMuteState ? mIsMuted : groupIndex == 0 || mIsMuted);
+                    }
+                }
+            }
+        }
+
         /**
          * Mute/unmute the stream
          * @param state the new mute state
@@ -8094,27 +8410,10 @@
         public boolean mute(boolean state) {
             boolean changed = false;
             synchronized (VolumeStreamState.class) {
-                if (state != mIsMuted) {
-                    changed = true;
-                    mIsMuted = state;
-
-                    // Set the new mute volume. This propagates the values to
-                    // the audio system, otherwise the volume won't be changed
-                    // at the lower level.
-                    sendMsg(mAudioHandler,
-                            MSG_SET_ALL_VOLUMES,
-                            SENDMSG_QUEUE,
-                            0,
-                            0,
-                            this, 0);
-                }
+                changed = mute(state, true);
             }
             if (changed) {
-                // Stream mute changed, fire the intent.
-                Intent intent = new Intent(AudioManager.STREAM_MUTE_CHANGED_ACTION);
-                intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mStreamType);
-                intent.putExtra(AudioManager.EXTRA_STREAM_VOLUME_MUTED, state);
-                sendBroadcastToAll(intent);
+                broadcastMuteSetting(mStreamType, state);
             }
             return changed;
         }
@@ -8146,6 +8445,50 @@
             return mIsMuted || mIsMutedInternally;
         }
 
+
+        private boolean isMutable() {
+            return isStreamAffectedByMute(mStreamType)
+                    && (mIndexMin == 0 || isCallStream(mStreamType));
+        }
+
+        /**
+         * Mute/unmute the stream
+         * @param state the new mute state
+         * @param apply true to propagate to HW, or false just to update the cache. May be needed
+         * to mute a stream and its aliases as applyAllVolume will force settings to aliases.
+         * It prevents unnecessary calls to {@see AudioSystem#setStreamVolume}
+         * @return true if the mute state was changed
+         */
+        public boolean mute(boolean state, boolean apply) {
+            synchronized (VolumeStreamState.class) {
+                boolean changed = state != mIsMuted;
+                if (changed) {
+                    mIsMuted = state;
+                    if (apply) {
+                        doMute();
+                    }
+                }
+                return changed;
+            }
+        }
+
+        public void doMute() {
+            synchronized (VolumeStreamState.class) {
+                // If associated to volume group, update group cache
+                updateVolumeGroupIndex(getDeviceForStream(mStreamType), /* forceMuteState= */ true);
+
+                // Set the new mute volume. This propagates the values to
+                // the audio system, otherwise the volume won't be changed
+                // at the lower level.
+                sendMsg(mAudioHandler,
+                        MSG_SET_ALL_VOLUMES,
+                        SENDMSG_QUEUE,
+                        0,
+                        0,
+                        this, 0);
+            }
+        }
+
         public int getStreamType() {
             return mStreamType;
         }
@@ -8215,6 +8558,9 @@
             pw.println();
             pw.print("   Devices: ");
             pw.print(AudioSystem.deviceSetToString(getDeviceSetForStream(mStreamType)));
+            pw.println();
+            pw.print("   Volume Group: ");
+            pw.println(mVolumeGroupState != null ? mVolumeGroupState.name() : "n/a");
         }
     }
 
@@ -9881,7 +10227,7 @@
     private static final int CHECK_MODE_FOR_UID_PERIOD_MS = 6000;
 
     private static final String ACTION_CHECK_MUSIC_ACTIVE =
-            AudioService.class.getSimpleName() + ".CHECK_MUSIC_ACTIVE";
+            "com.android.server.audio.action.CHECK_MUSIC_ACTIVE";
     private static final int REQUEST_CODE_CHECK_MUSIC_ACTIVE = 1;
 
     private int safeMediaVolumeIndex(int device) {
@@ -9957,7 +10303,8 @@
                                   mPendingVolumeCommand.mIndex,
                                   mPendingVolumeCommand.mFlags,
                                   mPendingVolumeCommand.mDevice,
-                                  callingPackage, true /*hasModifyAudioSettings*/);
+                                  callingPackage, true /*hasModifyAudioSettings*/,
+                                  true /*canChangeMute*/);
                 mPendingVolumeCommand = null;
             }
         }
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index c2c3f02..36908dc 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -17,7 +17,6 @@
 package com.android.server.audio;
 
 import android.annotation.NonNull;
-import android.media.AudioAttributes;
 import android.media.AudioDeviceAttributes;
 import android.media.AudioManager;
 import android.media.AudioSystem;
@@ -175,15 +174,17 @@
         final String mDeviceAddress;
         final String mCaller;
         final int mDeviceForStream;
+        final boolean mSkipped;
 
         DeviceVolumeEvent(int streamType, int index, @NonNull AudioDeviceAttributes device,
-                int deviceForStream, String callingPackage) {
+                int deviceForStream, String callingPackage, boolean skipped) {
             mStream = streamType;
             mVolIndex = index;
             mDeviceNativeType = "0x" + Integer.toHexString(device.getInternalType());
             mDeviceAddress = device.getAddress();
             mDeviceForStream = deviceForStream;
             mCaller = callingPackage;
+            mSkipped = skipped;
             // log metrics
             new MediaMetrics.Item(MediaMetrics.Name.AUDIO_VOLUME_EVENT)
                     .set(MediaMetrics.Property.EVENT, "setDeviceVolume")
@@ -198,14 +199,18 @@
 
         @Override
         public String eventToString() {
-            return new StringBuilder("setDeviceVolume(stream:")
+            final StringBuilder sb = new StringBuilder("setDeviceVolume(stream:")
                     .append(AudioSystem.streamToString(mStream))
                     .append(" index:").append(mVolIndex)
                     .append(" device:").append(mDeviceNativeType)
                     .append(" addr:").append(mDeviceAddress)
-                    .append(") from ").append(mCaller)
-                    .append(" currDevForStream:Ox").append(Integer.toHexString(mDeviceForStream))
-                    .toString();
+                    .append(") from ").append(mCaller);
+            if (mSkipped) {
+                sb.append(" skipped [device in use]");
+            } else {
+                sb.append(" currDevForStream:Ox").append(Integer.toHexString(mDeviceForStream));
+            }
+            return sb.toString();
         }
     }
 
@@ -221,6 +226,7 @@
         static final int VOL_SET_GROUP_VOL = 8;
         static final int VOL_MUTE_STREAM_INT = 9;
         static final int VOL_SET_LE_AUDIO_VOL = 10;
+        static final int VOL_ADJUST_GROUP_VOL = 11;
 
         final int mOp;
         final int mStream;
@@ -228,7 +234,6 @@
         final int mVal2;
         final String mCaller;
         final String mGroupName;
-        final AudioAttributes mAudioAttributes;
 
         /** used for VOL_ADJUST_VOL_UID,
          *           VOL_ADJUST_SUGG_VOL,
@@ -241,7 +246,6 @@
             mVal2 = val2;
             mCaller = caller;
             mGroupName = null;
-            mAudioAttributes = null;
             logMetricEvent();
         }
 
@@ -254,7 +258,6 @@
             mStream = -1;
             mCaller = null;
             mGroupName = null;
-            mAudioAttributes = null;
             logMetricEvent();
         }
 
@@ -267,7 +270,6 @@
             mStream = -1;
             mCaller = null;
             mGroupName = null;
-            mAudioAttributes = null;
             logMetricEvent();
         }
 
@@ -280,7 +282,6 @@
             // unused
             mCaller = null;
             mGroupName = null;
-            mAudioAttributes = null;
             logMetricEvent();
         }
 
@@ -293,19 +294,18 @@
             // unused
             mCaller = null;
             mGroupName = null;
-            mAudioAttributes = null;
             logMetricEvent();
         }
 
-        /** used for VOL_SET_GROUP_VOL */
-        VolumeEvent(int op, AudioAttributes aa, String group, int index, int flags, String caller) {
+        /** used for VOL_SET_GROUP_VOL,
+         *           VOL_ADJUST_GROUP_VOL */
+        VolumeEvent(int op, String group, int index, int flags, String caller) {
             mOp = op;
             mStream = -1;
             mVal1 = index;
             mVal2 = flags;
             mCaller = caller;
             mGroupName = group;
-            mAudioAttributes = aa;
             logMetricEvent();
         }
 
@@ -317,7 +317,6 @@
             mVal2 = 0;
             mCaller = null;
             mGroupName = null;
-            mAudioAttributes = null;
             logMetricEvent();
         }
 
@@ -359,6 +358,15 @@
                             .record();
                     return;
                 }
+                case VOL_ADJUST_GROUP_VOL:
+                    new MediaMetrics.Item(mMetricsId)
+                            .set(MediaMetrics.Property.CALLING_PACKAGE, mCaller)
+                            .set(MediaMetrics.Property.DIRECTION, mVal1 > 0 ? "up" : "down")
+                            .set(MediaMetrics.Property.EVENT, "adjustVolumeGroupVolume")
+                            .set(MediaMetrics.Property.FLAGS, mVal2)
+                            .set(MediaMetrics.Property.GROUP, mGroupName)
+                            .record();
+                    return;
                 case VOL_SET_STREAM_VOL:
                     new MediaMetrics.Item(mMetricsId)
                             .set(MediaMetrics.Property.CALLING_PACKAGE, mCaller)
@@ -410,7 +418,6 @@
                     return;
                 case VOL_SET_GROUP_VOL:
                     new MediaMetrics.Item(mMetricsId)
-                            .set(MediaMetrics.Property.ATTRIBUTES, mAudioAttributes.toString())
                             .set(MediaMetrics.Property.CALLING_PACKAGE, mCaller)
                             .set(MediaMetrics.Property.EVENT, "setVolumeIndexForAttributes")
                             .set(MediaMetrics.Property.FLAGS, mVal2)
@@ -436,6 +443,13 @@
                             .append(" flags:0x").append(Integer.toHexString(mVal2))
                             .append(") from ").append(mCaller)
                             .toString();
+                case VOL_ADJUST_GROUP_VOL:
+                    return new StringBuilder("adjustVolumeGroupVolume(group:")
+                            .append(mGroupName)
+                            .append(" dir:").append(AudioManager.adjustToString(mVal1))
+                            .append(" flags:0x").append(Integer.toHexString(mVal2))
+                            .append(") from ").append(mCaller)
+                            .toString();
                 case VOL_ADJUST_STREAM_VOL:
                     return new StringBuilder("adjustStreamVolume(stream:")
                             .append(AudioSystem.streamToString(mStream))
@@ -484,8 +498,7 @@
                             .append(" stream:").append(AudioSystem.streamToString(mStream))
                             .toString();
                 case VOL_SET_GROUP_VOL:
-                    return new StringBuilder("setVolumeIndexForAttributes(attr:")
-                            .append(mAudioAttributes.toString())
+                    return new StringBuilder("setVolumeIndexForAttributes(group:")
                             .append(" group: ").append(mGroupName)
                             .append(" index:").append(mVal1)
                             .append(" flags:0x").append(Integer.toHexString(mVal2))
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index 2588371..a17b4bf 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -29,8 +29,12 @@
 import com.android.internal.annotations.GuardedBy;
 
 import java.io.PrintWriter;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -60,8 +64,12 @@
     private String[] mMethodNames = {"getDevicesForAttributes"};
 
     private static final boolean USE_CACHE_FOR_GETDEVICES = true;
+    private static final Object sDeviceCacheLock = new Object();
+    @GuardedBy("sDeviceCacheLock")
     private ConcurrentHashMap<Pair<AudioAttributes, Boolean>, ArrayList<AudioDeviceAttributes>>
             mDevicesForAttrCache;
+    @GuardedBy("sDeviceCacheLock")
+    private long mDevicesForAttributesCacheClearTimeMs = System.currentTimeMillis();
     private int[] mMethodCacheHit;
     private static final Object sRoutingListenerLock = new Object();
     @GuardedBy("sRoutingListenerLock")
@@ -147,9 +155,11 @@
             AudioSystem.setRoutingCallback(sSingletonDefaultAdapter);
             AudioSystem.setVolumeRangeInitRequestCallback(sSingletonDefaultAdapter);
             if (USE_CACHE_FOR_GETDEVICES) {
-                sSingletonDefaultAdapter.mDevicesForAttrCache =
-                        new ConcurrentHashMap<>(AudioSystem.getNumStreamTypes());
-                sSingletonDefaultAdapter.mMethodCacheHit = new int[NB_MEASUREMENTS];
+                synchronized (sDeviceCacheLock) {
+                    sSingletonDefaultAdapter.mDevicesForAttrCache =
+                            new ConcurrentHashMap<>(AudioSystem.getNumStreamTypes());
+                    sSingletonDefaultAdapter.mMethodCacheHit = new int[NB_MEASUREMENTS];
+                }
             }
             if (ENABLE_GETDEVICES_STATS) {
                 sSingletonDefaultAdapter.mMethodCallCounter = new int[NB_MEASUREMENTS];
@@ -163,8 +173,9 @@
         if (DEBUG_CACHE) {
             Log.d(TAG, "---- clearing cache ----------");
         }
-        if (mDevicesForAttrCache != null) {
-            synchronized (mDevicesForAttrCache) {
+        synchronized (sDeviceCacheLock) {
+            if (mDevicesForAttrCache != null) {
+                mDevicesForAttributesCacheClearTimeMs = System.currentTimeMillis();
                 mDevicesForAttrCache.clear();
             }
         }
@@ -193,7 +204,7 @@
         if (USE_CACHE_FOR_GETDEVICES) {
             ArrayList<AudioDeviceAttributes> res;
             final Pair<AudioAttributes, Boolean> key = new Pair(attributes, forVolume);
-            synchronized (mDevicesForAttrCache) {
+            synchronized (sDeviceCacheLock) {
                 res = mDevicesForAttrCache.get(key);
                 if (res == null) {
                     // result from AudioSystem guaranteed non-null, but could be invalid
@@ -508,23 +519,31 @@
      */
     public void dump(PrintWriter pw) {
         pw.println("\nAudioSystemAdapter:");
-        pw.println(" mDevicesForAttrCache:");
-        if (mDevicesForAttrCache != null) {
-            for (Map.Entry<Pair<AudioAttributes, Boolean>, ArrayList<AudioDeviceAttributes>>
-                    entry : mDevicesForAttrCache.entrySet()) {
-                final AudioAttributes attributes = entry.getKey().first;
-                try {
-                    final int stream = attributes.getVolumeControlStream();
-                    pw.println("\t" + attributes + " forVolume: " + entry.getKey().second
-                            + " stream: "
-                            + AudioSystem.STREAM_NAMES[stream] + "(" + stream + ")");
-                    for (AudioDeviceAttributes devAttr : entry.getValue()) {
-                        pw.println("\t\t" + devAttr);
+        final DateTimeFormatter formatter = DateTimeFormatter
+                .ofPattern("MM-dd HH:mm:ss:SSS")
+                .withLocale(Locale.US)
+                .withZone(ZoneId.systemDefault());
+        synchronized (sDeviceCacheLock) {
+            pw.println(" last cache clear time: " + formatter.format(
+                    Instant.ofEpochMilli(mDevicesForAttributesCacheClearTimeMs)));
+            pw.println(" mDevicesForAttrCache:");
+            if (mDevicesForAttrCache != null) {
+                for (Map.Entry<Pair<AudioAttributes, Boolean>, ArrayList<AudioDeviceAttributes>>
+                        entry : mDevicesForAttrCache.entrySet()) {
+                    final AudioAttributes attributes = entry.getKey().first;
+                    try {
+                        final int stream = attributes.getVolumeControlStream();
+                        pw.println("\t" + attributes + " forVolume: " + entry.getKey().second
+                                + " stream: "
+                                + AudioSystem.STREAM_NAMES[stream] + "(" + stream + ")");
+                        for (AudioDeviceAttributes devAttr : entry.getValue()) {
+                            pw.println("\t\t" + devAttr);
+                        }
+                    } catch (IllegalArgumentException e) {
+                        // dump could fail if attributes do not map to a stream.
+                        pw.println("\t dump failed for attributes: " + attributes);
+                        Log.e(TAG, "dump failed", e);
                     }
-                } catch (IllegalArgumentException e) {
-                    // dump could fail if attributes do not map to a stream.
-                    pw.println("\t dump failed for attributes: " + attributes);
-                    Log.e(TAG, "dump failed", e);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 6cd42f8..691ce93 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -279,7 +279,11 @@
         }
         AudioService.sVolumeLogger.log(new AudioServiceEvents.VolumeEvent(
                 AudioServiceEvents.VolumeEvent.VOL_SET_AVRCP_VOL, index));
-        mA2dp.setAvrcpAbsoluteVolume(index);
+        try {
+            mA2dp.setAvrcpAbsoluteVolume(index);
+        } catch (Exception e) {
+            Log.e(TAG, "Exception while changing abs volume", e);
+        }
     }
 
     /*package*/ synchronized @AudioSystem.AudioFormatNativeEnumForBtCodec int getA2dpCodec(
@@ -287,7 +291,12 @@
         if (mA2dp == null) {
             return AudioSystem.AUDIO_FORMAT_DEFAULT;
         }
-        final BluetoothCodecStatus btCodecStatus = mA2dp.getCodecStatus(device);
+        BluetoothCodecStatus btCodecStatus = null;
+        try {
+            btCodecStatus = mA2dp.getCodecStatus(device);
+        } catch (Exception e) {
+            Log.e(TAG, "Exception while getting status of " + device, e);
+        }
         if (btCodecStatus == null) {
             return AudioSystem.AUDIO_FORMAT_DEFAULT;
         }
@@ -421,7 +430,11 @@
         }
         AudioService.sVolumeLogger.log(new AudioServiceEvents.VolumeEvent(
                 AudioServiceEvents.VolumeEvent.VOL_SET_LE_AUDIO_VOL, index, maxIndex));
-        mLeAudio.setVolume(volume);
+        try {
+            mLeAudio.setVolume(volume);
+        } catch (Exception e) {
+            Log.e(TAG, "Exception while setting LE volume", e);
+        }
     }
 
     /*package*/ synchronized void setHearingAidVolume(int index, int streamType,
@@ -447,7 +460,11 @@
             AudioService.sVolumeLogger.log(new AudioServiceEvents.VolumeEvent(
                     AudioServiceEvents.VolumeEvent.VOL_SET_HEARING_AID_VOL, index, gainDB));
         }
-        mHearingAid.setVolume(gainDB);
+        try {
+            mHearingAid.setVolume(gainDB);
+        } catch (Exception e) {
+            Log.i(TAG, "Exception while setting hearing aid volume", e);
+        }
     }
 
     /*package*/ synchronized void onBroadcastScoConnectionState(int state) {
@@ -472,7 +489,7 @@
     }
 
     // @GuardedBy("AudioDeviceBroker.mSetModeLock")
-    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+    //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
     /*package*/ synchronized void resetBluetoothSco() {
         mScoAudioState = SCO_STATE_INACTIVE;
         broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
@@ -487,6 +504,35 @@
         mBluetoothHeadset = null;
     }
 
+    //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+    /*package*/ synchronized void onBtProfileDisconnected(int profile) {
+        switch (profile) {
+            case BluetoothProfile.A2DP:
+                mA2dp = null;
+                break;
+            case BluetoothProfile.HEARING_AID:
+                mHearingAid = null;
+                break;
+            case BluetoothProfile.LE_AUDIO:
+                mLeAudio = null;
+                break;
+
+            case BluetoothProfile.A2DP_SINK:
+            case BluetoothProfile.LE_AUDIO_BROADCAST:
+                // shouldn't be received here as profile doesn't involve BtHelper
+                Log.e(TAG, "onBtProfileDisconnected: Not a profile handled by BtHelper "
+                        + BluetoothProfile.getProfileName(profile));
+                break;
+
+            default:
+                // Not a valid profile to disconnect
+                Log.e(TAG, "onBtProfileDisconnected: Not a valid profile to disconnect "
+                        + BluetoothProfile.getProfileName(profile));
+                break;
+        }
+    }
+
+    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
     /*package*/ synchronized void onBtProfileConnected(int profile, BluetoothProfile proxy) {
         if (profile == BluetoothProfile.HEADSET) {
             onHeadsetProfileConnected((BluetoothHeadset) proxy);
@@ -518,7 +564,7 @@
     }
 
     // @GuardedBy("AudioDeviceBroker.mSetModeLock")
-    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+    //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
     /*package*/ synchronized void onHeadsetProfileConnected(BluetoothHeadset headset) {
         // Discard timeout message
         mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService();
@@ -672,7 +718,6 @@
                 public void onServiceConnected(int profile, BluetoothProfile proxy) {
                     switch(profile) {
                         case BluetoothProfile.A2DP:
-                        case BluetoothProfile.A2DP_SINK:
                         case BluetoothProfile.HEADSET:
                         case BluetoothProfile.HEARING_AID:
                         case BluetoothProfile.LE_AUDIO:
@@ -682,6 +727,10 @@
                             mDeviceBroker.postBtProfileConnected(profile, proxy);
                             break;
 
+                        case BluetoothProfile.A2DP_SINK:
+                            // no A2DP sink functionality handled by BtHelper
+                        case BluetoothProfile.LE_AUDIO_BROADCAST:
+                            // no broadcast functionality handled by BtHelper
                         default:
                             break;
                     }
@@ -690,14 +739,19 @@
 
                     switch (profile) {
                         case BluetoothProfile.A2DP:
-                        case BluetoothProfile.A2DP_SINK:
                         case BluetoothProfile.HEADSET:
                         case BluetoothProfile.HEARING_AID:
                         case BluetoothProfile.LE_AUDIO:
-                        case BluetoothProfile.LE_AUDIO_BROADCAST:
+                            AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
+                                    "BT profile service: disconnecting "
+                                        + BluetoothProfile.getProfileName(profile) + " profile"));
                             mDeviceBroker.postBtProfileDisconnected(profile);
                             break;
 
+                        case BluetoothProfile.A2DP_SINK:
+                            // no A2DP sink functionality handled by BtHelper
+                        case BluetoothProfile.LE_AUDIO_BROADCAST:
+                            // no broadcast functionality handled by BtHelper
                         default:
                             break;
                     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 598e2b9..94b67ce 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -452,6 +452,13 @@
                 return -1;
             }
 
+            if (!Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)) {
+                // If this happens, something in KeyguardUpdateMonitor is wrong. This should only
+                // ever be invoked when the user is encrypted or lockdown.
+                Slog.e(TAG, "detectFingerprint invoked when user is not encrypted or lockdown");
+                return -1;
+            }
+
             final Pair<Integer, ServiceProvider> provider = getSingleProvider();
             if (provider == null) {
                 Slog.w(TAG, "Null provider for detectFingerprint");
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 787bfb0..7d390415 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -228,7 +228,16 @@
 
     @Override
     public void onError(int errorCode, int vendorCode) {
-        super.onError(errorCode, vendorCode);
+        if (getContext().getResources().getBoolean(R.bool.config_powerPressMapping)
+                && errorCode == BiometricFingerprintConstants.FINGERPRINT_ERROR_VENDOR
+                && vendorCode == getContext().getResources()
+                .getInteger(R.integer.config_powerPressCode)) {
+            // Translating vendor code to internal code
+            super.onError(BiometricFingerprintConstants.BIOMETRIC_ERROR_POWER_PRESSED,
+                    0 /* vendorCode */);
+        } else {
+            super.onError(errorCode, vendorCode);
+        }
 
         if (errorCode == BiometricFingerprintConstants.FINGERPRINT_ERROR_BAD_CALIBRATION) {
             BiometricNotificationUtils.showBadCalibrationNotification(getContext());
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index 612d906..14b19fb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -37,6 +37,7 @@
 import android.util.Slog;
 import android.view.accessibility.AccessibilityManager;
 
+import com.android.internal.R;
 import com.android.server.biometrics.HardwareAuthTokenUtils;
 import com.android.server.biometrics.log.BiometricContext;
 import com.android.server.biometrics.log.BiometricLogger;
@@ -143,7 +144,17 @@
             }
         });
         mCallback.onBiometricAction(BiometricStateListener.ACTION_SENSOR_TOUCH);
-        super.onAcquired(acquiredInfo, vendorCode);
+
+        if (getContext().getResources().getBoolean(R.bool.config_powerPressMapping)
+                && acquiredInfo == BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR
+                && vendorCode == getContext().getResources()
+                .getInteger(R.integer.config_powerPressCode)) {
+            // Translating vendor code to internal code
+            super.onAcquired(BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_POWER_PRESSED,
+                    0 /* vendorCode */);
+        } else {
+            super.onAcquired(acquiredInfo, vendorCode);
+        }
     }
 
     @Override
@@ -270,8 +281,5 @@
     }
 
     @Override
-    public void onPowerPressed() {
-        onAcquired(BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_POWER_PRESSED,
-                0 /* vendorCode */);
-    }
+    public void onPowerPressed() {}
 }
diff --git a/services/core/java/com/android/server/devicestate/DeviceStatePolicy.java b/services/core/java/com/android/server/devicestate/DeviceStatePolicy.java
index 5c4e2f3..c876a8b 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStatePolicy.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStatePolicy.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.text.TextUtils;
+import android.util.Slog;
 
 import com.android.server.policy.DeviceStatePolicyImpl;
 
@@ -92,11 +93,16 @@
 
             try {
                 return (DeviceStatePolicy.Provider) Class.forName(name).newInstance();
-            } catch (ReflectiveOperationException | ClassCastException e) {
+            } catch (ClassCastException e) {
                 throw new IllegalStateException("Couldn't instantiate class " + name
                         + " for config_deviceSpecificDeviceStatePolicyProvider:"
                         + " make sure it has a public zero-argument constructor"
-                        + " and implements DeviceStatePolicy.Provider", e);
+                        + " and implements DeviceStatePolicy.Provider");
+            } catch (ReflectiveOperationException e) {
+                Slog.e("DeviceStatePolicy", "Couldn't instantiate class " + name
+                        + " for config_deviceSpecificDeviceStatePolicyProvider:"
+                        + " using default provider", e);
+                return new DeviceStatePolicy.DefaultProvider();
             }
         }
     }
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 2b7fbfb..482dd7a 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -135,6 +135,8 @@
  *      </thermalThrottling>
  *
  *      <refreshRate>
+ *        <defaultRefreshRateInHbmHdr>75</defaultRefreshRateInHbmHdr>
+ *        <defaultRefreshRateInHbmSunlight>75</defaultRefreshRateInHbmSunlight>
  *        <lowerBlockingZoneConfigs>
  *          <defaultRefreshRate>75</defaultRefreshRate>
  *          <blockingZoneThreshold>
@@ -404,6 +406,7 @@
     private static final long STABLE_FLAG = 1L << 62;
     private static final int DEFAULT_PEAK_REFRESH_RATE = 0;
     private static final int DEFAULT_REFRESH_RATE = 60;
+    private static final int DEFAULT_REFRESH_RATE_IN_HBM = 0;
     private static final int DEFAULT_LOW_REFRESH_RATE = 60;
     private static final int DEFAULT_HIGH_REFRESH_RATE = 0;
     private static final int[] DEFAULT_BRIGHTNESS_THRESHOLDS = new int[]{};
@@ -585,6 +588,15 @@
     private int mDefaultRefreshRate = DEFAULT_REFRESH_RATE;
 
     /**
+     * Default refresh rate while the device has high brightness mode enabled for HDR.
+     */
+    private int mDefaultRefreshRateInHbmHdr = DEFAULT_REFRESH_RATE_IN_HBM;
+
+    /**
+     * Default refresh rate while the device has high brightness mode enabled for Sunlight.
+     */
+    private int mDefaultRefreshRateInHbmSunlight = DEFAULT_REFRESH_RATE_IN_HBM;
+    /**
      * Default refresh rate in the high zone defined by brightness and ambient thresholds.
      * If non-positive, then the refresh rate is unchanged even if thresholds are configured.
      */
@@ -1322,6 +1334,21 @@
     }
 
     /**
+     * @return Default refresh rate while the device has high brightness mode enabled for HDR.
+     */
+    public int getDefaultRefreshRateInHbmHdr() {
+        return mDefaultRefreshRateInHbmHdr;
+    }
+
+    /**
+     * @return Default refresh rate while the device has high brightness mode enabled because of
+     * high lux.
+     */
+    public int getDefaultRefreshRateInHbmSunlight() {
+        return mDefaultRefreshRateInHbmSunlight;
+    }
+
+    /**
      * @return Default refresh rate in the higher blocking zone of the associated display
      */
     public int getDefaultHighBlockingZoneRefreshRate() {
@@ -1474,6 +1501,8 @@
                 + ", mDefaultHighBlockingZoneRefreshRate= " + mDefaultHighBlockingZoneRefreshRate
                 + ", mDefaultPeakRefreshRate= " + mDefaultPeakRefreshRate
                 + ", mDefaultRefreshRate= " + mDefaultRefreshRate
+                + ", mDefaultRefreshRateInHbmHdr= " + mDefaultRefreshRateInHbmHdr
+                + ", mDefaultRefreshRateInHbmSunlight= " + mDefaultRefreshRateInHbmSunlight
                 + ", mLowDisplayBrightnessThresholds= "
                 + Arrays.toString(mLowDisplayBrightnessThresholds)
                 + ", mLowAmbientBrightnessThresholds= "
@@ -1789,6 +1818,7 @@
                         : refreshRateConfigs.getHigherBlockingZoneConfigs();
         loadPeakDefaultRefreshRate(refreshRateConfigs);
         loadDefaultRefreshRate(refreshRateConfigs);
+        loadDefaultRefreshRateInHbm(refreshRateConfigs);
         loadLowerRefreshRateBlockingZones(lowerBlockingZoneConfig);
         loadHigherRefreshRateBlockingZones(higherBlockingZoneConfig);
     }
@@ -1813,6 +1843,26 @@
         }
     }
 
+    private void loadDefaultRefreshRateInHbm(RefreshRateConfigs refreshRateConfigs) {
+        if (refreshRateConfigs != null
+                && refreshRateConfigs.getDefaultRefreshRateInHbmHdr() != null) {
+            mDefaultRefreshRateInHbmHdr = refreshRateConfigs.getDefaultRefreshRateInHbmHdr()
+                    .intValue();
+        } else {
+            mDefaultRefreshRateInHbmHdr = mContext.getResources().getInteger(
+                    R.integer.config_defaultRefreshRateInHbmHdr);
+        }
+
+        if (refreshRateConfigs != null
+                && refreshRateConfigs.getDefaultRefreshRateInHbmSunlight() != null) {
+            mDefaultRefreshRateInHbmSunlight =
+                    refreshRateConfigs.getDefaultRefreshRateInHbmSunlight().intValue();
+        } else {
+            mDefaultRefreshRateInHbmSunlight = mContext.getResources().getInteger(
+                R.integer.config_defaultRefreshRateInHbmSunlight);
+        }
+    }
+
     /**
      * Loads the refresh rate configurations pertaining to the upper blocking zones.
      */
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 4341634..909c531 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -434,6 +434,8 @@
     private boolean mIsDocked;
     private boolean mIsDreaming;
 
+    private boolean mBootCompleted = false;
+
     private final BroadcastReceiver mIdleModeReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -573,6 +575,12 @@
                 }
             }
         } else if (phase == PHASE_BOOT_COMPLETED) {
+            synchronized (mSyncRoot) {
+                mBootCompleted = true;
+                for (int i = 0; i < mDisplayPowerControllers.size(); i++) {
+                    mDisplayPowerControllers.valueAt(i).onBootCompleted();
+                }
+            }
             mDisplayModeDirector.onBootCompleted();
             mLogicalDisplayMapper.onBootCompleted();
         }
@@ -2680,7 +2688,7 @@
         final DisplayPowerController displayPowerController = new DisplayPowerController(
                 mContext, mDisplayPowerCallbacks, mPowerHandler, mSensorManager,
                 mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
-                () -> handleBrightnessChange(display), hbmMetadata);
+                () -> handleBrightnessChange(display), hbmMetadata, mBootCompleted);
         mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController);
     }
 
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index fdfc20a..1bbdc20 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -121,6 +121,10 @@
     private final DeviceConfigInterface mDeviceConfig;
     private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
 
+    @GuardedBy("mLock")
+    @Nullable
+    private DisplayDeviceConfig mDefaultDisplayDeviceConfig;
+
     // A map from the display ID to the collection of votes and their priority. The latter takes
     // the form of another map from the priority to the vote itself so that each priority is
     // guaranteed to have exactly one vote, which is also easily and efficiently replaceable.
@@ -160,6 +164,7 @@
         mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
         mSettingsObserver = new SettingsObserver(context, handler);
         mBrightnessObserver = new BrightnessObserver(context, handler, injector);
+        mDefaultDisplayDeviceConfig = null;
         mUdfpsObserver = new UdfpsObserver();
         final BallotBox ballotBox = (displayId, priority, vote) -> {
             synchronized (mLock) {
@@ -529,11 +534,15 @@
      * @param displayDeviceConfig configurations relating to the underlying display device.
      */
     public void defaultDisplayDeviceUpdated(DisplayDeviceConfig displayDeviceConfig) {
-        mSettingsObserver.setRefreshRates(displayDeviceConfig,
-            /* attemptLoadingFromDeviceConfig= */ true);
-        mBrightnessObserver.updateBlockingZoneThresholds(displayDeviceConfig,
-            /* attemptLoadingFromDeviceConfig= */ true);
-        mBrightnessObserver.reloadLightSensor(displayDeviceConfig);
+        synchronized (mLock) {
+            mDefaultDisplayDeviceConfig = displayDeviceConfig;
+            mSettingsObserver.setRefreshRates(displayDeviceConfig,
+                /* attemptLoadingFromDeviceConfig= */ true);
+            mBrightnessObserver.updateBlockingZoneThresholds(displayDeviceConfig,
+                /* attemptLoadingFromDeviceConfig= */ true);
+            mBrightnessObserver.reloadLightSensor(displayDeviceConfig);
+            mHbmObserver.setupHdrRefreshRates(displayDeviceConfig);
+        }
     }
 
     /**
@@ -561,7 +570,7 @@
 
     /**
      * Sets the display mode switching type.
-     * @param newType
+     * @param newType new mode switching type
      */
     public void setModeSwitchingType(@DisplayManager.SwitchingType int newType) {
         synchronized (mLock) {
@@ -678,6 +687,18 @@
         notifyDesiredDisplayModeSpecsChangedLocked();
     }
 
+    @GuardedBy("mLock")
+    private float getMaxRefreshRateLocked(int displayId) {
+        Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
+        float maxRefreshRate = 0f;
+        for (Display.Mode mode : modes) {
+            if (mode.getRefreshRate() > maxRefreshRate) {
+                maxRefreshRate = mode.getRefreshRate();
+            }
+        }
+        return maxRefreshRate;
+    }
+
     private void notifyDesiredDisplayModeSpecsChangedLocked() {
         if (mDesiredDisplayModeSpecsListener != null
                 && !mHandler.hasMessages(MSG_REFRESH_RATE_RANGE_CHANGED)) {
@@ -996,25 +1017,29 @@
         // of low priority voters. It votes [0, max(PEAK, MIN)]
         public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 7;
 
+        // To avoid delay in switching between 60HZ -> 90HZ when activating LHBM, set refresh
+        // rate to max value (same as for PRIORITY_UDFPS) on lock screen
+        public static final int PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE = 8;
+
         // LOW_POWER_MODE force display to [0, 60HZ] if Settings.Global.LOW_POWER_MODE is on.
-        public static final int PRIORITY_LOW_POWER_MODE = 8;
+        public static final int PRIORITY_LOW_POWER_MODE = 9;
 
         // PRIORITY_FLICKER_REFRESH_RATE_SWITCH votes for disabling refresh rate switching. If the
         // higher priority voters' result is a range, it will fix the rate to a single choice.
         // It's used to avoid refresh rate switches in certain conditions which may result in the
         // user seeing the display flickering when the switches occur.
-        public static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 9;
+        public static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 10;
 
         // Force display to [0, 60HZ] if skin temperature is at or above CRITICAL.
-        public static final int PRIORITY_SKIN_TEMPERATURE = 10;
+        public static final int PRIORITY_SKIN_TEMPERATURE = 11;
 
         // The proximity sensor needs the refresh rate to be locked in order to function, so this is
         // set to a high priority.
-        public static final int PRIORITY_PROXIMITY = 11;
+        public static final int PRIORITY_PROXIMITY = 12;
 
         // The Under-Display Fingerprint Sensor (UDFPS) needs the refresh rate to be locked in order
         // to function, so this needs to be the highest priority of all votes.
-        public static final int PRIORITY_UDFPS = 12;
+        public static final int PRIORITY_UDFPS = 13;
 
         // Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and
         // APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString.
@@ -1117,6 +1142,8 @@
                     return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE";
                 case PRIORITY_USER_SETTING_PEAK_REFRESH_RATE:
                     return "PRIORITY_USER_SETTING_PEAK_REFRESH_RATE";
+                case PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE:
+                    return "PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE";
                 default:
                     return Integer.toString(priority);
             }
@@ -2329,6 +2356,7 @@
 
     private class UdfpsObserver extends IUdfpsHbmListener.Stub {
         private final SparseBooleanArray mLocalHbmEnabled = new SparseBooleanArray();
+        private final SparseBooleanArray mAuthenticationPossible = new SparseBooleanArray();
 
         public void observe() {
             StatusBarManagerInternal statusBar =
@@ -2354,25 +2382,28 @@
 
         private void updateHbmStateLocked(int displayId, boolean enabled) {
             mLocalHbmEnabled.put(displayId, enabled);
-            updateVoteLocked(displayId);
+            updateVoteLocked(displayId, enabled, Vote.PRIORITY_UDFPS);
         }
 
-        private void updateVoteLocked(int displayId) {
+        @Override
+        public void onAuthenticationPossible(int displayId, boolean isPossible) {
+            synchronized (mLock) {
+                mAuthenticationPossible.put(displayId, isPossible);
+                updateVoteLocked(displayId, isPossible,
+                        Vote.PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE);
+            }
+        }
+
+        @GuardedBy("mLock")
+        private void updateVoteLocked(int displayId, boolean enabled, int votePriority) {
             final Vote vote;
-            if (mLocalHbmEnabled.get(displayId)) {
-                Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
-                float maxRefreshRate = 0f;
-                for (Display.Mode mode : modes) {
-                    if (mode.getRefreshRate() > maxRefreshRate) {
-                        maxRefreshRate = mode.getRefreshRate();
-                    }
-                }
+            if (enabled) {
+                float maxRefreshRate = DisplayModeDirector.this.getMaxRefreshRateLocked(displayId);
                 vote = Vote.forRefreshRates(maxRefreshRate, maxRefreshRate);
             } else {
                 vote = null;
             }
-
-            DisplayModeDirector.this.updateVoteLocked(displayId, Vote.PRIORITY_UDFPS, vote);
+            DisplayModeDirector.this.updateVoteLocked(displayId, votePriority, vote);
         }
 
         void dumpLocked(PrintWriter pw) {
@@ -2383,6 +2414,13 @@
                 final String enabled = mLocalHbmEnabled.valueAt(i) ? "enabled" : "disabled";
                 pw.println("      Display " + displayId + ": " + enabled);
             }
+            pw.println("    mAuthenticationPossible: ");
+            for (int i = 0; i < mAuthenticationPossible.size(); i++) {
+                final int displayId = mAuthenticationPossible.keyAt(i);
+                final String isPossible = mAuthenticationPossible.valueAt(i) ? "possible"
+                        : "impossible";
+                pw.println("      Display " + displayId + ": " + isPossible);
+            }
         }
     }
 
@@ -2505,7 +2543,7 @@
      * HBM that are associated with that display. Restrictions are retrieved from
      * DisplayManagerInternal but originate in the display-device-config file.
      */
-    public static class HbmObserver implements DisplayManager.DisplayListener {
+    public class HbmObserver implements DisplayManager.DisplayListener {
         private final BallotBox mBallotBox;
         private final Handler mHandler;
         private final SparseIntArray mHbmMode = new SparseIntArray();
@@ -2525,10 +2563,24 @@
             mDeviceConfigDisplaySettings = displaySettings;
         }
 
-        public void observe() {
-            mRefreshRateInHbmSunlight = mDeviceConfigDisplaySettings.getRefreshRateInHbmSunlight();
-            mRefreshRateInHbmHdr = mDeviceConfigDisplaySettings.getRefreshRateInHbmHdr();
+        /**
+         * Sets up the refresh rate to be used when HDR is enabled
+         */
+        public void setupHdrRefreshRates(DisplayDeviceConfig displayDeviceConfig) {
+            mRefreshRateInHbmHdr = mDeviceConfigDisplaySettings
+                .getRefreshRateInHbmHdr(displayDeviceConfig);
+            mRefreshRateInHbmSunlight = mDeviceConfigDisplaySettings
+                .getRefreshRateInHbmSunlight(displayDeviceConfig);
+        }
 
+        /**
+         * Sets up the HDR refresh rates, and starts observing for the changes in the display that
+         * might impact it
+         */
+        public void observe() {
+            synchronized (mLock) {
+                setupHdrRefreshRates(mDefaultDisplayDeviceConfig);
+            }
             mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
             mInjector.registerDisplayListener(this, mHandler,
                     DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
@@ -2760,26 +2812,33 @@
                     -1);
         }
 
-        public int getRefreshRateInHbmSunlight() {
-            final int defaultRefreshRateInHbmSunlight =
-                    mContext.getResources().getInteger(
-                            R.integer.config_defaultRefreshRateInHbmSunlight);
-
-            final int refreshRate = mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
-                    DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_SUNLIGHT,
-                    defaultRefreshRateInHbmSunlight);
-
+        public int getRefreshRateInHbmHdr(DisplayDeviceConfig displayDeviceConfig) {
+            int refreshRate =
+                    (displayDeviceConfig == null) ? mContext.getResources().getInteger(
+                            R.integer.config_defaultRefreshRateInHbmHdr)
+                            : displayDeviceConfig.getDefaultRefreshRateInHbmHdr();
+            try {
+                refreshRate = mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+                    DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_HDR,
+                    refreshRate);
+            } catch (NullPointerException e) {
+                // Do Nothing
+            }
             return refreshRate;
         }
 
-        public int getRefreshRateInHbmHdr() {
-            final int defaultRefreshRateInHbmHdr =
-                    mContext.getResources().getInteger(R.integer.config_defaultRefreshRateInHbmHdr);
-
-            final int refreshRate = mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
-                    DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_HDR,
-                    defaultRefreshRateInHbmHdr);
-
+        public int getRefreshRateInHbmSunlight(DisplayDeviceConfig displayDeviceConfig) {
+            int refreshRate =
+                    (displayDeviceConfig == null) ? mContext.getResources()
+                        .getInteger(R.integer.config_defaultRefreshRateInHbmSunlight)
+                        : displayDeviceConfig.getDefaultRefreshRateInHbmSunlight();
+            try {
+                refreshRate = mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+                    DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_SUNLIGHT,
+                    refreshRate);
+            } catch (NullPointerException e) {
+                // Do Nothing
+            }
             return refreshRate;
         }
 
@@ -2812,8 +2871,8 @@
                     .sendToTarget();
 
             if (refreshRateInLowZone != -1) {
-                mHandler.obtainMessage(MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED, refreshRateInLowZone)
-                    .sendToTarget();
+                mHandler.obtainMessage(MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED, refreshRateInLowZone,
+                        0).sendToTarget();
             }
 
             int[] highDisplayBrightnessThresholds = getHighDisplayBrightnessThresholds();
@@ -2825,18 +2884,22 @@
                     .sendToTarget();
 
             if (refreshRateInHighZone != -1) {
-                mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED, refreshRateInHighZone)
-                    .sendToTarget();
+                mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED, refreshRateInHighZone,
+                        0).sendToTarget();
             }
 
-            final int refreshRateInHbmSunlight = getRefreshRateInHbmSunlight();
-            mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HBM_SUNLIGHT_CHANGED,
-                    refreshRateInHbmSunlight, 0)
+            synchronized (mLock) {
+                final int refreshRateInHbmSunlight =
+                        getRefreshRateInHbmSunlight(mDefaultDisplayDeviceConfig);
+                mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HBM_SUNLIGHT_CHANGED,
+                        refreshRateInHbmSunlight, 0)
                     .sendToTarget();
 
-            final int refreshRateInHbmHdr = getRefreshRateInHbmHdr();
-            mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HBM_HDR_CHANGED, refreshRateInHbmHdr, 0)
+                final int refreshRateInHbmHdr =
+                        getRefreshRateInHbmHdr(mDefaultDisplayDeviceConfig);
+                mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HBM_HDR_CHANGED, refreshRateInHbmHdr, 0)
                     .sendToTarget();
+            }
         }
 
         private int[] getIntArrayProperty(String prop) {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index a9a35f2..5ecc7f2 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -132,6 +132,8 @@
     private static final int MSG_UPDATE_RBC = 11;
     private static final int MSG_BRIGHTNESS_RAMP_DONE = 12;
     private static final int MSG_STATSD_HBM_BRIGHTNESS = 13;
+    private static final int MSG_SWITCH_USER = 14;
+    private static final int MSG_BOOT_COMPLETED = 15;
 
     private static final int PROXIMITY_UNKNOWN = -1;
     private static final int PROXIMITY_NEGATIVE = 0;
@@ -505,6 +507,8 @@
     private boolean mIsEnabled;
     private boolean mIsInTransition;
 
+    private boolean mBootCompleted;
+
     /**
      * Creates the display power controller.
      */
@@ -512,7 +516,8 @@
             DisplayPowerCallbacks callbacks, Handler handler,
             SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
             BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
-            Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata) {
+            Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata,
+            boolean bootCompleted) {
         mLogicalDisplay = logicalDisplay;
         mDisplayId = mLogicalDisplay.getDisplayIdLocked();
         final String displayIdStr = "[" + mDisplayId + "]";
@@ -654,6 +659,7 @@
         mTemporaryAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
         mPendingAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
 
+        mBootCompleted = bootCompleted;
     }
 
     private void applyReduceBrightColorsSplineAdjustment() {
@@ -703,6 +709,11 @@
     }
 
     public void onSwitchUser(@UserIdInt int newUserId) {
+        Message msg = mHandler.obtainMessage(MSG_SWITCH_USER, newUserId);
+        mHandler.sendMessage(msg);
+    }
+
+    private void handleOnSwitchUser(@UserIdInt int newUserId) {
         handleSettingsChange(true /* userSwitch */);
         handleBrightnessModeChange();
         if (mBrightnessTracker != null) {
@@ -1364,7 +1375,7 @@
 
         // Initialize things the first time the power state is changed.
         if (mustInitialize) {
-            initialize(state);
+            initialize(readyToUpdateDisplayState() ? state : Display.STATE_UNKNOWN);
         }
 
         // Animate the screen state change unless already animating.
@@ -2044,7 +2055,8 @@
                 }
             }
 
-            if (!reportOnly && mPowerState.getScreenState() != state) {
+            if (!reportOnly && mPowerState.getScreenState() != state
+                    && readyToUpdateDisplayState()) {
                 Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenState", state);
                 // TODO(b/153319140) remove when we can get this from the above trace invocation
                 SystemProperties.set("debug.tracing.screen_state", String.valueOf(state));
@@ -2491,6 +2503,10 @@
         mBrightnessSetting.setBrightness(brightnessValue);
     }
 
+    void onBootCompleted() {
+        mHandler.obtainMessage(MSG_BOOT_COMPLETED).sendToTarget();
+    }
+
     private void updateScreenBrightnessSetting(float brightnessValue) {
         if (!isValidBrightnessValue(brightnessValue)
                 || brightnessValue == mCurrentScreenBrightnessSetting) {
@@ -2636,6 +2652,17 @@
         }
     };
 
+    /**
+     * Indicates whether the display state is ready to update. If this is the default display, we
+     * want to update it right away so that we can draw the boot animation on it. If it is not
+     * the default display, drawing the boot animation on it would look incorrect, so we need
+     * to wait until boot is completed.
+     * @return True if the display state is ready to update
+     */
+    private boolean readyToUpdateDisplayState() {
+        return mDisplayId == Display.DEFAULT_DISPLAY || mBootCompleted;
+    }
+
     public void dump(final PrintWriter pw) {
         synchronized (mLock) {
             pw.println();
@@ -3167,6 +3194,15 @@
                 case MSG_STATSD_HBM_BRIGHTNESS:
                     logHbmBrightnessStats(Float.intBitsToFloat(msg.arg1), msg.arg2);
                     break;
+
+                case MSG_SWITCH_USER:
+                    handleOnSwitchUser(msg.arg1);
+                    break;
+
+                case MSG_BOOT_COMPLETED:
+                    mBootCompleted = true;
+                    updatePowerState();
+                    break;
             }
         }
     }
diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java
index 017b96c..2276715 100644
--- a/services/core/java/com/android/server/display/color/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java
@@ -107,11 +107,6 @@
         Matrix.setIdentityM(MATRIX_IDENTITY, 0);
     }
 
-    /**
-     * The transition time, in milliseconds, for Night Display to turn on/off.
-     */
-    private static final long TRANSITION_DURATION = 3000L;
-
     private static final int MSG_USER_CHANGED = 0;
     private static final int MSG_SET_UP = 1;
     private static final int MSG_APPLY_NIGHT_DISPLAY_IMMEDIATE = 2;
@@ -661,7 +656,7 @@
             TintValueAnimator valueAnimator = TintValueAnimator.ofMatrix(COLOR_MATRIX_EVALUATOR,
                     from == null ? MATRIX_IDENTITY : from, to);
             tintController.setAnimator(valueAnimator);
-            valueAnimator.setDuration(TRANSITION_DURATION);
+            valueAnimator.setDuration(tintController.getTransitionDurationMilliseconds());
             valueAnimator.setInterpolator(AnimationUtils.loadInterpolator(
                     getContext(), android.R.interpolator.fast_out_slow_in));
             valueAnimator.addUpdateListener((ValueAnimator animator) -> {
diff --git a/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java b/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java
index 93a78c1..f27ccc7 100644
--- a/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java
+++ b/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java
@@ -59,7 +59,8 @@
     private float[] mCurrentColorTemperatureXYZ;
     @VisibleForTesting
     boolean mSetUp = false;
-    private float[] mMatrixDisplayWhiteBalance = new float[16];
+    private final float[] mMatrixDisplayWhiteBalance = new float[16];
+    private long mTransitionDuration;
     private Boolean mIsAvailable;
     // This feature becomes disallowed if the device is in an unsupported strong/light state.
     private boolean mIsAllowed = true;
@@ -119,6 +120,9 @@
         final int colorTemperature = res.getInteger(
                 R.integer.config_displayWhiteBalanceColorTemperatureDefault);
 
+        mTransitionDuration = res.getInteger(
+                R.integer.config_displayWhiteBalanceTransitionTime);
+
         synchronized (mLock) {
             mDisplayColorSpaceRGB = displayColorSpaceRGB;
             mDisplayNominalWhiteXYZ = displayNominalWhiteXYZ;
@@ -232,6 +236,11 @@
     }
 
     @Override
+    public long getTransitionDurationMilliseconds() {
+        return mTransitionDuration;
+    }
+
+    @Override
     public void dump(PrintWriter pw) {
         synchronized (mLock) {
             pw.println("    mSetUp = " + mSetUp);
diff --git a/services/core/java/com/android/server/display/color/TintController.java b/services/core/java/com/android/server/display/color/TintController.java
index 422dd32..c53ac06 100644
--- a/services/core/java/com/android/server/display/color/TintController.java
+++ b/services/core/java/com/android/server/display/color/TintController.java
@@ -16,7 +16,6 @@
 
 package com.android.server.display.color;
 
-import android.animation.ValueAnimator;
 import android.content.Context;
 import android.util.Slog;
 
@@ -24,6 +23,11 @@
 
 abstract class TintController {
 
+    /**
+     * The default transition time, in milliseconds, for color transforms to turn on/off.
+     */
+    private static final long TRANSITION_DURATION = 3000L;
+
     private ColorDisplayService.TintValueAnimator mAnimator;
     private Boolean mIsActivated;
 
@@ -66,6 +70,10 @@
         return mIsActivated == null;
     }
 
+    public long getTransitionDurationMilliseconds() {
+        return TRANSITION_DURATION;
+    }
+
     /**
      * Dump debug information.
      */
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
index 85d5b4f..5b772fc 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
@@ -419,16 +419,16 @@
         float ambientBrightness = mBrightnessFilter.getEstimate(time);
         mLatestAmbientBrightness = ambientBrightness;
 
-        if (ambientColorTemperature != -1.0f &&
-                mLowLightAmbientBrightnessToBiasSpline != null) {
+        if (ambientColorTemperature != -1.0f && ambientBrightness != -1.0f
+                && mLowLightAmbientBrightnessToBiasSpline != null) {
             float bias = mLowLightAmbientBrightnessToBiasSpline.interpolate(ambientBrightness);
             ambientColorTemperature =
                     bias * ambientColorTemperature + (1.0f - bias)
                     * mLowLightAmbientColorTemperature;
             mLatestLowLightBias = bias;
         }
-        if (ambientColorTemperature != -1.0f &&
-                mHighLightAmbientBrightnessToBiasSpline != null) {
+        if (ambientColorTemperature != -1.0f && ambientBrightness != -1.0f
+                && mHighLightAmbientBrightnessToBiasSpline != null) {
             float bias = mHighLightAmbientBrightnessToBiasSpline.interpolate(ambientBrightness);
             ambientColorTemperature =
                     (1.0f - bias) * ambientColorTemperature + bias
diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java
index db9deb1..97ab587 100644
--- a/services/core/java/com/android/server/dreams/DreamController.java
+++ b/services/core/java/com/android/server/dreams/DreamController.java
@@ -42,6 +42,7 @@
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
+import java.util.Objects;
 
 /**
  * Internal controller for starting and stopping the current dream and managing related state.
@@ -119,10 +120,20 @@
                     + ", isPreviewMode=" + isPreviewMode + ", canDoze=" + canDoze
                     + ", userId=" + userId + ", reason='" + reason + "'");
 
-            if (mCurrentDream != null) {
-                mPreviousDreams.add(mCurrentDream);
-            }
+            final DreamRecord oldDream = mCurrentDream;
             mCurrentDream = new DreamRecord(token, name, isPreviewMode, canDoze, userId, wakeLock);
+            if (oldDream != null) {
+                if (!oldDream.mWakingGently) {
+                    // We will stop these previous dreams once the new dream is started.
+                    mPreviousDreams.add(oldDream);
+                } else if (Objects.equals(oldDream.mName, mCurrentDream.mName)) {
+                    // We are attempting to start a dream that is currently waking up gently.
+                    // Let's silently stop the old instance here to clear the dream state.
+                    // This should happen after the new mCurrentDream is set to avoid announcing
+                    // a "dream stopped" state.
+                    stopDreamInstance(/* immediately */ true, "restarting same dream", oldDream);
+                }
+            }
 
             mCurrentDream.mDreamStartTime = SystemClock.elapsedRealtime();
             MetricsLogger.visible(mContext,
@@ -245,6 +256,7 @@
 
                 mListener.onDreamStopped(dream.mToken);
             }
+
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_POWER);
         }
@@ -269,7 +281,7 @@
         try {
             service.asBinder().linkToDeath(mCurrentDream, 0);
             service.attach(mCurrentDream.mToken, mCurrentDream.mCanDoze,
-                    mCurrentDream.mDreamingStartedCallback);
+                    mCurrentDream.mIsPreviewMode, mCurrentDream.mDreamingStartedCallback);
         } catch (RemoteException ex) {
             Slog.e(TAG, "The dream service died unexpectedly.", ex);
             stopDream(true /*immediate*/, "attach failed");
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index bb1e393..5b375d7 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -660,6 +660,10 @@
 
         Slog.i(TAG, "Entering dreamland.");
 
+        if (mCurrentDream != null && mCurrentDream.isDozing) {
+            stopDozingInternal(mCurrentDream.token);
+        }
+
         mCurrentDream = new DreamRecord(name, userId, isPreviewMode, canDoze);
 
         if (!mCurrentDream.name.equals(mAmbientDisplayComponent)) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index faa219e..cbbee5d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -4831,6 +4831,13 @@
             Slog.w(TAG, "Ignoring setInputMethod of uid " + Binder.getCallingUid()
                     + " token: " + token);
             return;
+        } else {
+            // Called with current IME's token.
+            if (mMethodMap.get(id) != null
+                    && mSettings.getEnabledInputMethodListWithFilterLocked(
+                            (info) -> info.getId().equals(id)).isEmpty()) {
+                throw new IllegalStateException("Requested IME is not enabled: " + id);
+            }
         }
 
         final long ident = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
index dc52990..1fb00ef 100644
--- a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
@@ -16,6 +16,8 @@
 
 package com.android.server.location.injector;
 
+import static com.android.server.location.LocationManagerService.TAG;
+
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -23,6 +25,7 @@
 import android.os.SystemClock;
 import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyManager;
+import android.util.Log;
 
 import com.android.server.FgThread;
 
@@ -67,8 +70,12 @@
                 }
 
                 synchronized (SystemEmergencyHelper.this) {
-                    mIsInEmergencyCall = mTelephonyManager.isEmergencyNumber(
-                            intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER));
+                    try {
+                        mIsInEmergencyCall = mTelephonyManager.isEmergencyNumber(
+                                intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER));
+                    } catch (IllegalStateException e) {
+                        Log.w(TAG, "Failed to call TelephonyManager.isEmergencyNumber().", e);
+                    }
                 }
             }
         }, new IntentFilter(Intent.ACTION_NEW_OUTGOING_CALL));
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 1235352..f0aff2a 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -300,6 +300,7 @@
         public void deliverOnFlushComplete(int requestCode) throws PendingIntent.CanceledException {
             BroadcastOptions options = BroadcastOptions.makeBasic();
             options.setDontSendToRestrictedApps(true);
+            options.setPendingIntentBackgroundActivityLaunchAllowed(false);
 
             mPendingIntent.send(mContext, 0, new Intent().putExtra(KEY_FLUSH_COMPLETE, requestCode),
                     null, null, null, options.toBundle());
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 78cffa6..59794f4 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -80,6 +80,7 @@
 import android.hardware.fingerprint.FingerprintManager;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -117,6 +118,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.EventLog;
+import android.util.Log;
 import android.util.LongSparseArray;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -203,7 +205,7 @@
     private static final String TAG = "LockSettingsService";
     private static final String PERMISSION = ACCESS_KEYGUARD_SECURE_STORAGE;
     private static final String BIOMETRIC_PERMISSION = MANAGE_BIOMETRIC;
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.DEBUG);
 
     private static final int PROFILE_KEY_IV_SIZE = 12;
     private static final String SEPARATE_PROFILE_CHALLENGE_KEY = "lockscreen.profilechallenge";
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
index 1203769..678698b 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
@@ -25,6 +25,7 @@
 import android.app.admin.DevicePolicyManager;
 import android.app.trust.IStrongAuthTracker;
 import android.content.Context;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -33,6 +34,7 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.ArrayMap;
+import android.util.Log;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
@@ -46,8 +48,8 @@
  */
 public class LockSettingsStrongAuth {
 
-    private static final String TAG = "LockSettings";
-    private static final boolean DEBUG = false;
+    private static final String TAG = "LockSettingsStrongAuth";
+    private static final boolean DEBUG = Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.DEBUG);
 
     private static final int MSG_REQUIRE_STRONG_AUTH = 1;
     private static final int MSG_REGISTER_TRACKER = 2;
@@ -267,6 +269,7 @@
     }
 
     private void handleScheduleStrongAuthTimeout(int userId) {
+        if (DEBUG) Slog.d(TAG, "handleScheduleStrongAuthTimeout for userId=" + userId);
         rescheduleStrongAuthTimeoutAlarm(mInjector.getElapsedRealtimeMs(), userId);
 
         // cancel current non-strong biometric alarm listener for the user (if there was one)
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 4d55d4e..25fefad 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -132,6 +132,7 @@
 
     // contains connections to all connected services, including app services
     // and system services
+    @GuardedBy("mMutex")
     private final ArrayList<ManagedServiceInfo> mServices = new ArrayList<>();
     /**
      * The services that have been bound by us. If the service is also connected, it will also
@@ -150,13 +151,15 @@
             = new ArraySet<>();
     // Just the packages from mEnabledServicesForCurrentProfiles
     private ArraySet<String> mEnabledServicesPackageNames = new ArraySet<>();
-    // List of enabled packages that have nevertheless asked not to be run
-    private ArraySet<ComponentName> mSnoozingForCurrentProfiles = new ArraySet<>();
+    // Per user id, list of enabled packages that have nevertheless asked not to be run
+    private final android.util.SparseSetArray<ComponentName> mSnoozing =
+            new android.util.SparseSetArray<>();
 
     // List of approved packages or components (by user, then by primary/secondary) that are
     // allowed to be bound as managed services. A package or component appearing in this list does
     // not mean that we are currently bound to said package/component.
-    protected ArrayMap<Integer, ArrayMap<Boolean, ArraySet<String>>> mApproved = new ArrayMap<>();
+    protected final ArrayMap<Integer, ArrayMap<Boolean, ArraySet<String>>> mApproved =
+            new ArrayMap<>();
 
     // List of packages or components (by user) that are configured to be enabled/disabled
     // explicitly by the user
@@ -315,6 +318,7 @@
         return changes;
     }
 
+    @GuardedBy("mApproved")
     private boolean clearUserSetFlagLocked(ComponentName component, int userId) {
         String approvedValue = getApprovedValue(component.flattenToString());
         ArraySet<String> userSet = mUserSetServices.get(userId);
@@ -375,8 +379,8 @@
             pw.println("      " + cmpt);
         }
 
-        pw.println("    Live " + getCaption() + "s (" + mServices.size() + "):");
         synchronized (mMutex) {
+            pw.println("    Live " + getCaption() + "s (" + mServices.size() + "):");
             for (ManagedServiceInfo info : mServices) {
                 if (filter != null && !filter.matches(info.component)) continue;
                 pw.println("      " + info.component
@@ -386,10 +390,15 @@
             }
         }
 
-        pw.println("    Snoozed " + getCaption() + "s (" +
-                mSnoozingForCurrentProfiles.size() + "):");
-        for (ComponentName name : mSnoozingForCurrentProfiles) {
-            pw.println("      " + name.flattenToShortString());
+        synchronized (mSnoozing) {
+            pw.println("    Snoozed " + getCaption() + "s ("
+                    + mSnoozing.size() + "):");
+            for (int i = 0; i < mSnoozing.size(); i++) {
+                pw.println("      User: " + mSnoozing.keyAt(i));
+                for (ComponentName name : mSnoozing.valuesAt(i)) {
+                    pw.println("        " + name.flattenToShortString());
+                }
+            }
         }
     }
 
@@ -431,8 +440,16 @@
             }
         }
 
-        for (ComponentName name : mSnoozingForCurrentProfiles) {
-            name.dumpDebug(proto, ManagedServicesProto.SNOOZED);
+        synchronized (mSnoozing) {
+            for (int i = 0; i < mSnoozing.size(); i++) {
+                long token = proto.start(ManagedServicesProto.SNOOZED);
+                proto.write(ManagedServicesProto.SnoozedServices.USER_ID,
+                        mSnoozing.keyAt(i));
+                for (ComponentName name : mSnoozing.valuesAt(i)) {
+                    name.dumpDebug(proto, ManagedServicesProto.SnoozedServices.SNOOZED);
+                }
+                proto.end(token);
+            }
         }
     }
 
@@ -975,6 +992,9 @@
         synchronized (mApproved) {
             mApproved.remove(user);
         }
+        synchronized (mSnoozing) {
+            mSnoozing.remove(user);
+        }
         rebindServices(true, user);
     }
 
@@ -994,10 +1014,12 @@
             return null;
         }
         final IBinder token = service.asBinder();
-        final int N = mServices.size();
-        for (int i = 0; i < N; i++) {
-            final ManagedServiceInfo info = mServices.get(i);
-            if (info.service.asBinder() == token) return info;
+        synchronized (mMutex) {
+            final int nServices = mServices.size();
+            for (int i = 0; i < nServices; i++) {
+                final ManagedServiceInfo info = mServices.get(i);
+                if (info.service.asBinder() == token) return info;
+            }
         }
         return null;
     }
@@ -1066,15 +1088,17 @@
     }
 
     protected void setComponentState(ComponentName component, int userId, boolean enabled) {
-        boolean previous = !mSnoozingForCurrentProfiles.contains(component);
-        if (previous == enabled) {
-            return;
-        }
+        synchronized (mSnoozing) {
+            boolean previous = !mSnoozing.contains(userId, component);
+            if (previous == enabled) {
+                return;
+            }
 
-        if (enabled) {
-            mSnoozingForCurrentProfiles.remove(component);
-        } else {
-            mSnoozingForCurrentProfiles.add(component);
+            if (enabled) {
+                mSnoozing.remove(userId, component);
+            } else {
+                mSnoozing.add(userId, component);
+            }
         }
 
         // State changed
@@ -1287,7 +1311,10 @@
             }
 
             final Set<ComponentName> add = new HashSet<>(userComponents);
-            add.removeAll(mSnoozingForCurrentProfiles);
+            ArraySet<ComponentName> snoozed = mSnoozing.get(userId);
+            if (snoozed != null) {
+                add.removeAll(snoozed);
+            }
 
             componentsToBind.put(userId, add);
 
@@ -1466,10 +1493,12 @@
         }
     }
 
+    @GuardedBy("mMutex")
     private void registerServiceLocked(final ComponentName name, final int userid) {
         registerServiceLocked(name, userid, false /* isSystem */);
     }
 
+    @GuardedBy("mMutex")
     private void registerServiceLocked(final ComponentName name, final int userid,
             final boolean isSystem) {
         if (DEBUG) Slog.v(TAG, "registerService: " + name + " u=" + userid);
@@ -1600,6 +1629,7 @@
         }
     }
 
+    @GuardedBy("mMutex")
     private void unregisterServiceLocked(ComponentName name, int userid) {
         final int N = mServices.size();
         for (int i = N - 1; i >= 0; i--) {
@@ -1634,6 +1664,7 @@
         return serviceInfo;
     }
 
+    @GuardedBy("mMutex")
     private ManagedServiceInfo removeServiceLocked(int i) {
         final ManagedServiceInfo info = mServices.remove(i);
         onServiceRemovedLocked(info);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6bc8582..ff10cbc 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -6687,6 +6687,31 @@
             }
         }
 
+        // Ensure all actions are present
+        if (notification.actions != null) {
+            boolean hasNullActions = false;
+            int nActions = notification.actions.length;
+            for (int i = 0; i < nActions; i++) {
+                if (notification.actions[i] == null) {
+                    hasNullActions = true;
+                    break;
+                }
+            }
+            if (hasNullActions) {
+                ArrayList<Notification.Action> nonNullActions = new ArrayList<>();
+                for (int i = 0; i < nActions; i++) {
+                    if (notification.actions[i] != null) {
+                        nonNullActions.add(notification.actions[i]);
+                    }
+                }
+                if (nonNullActions.size() != 0) {
+                    notification.actions = nonNullActions.toArray(new Notification.Action[0]);
+                } else {
+                    notification.actions = null;
+                }
+            }
+        }
+
         // Ensure CallStyle has all the correct actions
         if (notification.isStyle(Notification.CallStyle.class)) {
             Notification.Builder builder =
@@ -8518,6 +8543,9 @@
             if (interceptBefore && !record.isIntercepted()
                     && record.isNewEnoughForAlerting(System.currentTimeMillis())) {
                 buzzBeepBlinkLocked(record);
+
+                // Log alert after change in intercepted state to Zen Log as well
+                ZenLog.traceAlertOnUpdatedIntercept(record);
             }
         }
         if (changed) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index d344306..1501d69 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -114,6 +114,8 @@
 
     // is this notification currently being intercepted by Zen Mode?
     private boolean mIntercept;
+    // has the intercept value been set explicitly? we only want to log it if new or changed
+    private boolean mInterceptSet;
 
     // is this notification hidden since the app pkg is suspended?
     private boolean mHidden;
@@ -914,6 +916,7 @@
 
     public boolean setIntercepted(boolean intercept) {
         mIntercept = intercept;
+        mInterceptSet = true;
         return mIntercept;
     }
 
@@ -934,6 +937,10 @@
         return mIntercept;
     }
 
+    public boolean hasInterceptBeenSet() {
+        return mInterceptSet;
+    }
+
     public boolean isNewEnoughForAlerting(long now) {
         return getFreshnessMs(now) <= MAX_SOUND_DELAY_MS;
     }
diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java
index c0bc474..35b94e7 100644
--- a/services/core/java/com/android/server/notification/ZenLog.java
+++ b/services/core/java/com/android/server/notification/ZenLog.java
@@ -68,20 +68,23 @@
     private static final int TYPE_MATCHES_CALL_FILTER = 18;
     private static final int TYPE_RECORD_CALLER = 19;
     private static final int TYPE_CHECK_REPEAT_CALLER = 20;
+    private static final int TYPE_ALERT_ON_UPDATED_INTERCEPT = 21;
 
     private static int sNext;
     private static int sSize;
 
     public static void traceIntercepted(NotificationRecord record, String reason) {
-        if (record != null && record.isIntercepted()) return;  // already logged
         append(TYPE_INTERCEPTED, record.getKey() + "," + reason);
     }
 
     public static void traceNotIntercepted(NotificationRecord record, String reason) {
-        if (record != null && record.isUpdate) return;  // already logged
         append(TYPE_NOT_INTERCEPTED, record.getKey() + "," + reason);
     }
 
+    public static void traceAlertOnUpdatedIntercept(NotificationRecord record) {
+        append(TYPE_ALERT_ON_UPDATED_INTERCEPT, record.getKey());
+    }
+
     public static void traceSetRingerModeExternal(int ringerModeOld, int ringerModeNew,
             String caller, int ringerModeInternalIn, int ringerModeInternalOut) {
         append(TYPE_SET_RINGER_MODE_EXTERNAL, caller + ",e:" +
@@ -219,6 +222,7 @@
             case TYPE_MATCHES_CALL_FILTER: return "matches_call_filter";
             case TYPE_RECORD_CALLER: return "record_caller";
             case TYPE_CHECK_REPEAT_CALLER: return "check_repeat_caller";
+            case TYPE_ALERT_ON_UPDATED_INTERCEPT: return "alert_on_updated_intercept";
             default: return "unknown";
         }
     }
diff --git a/services/core/java/com/android/server/notification/ZenModeFiltering.java b/services/core/java/com/android/server/notification/ZenModeFiltering.java
index db0ce2e..5b7b0c1 100644
--- a/services/core/java/com/android/server/notification/ZenModeFiltering.java
+++ b/services/core/java/com/android/server/notification/ZenModeFiltering.java
@@ -155,85 +155,85 @@
 
         if (isCritical(record)) {
             // Zen mode is ignored for critical notifications.
-            ZenLog.traceNotIntercepted(record, "criticalNotification");
+            maybeLogInterceptDecision(record, false, "criticalNotification");
             return false;
         }
         // Make an exception to policy for the notification saying that policy has changed
         if (NotificationManager.Policy.areAllVisualEffectsSuppressed(policy.suppressedVisualEffects)
                 && "android".equals(record.getSbn().getPackageName())
                 && SystemMessageProto.SystemMessage.NOTE_ZEN_UPGRADE == record.getSbn().getId()) {
-            ZenLog.traceNotIntercepted(record, "systemDndChangedNotification");
+            maybeLogInterceptDecision(record, false, "systemDndChangedNotification");
             return false;
         }
         switch (zen) {
             case Global.ZEN_MODE_NO_INTERRUPTIONS:
                 // #notevenalarms
-                ZenLog.traceIntercepted(record, "none");
+                maybeLogInterceptDecision(record, true, "none");
                 return true;
             case Global.ZEN_MODE_ALARMS:
                 if (isAlarm(record)) {
                     // Alarms only
-                    ZenLog.traceNotIntercepted(record, "alarm");
+                    maybeLogInterceptDecision(record, false, "alarm");
                     return false;
                 }
-                ZenLog.traceIntercepted(record, "alarmsOnly");
+                maybeLogInterceptDecision(record, true, "alarmsOnly");
                 return true;
             case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
                 // allow user-prioritized packages through in priority mode
                 if (record.getPackagePriority() == Notification.PRIORITY_MAX) {
-                    ZenLog.traceNotIntercepted(record, "priorityApp");
+                    maybeLogInterceptDecision(record, false, "priorityApp");
                     return false;
                 }
 
                 if (isAlarm(record)) {
                     if (!policy.allowAlarms()) {
-                        ZenLog.traceIntercepted(record, "!allowAlarms");
+                        maybeLogInterceptDecision(record, true, "!allowAlarms");
                         return true;
                     }
-                    ZenLog.traceNotIntercepted(record, "allowedAlarm");
+                    maybeLogInterceptDecision(record, false, "allowedAlarm");
                     return false;
                 }
                 if (isEvent(record)) {
                     if (!policy.allowEvents()) {
-                        ZenLog.traceIntercepted(record, "!allowEvents");
+                        maybeLogInterceptDecision(record, true, "!allowEvents");
                         return true;
                     }
-                    ZenLog.traceNotIntercepted(record, "allowedEvent");
+                    maybeLogInterceptDecision(record, false, "allowedEvent");
                     return false;
                 }
                 if (isReminder(record)) {
                     if (!policy.allowReminders()) {
-                        ZenLog.traceIntercepted(record, "!allowReminders");
+                        maybeLogInterceptDecision(record, true, "!allowReminders");
                         return true;
                     }
-                    ZenLog.traceNotIntercepted(record, "allowedReminder");
+                    maybeLogInterceptDecision(record, false, "allowedReminder");
                     return false;
                 }
                 if (isMedia(record)) {
                     if (!policy.allowMedia()) {
-                        ZenLog.traceIntercepted(record, "!allowMedia");
+                        maybeLogInterceptDecision(record, true, "!allowMedia");
                         return true;
                     }
-                    ZenLog.traceNotIntercepted(record, "allowedMedia");
+                    maybeLogInterceptDecision(record, false, "allowedMedia");
                     return false;
                 }
                 if (isSystem(record)) {
                     if (!policy.allowSystem()) {
-                        ZenLog.traceIntercepted(record, "!allowSystem");
+                        maybeLogInterceptDecision(record, true, "!allowSystem");
                         return true;
                     }
-                    ZenLog.traceNotIntercepted(record, "allowedSystem");
+                    maybeLogInterceptDecision(record, false, "allowedSystem");
                     return false;
                 }
                 if (isConversation(record)) {
                     if (policy.allowConversations()) {
                         if (policy.priorityConversationSenders == CONVERSATION_SENDERS_ANYONE) {
-                            ZenLog.traceNotIntercepted(record, "conversationAnyone");
+                            maybeLogInterceptDecision(record, false, "conversationAnyone");
                             return false;
                         } else if (policy.priorityConversationSenders
                                 == NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT
                                 && record.getChannel().isImportantConversation()) {
-                            ZenLog.traceNotIntercepted(record, "conversationMatches");
+                            maybeLogInterceptDecision(record, false, "conversationMatches");
                             return false;
                         }
                     }
@@ -244,31 +244,59 @@
                     if (policy.allowRepeatCallers()
                             && REPEAT_CALLERS.isRepeat(
                                     mContext, extras(record), record.getPhoneNumbers())) {
-                        ZenLog.traceNotIntercepted(record, "repeatCaller");
+                        maybeLogInterceptDecision(record, false, "repeatCaller");
                         return false;
                     }
                     if (!policy.allowCalls()) {
-                        ZenLog.traceIntercepted(record, "!allowCalls");
+                        maybeLogInterceptDecision(record, true, "!allowCalls");
                         return true;
                     }
                     return shouldInterceptAudience(policy.allowCallsFrom(), record);
                 }
                 if (isMessage(record)) {
                     if (!policy.allowMessages()) {
-                        ZenLog.traceIntercepted(record, "!allowMessages");
+                        maybeLogInterceptDecision(record, true, "!allowMessages");
                         return true;
                     }
                     return shouldInterceptAudience(policy.allowMessagesFrom(), record);
                 }
 
-                ZenLog.traceIntercepted(record, "!priority");
+                maybeLogInterceptDecision(record, true, "!priority");
                 return true;
             default:
-                ZenLog.traceNotIntercepted(record, "unknownZenMode");
+                maybeLogInterceptDecision(record, false, "unknownZenMode");
                 return false;
         }
     }
 
+    // Consider logging the decision of shouldIntercept for the given record.
+    // This will log the outcome if one of the following is true:
+    //   - it's the first time the intercept decision is set for the record
+    //   - OR it's not the first time, but the intercept decision changed
+    private static void maybeLogInterceptDecision(NotificationRecord record, boolean intercept,
+            String reason) {
+        boolean interceptBefore = record.isIntercepted();
+        if (record.hasInterceptBeenSet() && (interceptBefore == intercept)) {
+            // this record has already been evaluated for whether it should be intercepted, and
+            // the decision has not changed.
+            return;
+        }
+
+        // add a note to the reason indicating whether it's new or updated
+        String annotatedReason = reason;
+        if (!record.hasInterceptBeenSet()) {
+            annotatedReason = "new:" + reason;
+        } else if (interceptBefore != intercept) {
+            annotatedReason = "updated:" + reason;
+        }
+
+        if (intercept) {
+            ZenLog.traceIntercepted(record, annotatedReason);
+        } else {
+            ZenLog.traceNotIntercepted(record, annotatedReason);
+        }
+    }
+
     /**
      * Check if the notification is too critical to be suppressed.
      *
@@ -285,10 +313,10 @@
     private static boolean shouldInterceptAudience(int source, NotificationRecord record) {
         float affinity = record.getContactAffinity();
         if (!audienceMatches(source, affinity)) {
-            ZenLog.traceIntercepted(record, "!audienceMatches,affinity=" + affinity);
+            maybeLogInterceptDecision(record, true, "!audienceMatches,affinity=" + affinity);
             return true;
         }
-        ZenLog.traceNotIntercepted(record, "affinity=" + affinity);
+        maybeLogInterceptDecision(record, false, "affinity=" + affinity);
         return false;
     }
 
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 7da5f51..c32a57c 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -94,6 +94,7 @@
 import android.annotation.UserIdInt;
 import android.app.AppOpsManager;
 import android.app.ApplicationPackageManager;
+import android.app.BroadcastOptions;
 import android.app.backup.IBackupManager;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -641,7 +642,10 @@
         fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
                 PackageManager.installStatusToPublicStatus(returnCode));
         try {
-            target.sendIntent(context, 0, fillIn, null, null);
+            final BroadcastOptions options = BroadcastOptions.makeBasic();
+            options.setPendingIntentBackgroundActivityLaunchAllowed(false);
+            target.sendIntent(context, 0, fillIn, null /* onFinished*/, null /* handler */,
+                    null /* requiredPermission */, options.toBundle());
         } catch (IntentSender.SendIntentException ignored) {
         }
     }
@@ -2425,10 +2429,10 @@
             // will be null whereas dataOwnerPkg will contain information about the package
             // which was uninstalled while keeping its data.
             AndroidPackage dataOwnerPkg = mPm.mPackages.get(packageName);
+            PackageSetting dataOwnerPs = mPm.mSettings.getPackageLPr(packageName);
             if (dataOwnerPkg  == null) {
-                PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
-                if (ps != null) {
-                    dataOwnerPkg = ps.getPkg();
+                if (dataOwnerPs != null) {
+                    dataOwnerPkg = dataOwnerPs.getPkg();
                 }
             }
 
@@ -2456,6 +2460,7 @@
             if (dataOwnerPkg != null && !dataOwnerPkg.isSdkLibrary()) {
                 if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags,
                         dataOwnerPkg.isDebuggable())) {
+                    // Downgrade is not permitted; a lower version of the app will not be allowed
                     try {
                         PackageManagerServiceUtils.checkDowngrade(dataOwnerPkg, pkgLite);
                     } catch (PackageManagerException e) {
@@ -2464,6 +2469,28 @@
                         return Pair.create(
                                 PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE, errorMsg);
                     }
+                } else if (dataOwnerPs.isSystem()) {
+                    // Downgrade is permitted, but system apps can't be downgraded below
+                    // the version preloaded onto the system image
+                    final PackageSetting disabledPs = mPm.mSettings.getDisabledSystemPkgLPr(
+                            dataOwnerPs);
+                    if (disabledPs != null) {
+                        dataOwnerPkg = disabledPs.getPkg();
+                    }
+                    if (!Build.IS_DEBUGGABLE && !dataOwnerPkg.isDebuggable()) {
+                        // Only restrict non-debuggable builds and non-debuggable version of the app
+                        try {
+                            PackageManagerServiceUtils.checkDowngrade(dataOwnerPkg, pkgLite);
+                        } catch (PackageManagerException e) {
+                            String errorMsg =
+                                    "System app: " + packageName + " cannot be downgraded to"
+                                            + " older than its preloaded version on the system"
+                                            + " image. " + e.getMessage();
+                            Slog.w(TAG, errorMsg);
+                            return Pair.create(
+                                    PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE, errorMsg);
+                        }
+                    }
                 }
             }
         }
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 5e0fc3b..8b04c3a 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -22,6 +22,7 @@
 import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
 import static android.content.pm.LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS;
 import static android.content.pm.LauncherApps.FLAG_CACHE_NOTIFICATION_SHORTCUTS;
 import static android.content.pm.LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS;
@@ -1114,12 +1115,19 @@
 
             // Flag for bubble
             ActivityOptions options = ActivityOptions.fromBundle(startActivityOptions);
-            if (options != null && options.isApplyActivityFlagsForBubbles()) {
-                // Flag for bubble to make behaviour match documentLaunchMode=always.
-                intents[0].addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
-                intents[0].addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+            if (options != null) {
+                if (options.isApplyActivityFlagsForBubbles()) {
+                    // Flag for bubble to make behaviour match documentLaunchMode=always.
+                    intents[0].addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
+                    intents[0].addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+                }
+                if (options.isApplyMultipleTaskFlagForShortcut()) {
+                    intents[0].addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+                }
+                if (options.isApplyNoUserActionFlagForShortcut()) {
+                    intents[0].addFlags(FLAG_ACTIVITY_NO_USER_ACTION);
+                }
             }
-
             intents[0].addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             intents[0].setSourceBounds(sourceBounds);
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index bb23d89d..02cf433 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -27,6 +27,7 @@
 import android.app.ActivityManager;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
+import android.app.BroadcastOptions;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PackageDeleteObserver;
@@ -1360,7 +1361,10 @@
                     PackageInstaller.STATUS_PENDING_USER_ACTION);
             fillIn.putExtra(Intent.EXTRA_INTENT, intent);
             try {
-                mTarget.sendIntent(mContext, 0, fillIn, null, null);
+                final BroadcastOptions options = BroadcastOptions.makeBasic();
+                options.setPendingIntentBackgroundActivityLaunchAllowed(false);
+                mTarget.sendIntent(mContext, 0, fillIn, null /* onFinished*/,
+                        null /* handler */, null /* requiredPermission */, options.toBundle());
             } catch (SendIntentException ignored) {
             }
         }
@@ -1385,7 +1389,10 @@
                     PackageManager.deleteStatusToString(returnCode, msg));
             fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
             try {
-                mTarget.sendIntent(mContext, 0, fillIn, null, null);
+                final BroadcastOptions options = BroadcastOptions.makeBasic();
+                options.setPendingIntentBackgroundActivityLaunchAllowed(false);
+                mTarget.sendIntent(mContext, 0, fillIn, null /* onFinished*/,
+                        null /* handler */, null /* requiredPermission */, options.toBundle());
             } catch (SendIntentException ignored) {
             }
         }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 7c2e3ea..1823de8 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -54,6 +54,7 @@
 import android.annotation.Nullable;
 import android.annotation.WorkerThread;
 import android.app.AppOpsManager;
+import android.app.BroadcastOptions;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.admin.DevicePolicyEventLogger;
@@ -4274,7 +4275,10 @@
         fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_PENDING_USER_ACTION);
         fillIn.putExtra(Intent.EXTRA_INTENT, intent);
         try {
-            target.sendIntent(context, 0, fillIn, null, null);
+            final BroadcastOptions options = BroadcastOptions.makeBasic();
+            options.setPendingIntentBackgroundActivityLaunchAllowed(false);
+            target.sendIntent(context, 0, fillIn, null /* onFinished */,
+                    null /* handler */, null /* requiredPermission */, options.toBundle());
         } catch (IntentSender.SendIntentException ignored) {
         }
     }
@@ -4315,7 +4319,10 @@
             }
         }
         try {
-            target.sendIntent(context, 0, fillIn, null, null);
+            final BroadcastOptions options = BroadcastOptions.makeBasic();
+            options.setPendingIntentBackgroundActivityLaunchAllowed(false);
+            target.sendIntent(context, 0, fillIn, null /* onFinished */,
+                    null /* handler */, null /* requiredPermission */, options.toBundle());
         } catch (IntentSender.SendIntentException ignored) {
         }
     }
@@ -4349,7 +4356,10 @@
             intent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, "Staging Image Not Ready");
         }
         try {
-            target.sendIntent(context, 0, intent, null, null);
+            final BroadcastOptions options = BroadcastOptions.makeBasic();
+            options.setPendingIntentBackgroundActivityLaunchAllowed(false);
+            target.sendIntent(context, 0, intent, null /* onFinished */,
+                    null /* handler */, null /* requiredPermission */, options.toBundle());
         } catch (IntentSender.SendIntentException ignored) {
         }
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8d2714c..4786037 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -53,6 +53,7 @@
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.ApplicationPackageManager;
+import android.app.BroadcastOptions;
 import android.app.IActivityManager;
 import android.app.admin.IDevicePolicyManager;
 import android.app.admin.SecurityLog;
@@ -4798,7 +4799,11 @@
                 }
                 if (pi != null) {
                     try {
-                        pi.sendIntent(null, success ? 1 : 0, null, null, null);
+                        final BroadcastOptions options = BroadcastOptions.makeBasic();
+                        options.setPendingIntentBackgroundActivityLaunchAllowed(false);
+                        pi.sendIntent(null, success ? 1 : 0, null /* intent */,
+                                null /* onFinished*/, null /* handler */,
+                                null /* requiredPermission */, options.toBundle());
                     } catch (SendIntentException e) {
                         Slog.w(TAG, e);
                     }
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 890c891..632a34e 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -885,7 +885,12 @@
      * available ShareTarget definitions in this package.
      */
     public List<ShortcutManager.ShareShortcutInfo> getMatchingShareTargets(
-            @NonNull IntentFilter filter) {
+            @NonNull final IntentFilter filter) {
+        return getMatchingShareTargets(filter, null);
+    }
+
+    List<ShortcutManager.ShareShortcutInfo> getMatchingShareTargets(
+            @NonNull final IntentFilter filter, @Nullable final String pkgName) {
         synchronized (mLock) {
             final List<ShareTargetInfo> matchedTargets = new ArrayList<>();
             for (int i = 0; i < mShareTargets.size(); i++) {
@@ -909,8 +914,7 @@
             // included in the result
             findAll(shortcuts, ShortcutInfo::isNonManifestVisible,
                     ShortcutInfo.CLONE_REMOVE_FOR_APP_PREDICTION,
-                    mShortcutUser.mService.mContext.getPackageName(),
-                    0, /*getPinnedByAnyLauncher=*/ false);
+                    pkgName, 0, /*getPinnedByAnyLauncher=*/ false);
 
             final List<ShortcutManager.ShareShortcutInfo> result = new ArrayList<>();
             for (int i = 0; i < shortcuts.size(); i++) {
@@ -1108,7 +1112,7 @@
 
         // Now prepare to publish manifest shortcuts.
         List<ShortcutInfo> newManifestShortcutList = null;
-        final int shareTargetSize;
+        int shareTargetSize = 0;
         synchronized (mLock) {
             try {
                 shareTargetSize = mShareTargets.size();
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 0b20683..49831d7 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -2512,11 +2512,17 @@
         }
         enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APP_PREDICTIONS,
                 "getShareTargets");
+        final ComponentName chooser = injectChooserActivity();
+        final String pkg = (chooser != null
+                && mPackageManagerInternal.getComponentEnabledSetting(chooser,
+                injectBinderCallingUid(), userId) == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)
+                ? chooser.getPackageName() : mContext.getPackageName();
         synchronized (mLock) {
             throwIfUserLockedL(userId);
             final List<ShortcutManager.ShareShortcutInfo> shortcutInfoList = new ArrayList<>();
             final ShortcutUser user = getUserShortcutsLocked(userId);
-            user.forAllPackages(p -> shortcutInfoList.addAll(p.getMatchingShareTargets(filter)));
+            user.forAllPackages(p -> shortcutInfoList.addAll(
+                    p.getMatchingShareTargets(filter, pkg)));
             return new ParceledListSlice<>(shortcutInfoList);
         }
     }
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index 20c9a21..9ed5aa7 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -3268,7 +3268,7 @@
         if (Objects.equals(packageName, PLATFORM_PACKAGE_NAME)) {
             return true;
         }
-        if (!pkg.isPrivileged()) {
+        if (!(pkg.isSystem() && pkg.isPrivileged())) {
             return true;
         }
         if (!mPrivilegedPermissionAllowlistSourcePackageNames
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index 7ce7f7e..810fa5f 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -247,6 +247,9 @@
     private static final String MAX_NUM_COMPONENTS_ERR_MSG =
             "Total number of components has exceeded the maximum number: " + MAX_NUM_COMPONENTS;
 
+    /** The maximum permission name length. */
+    private static final int MAX_PERMISSION_NAME_LENGTH = 512;
+
     @IntDef(flag = true, prefix = { "PARSE_" }, value = {
             PARSE_CHATTY,
             PARSE_COLLECT_CERTIFICATES,
@@ -1275,6 +1278,11 @@
             // that may change.
             String name = sa.getNonResourceString(
                     R.styleable.AndroidManifestUsesPermission_name);
+            if (TextUtils.length(name) > MAX_PERMISSION_NAME_LENGTH) {
+                return input.error(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                        "The name in the <uses-permission> is greater than "
+                                + MAX_PERMISSION_NAME_LENGTH);
+            }
 
             int maxSdkVersion = 0;
             TypedValue val = sa.peekValue(
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index b380d84..f913cef 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -329,6 +329,8 @@
     static public final String SYSTEM_DIALOG_REASON_SCREENSHOT = "screenshot";
     static public final String SYSTEM_DIALOG_REASON_GESTURE_NAV = "gestureNav";
 
+    public static final String TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD = "waitForAllWindowsDrawn";
+
     private static final String TALKBACK_LABEL = "TalkBack";
 
     private static final int POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS = 800;
@@ -975,12 +977,7 @@
             powerMultiPressAction(eventTime, interactive, mTriplePressOnPowerBehavior);
         } else if (count > 3 && count <= getMaxMultiPressPowerCount()) {
             Slog.d(TAG, "No behavior defined for power press count " + count);
-        } else if (count == 1 && interactive) {
-            if (beganFromNonInteractive) {
-                // The screen off case, where we might want to start dreaming on power button press.
-                attemptToDreamFromShortPowerButtonPress(false, () -> {});
-                return;
-            }
+        } else if (count == 1 && interactive && !beganFromNonInteractive) {
             if (mSideFpsEventHandler.shouldConsumeSinglePress(eventTime)) {
                 Slog.i(TAG, "Suppressing power key because the user is interacting with the "
                         + "fingerprint sensor");
@@ -3327,8 +3324,8 @@
     }
 
     @Override
-    public void onKeyguardOccludedChangedLw(boolean occluded) {
-        if (mKeyguardDelegate != null && mKeyguardDelegate.isShowing()) {
+    public void onKeyguardOccludedChangedLw(boolean occluded, boolean waitAppTransition) {
+        if (mKeyguardDelegate != null && waitAppTransition) {
             mPendingKeyguardOccluded = occluded;
             mKeyguardOccludedChanged = true;
         } else {
@@ -4729,10 +4726,15 @@
 
         // ... eventually calls finishWindowsDrawn which will finalize our screen turn on
         // as well as enabling the orientation change logic/sensor.
+        Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER,
+                TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, /* cookie= */ 0);
         mWindowManagerInternal.waitForAllWindowsDrawn(() -> {
             if (DEBUG_WAKEUP) Slog.i(TAG, "All windows ready for every display");
             mHandler.sendMessage(mHandler.obtainMessage(MSG_WINDOW_MANAGER_DRAWN_COMPLETE,
                     INVALID_DISPLAY, 0));
+
+            Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER,
+                    TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, /* cookie= */ 0);
             }, WAITING_FOR_DRAWN_TIMEOUT, INVALID_DISPLAY);
     }
 
@@ -4788,10 +4790,16 @@
             }
         } else {
             mScreenOnListeners.put(displayId, screenOnListener);
+
+            Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER,
+                    TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, /* cookie= */ 0);
             mWindowManagerInternal.waitForAllWindowsDrawn(() -> {
                 if (DEBUG_WAKEUP) Slog.i(TAG, "All windows ready for display: " + displayId);
                 mHandler.sendMessage(mHandler.obtainMessage(MSG_WINDOW_MANAGER_DRAWN_COMPLETE,
                         displayId, 0));
+
+                Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER,
+                        TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, /* cookie= */ 0);
             }, WAITING_FOR_DRAWN_TIMEOUT, displayId);
         }
     }
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 4f00992..77007fa 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -166,9 +166,10 @@
 
     /**
      * Called when the Keyguard occluded state changed.
+     *
      * @param occluded Whether Keyguard is currently occluded or not.
      */
-    void onKeyguardOccludedChangedLw(boolean occluded);
+    void onKeyguardOccludedChangedLw(boolean occluded, boolean waitAppTransition);
 
     /**
      * Applies a keyguard occlusion change if one happened.
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 651bb93..5d1a581 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -3262,8 +3262,7 @@
             }
             final PowerGroup powerGroup = mPowerGroups.get(groupId);
             wakefulness = powerGroup.getWakefulnessLocked();
-            if ((wakefulness == WAKEFULNESS_DREAMING || wakefulness == WAKEFULNESS_DOZING) &&
-                    powerGroup.isSandmanSummonedLocked() && powerGroup.isReadyLocked()) {
+            if (powerGroup.isSandmanSummonedLocked() && powerGroup.isReadyLocked()) {
                 startDreaming = canDreamLocked(powerGroup) || canDozeLocked(powerGroup);
                 powerGroup.setSandmanSummonedLocked(/* isSandmanSummoned= */ false);
             } else {
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 7252545..7489f80 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -709,7 +709,8 @@
         synchronized (mGlobalLock) {
             final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
             return r != null
-                    ? r.getRequestedOrientation() : ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+                    ? r.getOverrideOrientation()
+                    : ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 6b01a77..8aca912 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -439,6 +439,9 @@
     // finished destroying itself.
     private static final int DESTROY_TIMEOUT = 10 * 1000;
 
+    // Rounding tolerance to be used in aspect ratio computations
+    private static final float ASPECT_RATIO_ROUNDING_TOLERANCE = 0.005f;
+
     final ActivityTaskManagerService mAtmService;
     @NonNull
     final ActivityInfo info; // activity info provided by developer in AndroidManifest
@@ -1165,8 +1168,10 @@
             pw.println(prefix + "mVoiceInteraction=true");
         }
         pw.print(prefix); pw.print("mOccludesParent="); pw.println(mOccludesParent);
-        pw.print(prefix); pw.print("mOrientation=");
-        pw.println(ActivityInfo.screenOrientationToString(mOrientation));
+        pw.print(prefix); pw.print("overrideOrientation=");
+        pw.println(ActivityInfo.screenOrientationToString(getOverrideOrientation()));
+        pw.print(prefix); pw.print("requestedOrientation=");
+        pw.println(ActivityInfo.screenOrientationToString(super.getOverrideOrientation()));
         pw.println(prefix + "mVisibleRequested=" + mVisibleRequested
                 + " mVisible=" + mVisible + " mClientVisible=" + isClientVisible()
                 + ((mDeferHidingClient) ? " mDeferHidingClient=" + mDeferHidingClient : "")
@@ -1964,6 +1969,15 @@
                     new ComponentName(info.packageName, info.targetActivity);
         }
 
+        // Don't move below setActivityType since it triggers onConfigurationChange ->
+        // resolveOverrideConfiguration that requires having mLetterboxUiController initialised.
+        // Don't move below setOrientation(info.screenOrientation) since it triggers
+        // getOverrideOrientation that requires having mLetterboxUiController
+        // initialised.
+        mLetterboxUiController = new LetterboxUiController(mWmService, this);
+        mCameraCompatControlEnabled = mWmService.mContext.getResources()
+                .getBoolean(R.bool.config_isCameraCompatControlForStretchedIssuesEnabled);
+
         mTargetSdk = info.applicationInfo.targetSdkVersion;
         mShowForAllUsers = (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0;
         setOrientation(info.screenOrientation);
@@ -2084,12 +2098,6 @@
 
         launchMode = aInfo.launchMode;
 
-        // Don't move below setActivityType since it triggers onConfigurationChange ->
-        // resolveOverrideConfiguration that requires having mLetterboxUiController initialised.
-        mLetterboxUiController = new LetterboxUiController(mWmService, this);
-        mCameraCompatControlEnabled = mWmService.mContext.getResources()
-                .getBoolean(R.bool.config_isCameraCompatControlForStretchedIssuesEnabled);
-
         setActivityType(_componentSpecified, _launchedFromUid, _intent, options, sourceRecord);
 
         immersive = (aInfo.flags & FLAG_IMMERSIVE) != 0;
@@ -2486,7 +2494,8 @@
             if (topAttached != null) {
                 if (topAttached.isSnapshotCompatible(snapshot)
                         // This trampoline must be the same rotation.
-                        && mDisplayContent.getDisplayRotation().rotationForOrientation(mOrientation,
+                        && mDisplayContent.getDisplayRotation().rotationForOrientation(
+                                getOverrideOrientation(),
                                 mDisplayContent.getRotation()) == snapshot.getRotation()) {
                     return STARTING_WINDOW_TYPE_SNAPSHOT;
                 }
@@ -3897,6 +3906,10 @@
         }
     }
 
+    boolean isFinishing() {
+        return finishing;
+    }
+
     /**
      * This method is to only be called from the client via binder when the activity is destroyed
      * AND finished.
@@ -7704,13 +7717,13 @@
                 return mLetterboxUiController.getInheritedOrientation();
             }
         }
-        if (mOrientation == SCREEN_ORIENTATION_BEHIND && task != null) {
+        if (task != null && getOverrideOrientation() == SCREEN_ORIENTATION_BEHIND) {
             // We use Task here because we want to be consistent with what happens in
             // multi-window mode where other tasks orientations are ignored.
             final ActivityRecord belowCandidate = task.getActivity(
-                    a -> a.mOrientation != SCREEN_ORIENTATION_UNSET && !a.finishing
-                            && a.mOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND, this,
-                    false /* includeBoundary */, true /* traverseTopToBottom */);
+                    a -> a.canDefineOrientationForActivitiesAbove() /* callback */,
+                    this /* boundary */, false /* includeBoundary */,
+                    true /* traverseTopToBottom */);
             if (belowCandidate != null) {
                 return belowCandidate.getRequestedConfigurationOrientation(forDisplay);
             }
@@ -7718,6 +7731,19 @@
         return super.getRequestedConfigurationOrientation(forDisplay);
     }
 
+    /**
+     * Whether this activity can be used as an orientation source for activities above with
+     * {@link SCREEN_ORIENTATION_BEHIND}.
+     */
+    boolean canDefineOrientationForActivitiesAbove() {
+        if (finishing) {
+            return false;
+        }
+        final int overrideOrientation = getOverrideOrientation();
+        return overrideOrientation != SCREEN_ORIENTATION_UNSET
+                && overrideOrientation != SCREEN_ORIENTATION_BEHIND;
+    }
+
     @Override
     void onCancelFixedRotationTransform(int originalDisplayRotation) {
         if (this != mDisplayContent.getLastOrientationSource()) {
@@ -7744,7 +7770,7 @@
         }
     }
 
-    void setRequestedOrientation(int requestedOrientation) {
+    void setRequestedOrientation(@ActivityInfo.ScreenOrientation int requestedOrientation) {
         if (mLetterboxUiController.shouldIgnoreRequestedOrientation(requestedOrientation)) {
             return;
         }
@@ -7789,7 +7815,7 @@
             // Allow app to specify orientation regardless of its visibility state if the current
             // candidate want us to use orientation behind. I.e. the visible app on-top of this one
             // wants us to use the orientation of the app behind it.
-            return mOrientation;
+            return getOverrideOrientation();
         }
 
         // The {@link ActivityRecord} should only specify an orientation when it is not closing.
@@ -7797,15 +7823,31 @@
         // task being started in the wrong orientation during the transition.
         if (!getDisplayContent().mClosingApps.contains(this)
                 && (isVisibleRequested() || getDisplayContent().mOpeningApps.contains(this))) {
-            return mOrientation;
+            return getOverrideOrientation();
         }
 
         return SCREEN_ORIENTATION_UNSET;
     }
 
-    /** Returns the app's preferred orientation regardless of its currently visibility state. */
+    /**
+     * Returns the app's preferred orientation regardless of its current visibility state taking
+     * into account orientation per-app overrides applied by the device manufacturers.
+     */
+    @Override
+    protected int getOverrideOrientation() {
+        return mLetterboxUiController.overrideOrientationIfNeeded(super.getOverrideOrientation());
+    }
+
+    /**
+     * Returns the app's preferred orientation regardless of its currently visibility state. This
+     * is used to return a requested value to an app if they call {@link
+     * android.app.Activity#getRequestedOrientation} since {@link #getOverrideOrientation} value
+     * with override can confuse an app if it's different from what they requested with {@link
+     * android.app.Activity#setRequestedOrientation}.
+     */
+    @ActivityInfo.ScreenOrientation
     int getRequestedOrientation() {
-        return mOrientation;
+        return super.getOverrideOrientation();
     }
 
     /**
@@ -8204,8 +8246,12 @@
             if (screenResolvedBounds.width() <= parentAppBounds.width()) {
                 float positionMultiplier = mLetterboxUiController.getHorizontalPositionMultiplier(
                         newParentConfiguration);
-                offsetX = (int) Math.ceil((parentAppBounds.width() - screenResolvedBounds.width())
-                        * positionMultiplier);
+                offsetX = Math.max(0, (int) Math.ceil((parentAppBounds.width()
+                        - screenResolvedBounds.width()) * positionMultiplier)
+                        // This is added to make sure that insets added inside
+                        // CompatDisplayInsets#getContainerBounds() do not break the alignment
+                        // provided by the positionMultiplier
+                        - screenResolvedBounds.left + parentAppBounds.left);
             }
         }
 
@@ -8215,8 +8261,12 @@
             if (screenResolvedBounds.height() <= parentAppBounds.height()) {
                 float positionMultiplier = mLetterboxUiController.getVerticalPositionMultiplier(
                         newParentConfiguration);
-                offsetY = (int) Math.ceil((parentAppBounds.height() - screenResolvedBounds.height())
-                        * positionMultiplier);
+                offsetY = Math.max(0, (int) Math.ceil((parentAppBounds.height()
+                        - screenResolvedBounds.height()) * positionMultiplier)
+                        // This is added to make sure that insets added inside
+                        // CompatDisplayInsets#getContainerBounds() do not break the alignment
+                        // provided by the positionMultiplier
+                        - screenResolvedBounds.top + parentAppBounds.top);
             }
         }
 
@@ -8243,7 +8293,13 @@
     }
 
     void recomputeConfiguration() {
-        onRequestedOverrideConfigurationChanged(getRequestedOverrideConfiguration());
+        // We check if the current activity is transparent. In that case we need to
+        // recomputeConfiguration of the first opaque activity beneath, to allow a
+        // proper computation of the new bounds.
+        if (!mLetterboxUiController.applyOnOpaqueActivityBelow(
+                ActivityRecord::recomputeConfiguration)) {
+            onRequestedOverrideConfigurationChanged(getRequestedOverrideConfiguration());
+        }
     }
 
     boolean isInTransition() {
@@ -8357,8 +8413,8 @@
         // If orientation is respected when insets are applied, then stableBounds will be empty.
         boolean orientationRespectedWithInsets =
                 orientationRespectedWithInsets(parentBounds, stableBounds);
-        if (orientationRespectedWithInsets
-                && handlesOrientationChangeFromDescendant(mOrientation)) {
+        if (orientationRespectedWithInsets && handlesOrientationChangeFromDescendant(
+                getOverrideOrientation())) {
             // No need to letterbox because of fixed orientation. Display will handle
             // fixed-orientation requests and a display rotation is enough to respect requested
             // orientation with insets applied.
@@ -8915,7 +8971,7 @@
         int activityWidth = containingAppWidth;
         int activityHeight = containingAppHeight;
 
-        if (containingRatio > desiredAspectRatio) {
+        if (containingRatio - desiredAspectRatio > ASPECT_RATIO_ROUNDING_TOLERANCE) {
             if (containingAppWidth < containingAppHeight) {
                 // Width is the shorter side, so we use that to figure-out what the max. height
                 // should be given the aspect ratio.
@@ -8925,7 +8981,7 @@
                 // should be given the aspect ratio.
                 activityWidth = (int) ((activityHeight * desiredAspectRatio) + 0.5f);
             }
-        } else if (containingRatio < desiredAspectRatio) {
+        } else if (desiredAspectRatio - containingRatio > ASPECT_RATIO_ROUNDING_TOLERANCE) {
             boolean adjustWidth;
             switch (getRequestedConfigurationOrientation()) {
                 case ORIENTATION_LANDSCAPE:
@@ -9003,7 +9059,8 @@
         }
 
         if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY)
-                && !ActivityInfo.isFixedOrientationPortrait(getRequestedOrientation())) {
+                && !ActivityInfo.isFixedOrientationPortrait(
+                        getOverrideOrientation())) {
             return info.getMinAspectRatio();
         }
 
@@ -9998,6 +10055,7 @@
                     isLandscape ? shortSide : longSide);
         }
 
+        // TODO(b/267151420): Explore removing getContainerBounds() from CompatDisplayInsets.
         /** Gets the horizontal centered container bounds for size compatibility mode. */
         void getContainerBounds(Rect outAppBounds, Rect outBounds, int rotation, int orientation,
                 boolean orientationRequested, boolean isFixedToUserRotation) {
@@ -10166,8 +10224,8 @@
     // TODO(b/263592337): Explore enabling compat fake focus for fullscreen, e.g. for when
     // covered with bubbles.
     boolean shouldSendCompatFakeFocus() {
-        return mWmService.mLetterboxConfiguration.isCompatFakeFocusEnabled(info)
-                && inMultiWindowMode() && !inPinnedWindowingMode() && !inFreeformWindowingMode();
+        return mLetterboxUiController.shouldSendFakeFocus() && inMultiWindowMode()
+                && !inPinnedWindowingMode() && !inFreeformWindowingMode();
     }
 
     static class Builder {
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 0ea6157..87f985a 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -181,6 +181,42 @@
                 || !transitionGoodToGoForTaskFragments()) {
             return;
         }
+        final boolean isRecentsInOpening = mDisplayContent.mOpeningApps.stream().anyMatch(
+                ConfigurationContainer::isActivityTypeRecents);
+        // In order to avoid visual clutter caused by a conflict between app transition
+        // animation and recents animation, app transition is delayed until recents finishes.
+        // One exceptional case. When 3P launcher is used and a user taps a task screenshot in
+        // task switcher (isRecentsInOpening=true), app transition must start even though
+        // recents is running. Otherwise app transition is blocked until timeout (b/232984498).
+        // When 1P launcher is used, this animation is controlled by the launcher outside of
+        // the app transition, so delaying app transition doesn't cause visible delay. After
+        // recents finishes, app transition is handled just to commit visibility on apps.
+        if (!isRecentsInOpening) {
+            final ArraySet<WindowContainer> participants = new ArraySet<>();
+            participants.addAll(mDisplayContent.mOpeningApps);
+            participants.addAll(mDisplayContent.mChangingContainers);
+            boolean deferForRecents = false;
+            for (int i = 0; i < participants.size(); i++) {
+                WindowContainer wc = participants.valueAt(i);
+                final ActivityRecord activity = getAppFromContainer(wc);
+                if (activity == null) {
+                    continue;
+                }
+                // Don't defer recents animation if one of activity isn't running for it, that one
+                // might be started from quickstep.
+                if (!activity.isAnimating(PARENTS, ANIMATION_TYPE_RECENTS)) {
+                    deferForRecents = false;
+                    break;
+                }
+                deferForRecents = true;
+            }
+            if (deferForRecents) {
+                ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+                        "Delaying app transition for recents animation to finish");
+                return;
+            }
+        }
+
         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
 
         ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "**** GOOD TO GO");
@@ -1249,27 +1285,12 @@
                     "Delaying app transition for screen rotation animation to finish");
             return false;
         }
-        final boolean isRecentsInOpening = mDisplayContent.mOpeningApps.stream().anyMatch(
-                ConfigurationContainer::isActivityTypeRecents);
         for (int i = 0; i < apps.size(); i++) {
             WindowContainer wc = apps.valueAt(i);
             final ActivityRecord activity = getAppFromContainer(wc);
             if (activity == null) {
                 continue;
             }
-            // In order to avoid visual clutter caused by a conflict between app transition
-            // animation and recents animation, app transition is delayed until recents finishes.
-            // One exceptional case. When 3P launcher is used and a user taps a task screenshot in
-            // task switcher (isRecentsInOpening=true), app transition must start even though
-            // recents is running. Otherwise app transition is blocked until timeout (b/232984498).
-            // When 1P launcher is used, this animation is controlled by the launcher outside of
-            // the app transition, so delaying app transition doesn't cause visible delay. After
-            // recents finishes, app transition is handled just to commit visibility on apps.
-            if (!isRecentsInOpening && activity.isAnimating(PARENTS, ANIMATION_TYPE_RECENTS)) {
-                ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
-                        "Delaying app transition for recents animation to finish");
-                return false;
-            }
             ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
                     "Check opening app=%s: allDrawn=%b startingDisplayed=%b "
                             + "startingMoved=%b isRelaunching()=%b startingWindow=%s",
diff --git a/services/core/java/com/android/server/wm/DeviceStateController.java b/services/core/java/com/android/server/wm/DeviceStateController.java
index a6f8557..7d9a4ec 100644
--- a/services/core/java/com/android/server/wm/DeviceStateController.java
+++ b/services/core/java/com/android/server/wm/DeviceStateController.java
@@ -16,80 +16,107 @@
 
 package com.android.server.wm;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.devicestate.DeviceStateManager;
 import android.os.Handler;
 import android.os.HandlerExecutor;
 
+import com.android.internal.R;
 import com.android.internal.util.ArrayUtils;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.function.Consumer;
 
 /**
- * Class that registers callbacks with the {@link DeviceStateManager} and
- * responds to fold state changes by forwarding such events to a delegate.
+ * Class that registers callbacks with the {@link DeviceStateManager} and responds to device
+ * changes.
  */
-final class DeviceStateController {
+final class DeviceStateController implements DeviceStateManager.DeviceStateCallback {
+
+    @NonNull
     private final DeviceStateManager mDeviceStateManager;
-    private final Context mContext;
+    @NonNull
+    private final int[] mOpenDeviceStates;
+    @NonNull
+    private final int[] mHalfFoldedDeviceStates;
+    @NonNull
+    private final int[] mFoldedDeviceStates;
+    @NonNull
+    private final int[] mRearDisplayDeviceStates;
+    @NonNull
+    private final int[] mReverseRotationAroundZAxisStates;
+    @NonNull
+    private final List<Consumer<DeviceState>> mDeviceStateCallbacks = new ArrayList<>();
 
-    private FoldStateListener mDeviceStateListener;
+    @Nullable
+    private DeviceState mLastDeviceState;
+    private int mCurrentState;
 
-    public enum FoldState {
-        UNKNOWN, OPEN, FOLDED, HALF_FOLDED
+    public enum DeviceState {
+        UNKNOWN, OPEN, FOLDED, HALF_FOLDED, REAR,
     }
 
-    DeviceStateController(Context context, Handler handler, Consumer<FoldState> delegate) {
-        mContext = context;
-        mDeviceStateManager = mContext.getSystemService(DeviceStateManager.class);
+    DeviceStateController(@NonNull Context context, @NonNull Handler handler) {
+        mDeviceStateManager = context.getSystemService(DeviceStateManager.class);
+
+        mOpenDeviceStates = context.getResources()
+                .getIntArray(R.array.config_openDeviceStates);
+        mHalfFoldedDeviceStates = context.getResources()
+                .getIntArray(R.array.config_halfFoldedDeviceStates);
+        mFoldedDeviceStates = context.getResources()
+                .getIntArray(R.array.config_foldedDeviceStates);
+        mRearDisplayDeviceStates = context.getResources()
+                .getIntArray(R.array.config_rearDisplayDeviceStates);
+        mReverseRotationAroundZAxisStates = context.getResources()
+                .getIntArray(R.array.config_deviceStatesToReverseDefaultDisplayRotationAroundZAxis);
+
         if (mDeviceStateManager != null) {
-            mDeviceStateListener = new FoldStateListener(mContext, delegate);
-            mDeviceStateManager
-                    .registerCallback(new HandlerExecutor(handler),
-                            mDeviceStateListener);
+            mDeviceStateManager.registerCallback(new HandlerExecutor(handler), this);
         }
     }
 
     void unregisterFromDeviceStateManager() {
-        if (mDeviceStateListener != null) {
-            mDeviceStateManager.unregisterCallback(mDeviceStateListener);
+        if (mDeviceStateManager != null) {
+            mDeviceStateManager.unregisterCallback(this);
         }
     }
 
+    void registerDeviceStateCallback(@NonNull Consumer<DeviceState> callback) {
+        mDeviceStateCallbacks.add(callback);
+    }
+
     /**
-     * A listener for half-fold device state events that dispatches state changes to a delegate.
+     * @return true if the rotation direction on the Z axis should be reversed.
      */
-    static final class FoldStateListener implements DeviceStateManager.DeviceStateCallback {
+    boolean shouldReverseRotationDirectionAroundZAxis() {
+        return ArrayUtils.contains(mReverseRotationAroundZAxisStates, mCurrentState);
+    }
 
-        private final int[] mHalfFoldedDeviceStates;
-        private final int[] mFoldedDeviceStates;
+    @Override
+    public void onStateChanged(int state) {
+        mCurrentState = state;
 
-        @Nullable
-        private FoldState mLastResult;
-        private final Consumer<FoldState> mDelegate;
-
-        FoldStateListener(Context context, Consumer<FoldState> delegate) {
-            mFoldedDeviceStates = context.getResources().getIntArray(
-                    com.android.internal.R.array.config_foldedDeviceStates);
-            mHalfFoldedDeviceStates = context.getResources().getIntArray(
-                    com.android.internal.R.array.config_halfFoldedDeviceStates);
-            mDelegate = delegate;
+        final DeviceState deviceState;
+        if (ArrayUtils.contains(mHalfFoldedDeviceStates, state)) {
+            deviceState = DeviceState.HALF_FOLDED;
+        } else if (ArrayUtils.contains(mFoldedDeviceStates, state)) {
+            deviceState = DeviceState.FOLDED;
+        } else if (ArrayUtils.contains(mRearDisplayDeviceStates, state)) {
+            deviceState = DeviceState.REAR;
+        } else if (ArrayUtils.contains(mOpenDeviceStates, state)) {
+            deviceState = DeviceState.OPEN;
+        } else {
+            deviceState = DeviceState.UNKNOWN;
         }
 
-        @Override
-        public void onStateChanged(int state) {
-            final boolean halfFolded = ArrayUtils.contains(mHalfFoldedDeviceStates, state);
-            FoldState result;
-            if (halfFolded) {
-                result = FoldState.HALF_FOLDED;
-            } else {
-                final boolean folded = ArrayUtils.contains(mFoldedDeviceStates, state);
-                result = folded ? FoldState.FOLDED : FoldState.OPEN;
-            }
-            if (mLastResult == null || !mLastResult.equals(result)) {
-                mLastResult = result;
-                mDelegate.accept(result);
+        if (mLastDeviceState == null || !mLastDeviceState.equals(deviceState)) {
+            mLastDeviceState = deviceState;
+
+            for (Consumer<DeviceState> callback : mDeviceStateCallbacks) {
+                callback.accept(mLastDeviceState);
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index af5bd14..b8870a1 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -93,7 +94,7 @@
     DisplayArea(WindowManagerService wms, Type type, String name, int featureId) {
         super(wms);
         // TODO(display-area): move this up to ConfigurationContainer
-        mOrientation = SCREEN_ORIENTATION_UNSET;
+        setOverrideOrientation(SCREEN_ORIENTATION_UNSET);
         mType = type;
         mName = name;
         mFeatureId = featureId;
@@ -165,7 +166,8 @@
         // If this is set to ignore the orientation request, we don't propagate descendant
         // orientation request.
         final int orientation = requestingContainer != null
-                ? requestingContainer.mOrientation : SCREEN_ORIENTATION_UNSET;
+                ? requestingContainer.getOverrideOrientation()
+                : SCREEN_ORIENTATION_UNSET;
         return !getIgnoreOrientationRequest(orientation)
                 && super.onDescendantOrientationChanged(requestingContainer);
     }
@@ -244,7 +246,22 @@
                 || orientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
             return false;
         }
-        return getIgnoreOrientationRequest();
+        return getIgnoreOrientationRequest()
+                && !shouldRespectOrientationRequestDueToPerAppOverride();
+    }
+
+    private boolean shouldRespectOrientationRequestDueToPerAppOverride() {
+        if (mDisplayContent == null) {
+            return false;
+        }
+        ActivityRecord activity = mDisplayContent.topRunningActivity(
+                /* considerKeyguardState= */ true);
+        return activity != null && activity.getTaskFragment() != null
+                // Checking TaskFragment rather than ActivityRecord to ensure that transition
+                // between fullscreen and PiP would work well. Checking TaskFragment rather than
+                // Task to ensure that Activity Embedding is excluded.
+                && activity.getTaskFragment().getWindowingMode() == WINDOWING_MODE_FULLSCREEN
+                && activity.mLetterboxUiController.isOverrideRespectRequestedOrientationEnabled();
     }
 
     boolean getIgnoreOrientationRequest() {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 9af1b2b..b0eeceb 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -27,6 +27,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
@@ -1125,14 +1126,18 @@
                     mWmService.mAtmService.getRecentTasks().getInputListener());
         }
 
-        mDisplayPolicy = new DisplayPolicy(mWmService, this);
-        mDisplayRotation = new DisplayRotation(mWmService, this, mDisplayInfo.address);
+        mDeviceStateController = new DeviceStateController(mWmService.mContext, mWmService.mH);
 
-        mDeviceStateController = new DeviceStateController(mWmService.mContext, mWmService.mH,
-                newFoldState -> {
+        mDisplayPolicy = new DisplayPolicy(mWmService, this);
+        mDisplayRotation = new DisplayRotation(mWmService, this, mDisplayInfo.address,
+                mDeviceStateController);
+
+        final Consumer<DeviceStateController.DeviceState> deviceStateConsumer =
+                (@NonNull DeviceStateController.DeviceState newFoldState) -> {
                     mDisplaySwitchTransitionLauncher.foldStateChanged(newFoldState);
                     mDisplayRotation.foldStateChanged(newFoldState);
-                });
+                };
+        mDeviceStateController.registerDeviceStateCallback(deviceStateConsumer);
 
         mCloseToSquareMaxAspectRatio = mWmService.mContext.getResources().getFloat(
                 R.dimen.config_closeToSquareDisplayMaxAspectRatio);
@@ -1572,7 +1577,8 @@
         // If display rotation class tells us that it doesn't consider app requested orientation,
         // this display won't rotate just because of an app changes its requested orientation. Thus
         // it indicates that this display chooses not to handle this request.
-        final int orientation = requestingContainer != null ? requestingContainer.mOrientation
+        final int orientation = requestingContainer != null
+                ? requestingContainer.getOverrideOrientation()
                 : SCREEN_ORIENTATION_UNSET;
         final boolean handled = handlesOrientationChangeFromDescendant(orientation);
         if (config == null) {
@@ -1673,7 +1679,7 @@
             }
             // The orientation source may not be the top if it uses SCREEN_ORIENTATION_BEHIND.
             final ActivityRecord topCandidate = !r.isVisibleRequested() ? topRunningActivity() : r;
-            if (handleTopActivityLaunchingInDifferentOrientation(
+            if (topCandidate != null && handleTopActivityLaunchingInDifferentOrientation(
                     topCandidate, r, true /* checkOpening */)) {
                 // Display orientation should be deferred until the top fixed rotation is finished.
                 return false;
@@ -1698,15 +1704,15 @@
         if (mTransitionController.useShellTransitionsRotation()) {
             return ROTATION_UNDEFINED;
         }
+        final int activityOrientation = r.getOverrideOrientation();
         if (!WindowManagerService.ENABLE_FIXED_ROTATION_TRANSFORM
-                || getIgnoreOrientationRequest(r.mOrientation)) {
+                || getIgnoreOrientationRequest(activityOrientation)) {
             return ROTATION_UNDEFINED;
         }
-        if (r.mOrientation == ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
+        if (activityOrientation == ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
             final ActivityRecord nextCandidate = getActivity(
-                    a -> a.mOrientation != SCREEN_ORIENTATION_UNSET
-                            && a.mOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND,
-                    r, false /* includeBoundary */, true /* traverseTopToBottom */);
+                    a -> a.canDefineOrientationForActivitiesAbove() /* callback */,
+                    r /* boundary */, false /* includeBoundary */, true /* traverseTopToBottom */);
             if (nextCandidate != null) {
                 r = nextCandidate;
             }
@@ -2110,6 +2116,10 @@
                 w.seamlesslyRotateIfAllowed(transaction, oldRotation, rotation, rotateSeamlessly);
             }, true /* traverseTopToBottom */);
             mPinnedTaskController.startSeamlessRotationIfNeeded(transaction, oldRotation, rotation);
+            if (!mDisplayRotation.hasSeamlessRotatingWindow()) {
+                // Make sure DisplayRotation#isRotatingSeamlessly() will return false.
+                mDisplayRotation.cancelSeamlessRotation();
+            }
         }
 
         mWmService.mDisplayManagerInternal.performTraversal(transaction);
@@ -2723,6 +2733,15 @@
         final int orientation = super.getOrientation();
 
         if (!handlesOrientationChangeFromDescendant(orientation)) {
+            ActivityRecord topActivity = topRunningActivity(/* considerKeyguardState= */ true);
+            if (topActivity != null && topActivity.mLetterboxUiController
+                    .shouldUseDisplayLandscapeNaturalOrientation()) {
+                ProtoLog.v(WM_DEBUG_ORIENTATION,
+                        "Display id=%d is ignoring orientation request for %d, return %d"
+                        + " following a per-app override for %s",
+                        mDisplayId, orientation, SCREEN_ORIENTATION_LANDSCAPE, topActivity);
+                return SCREEN_ORIENTATION_LANDSCAPE;
+            }
             mLastOrientationSource = null;
             // Return SCREEN_ORIENTATION_UNSPECIFIED so that Display respect sensor rotation
             ProtoLog.v(WM_DEBUG_ORIENTATION,
@@ -3080,27 +3099,6 @@
     }
 
     /**
-     * Returns true if the input point is within an app window.
-     */
-    boolean pointWithinAppWindow(int x, int y) {
-        final int[] targetWindowType = {-1};
-        final PooledConsumer fn = PooledLambda.obtainConsumer((w, nonArg) -> {
-            if (targetWindowType[0] != -1) {
-                return;
-            }
-
-            if (w.isOnScreen() && w.isVisible() && w.getFrame().contains(x, y)) {
-                targetWindowType[0] = w.mAttrs.type;
-                return;
-            }
-        }, PooledLambda.__(WindowState.class), mTmpRect);
-        forAllWindows(fn, true /* traverseTopToBottom */);
-        fn.recycle();
-        return FIRST_APPLICATION_WINDOW <= targetWindowType[0]
-                && targetWindowType[0] <= LAST_APPLICATION_WINDOW;
-    }
-
-    /**
      * Find the task whose outside touch area (for resizing) (x, y) falls within.
      * Returns null if the touch doesn't fall into a resizing area.
      */
@@ -4809,7 +4807,7 @@
         mInsetsStateController.getImeSourceProvider().checkShowImePostLayout();
 
         mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent;
-        if (!mWmService.mDisplayFrozen) {
+        if (!mWmService.mDisplayFrozen && !mDisplayRotation.isRotatingSeamlessly()) {
             mWmService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
                     mLastHasContent,
                     mTmpApplySurfaceChangesTransactionState.preferredRefreshRate,
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index f8fd2b9..8e9a214 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2007,7 +2007,8 @@
                 dc.getDisplayPolicy().simulateLayoutDisplay(df);
                 final InsetsState insetsState = df.mInsetsState;
                 final Rect displayFrame = insetsState.getDisplayFrame();
-                final Insets decor = calculateDecorInsetsWithInternalTypes(insetsState);
+                final Insets decor = insetsState.calculateInsets(displayFrame, DECOR_TYPES,
+                        true /* ignoreVisibility */);
                 final Insets statusBar = insetsState.calculateInsets(displayFrame,
                         Type.statusBars(), true /* ignoreVisibility */);
                 mNonDecorInsets.set(decor.left, decor.top, decor.right, decor.bottom);
@@ -2039,17 +2040,8 @@
             }
         }
 
-        // TODO (b/235842600): Use public type once we can treat task bar as navigation bar.
-        static final int[] INTERNAL_DECOR_TYPES;
-        static {
-            final ArraySet<Integer> decorTypes = InsetsState.toInternalType(
-                    Type.displayCutout() | Type.navigationBars());
-            decorTypes.remove(ITYPE_EXTRA_NAVIGATION_BAR);
-            INTERNAL_DECOR_TYPES = new int[decorTypes.size()];
-            for (int i = 0; i < INTERNAL_DECOR_TYPES.length; i++) {
-                INTERNAL_DECOR_TYPES[i] = decorTypes.valueAt(i);
-            }
-        }
+
+        static final int DECOR_TYPES = Type.displayCutout() | Type.navigationBars();
 
         private final DisplayContent mDisplayContent;
         private final Info[] mInfoForRotation = new Info[4];
@@ -2076,20 +2068,6 @@
                 info.mNeedUpdate = true;
             }
         }
-
-        // TODO (b/235842600): Remove this method once we can treat task bar as navigation bar.
-        private static Insets calculateDecorInsetsWithInternalTypes(InsetsState state) {
-            final Rect frame = state.getDisplayFrame();
-            Insets insets = Insets.NONE;
-            for (int i = INTERNAL_DECOR_TYPES.length - 1; i >= 0; i--) {
-                final InsetsSource source = state.peekSource(INTERNAL_DECOR_TYPES[i]);
-                if (source != null) {
-                    insets = Insets.max(source.calculateInsets(frame, true /* ignoreVisibility */),
-                            insets);
-                }
-            }
-            return insets;
-        }
     }
 
     /**
@@ -2222,7 +2200,7 @@
      * Called when an app has started replacing its main window.
      */
     void addRelaunchingApp(ActivityRecord app) {
-        if (mSystemBarColorApps.contains(app)) {
+        if (mSystemBarColorApps.contains(app) && !app.hasStartingWindow()) {
             mRelaunchingSystemBarColorApps.add(app);
         }
     }
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index e6d8b3d..3404279 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -41,6 +41,7 @@
 
 import android.annotation.AnimRes;
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.content.ContentResolver;
@@ -117,6 +118,8 @@
     private SettingsObserver mSettingsObserver;
     @Nullable
     private FoldController mFoldController;
+    @NonNull
+    private final DeviceStateController mDeviceStateController;
 
     @ScreenOrientation
     private int mCurrentAppOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
@@ -218,21 +221,24 @@
     private boolean mDemoRotationLock;
 
     DisplayRotation(WindowManagerService service, DisplayContent displayContent,
-            DisplayAddress displayAddress) {
+            DisplayAddress displayAddress, @NonNull DeviceStateController deviceStateController) {
         this(service, displayContent, displayAddress, displayContent.getDisplayPolicy(),
-                service.mDisplayWindowSettings, service.mContext, service.getWindowManagerLock());
+                service.mDisplayWindowSettings, service.mContext, service.getWindowManagerLock(),
+                deviceStateController);
     }
 
     @VisibleForTesting
     DisplayRotation(WindowManagerService service, DisplayContent displayContent,
             DisplayAddress displayAddress, DisplayPolicy displayPolicy,
-            DisplayWindowSettings displayWindowSettings, Context context, Object lock) {
+            DisplayWindowSettings displayWindowSettings, Context context, Object lock,
+            @NonNull DeviceStateController deviceStateController) {
         mService = service;
         mDisplayContent = displayContent;
         mDisplayPolicy = displayPolicy;
         mDisplayWindowSettings = displayWindowSettings;
         mContext = context;
         mLock = lock;
+        mDeviceStateController = deviceStateController;
         isDefaultDisplay = displayContent.isDefaultDisplay;
         mCompatPolicyForImmersiveApps = initImmersiveAppCompatPolicy(service, displayContent);
 
@@ -243,11 +249,13 @@
         mDeskDockRotation = readRotation(R.integer.config_deskDockRotation);
         mUndockedHdmiRotation = readRotation(R.integer.config_undockedHdmiRotation);
 
-        mRotation = readDefaultDisplayRotation(displayAddress);
+        int defaultRotation = readDefaultDisplayRotation(displayAddress);
+        mRotation = defaultRotation;
 
         if (isDefaultDisplay) {
             final Handler uiHandler = UiThread.getHandler();
-            mOrientationListener = new OrientationListener(mContext, uiHandler);
+            mOrientationListener =
+                    new OrientationListener(mContext, uiHandler, defaultRotation);
             mOrientationListener.setCurrentRotation(mRotation);
             mSettingsObserver = new SettingsObserver(uiHandler);
             mSettingsObserver.observe();
@@ -1137,6 +1145,15 @@
         int sensorRotation = mOrientationListener != null
                 ? mOrientationListener.getProposedRotation() // may be -1
                 : -1;
+        if (mDeviceStateController.shouldReverseRotationDirectionAroundZAxis()) {
+            // Flipping 270 and 90 has the same effect as changing the direction which rotation is
+            // applied.
+            if (sensorRotation == Surface.ROTATION_90) {
+                sensorRotation = Surface.ROTATION_270;
+            } else if (sensorRotation == Surface.ROTATION_270) {
+                sensorRotation = Surface.ROTATION_90;
+            }
+        }
         mLastSensorRotation = sensorRotation;
         if (sensorRotation < 0) {
             sensorRotation = lastRotation;
@@ -1573,7 +1590,7 @@
         proto.end(token);
     }
 
-    boolean isDeviceInPosture(DeviceStateController.FoldState state, boolean isTabletop) {
+    boolean isDeviceInPosture(DeviceStateController.DeviceState state, boolean isTabletop) {
         if (mFoldController == null) return false;
         return mFoldController.isDeviceInPosture(state, isTabletop);
     }
@@ -1585,10 +1602,10 @@
     /**
      * Called by the DeviceStateManager callback when the device state changes.
      */
-    void foldStateChanged(DeviceStateController.FoldState foldState) {
+    void foldStateChanged(DeviceStateController.DeviceState deviceState) {
         if (mFoldController != null) {
             synchronized (mLock) {
-                mFoldController.foldStateChanged(foldState);
+                mFoldController.foldStateChanged(deviceState);
             }
         }
     }
@@ -1596,8 +1613,8 @@
     private class FoldController {
         @Surface.Rotation
         private int mHalfFoldSavedRotation = -1; // No saved rotation
-        private DeviceStateController.FoldState mFoldState =
-                DeviceStateController.FoldState.UNKNOWN;
+        private DeviceStateController.DeviceState mDeviceState =
+                DeviceStateController.DeviceState.UNKNOWN;
         private boolean mInHalfFoldTransition = false;
         private final boolean mIsDisplayAlwaysSeparatingHinge;
         private final Set<Integer> mTabletopRotations;
@@ -1637,32 +1654,33 @@
                     R.bool.config_isDisplayHingeAlwaysSeparating);
         }
 
-        boolean isDeviceInPosture(DeviceStateController.FoldState state, boolean isTabletop) {
-            if (state != mFoldState) {
+        boolean isDeviceInPosture(DeviceStateController.DeviceState state, boolean isTabletop) {
+            if (state != mDeviceState) {
                 return false;
             }
-            if (mFoldState == DeviceStateController.FoldState.HALF_FOLDED) {
+            if (mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED) {
                 return !(isTabletop ^ mTabletopRotations.contains(mRotation));
             }
             return true;
         }
 
-        DeviceStateController.FoldState getFoldState() {
-            return mFoldState;
+        DeviceStateController.DeviceState getFoldState() {
+            return mDeviceState;
         }
 
         boolean isSeparatingHinge() {
-            return mFoldState == DeviceStateController.FoldState.HALF_FOLDED
-                    || (mFoldState == DeviceStateController.FoldState.OPEN
+            return mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED
+                    || (mDeviceState == DeviceStateController.DeviceState.OPEN
                         && mIsDisplayAlwaysSeparatingHinge);
         }
 
         boolean overrideFrozenRotation() {
-            return mFoldState == DeviceStateController.FoldState.HALF_FOLDED;
+            return mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED;
         }
 
         boolean shouldRevertOverriddenRotation() {
-            return mFoldState == DeviceStateController.FoldState.OPEN // When transitioning to open.
+            // When transitioning to open.
+            return mDeviceState == DeviceStateController.DeviceState.OPEN
                     && mInHalfFoldTransition
                     && mHalfFoldSavedRotation != -1 // Ignore if we've already reverted.
                     && mUserRotationMode
@@ -1676,30 +1694,30 @@
             return savedRotation;
         }
 
-        void foldStateChanged(DeviceStateController.FoldState newState) {
+        void foldStateChanged(DeviceStateController.DeviceState newState) {
             ProtoLog.v(WM_DEBUG_ORIENTATION,
                     "foldStateChanged: displayId %d, halfFoldStateChanged %s, "
                     + "saved rotation: %d, mUserRotation: %d, mLastSensorRotation: %d, "
                     + "mLastOrientation: %d, mRotation: %d",
                     mDisplayContent.getDisplayId(), newState.name(), mHalfFoldSavedRotation,
                     mUserRotation, mLastSensorRotation, mLastOrientation, mRotation);
-            if (mFoldState == DeviceStateController.FoldState.UNKNOWN) {
-                mFoldState = newState;
+            if (mDeviceState == DeviceStateController.DeviceState.UNKNOWN) {
+                mDeviceState = newState;
                 return;
             }
-            if (newState == DeviceStateController.FoldState.HALF_FOLDED
-                    && mFoldState != DeviceStateController.FoldState.HALF_FOLDED) {
+            if (newState == DeviceStateController.DeviceState.HALF_FOLDED
+                    && mDeviceState != DeviceStateController.DeviceState.HALF_FOLDED) {
                 // The device has transitioned to HALF_FOLDED state: save the current rotation and
                 // update the device rotation.
                 mHalfFoldSavedRotation = mRotation;
-                mFoldState = newState;
+                mDeviceState = newState;
                 // Now mFoldState is set to HALF_FOLDED, the overrideFrozenRotation function will
                 // return true, so rotation is unlocked.
                 mService.updateRotation(false /* alwaysSendConfiguration */,
                         false /* forceRelayout */);
             } else {
                 mInHalfFoldTransition = true;
-                mFoldState = newState;
+                mDeviceState = newState;
                 // Tell the device to update its orientation.
                 mService.updateRotation(false /* alwaysSendConfiguration */,
                         false /* forceRelayout */);
@@ -1719,8 +1737,9 @@
     private class OrientationListener extends WindowOrientationListener implements Runnable {
         transient boolean mEnabled;
 
-        OrientationListener(Context context, Handler handler) {
-            super(context, handler);
+        OrientationListener(Context context, Handler handler,
+                @Surface.Rotation int defaultRotation) {
+            super(context, handler, defaultRotation);
         }
 
         @Override
@@ -1822,7 +1841,7 @@
             final long mTimestamp = System.currentTimeMillis();
             final int mHalfFoldSavedRotation;
             final boolean mInHalfFoldTransition;
-            final DeviceStateController.FoldState mFoldState;
+            final DeviceStateController.DeviceState mDeviceState;
             @Nullable final String mDisplayRotationCompatPolicySummary;
 
             Record(DisplayRotation dr, int fromRotation, int toRotation) {
@@ -1843,8 +1862,9 @@
                 if (source != null) {
                     mLastOrientationSource = source.toString();
                     final WindowState w = source.asWindowState();
-                    mSourceOrientation =
-                            w != null ? w.mAttrs.screenOrientation : source.mOrientation;
+                    mSourceOrientation = w != null
+                            ? w.mAttrs.screenOrientation
+                            : source.getOverrideOrientation();
                 } else {
                     mLastOrientationSource = null;
                     mSourceOrientation = SCREEN_ORIENTATION_UNSET;
@@ -1852,11 +1872,11 @@
                 if (dr.mFoldController != null) {
                     mHalfFoldSavedRotation = dr.mFoldController.mHalfFoldSavedRotation;
                     mInHalfFoldTransition = dr.mFoldController.mInHalfFoldTransition;
-                    mFoldState = dr.mFoldController.mFoldState;
+                    mDeviceState = dr.mFoldController.mDeviceState;
                 } else {
                     mHalfFoldSavedRotation = NO_FOLD_CONTROLLER;
                     mInHalfFoldTransition = false;
-                    mFoldState = DeviceStateController.FoldState.UNKNOWN;
+                    mDeviceState = DeviceStateController.DeviceState.UNKNOWN;
                 }
                 mDisplayRotationCompatPolicySummary = dc.mDisplayRotationCompatPolicy == null
                         ? null
@@ -1882,7 +1902,7 @@
                     pw.println(prefix + " halfFoldSavedRotation="
                             + mHalfFoldSavedRotation
                             + " mInHalfFoldTransition=" + mInHalfFoldTransition
-                            + " mFoldState=" + mFoldState);
+                            + " mFoldState=" + mDeviceState);
                 }
                 if (mDisplayRotationCompatPolicySummary != null) {
                     pw.println(prefix + mDisplayRotationCompatPolicySummary);
diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
index c6037da..47bdba3 100644
--- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
 import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
@@ -34,6 +36,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.StringRes;
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.RefreshCallbackItem;
 import android.app.servertransaction.ResumeActivityItem;
@@ -44,10 +47,13 @@
 import android.os.RemoteException;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.widget.Toast;
 
+import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.common.ProtoLog;
+import com.android.server.UiThread;
 
 import java.util.Map;
 import java.util.Set;
@@ -232,6 +238,24 @@
         activity.mLetterboxUiController.setIsRefreshAfterRotationRequested(false);
     }
 
+    /**
+     * Notifies that animation in {@link ScreenAnimationRotation} has finished.
+     *
+     * <p>This class uses this signal as a trigger for notifying the user about forced rotation
+     * reason with the {@link Toast}.
+     */
+    void onScreenRotationAnimationFinished() {
+        if (!isTreatmentEnabledForDisplay() || mCameraIdPackageBiMap.isEmpty()) {
+            return;
+        }
+        ActivityRecord topActivity = mDisplayContent.topRunningActivity(
+                    /* considerKeyguardState= */ true);
+        if (!isTreatmentEnabledForActivity(topActivity)) {
+            return;
+        }
+        showToast(R.string.display_rotation_camera_compat_toast_after_rotation);
+    }
+
     String getSummaryForDisplayRotationHistoryRecord() {
         String summaryIfEnabled = "";
         if (isTreatmentEnabledForDisplay()) {
@@ -281,23 +305,40 @@
                 && mDisplayContent.getDisplay().getType() == TYPE_INTERNAL;
     }
 
+    boolean isActivityEligibleForOrientationOverride(@NonNull ActivityRecord activity) {
+        return isTreatmentEnabledForDisplay()
+                && isCameraActive(activity, /* mustBeFullscreen */ true);
+    }
+
+
     /**
      * Whether camera compat treatment is applicable for the given activity.
      *
      * <p>Conditions that need to be met:
      * <ul>
-     *     <li>{@link #isCameraActiveForPackage} is {@code true} for the activity.
+     *     <li>Camera is active for the package.
      *     <li>The activity is in fullscreen
      *     <li>The activity has fixed orientation but not "locked" or "nosensor" one.
      * </ul>
      */
     boolean isTreatmentEnabledForActivity(@Nullable ActivityRecord activity) {
-        return activity != null && !activity.inMultiWindowMode()
+        return isTreatmentEnabledForActivity(activity, /* mustBeFullscreen */ true);
+    }
+
+    private boolean isTreatmentEnabledForActivity(@Nullable ActivityRecord activity,
+            boolean mustBeFullscreen) {
+        return activity != null && isCameraActive(activity, mustBeFullscreen)
                 && activity.getRequestedConfigurationOrientation() != ORIENTATION_UNDEFINED
                 // "locked" and "nosensor" values are often used by camera apps that can't
                 // handle dynamic changes so we shouldn't force rotate them.
-                && activity.getRequestedOrientation() != SCREEN_ORIENTATION_NOSENSOR
-                && activity.getRequestedOrientation() != SCREEN_ORIENTATION_LOCKED
+                && activity.getOverrideOrientation() != SCREEN_ORIENTATION_NOSENSOR
+                && activity.getOverrideOrientation() != SCREEN_ORIENTATION_LOCKED;
+    }
+
+    private boolean isCameraActive(@NonNull ActivityRecord activity, boolean mustBeFullscreen) {
+        // Checking windowing mode on activity level because we don't want to
+        // apply treatment in case of activity embedding.
+        return (!mustBeFullscreen || !activity.inMultiWindowMode())
                 && mCameraIdPackageBiMap.containsPackageName(activity.packageName)
                 && activity.mLetterboxUiController.shouldForceRotateForCameraCompat();
     }
@@ -334,7 +375,32 @@
             }
             mCameraIdPackageBiMap.put(packageName, cameraId);
         }
-        updateOrientationWithWmLock();
+        ActivityRecord topActivity = mDisplayContent.topRunningActivity(
+                    /* considerKeyguardState= */ true);
+        if (topActivity == null || topActivity.getTask() == null) {
+            return;
+        }
+        // Checking whether an activity in fullscreen rather than the task as this camera compat
+        // treatment doesn't cover activity embedding.
+        if (topActivity.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
+            if (topActivity.mLetterboxUiController.isOverrideOrientationOnlyForCameraEnabled()) {
+                topActivity.recomputeConfiguration();
+            }
+            updateOrientationWithWmLock();
+            return;
+        }
+        // Checking that the whole app is in multi-window mode as we shouldn't show toast
+        // for the activity embedding case.
+        if (topActivity.getTask().getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW
+                && isTreatmentEnabledForActivity(topActivity, /* mustBeFullscreen */ false)) {
+            showToast(R.string.display_rotation_camera_compat_toast_in_split_screen);
+        }
+    }
+
+    @VisibleForTesting
+    void showToast(@StringRes int stringRes) {
+        UiThread.getHandler().post(
+                () -> Toast.makeText(mWmService.mContext, stringRes, Toast.LENGTH_LONG).show());
     }
 
     private synchronized void notifyCameraClosed(@NonNull String cameraId) {
@@ -375,6 +441,17 @@
         ProtoLog.v(WM_DEBUG_ORIENTATION,
                 "Display id=%d is notified that Camera %s is closed, updating rotation.",
                 mDisplayContent.mDisplayId, cameraId);
+        ActivityRecord topActivity = mDisplayContent.topRunningActivity(
+                /* considerKeyguardState= */ true);
+        if (topActivity == null
+                // Checking whether an activity in fullscreen rather than the task as this camera
+                // compat treatment doesn't cover activity embedding.
+                || topActivity.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
+            return;
+        }
+        if (topActivity.mLetterboxUiController.isOverrideOrientationOnlyForCameraEnabled()) {
+            topActivity.recomputeConfiguration();
+        }
         updateOrientationWithWmLock();
     }
 
@@ -396,6 +473,10 @@
         private final Map<String, String> mPackageToCameraIdMap = new ArrayMap<>();
         private final Map<String, String> mCameraIdToPackageMap = new ArrayMap<>();
 
+        boolean isEmpty() {
+            return mCameraIdToPackageMap.isEmpty();
+        }
+
         void put(String packageName, String cameraId) {
             // Always using the last connected camera ID for the package even for the concurrent
             // camera use case since we can't guess which camera is more important anyway.
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 48258a1..1d21b9d 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -403,8 +403,10 @@
             return;
         }
 
-        mWindowManager.mPolicy.onKeyguardOccludedChangedLw(isDisplayOccluded(DEFAULT_DISPLAY));
-        if (isKeyguardLocked(displayId)) {
+        final boolean waitAppTransition = isKeyguardLocked(displayId);
+        mWindowManager.mPolicy.onKeyguardOccludedChangedLw(isDisplayOccluded(DEFAULT_DISPLAY),
+                waitAppTransition);
+        if (waitAppTransition) {
             mService.deferWindowLayout();
             try {
                 mRootWindowContainer.getDefaultDisplay()
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index 800fe09..5136670 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -18,14 +18,15 @@
 
 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.LetterboxConfigurationDeviceConfig.KEY_ALLOW_IGNORE_ORIENTATION_REQUEST;
+import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ENABLE_CAMERA_COMPAT_TREATMENT;
+import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ENABLE_COMPAT_FAKE_FOCUS;
 import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
 import android.graphics.Color;
 import android.provider.DeviceConfig;
 import android.util.Slog;
@@ -42,10 +43,6 @@
 
     private static final String TAG = TAG_WITH_CLASS_NAME ? "LetterboxConfiguration" : TAG_ATM;
 
-    @VisibleForTesting
-    static final String DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS =
-            "enable_compat_fake_focus";
-
     /**
      * Override of aspect ratio for fixed orientation letterboxing that is set via ADB with
      * set-fixed-orientation-letterbox-aspect-ratio or via {@link
@@ -115,12 +112,6 @@
     /** Letterboxed app window is aligned to the right side. */
     static final int LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM = 2;
 
-    @VisibleForTesting
-    static final String PROPERTY_COMPAT_FAKE_FOCUS_OPT_IN = "com.android.COMPAT_FAKE_FOCUS_OPT_IN";
-    @VisibleForTesting
-    static final String PROPERTY_COMPAT_FAKE_FOCUS_OPT_OUT =
-            "com.android.COMPAT_FAKE_FOCUS_OPT_OUT";
-
     final Context mContext;
 
     // Responsible for the persistence of letterbox[Horizontal|Vertical]PositionMultiplier
@@ -309,14 +300,31 @@
         mIsDisplayRotationImmersiveAppCompatPolicyEnabled = mContext.getResources().getBoolean(
                 R.bool.config_letterboxIsDisplayRotationImmersiveAppCompatPolicyEnabled);
         mDeviceConfig.updateFlagActiveStatus(
+                /* isActive */ mIsCameraCompatTreatmentEnabled,
+                /* key */ KEY_ENABLE_CAMERA_COMPAT_TREATMENT);
+        mDeviceConfig.updateFlagActiveStatus(
                 /* isActive */ mIsDisplayRotationImmersiveAppCompatPolicyEnabled,
                 /* key */ KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY);
+        mDeviceConfig.updateFlagActiveStatus(
+                /* isActive */ true,
+                /* key */ KEY_ALLOW_IGNORE_ORIENTATION_REQUEST);
+        mDeviceConfig.updateFlagActiveStatus(
+                /* isActive */ mIsCompatFakeFocusEnabled,
+                /* key */ KEY_ENABLE_COMPAT_FAKE_FOCUS);
 
         mLetterboxConfigurationPersister = letterboxConfigurationPersister;
         mLetterboxConfigurationPersister.start();
     }
 
     /**
+     * Whether enabling ignoreOrientationRequest is allowed on the device. This value is controlled
+     * via {@link android.provider.DeviceConfig}.
+     */
+    boolean isIgnoreOrientationRequestAllowed() {
+        return mDeviceConfig.getFlag(KEY_ALLOW_IGNORE_ORIENTATION_REQUEST);
+    }
+
+    /**
      * Overrides the aspect ratio of letterbox for fixed orientation. If given value is <= {@link
      * #MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO}, both it and a value of {@link
      * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored and
@@ -1050,41 +1058,9 @@
                 "enable_translucent_activity_letterbox", false);
     }
 
-    @VisibleForTesting
-    boolean getPackageManagerProperty(PackageManager pm, String property) {
-        boolean enabled = false;
-        try {
-            final PackageManager.Property p = pm.getProperty(property, mContext.getPackageName());
-            enabled = p.getBoolean();
-        } catch (PackageManager.NameNotFoundException e) {
-            // Property not found
-        }
-        return enabled;
-    }
-
-    @VisibleForTesting
-    boolean isCompatFakeFocusEnabled(ActivityInfo info) {
-        if (!isCompatFakeFocusEnabledOnDevice()) {
-            return false;
-        }
-        // See if the developer has chosen to opt in / out of treatment
-        PackageManager pm = mContext.getPackageManager();
-        if (getPackageManagerProperty(pm, PROPERTY_COMPAT_FAKE_FOCUS_OPT_OUT)) {
-            return false;
-        } else if (getPackageManagerProperty(pm, PROPERTY_COMPAT_FAKE_FOCUS_OPT_IN)) {
-            return true;
-        }
-        if (info.isChangeEnabled(ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS)) {
-            return true;
-        }
-        return false;
-    }
-
     /** Whether fake sending focus is enabled for unfocused apps in splitscreen */
-    boolean isCompatFakeFocusEnabledOnDevice() {
-        return mIsCompatFakeFocusEnabled
-                && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
-                        DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS, true);
+    boolean isCompatFakeFocusEnabled() {
+        return mIsCompatFakeFocusEnabled && mDeviceConfig.getFlag(KEY_ENABLE_COMPAT_FAKE_FOCUS);
     }
 
     /**
@@ -1106,15 +1082,8 @@
 
     /** Whether camera compatibility treatment is enabled. */
     boolean isCameraCompatTreatmentEnabled(boolean checkDeviceConfig) {
-        return mIsCameraCompatTreatmentEnabled
-                && (!checkDeviceConfig || isCameraCompatTreatmentAllowed());
-    }
-
-    // TODO(b/262977416): Cache a runtime flag and implement
-    // DeviceConfig.OnPropertiesChangedListener
-    private static boolean isCameraCompatTreatmentAllowed() {
-        return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
-                "enable_compat_camera_treatment", true);
+        return mIsCameraCompatTreatmentEnabled && (!checkDeviceConfig
+                || mDeviceConfig.getFlag(KEY_ENABLE_CAMERA_COMPAT_TREATMENT));
     }
 
     /** Whether camera compatibility refresh is enabled. */
diff --git a/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java b/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java
index cf123a1..b364872 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java
@@ -33,17 +33,38 @@
 final class LetterboxConfigurationDeviceConfig
         implements DeviceConfig.OnPropertiesChangedListener {
 
+    static final String KEY_ENABLE_CAMERA_COMPAT_TREATMENT = "enable_compat_camera_treatment";
+    private static final boolean DEFAULT_VALUE_ENABLE_CAMERA_COMPAT_TREATMENT = true;
+
     static final String KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY =
             "enable_display_rotation_immersive_app_compat_policy";
     private static final boolean DEFAULT_VALUE_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY =
             true;
 
+    static final String KEY_ALLOW_IGNORE_ORIENTATION_REQUEST =
+            "allow_ignore_orientation_request";
+    private static final boolean DEFAULT_VALUE_ALLOW_IGNORE_ORIENTATION_REQUEST = true;
+
+    static final String KEY_ENABLE_COMPAT_FAKE_FOCUS = "enable_compat_fake_focus";
+    private static final boolean DEFAULT_VALUE_ENABLE_COMPAT_FAKE_FOCUS = true;
+
     @VisibleForTesting
     static final Map<String, Boolean> sKeyToDefaultValueMap = Map.of(
+            KEY_ENABLE_CAMERA_COMPAT_TREATMENT,
+            DEFAULT_VALUE_ENABLE_CAMERA_COMPAT_TREATMENT,
             KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY,
-            DEFAULT_VALUE_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY
+            DEFAULT_VALUE_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY,
+            KEY_ALLOW_IGNORE_ORIENTATION_REQUEST,
+            DEFAULT_VALUE_ALLOW_IGNORE_ORIENTATION_REQUEST,
+            KEY_ENABLE_COMPAT_FAKE_FOCUS,
+            DEFAULT_VALUE_ENABLE_COMPAT_FAKE_FOCUS
     );
 
+    // Whether camera compatibility treatment is enabled.
+    // See DisplayRotationCompatPolicy for context.
+    private boolean mIsCameraCompatTreatmentEnabled =
+            DEFAULT_VALUE_ENABLE_CAMERA_COMPAT_TREATMENT;
+
     // Whether enabling rotation compat policy for immersive apps that prevents auto rotation
     // into non-optimal screen orientation while in fullscreen. This is needed because immersive
     // apps, such as games, are often not optimized for all orientations and can have a poor UX
@@ -52,6 +73,15 @@
     private boolean mIsDisplayRotationImmersiveAppCompatPolicyEnabled =
             DEFAULT_VALUE_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY;
 
+    // Whether enabling ignoreOrientationRequest is allowed on the device.
+    private boolean mIsAllowIgnoreOrientationRequest =
+            DEFAULT_VALUE_ALLOW_IGNORE_ORIENTATION_REQUEST;
+
+    // Whether sending compat fake focus for split screen resumed activities is enabled. This is
+    // needed because some game engines wait to get focus before drawing the content of the app
+    // which isn't guaranteed by default in multi-window modes.
+    private boolean mIsCompatFakeFocusAllowed = DEFAULT_VALUE_ENABLE_COMPAT_FAKE_FOCUS;
+
     // Set of active device configs that need to be updated in
     // DeviceConfig.OnPropertiesChangedListener#onPropertiesChanged.
     private final ArraySet<String> mActiveDeviceConfigsSet = new ArraySet<>();
@@ -91,8 +121,14 @@
      */
     boolean getFlag(String key) {
         switch (key) {
+            case KEY_ENABLE_CAMERA_COMPAT_TREATMENT:
+                return mIsCameraCompatTreatmentEnabled;
             case KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY:
                 return mIsDisplayRotationImmersiveAppCompatPolicyEnabled;
+            case KEY_ALLOW_IGNORE_ORIENTATION_REQUEST:
+                return mIsAllowIgnoreOrientationRequest;
+            case KEY_ENABLE_COMPAT_FAKE_FOCUS:
+                return mIsCompatFakeFocusAllowed;
             default:
                 throw new AssertionError("Unexpected flag name: " + key);
         }
@@ -104,10 +140,22 @@
             throw new AssertionError("Haven't found default value for flag: " + key);
         }
         switch (key) {
+            case KEY_ENABLE_CAMERA_COMPAT_TREATMENT:
+                mIsCameraCompatTreatmentEnabled =
+                        getDeviceConfig(key, defaultValue);
+                break;
             case KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY:
                 mIsDisplayRotationImmersiveAppCompatPolicyEnabled =
                         getDeviceConfig(key, defaultValue);
                 break;
+            case KEY_ALLOW_IGNORE_ORIENTATION_REQUEST:
+                mIsAllowIgnoreOrientationRequest =
+                        getDeviceConfig(key, defaultValue);
+                break;
+            case KEY_ENABLE_COMPAT_FAKE_FOCUS:
+                mIsCompatFakeFocusAllowed =
+                        getDeviceConfig(key, defaultValue);
+                break;
             default:
                 throw new AssertionError("Unexpected flag name: " + key);
         }
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index d2f62da..e343768 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -17,11 +17,24 @@
 package com.android.server.wm;
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION;
 import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION;
 import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH;
 import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
+import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS;
 import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION;
+import static android.content.pm.ActivityInfo.OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE;
+import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA;
+import static android.content.pm.ActivityInfo.OVERRIDE_RESPECT_REQUESTED_ORIENTATION;
+import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR;
+import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT;
+import static android.content.pm.ActivityInfo.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.ActivityInfo.isFixedOrientation;
+import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
 import static android.content.pm.ActivityInfo.screenOrientationToString;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -29,6 +42,9 @@
 import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION;
 import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH;
 import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS;
 import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION;
 
 import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__BOTTOM;
@@ -62,6 +78,10 @@
 import static com.android.server.wm.LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO;
 import static com.android.server.wm.LetterboxConfiguration.letterboxBackgroundTypeToString;
 
+import static java.lang.Boolean.FALSE;
+import static java.lang.Boolean.TRUE;
+
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager.TaskDescription;
 import android.content.pm.ActivityInfo.ScreenOrientation;
@@ -85,7 +105,10 @@
 import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType;
 
 import java.io.PrintWriter;
+import java.util.Optional;
 import java.util.function.BooleanSupplier;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
 
 /** Controls behaviour of the letterbox UI for {@link mActivityRecord}. */
 // TODO(b/185262487): Improve test coverage of this class. Parts of it are tested in
@@ -95,6 +118,9 @@
 // TODO(b/263021211): Consider renaming to more generic CompatUIController.
 final class LetterboxUiController {
 
+    private static final Predicate<ActivityRecord> FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE =
+            activityRecord -> activityRecord.fillsParent() && !activityRecord.isFinishing();
+
     private static final String TAG = TAG_WITH_CLASS_NAME ? "LetterboxUiController" : TAG_ATM;
 
     private static final float UNDEFINED_ASPECT_RATIO = 0f;
@@ -105,6 +131,47 @@
 
     private final ActivityRecord mActivityRecord;
 
+    /**
+     * Taskbar expanded height. Used to determine when to crop an app window to display the
+     * rounded corners above the expanded taskbar.
+     */
+    private final float mExpandedTaskBarHeight;
+
+    // TODO(b/265576778): Cache other overrides as well.
+
+    // Corresponds to OVERRIDE_ANY_ORIENTATION
+    private final boolean mIsOverrideAnyOrientationEnabled;
+    // Corresponds to OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT
+    private final boolean mIsOverrideToPortraitOrientationEnabled;
+    // Corresponds to OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR
+    private final boolean mIsOverrideToNosensorOrientationEnabled;
+    // Corresponds to OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE
+    private final boolean mIsOverrideToReverseLandscapeOrientationEnabled;
+    // Corresponds to OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA
+    private final boolean mIsOverrideOrientationOnlyForCameraEnabled;
+    // Corresponds to OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION
+    private final boolean mIsOverrideUseDisplayLandscapeNaturalOrientationEnabled;
+    // Corresponds to OVERRIDE_RESPECT_REQUESTED_ORIENTATION
+    private final boolean mIsOverrideRespectRequestedOrientationEnabled;
+
+    // Corresponds to OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION
+    private final boolean mIsOverrideCameraCompatDisableForceRotationEnabled;
+    // Corresponds to OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH
+    private final boolean mIsOverrideCameraCompatDisableRefreshEnabled;
+    // Corresponds to OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE
+    private final boolean mIsOverrideCameraCompatEnableRefreshViaPauseEnabled;
+
+    // Corresponds to OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION
+    private final boolean mIsOverrideEnableCompatIgnoreRequestedOrientationEnabled;
+
+    // Corresponds to OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS
+    private final boolean mIsOverrideEnableCompatFakeFocusEnabled;
+
+    @Nullable
+    private final Boolean mBooleanPropertyAllowOrientationOverride;
+    @Nullable
+    private final Boolean mBooleanPropertyAllowDisplayOrientationOverride;
+
     /*
      * WindowContainerListener responsible to make translucent activities inherit
      * constraints from the first opaque activity beneath them. It's null for not
@@ -155,6 +222,9 @@
     @Nullable
     private final Boolean mBooleanPropertyIgnoreRequestedOrientation;
 
+    @Nullable
+    private final Boolean mBooleanPropertyFakeFocus;
+
     private boolean mIsRelauchingAfterRequestedOrientationChanged;
 
     LetterboxUiController(WindowManagerService wmService, ActivityRecord activityRecord) {
@@ -169,6 +239,10 @@
                 readComponentProperty(packageManager, mActivityRecord.packageName,
                         mLetterboxConfiguration::isPolicyForIgnoringRequestedOrientationEnabled,
                         PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION);
+        mBooleanPropertyFakeFocus =
+                readComponentProperty(packageManager, mActivityRecord.packageName,
+                        mLetterboxConfiguration::isCompatFakeFocusEnabled,
+                        PROPERTY_COMPAT_ENABLE_FAKE_FOCUS);
         mBooleanPropertyCameraCompatAllowForceRotation =
                 readComponentProperty(packageManager, mActivityRecord.packageName,
                         () -> mLetterboxConfiguration.isCameraCompatTreatmentEnabled(
@@ -184,6 +258,45 @@
                         () -> mLetterboxConfiguration.isCameraCompatTreatmentEnabled(
                                 /* checkDeviceConfig */ true),
                         PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE);
+
+        mExpandedTaskBarHeight =
+                getResources().getDimensionPixelSize(R.dimen.taskbar_frame_height);
+
+        mBooleanPropertyAllowOrientationOverride =
+                readComponentProperty(packageManager, mActivityRecord.packageName,
+                        /* gatingCondition */ null,
+                        PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE);
+        mBooleanPropertyAllowDisplayOrientationOverride =
+                readComponentProperty(packageManager, mActivityRecord.packageName,
+                        /* gatingCondition */ null,
+                        PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE);
+
+        mIsOverrideAnyOrientationEnabled = isCompatChangeEnabled(OVERRIDE_ANY_ORIENTATION);
+        mIsOverrideToPortraitOrientationEnabled =
+                isCompatChangeEnabled(OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT);
+        mIsOverrideToReverseLandscapeOrientationEnabled =
+                isCompatChangeEnabled(OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE);
+        mIsOverrideToNosensorOrientationEnabled =
+                isCompatChangeEnabled(OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR);
+        mIsOverrideOrientationOnlyForCameraEnabled =
+                isCompatChangeEnabled(OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA);
+        mIsOverrideUseDisplayLandscapeNaturalOrientationEnabled =
+                isCompatChangeEnabled(OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION);
+        mIsOverrideRespectRequestedOrientationEnabled =
+                isCompatChangeEnabled(OVERRIDE_RESPECT_REQUESTED_ORIENTATION);
+
+        mIsOverrideCameraCompatDisableForceRotationEnabled =
+                isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION);
+        mIsOverrideCameraCompatDisableRefreshEnabled =
+                isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH);
+        mIsOverrideCameraCompatEnableRefreshViaPauseEnabled =
+                isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE);
+
+        mIsOverrideEnableCompatIgnoreRequestedOrientationEnabled =
+                isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION);
+
+        mIsOverrideEnableCompatFakeFocusEnabled =
+                isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS);
     }
 
     /**
@@ -198,8 +311,8 @@
      */
     @Nullable
     private static Boolean readComponentProperty(PackageManager packageManager, String packageName,
-            BooleanSupplier gatingCondition, String propertyName) {
-        if (!gatingCondition.getAsBoolean()) {
+            @Nullable BooleanSupplier gatingCondition, String propertyName) {
+        if (gatingCondition != null && !gatingCondition.getAsBoolean()) {
             return null;
         }
         try {
@@ -253,7 +366,7 @@
         if (!shouldEnableWithOverrideAndProperty(
                 /* gatingCondition */ mLetterboxConfiguration
                         ::isPolicyForIgnoringRequestedOrientationEnabled,
-                OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION,
+                mIsOverrideEnableCompatIgnoreRequestedOrientationEnabled,
                 mBooleanPropertyIgnoreRequestedOrientation)) {
             return false;
         }
@@ -279,6 +392,25 @@
     }
 
     /**
+     * Whether sending compat fake focus for split screen resumed activities is enabled. Needed
+     * because some game engines wait to get focus before drawing the content of the app which isn't
+     * guaranteed by default in multi-window modes.
+     *
+     * <p>This treatment is enabled when the following conditions are met:
+     * <ul>
+     *     <li>Flag gating the treatment is enabled
+     *     <li>Component property is NOT set to false
+     *     <li>Component property is set to true or per-app override is enabled
+     * </ul>
+     */
+    boolean shouldSendFakeFocus() {
+        return shouldEnableWithOverrideAndProperty(
+                /* gatingCondition */ mLetterboxConfiguration::isCompatFakeFocusEnabled,
+                mIsOverrideEnableCompatFakeFocusEnabled,
+                mBooleanPropertyFakeFocus);
+    }
+
+    /**
      * Sets whether an activity is relaunching after the app has called {@link
      * android.app.Activity#setRequestedOrientation}.
      */
@@ -298,6 +430,82 @@
         mIsRefreshAfterRotationRequested = isRequested;
     }
 
+    boolean isOverrideRespectRequestedOrientationEnabled() {
+        return mIsOverrideRespectRequestedOrientationEnabled;
+    }
+
+    /**
+     * Whether should fix display orientation to landscape natural orientation when a task is
+     * fullscreen and the display is ignoring orientation requests.
+     *
+     * <p>This treatment is enabled when the following conditions are met:
+     * <ul>
+     *     <li>Opt-out component property isn't enabled
+     *     <li>Opt-in per-app override is enabled
+     *     <li>Task is in fullscreen.
+     *     <li>{@link DisplayContent#getIgnoreOrientationRequest} is enabled
+     *     <li>Natural orientation of the display is landscape.
+     * </ul>
+     */
+    boolean shouldUseDisplayLandscapeNaturalOrientation() {
+        return shouldEnableWithOptInOverrideAndOptOutProperty(
+                /* gatingCondition */ () -> mActivityRecord.mDisplayContent != null
+                        && mActivityRecord.getTask() != null
+                        && mActivityRecord.mDisplayContent.getIgnoreOrientationRequest()
+                        && !mActivityRecord.getTask().inMultiWindowMode()
+                        && mActivityRecord.mDisplayContent.getNaturalOrientation()
+                                == ORIENTATION_LANDSCAPE,
+                mIsOverrideUseDisplayLandscapeNaturalOrientationEnabled,
+                mBooleanPropertyAllowDisplayOrientationOverride);
+    }
+
+    @ScreenOrientation
+    int overrideOrientationIfNeeded(@ScreenOrientation int candidate) {
+        if (FALSE.equals(mBooleanPropertyAllowOrientationOverride)) {
+            return candidate;
+        }
+
+        DisplayContent displayContent = mActivityRecord.mDisplayContent;
+        if (mIsOverrideOrientationOnlyForCameraEnabled && displayContent != null
+                && (displayContent.mDisplayRotationCompatPolicy == null
+                        || !displayContent.mDisplayRotationCompatPolicy
+                                .isActivityEligibleForOrientationOverride(mActivityRecord))) {
+            return candidate;
+        }
+
+        if (mIsOverrideToReverseLandscapeOrientationEnabled
+                && (isFixedOrientationLandscape(candidate) || mIsOverrideAnyOrientationEnabled)) {
+            Slog.w(TAG, "Requested orientation  " + screenOrientationToString(candidate) + " for "
+                    + mActivityRecord + " is overridden to "
+                    + screenOrientationToString(SCREEN_ORIENTATION_REVERSE_LANDSCAPE));
+            return SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
+        }
+
+        if (!mIsOverrideAnyOrientationEnabled && isFixedOrientation(candidate)) {
+            return candidate;
+        }
+
+        if (mIsOverrideToPortraitOrientationEnabled) {
+            Slog.w(TAG, "Requested orientation  " + screenOrientationToString(candidate) + " for "
+                    + mActivityRecord + " is overridden to "
+                    + screenOrientationToString(SCREEN_ORIENTATION_PORTRAIT));
+            return SCREEN_ORIENTATION_PORTRAIT;
+        }
+
+        if (mIsOverrideToNosensorOrientationEnabled) {
+            Slog.w(TAG, "Requested orientation  " + screenOrientationToString(candidate) + " for "
+                    + mActivityRecord + " is overridden to "
+                    + screenOrientationToString(SCREEN_ORIENTATION_NOSENSOR));
+            return SCREEN_ORIENTATION_NOSENSOR;
+        }
+
+        return candidate;
+    }
+
+    boolean isOverrideOrientationOnlyForCameraEnabled() {
+        return mIsOverrideOrientationOnlyForCameraEnabled;
+    }
+
     /**
      * Whether activity is eligible for activity "refresh" after camera compat force rotation
      * treatment. See {@link DisplayRotationCompatPolicy} for context.
@@ -313,7 +521,7 @@
         return shouldEnableWithOptOutOverrideAndProperty(
                 /* gatingCondition */ () -> mLetterboxConfiguration
                         .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true),
-                OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH,
+                mIsOverrideCameraCompatDisableRefreshEnabled,
                 mBooleanPropertyCameraCompatAllowRefresh);
     }
 
@@ -335,7 +543,7 @@
         return shouldEnableWithOverrideAndProperty(
                 /* gatingCondition */ () -> mLetterboxConfiguration
                         .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true),
-                OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE,
+                mIsOverrideCameraCompatEnableRefreshViaPauseEnabled,
                 mBooleanPropertyCameraCompatEnableRefreshViaPause);
     }
 
@@ -354,15 +562,19 @@
         return shouldEnableWithOptOutOverrideAndProperty(
                 /* gatingCondition */ () -> mLetterboxConfiguration
                         .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true),
-                OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION,
+                mIsOverrideCameraCompatDisableForceRotationEnabled,
                 mBooleanPropertyCameraCompatAllowForceRotation);
     }
 
+    private boolean isCompatChangeEnabled(long overrideChangeId) {
+        return mActivityRecord.info.isChangeEnabled(overrideChangeId);
+    }
+
     /**
      * Returns {@code true} when the following conditions are met:
      * <ul>
      *     <li>{@code gatingCondition} isn't {@code false}
-     *     <li>OEM didn't opt out with a {@code overrideChangeId} override
+     *     <li>OEM didn't opt out with a per-app override
      *     <li>App developers didn't opt out with a component {@code property}
      * </ul>
      *
@@ -370,12 +582,30 @@
      * disabled on per-app basis by OEMs or app developers.
      */
     private boolean shouldEnableWithOptOutOverrideAndProperty(BooleanSupplier gatingCondition,
-            long overrideChangeId, Boolean property) {
+            boolean isOverrideChangeEnabled, Boolean property) {
         if (!gatingCondition.getAsBoolean()) {
             return false;
         }
-        return !Boolean.FALSE.equals(property)
-                && !mActivityRecord.info.isChangeEnabled(overrideChangeId);
+        return !FALSE.equals(property) && !isOverrideChangeEnabled;
+    }
+
+    /**
+     * Returns {@code true} when the following conditions are met:
+     * <ul>
+     *     <li>{@code gatingCondition} isn't {@code false}
+     *     <li>OEM did opt in with a per-app override
+     *     <li>App developers didn't opt out with a component {@code property}
+     * </ul>
+     *
+     * <p>This is used for the treatments that are enabled based with the heuristic but can be
+     * disabled on per-app basis by OEMs or app developers.
+     */
+    private boolean shouldEnableWithOptInOverrideAndOptOutProperty(BooleanSupplier gatingCondition,
+            boolean isOverrideChangeEnabled, Boolean property) {
+        if (!gatingCondition.getAsBoolean()) {
+            return false;
+        }
+        return !FALSE.equals(property) && isOverrideChangeEnabled;
     }
 
     /**
@@ -384,21 +614,20 @@
      *     <li>{@code gatingCondition} isn't {@code false}
      *     <li>App developers didn't opt out with a component {@code property}
      *     <li>App developers opted in with a component {@code property} or an OEM opted in with a
-     *     component {@code property}
+     *     per-app override
      * </ul>
      *
      * <p>This is used for the treatments that are enabled only on per-app basis.
      */
     private boolean shouldEnableWithOverrideAndProperty(BooleanSupplier gatingCondition,
-            long overrideChangeId, Boolean property) {
+            boolean isOverrideChangeEnabled, Boolean property) {
         if (!gatingCondition.getAsBoolean()) {
             return false;
         }
-        if (Boolean.FALSE.equals(property)) {
+        if (FALSE.equals(property)) {
             return false;
         }
-        return Boolean.TRUE.equals(property)
-                || mActivityRecord.info.isChangeEnabled(overrideChangeId);
+        return TRUE.equals(property) || isOverrideChangeEnabled;
     }
 
     boolean hasWallpaperBackgroundForLetterbox() {
@@ -419,10 +648,9 @@
         if (mLetterbox != null) {
             outBounds.set(mLetterbox.getInnerFrame());
             final WindowState w = mActivityRecord.findMainWindow();
-            if (w == null) {
-                return;
+            if (w != null) {
+                adjustBoundsForTaskbar(w, outBounds);
             }
-            adjustBoundsForTaskbar(w, outBounds);
         } else {
             outBounds.setEmpty();
         }
@@ -465,13 +693,13 @@
         if (w == null || winHint != null && w != winHint) {
             return;
         }
-        updateRoundedCorners(w);
+        updateRoundedCornersIfNeeded(w);
         // If there is another main window that is not an application-starting window, we should
         // update rounded corners for it as well, to avoid flickering rounded corners.
         final WindowState nonStartingAppW = mActivityRecord.findMainWindow(
                 /* includeStartingApp= */ false);
         if (nonStartingAppW != null && nonStartingAppW != w) {
-            updateRoundedCorners(nonStartingAppW);
+            updateRoundedCornersIfNeeded(nonStartingAppW);
         }
 
         updateWallpaperForLetterbox(w);
@@ -533,7 +761,7 @@
     // Note that we check the task rather than the parent as with ActivityEmbedding the parent might
     // be a TaskFragment, and its windowing mode is always MULTI_WINDOW, even if the task is
     // actually fullscreen.
-    private boolean isDisplayFullScreenAndInPosture(DeviceStateController.FoldState state,
+    private boolean isDisplayFullScreenAndInPosture(DeviceStateController.DeviceState state,
             boolean isTabletop) {
         Task task = mActivityRecord.getTask();
         return mActivityRecord.mDisplayContent != null
@@ -559,7 +787,7 @@
         // Don't check resolved configuration because it may not be updated yet during
         // configuration change.
         boolean bookMode = isDisplayFullScreenAndInPosture(
-                DeviceStateController.FoldState.HALF_FOLDED, false /* isTabletop */);
+                DeviceStateController.DeviceState.HALF_FOLDED, false /* isTabletop */);
         return isHorizontalReachabilityEnabled(parentConfiguration)
                 // Using the last global dynamic position to avoid "jumps" when moving
                 // between apps or activities.
@@ -571,7 +799,7 @@
         // Don't check resolved configuration because it may not be updated yet during
         // configuration change.
         boolean tabletopMode = isDisplayFullScreenAndInPosture(
-                DeviceStateController.FoldState.HALF_FOLDED, true /* isTabletop */);
+                DeviceStateController.DeviceState.HALF_FOLDED, true /* isTabletop */);
         return isVerticalReachabilityEnabled(parentConfiguration)
                 // Using the last global dynamic position to avoid "jumps" when moving
                 // between apps or activities.
@@ -734,8 +962,8 @@
                 && (parentConfiguration.orientation == ORIENTATION_LANDSCAPE
                         && mActivityRecord.getOrientationForReachability() == ORIENTATION_PORTRAIT)
                 // Check whether the activity fills the parent vertically.
-                && parentConfiguration.windowConfiguration.getBounds().height()
-                        == mActivityRecord.getBounds().height();
+                && parentConfiguration.windowConfiguration.getAppBounds().height()
+                        <= mActivityRecord.getBounds().height();
     }
 
     @VisibleForTesting
@@ -772,19 +1000,16 @@
 
     @VisibleForTesting
     boolean shouldShowLetterboxUi(WindowState mainWindow) {
-        return isSurfaceReadyAndVisible(mainWindow) && mainWindow.areAppWindowBoundsLetterboxed()
+        return isSurfaceVisible(mainWindow) && mainWindow.areAppWindowBoundsLetterboxed()
                 // Check for FLAG_SHOW_WALLPAPER explicitly instead of using
                 // WindowContainer#showWallpaper because the later will return true when this
-                // activity is using blurred wallpaper for letterbox backgroud.
-                && (mainWindow.mAttrs.flags & FLAG_SHOW_WALLPAPER) == 0;
+                // activity is using blurred wallpaper for letterbox background.
+                && (mainWindow.getAttrs().flags & FLAG_SHOW_WALLPAPER) == 0;
     }
 
     @VisibleForTesting
-    boolean isSurfaceReadyAndVisible(WindowState mainWindow) {
-        boolean surfaceReady = mainWindow.isDrawn() // Regular case
-                // Waiting for relayoutWindow to call preserveSurface
-                || mainWindow.isDragResizeChanged();
-        return surfaceReady && (mActivityRecord.isVisible()
+    boolean isSurfaceVisible(WindowState mainWindow) {
+        return mainWindow.isOnScreen() && (mActivityRecord.isVisible()
                 || mActivityRecord.isVisibleRequested());
     }
 
@@ -828,108 +1053,109 @@
         return mLetterboxConfiguration.getLetterboxBackgroundColor();
     }
 
-    private void updateRoundedCorners(WindowState mainWindow) {
+    private void updateRoundedCornersIfNeeded(final WindowState mainWindow) {
         final SurfaceControl windowSurface = mainWindow.getSurfaceControl();
-        if (windowSurface != null && windowSurface.isValid()) {
-            final Transaction transaction = mActivityRecord.getSyncTransaction();
-
-            if (!requiresRoundedCorners(mainWindow) || mActivityRecord.isInLetterboxAnimation()) {
-                // We don't want corner radius on the window.
-                // In the case the ActivityRecord requires a letterboxed animation we never want
-                // rounded corners on the window because rounded corners are applied at the
-                // animation-bounds surface level and rounded corners on the window would interfere
-                // with that leading to unexpected rounded corner positioning during the animation.
-                transaction
-                        .setWindowCrop(windowSurface, null)
-                        .setCornerRadius(windowSurface, 0);
-                return;
-            }
-
-            Rect cropBounds = null;
-
-            if (hasVisibleTaskbar(mainWindow)) {
-                cropBounds = new Rect(mActivityRecord.getBounds());
-
-                // Rounded corners should be displayed above the taskbar.
-                // It is important to call adjustBoundsForTaskbarUnchecked before offsetTo
-                // because taskbar bounds are in screen coordinates
-                adjustBoundsForTaskbarUnchecked(mainWindow, cropBounds);
-
-                // Activity bounds are in screen coordinates while (0,0) for activity's surface
-                // control is at the top left corner of an app window so offsetting bounds
-                // accordingly.
-                cropBounds.offsetTo(0, 0);
-            }
-
-            transaction
-                    .setWindowCrop(windowSurface, cropBounds)
-                    .setCornerRadius(windowSurface, getRoundedCornersRadius(mainWindow));
+        if (windowSurface == null || !windowSurface.isValid()) {
+            return;
         }
+
+        // cropBounds must be non-null for the cornerRadius to be ever applied.
+        mActivityRecord.getSyncTransaction()
+                .setCrop(windowSurface, getCropBoundsIfNeeded(mainWindow))
+                .setCornerRadius(windowSurface, getRoundedCornersRadius(mainWindow));
     }
 
-    private boolean requiresRoundedCorners(WindowState mainWindow) {
-        final InsetsSource taskbarInsetsSource = getTaskbarInsetsSource(mainWindow);
+    @VisibleForTesting
+    @Nullable
+    Rect getCropBoundsIfNeeded(final WindowState mainWindow) {
+        if (!requiresRoundedCorners(mainWindow) || mActivityRecord.isInLetterboxAnimation()) {
+            // We don't want corner radius on the window.
+            // In the case the ActivityRecord requires a letterboxed animation we never want
+            // rounded corners on the window because rounded corners are applied at the
+            // animation-bounds surface level and rounded corners on the window would interfere
+            // with that leading to unexpected rounded corner positioning during the animation.
+            return null;
+        }
 
+        final Rect cropBounds = new Rect(mActivityRecord.getBounds());
+
+        // It is important to call {@link #adjustBoundsIfNeeded} before {@link cropBounds.offsetTo}
+        // because taskbar bounds used in {@link #adjustBoundsIfNeeded}
+        // are in screen coordinates
+        adjustBoundsForTaskbar(mainWindow, cropBounds);
+
+        final float scale = mainWindow.mInvGlobalScale;
+        if (scale != 1f && scale > 0f) {
+            cropBounds.scale(scale);
+        }
+
+        // ActivityRecord bounds are in screen coordinates while (0,0) for activity's surface
+        // control is in the top left corner of an app window so offsetting bounds
+        // accordingly.
+        cropBounds.offsetTo(0, 0);
+        return cropBounds;
+    }
+
+    private boolean requiresRoundedCorners(final WindowState mainWindow) {
         return isLetterboxedNotForDisplayCutout(mainWindow)
-                && mLetterboxConfiguration.isLetterboxActivityCornersRounded()
-                && taskbarInsetsSource != null;
+                && mLetterboxConfiguration.isLetterboxActivityCornersRounded();
     }
 
     // Returns rounded corners radius the letterboxed activity should have based on override in
     // R.integer.config_letterboxActivityCornersRadius or min device bottom corner radii.
-    // Device corners can be different on the right and left sides but we use the same radius
+    // Device corners can be different on the right and left sides, but we use the same radius
     // for all corners for consistency and pick a minimal bottom one for consistency with a
     // taskbar rounded corners.
-    int getRoundedCornersRadius(WindowState mainWindow) {
-        if (!requiresRoundedCorners(mainWindow)) {
+    int getRoundedCornersRadius(final WindowState mainWindow) {
+        if (!requiresRoundedCorners(mainWindow) || mActivityRecord.isInLetterboxAnimation()) {
             return 0;
         }
 
+        final int radius;
         if (mLetterboxConfiguration.getLetterboxActivityCornersRadius() >= 0) {
-            return mLetterboxConfiguration.getLetterboxActivityCornersRadius();
+            radius = mLetterboxConfiguration.getLetterboxActivityCornersRadius();
+        } else {
+            final InsetsState insetsState = mainWindow.getInsetsState();
+            radius = Math.min(
+                    getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_LEFT),
+                    getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_RIGHT));
         }
 
-        final InsetsState insetsState = mainWindow.getInsetsState();
-        return Math.min(
-                getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_LEFT),
-                getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_RIGHT));
+        final float scale = mainWindow.mInvGlobalScale;
+        return (scale != 1f && scale > 0f) ? (int) (scale * radius) : radius;
     }
 
     /**
-     * Returns whether the taskbar is visible. Returns false if the window is in immersive mode,
-     * since the user can swipe to show/hide the taskbar as an overlay.
+     * Returns the taskbar in case it is visible and expanded in height, otherwise returns null.
      */
-    private boolean hasVisibleTaskbar(WindowState mainWindow) {
-        final InsetsSource taskbarInsetsSource = getTaskbarInsetsSource(mainWindow);
-
-        return taskbarInsetsSource != null
-                && taskbarInsetsSource.isVisible();
+    @VisibleForTesting
+    @Nullable
+    InsetsSource getExpandedTaskbarOrNull(final WindowState mainWindow) {
+        final InsetsSource taskbar = mainWindow.getInsetsState().peekSource(
+                InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
+        if (taskbar != null && taskbar.isVisible()
+                && taskbar.getFrame().height() >= mExpandedTaskBarHeight) {
+            return taskbar;
+        }
+        return null;
     }
 
-    private InsetsSource getTaskbarInsetsSource(WindowState mainWindow) {
-        final InsetsState insetsState = mainWindow.getInsetsState();
-        return insetsState.peekSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
-    }
-
-    private void adjustBoundsForTaskbar(WindowState mainWindow, Rect bounds) {
+    private void adjustBoundsForTaskbar(final WindowState mainWindow, final Rect bounds) {
         // Rounded corners should be displayed above the taskbar. When taskbar is hidden,
         // an insets frame is equal to a navigation bar which shouldn't affect position of
         // rounded corners since apps are expected to handle navigation bar inset.
         // This condition checks whether the taskbar is visible.
         // Do not crop the taskbar inset if the window is in immersive mode - the user can
         // swipe to show/hide the taskbar as an overlay.
-        if (hasVisibleTaskbar(mainWindow)) {
-            adjustBoundsForTaskbarUnchecked(mainWindow, bounds);
+        // Adjust the bounds only in case there is an expanded taskbar,
+        // otherwise the rounded corners will be shown behind the navbar.
+        final InsetsSource expandedTaskbarOrNull = getExpandedTaskbarOrNull(mainWindow);
+        if (expandedTaskbarOrNull != null) {
+            // Rounded corners should be displayed above the expanded taskbar.
+            bounds.bottom = Math.min(bounds.bottom, expandedTaskbarOrNull.getFrame().top);
         }
     }
 
-    private void adjustBoundsForTaskbarUnchecked(WindowState mainWindow, Rect bounds) {
-        // Rounded corners should be displayed above the taskbar.
-        bounds.bottom =
-                Math.min(bounds.bottom, getTaskbarInsetsSource(mainWindow).getFrame().top);
-        scaleIfNeeded(bounds);
-    }
-
     private int getInsetsStateCornerRadius(
                 InsetsState insetsState, @RoundedCorner.Position int position) {
         RoundedCorner corner = insetsState.getRoundedCorners().getRoundedCorner(position);
@@ -1097,7 +1323,7 @@
             int letterboxPositionForHorizontalReachability = getLetterboxConfiguration()
                     .getLetterboxPositionForHorizontalReachability(
                             isDisplayFullScreenAndInPosture(
-                                    DeviceStateController.FoldState.HALF_FOLDED,
+                                    DeviceStateController.DeviceState.HALF_FOLDED,
                                     false /* isTabletop */));
             positionToLog = letterboxHorizontalReachabilityPositionToLetterboxPosition(
                     letterboxPositionForHorizontalReachability);
@@ -1105,7 +1331,7 @@
             int letterboxPositionForVerticalReachability = getLetterboxConfiguration()
                     .getLetterboxPositionForVerticalReachability(
                             isDisplayFullScreenAndInPosture(
-                                    DeviceStateController.FoldState.HALF_FOLDED,
+                                    DeviceStateController.DeviceState.HALF_FOLDED,
                                     true /* isTabletop */));
             positionToLog = letterboxVerticalReachabilityPositionToLetterboxPosition(
                     letterboxPositionForVerticalReachability);
@@ -1167,7 +1393,8 @@
             return;
         }
         final ActivityRecord firstOpaqueActivityBeneath = mActivityRecord.getTask().getActivity(
-                ActivityRecord::fillsParent, mActivityRecord, false /* includeBoundary */,
+                FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE /* callback */,
+                mActivityRecord /* boundary */, false /* includeBoundary */,
                 true /* traverseTopToBottom */);
         if (firstOpaqueActivityBeneath == null) {
             // We skip letterboxing if the translucent activity doesn't have any opaque
@@ -1214,7 +1441,8 @@
         // To avoid wrong behaviour, we're not forcing orientation for activities with not
         // fixed orientation (e.g. permission dialogs).
         return hasInheritedLetterboxBehavior()
-                && mActivityRecord.mOrientation != SCREEN_ORIENTATION_UNSPECIFIED;
+                && mActivityRecord.getOverrideOrientation()
+                        != SCREEN_ORIENTATION_UNSPECIFIED;
     }
 
     float getInheritedMinAspectRatio() {
@@ -1242,6 +1470,32 @@
         return mInheritedCompatDisplayInsets;
     }
 
+    /**
+     * In case of translucent activities, it consumes the {@link ActivityRecord} of the first opaque
+     * activity beneath using the given consumer and returns {@code true}.
+     */
+    boolean applyOnOpaqueActivityBelow(@NonNull Consumer<ActivityRecord> consumer) {
+        return findOpaqueNotFinishingActivityBelow()
+                .map(activityRecord -> {
+                    consumer.accept(activityRecord);
+                    return true;
+                }).orElse(false);
+    }
+
+    /**
+     * @return The first not finishing opaque activity beneath the current translucent activity
+     * if it exists and the strategy is enabled.
+     */
+    private Optional<ActivityRecord> findOpaqueNotFinishingActivityBelow() {
+        if (!hasInheritedLetterboxBehavior() || mActivityRecord.getTask() == null) {
+            return Optional.empty();
+        }
+        return Optional.ofNullable(mActivityRecord.getTask().getActivity(
+                FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE /* callback */,
+                mActivityRecord /* boundary */, false /* includeBoundary */,
+                true /* traverseTopToBottom */));
+    }
+
     private void inheritConfiguration(ActivityRecord firstOpaque) {
         // To avoid wrong behaviour, we're not forcing a specific aspet ratio to activities
         // which are not already providing one (e.g. permission dialogs) and presumably also
@@ -1269,20 +1523,4 @@
         mInheritedSizeCompatScale = 1f;
         mInheritedCompatDisplayInsets = null;
     }
-
-    private void scaleIfNeeded(Rect bounds) {
-        if (boundsNeedToScale()) {
-            bounds.scale(1.0f / mActivityRecord.getCompatScale());
-        }
-    }
-
-    private boolean boundsNeedToScale() {
-        if (hasInheritedLetterboxBehavior()) {
-            return mIsInheritedInSizeCompatMode
-                    && mInheritedSizeCompatScale < 1.0f;
-        } else {
-            return mActivityRecord.inSizeCompatMode()
-                    && mActivityRecord.getCompatScale() < 1.0f;
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java b/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java
index 30bdc34..2edb082 100644
--- a/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java
+++ b/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java
@@ -51,10 +51,10 @@
     /**
      *   Called by the DeviceStateManager callback when the state changes.
      */
-    void foldStateChanged(DeviceStateController.FoldState newFoldState) {
+    void foldStateChanged(DeviceStateController.DeviceState newDeviceState) {
         // Ignore transitions to/from half-folded.
-        if (newFoldState == DeviceStateController.FoldState.HALF_FOLDED) return;
-        mIsFolded = newFoldState == DeviceStateController.FoldState.FOLDED;
+        if (newDeviceState == DeviceStateController.DeviceState.HALF_FOLDED) return;
+        mIsFolded = newDeviceState == DeviceStateController.DeviceState.FOLDED;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 1fc061b..67fd557 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -33,6 +33,8 @@
 import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.os.Process.SYSTEM_UID;
+import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
@@ -215,10 +217,16 @@
             int y = (int) ev.getY();
             mService.mH.post(PooledLambda.obtainRunnable((nonArg) -> {
                 synchronized (mService.mGlobalLock) {
-                    // Unfreeze the task list once we touch down in a task
                     final RootWindowContainer rac = mService.mRootWindowContainer;
                     final DisplayContent dc = rac.getDisplayContent(displayId).mDisplayContent;
-                    if (dc.pointWithinAppWindow(x, y)) {
+                    final WindowState win = dc.getTouchableWinAtPointLocked((float) x, (float) y);
+                    if (win == null) {
+                        return;
+                    }
+                    // Unfreeze the task list once we touch down in a task
+                    final boolean isAppWindowTouch = FIRST_APPLICATION_WINDOW <= win.mAttrs.type
+                            && win.mAttrs.type <= LAST_APPLICATION_WINDOW;
+                    if (isAppWindowTouch) {
                         final Task stack = mService.getTopDisplayFocusedRootTask();
                         final Task topTask = stack != null ? stack.getTopMostTask() : null;
                         resetFreezeTaskListReordering(topTask);
diff --git a/services/core/java/com/android/server/wm/RefreshRatePolicy.java b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
index f3713eb..6b3c533 100644
--- a/services/core/java/com/android/server/wm/RefreshRatePolicy.java
+++ b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
@@ -53,6 +53,8 @@
         }
     }
 
+    private final DisplayInfo mDisplayInfo;
+    private final Mode mDefaultMode;
     private final Mode mLowRefreshRateMode;
     private final PackageRefreshRate mNonHighRefreshRatePackages = new PackageRefreshRate();
     private final HighRefreshRateDenylist mHighRefreshRateDenylist;
@@ -83,7 +85,9 @@
 
     RefreshRatePolicy(WindowManagerService wmService, DisplayInfo displayInfo,
             HighRefreshRateDenylist denylist) {
-        mLowRefreshRateMode = findLowRefreshRateMode(displayInfo);
+        mDisplayInfo = displayInfo;
+        mDefaultMode = displayInfo.getDefaultMode();
+        mLowRefreshRateMode = findLowRefreshRateMode(displayInfo, mDefaultMode);
         mHighRefreshRateDenylist = denylist;
         mWmService = wmService;
     }
@@ -92,10 +96,9 @@
      * Finds the mode id with the lowest refresh rate which is >= 60hz and same resolution as the
      * default mode.
      */
-    private Mode findLowRefreshRateMode(DisplayInfo displayInfo) {
-        Mode mode = displayInfo.getDefaultMode();
+    private Mode findLowRefreshRateMode(DisplayInfo displayInfo, Mode defaultMode) {
         float[] refreshRates = displayInfo.getDefaultRefreshRates();
-        float bestRefreshRate = mode.getRefreshRate();
+        float bestRefreshRate = defaultMode.getRefreshRate();
         mMinSupportedRefreshRate = bestRefreshRate;
         mMaxSupportedRefreshRate = bestRefreshRate;
         for (int i = refreshRates.length - 1; i >= 0; i--) {
@@ -121,13 +124,39 @@
     }
 
     int getPreferredModeId(WindowState w) {
-        // If app is animating, it's not able to control refresh rate because we want the animation
-        // to run in default refresh rate.
-        if (w.isAnimating(TRANSITION | PARENTS)) {
+        final int preferredDisplayModeId = w.mAttrs.preferredDisplayModeId;
+        if (preferredDisplayModeId <= 0) {
+            // Unspecified, use default mode.
             return 0;
         }
 
-        return w.mAttrs.preferredDisplayModeId;
+        // If app is animating, it's not able to control refresh rate because we want the animation
+        // to run in default refresh rate. But if the display size of default mode is different
+        // from the using preferred mode, then still keep the preferred mode to avoid disturbing
+        // the animation.
+        if (w.isAnimating(TRANSITION | PARENTS)) {
+            Display.Mode preferredMode = null;
+            for (Display.Mode mode : mDisplayInfo.supportedModes) {
+                if (preferredDisplayModeId == mode.getModeId()) {
+                    preferredMode = mode;
+                    break;
+                }
+            }
+            if (preferredMode != null) {
+                final int pW = preferredMode.getPhysicalWidth();
+                final int pH = preferredMode.getPhysicalHeight();
+                if ((pW != mDefaultMode.getPhysicalWidth()
+                        || pH != mDefaultMode.getPhysicalHeight())
+                        && pW == mDisplayInfo.getNaturalWidth()
+                        && pH == mDisplayInfo.getNaturalHeight()) {
+                    // Prefer not to change display size when animating.
+                    return preferredDisplayModeId;
+                }
+            }
+            return 0;
+        }
+
+        return preferredDisplayModeId;
     }
 
     /**
@@ -165,12 +194,9 @@
         // of that mode id.
         final int preferredModeId = w.mAttrs.preferredDisplayModeId;
         if (preferredModeId > 0) {
-            DisplayInfo info = w.getDisplayInfo();
-            if (info != null) {
-                for (Display.Mode mode : info.supportedModes) {
-                    if (preferredModeId == mode.getModeId()) {
-                        return mode.getRefreshRate();
-                    }
+            for (Display.Mode mode : mDisplayInfo.supportedModes) {
+                if (preferredModeId == mode.getModeId()) {
+                    return mode.getRefreshRate();
                 }
             }
         }
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index fd8b614..ef45c22 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -800,6 +800,10 @@
                 if (mDisplayContent.getRotationAnimation() == ScreenRotationAnimation.this) {
                     // It also invokes kill().
                     mDisplayContent.setRotationAnimation(null);
+                    if (mDisplayContent.mDisplayRotationCompatPolicy != null) {
+                        mDisplayContent.mDisplayRotationCompatPolicy
+                                .onScreenRotationAnimationFinished();
+                    }
                 } else {
                     kill();
                 }
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 00e3188..db8079a 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -45,6 +45,7 @@
 import static android.view.WindowManager.TransitionFlags;
 import static android.view.WindowManager.TransitionType;
 import static android.view.WindowManager.transitTypeToString;
+import static android.window.TaskFragmentAnimationParams.DEFAULT_ANIMATION_BACKGROUND_COLOR;
 import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS;
 import static android.window.TransitionInfo.FLAG_FILLS_TASK;
 import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
@@ -1736,7 +1737,7 @@
                         ? activityRecord.getOrganizedTaskFragment()
                         : taskFragment.getOrganizedTaskFragment();
                 if (organizedTf != null && organizedTf.getAnimationParams()
-                        .getAnimationBackgroundColor() != 0) {
+                        .getAnimationBackgroundColor() != DEFAULT_ANIMATION_BACKGROUND_COLOR) {
                     // This window is embedded and has an animation background color set on the
                     // TaskFragment. Pass this color with this window, so the handler can use it as
                     // the animation background color if needed,
@@ -1748,10 +1749,11 @@
                     final Task parentTask = activityRecord != null
                             ? activityRecord.getTask()
                             : taskFragment.getTask();
-                    backgroundColor = ColorUtils.setAlphaComponent(
-                            parentTask.getTaskDescription().getBackgroundColor(), 255);
+                    backgroundColor = parentTask.getTaskDescription().getBackgroundColor();
                 }
-                change.setBackgroundColor(backgroundColor);
+                // Set to opaque for animation background to prevent it from exposing the blank
+                // background or content below.
+                change.setBackgroundColor(ColorUtils.setAlphaComponent(backgroundColor, 255));
             }
 
             change.setRotation(info.mRotation, endRotation);
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 8bdab9c..ce03244 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -33,6 +33,7 @@
 import static android.view.SurfaceControl.Transaction;
 import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
 import static android.view.WindowManager.TRANSIT_CHANGE;
+import static android.window.TaskFragmentAnimationParams.DEFAULT_ANIMATION_BACKGROUND_COLOR;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
@@ -73,6 +74,7 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ActivityInfo.ScreenOrientation;
 import android.content.res.Configuration;
 import android.graphics.Color;
 import android.graphics.Point;
@@ -178,8 +180,9 @@
     protected final WindowList<E> mChildren = new WindowList<E>();
 
     // The specified orientation for this window container.
-    @ActivityInfo.ScreenOrientation
-    protected int mOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+    // Shouldn't be accessed directly since subclasses can override getOverrideOrientation.
+    @ScreenOrientation
+    private int mOverrideOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
 
     /**
      * The window container which decides its orientation since the last time
@@ -1427,19 +1430,20 @@
 
     /**
      * Gets the configuration orientation by the requested screen orientation
-     * ({@link ActivityInfo.ScreenOrientation}) of this activity.
+     * ({@link ScreenOrientation}) of this activity.
      *
      * @return orientation in ({@link Configuration#ORIENTATION_LANDSCAPE},
      *         {@link Configuration#ORIENTATION_PORTRAIT},
      *         {@link Configuration#ORIENTATION_UNDEFINED}).
      */
+    @ScreenOrientation
     int getRequestedConfigurationOrientation() {
         return getRequestedConfigurationOrientation(false /* forDisplay */);
     }
 
     /**
      * Gets the configuration orientation by the requested screen orientation
-     * ({@link ActivityInfo.ScreenOrientation}) of this activity.
+     * ({@link ScreenOrientation}) of this activity.
      *
      * @param forDisplay whether it is the requested config orientation for display.
      *                   If {@code true}, we may reverse the requested orientation if the root is
@@ -1450,8 +1454,9 @@
      *         {@link Configuration#ORIENTATION_PORTRAIT},
      *         {@link Configuration#ORIENTATION_UNDEFINED}).
      */
+    @ScreenOrientation
     int getRequestedConfigurationOrientation(boolean forDisplay) {
-        int requestedOrientation = mOrientation;
+        int requestedOrientation = getOverrideOrientation();
         final RootDisplayArea root = getRootDisplayArea();
         if (forDisplay && root != null && root.isOrientationDifferentFromDisplay()) {
             // Reverse the requested orientation if the orientation of its root is different from
@@ -1461,7 +1466,7 @@
             // (portrait).
             // When an app below the DAG is requesting landscape, it should actually request the
             // display to be portrait, so that the DAG and the app will be in landscape.
-            requestedOrientation = reverseOrientation(mOrientation);
+            requestedOrientation = reverseOrientation(getOverrideOrientation());
         }
 
         if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
@@ -1486,7 +1491,7 @@
      *
      * @param orientation the specified orientation.
      */
-    void setOrientation(int orientation) {
+    void setOrientation(@ScreenOrientation int orientation) {
         setOrientation(orientation, null /* requestingContainer */);
     }
 
@@ -1494,17 +1499,17 @@
      * Sets the specified orientation of this container. It percolates this change upward along the
      * hierarchy to let each level of the hierarchy a chance to respond to it.
      *
-     * @param orientation the specified orientation. Needs to be one of {@link
-     *      android.content.pm.ActivityInfo.ScreenOrientation}.
+     * @param orientation the specified orientation. Needs to be one of {@link ScreenOrientation}.
      * @param requestingContainer the container which orientation request has changed. Mostly used
      *                            to ensure it gets correct configuration.
      */
-    void setOrientation(int orientation, @Nullable WindowContainer requestingContainer) {
-        if (mOrientation == orientation) {
+    void setOrientation(@ScreenOrientation int orientation,
+            @Nullable WindowContainer requestingContainer) {
+        if (getOverrideOrientation() == orientation) {
             return;
         }
 
-        mOrientation = orientation;
+        setOverrideOrientation(orientation);
         final WindowContainer parent = getParent();
         if (parent != null) {
             if (getConfiguration().orientation != getRequestedConfigurationOrientation()
@@ -1523,9 +1528,9 @@
         }
     }
 
-    @ActivityInfo.ScreenOrientation
+    @ScreenOrientation
     int getOrientation() {
-        return getOrientation(mOrientation);
+        return getOrientation(getOverrideOrientation());
     }
 
     /**
@@ -1539,7 +1544,8 @@
      *                  better match.
      * @return The orientation as specified by this branch or the window hierarchy.
      */
-    int getOrientation(int candidate) {
+    @ScreenOrientation
+    int getOrientation(@ScreenOrientation int candidate) {
         mLastOrientationSource = null;
         if (!providesOrientation()) {
             return SCREEN_ORIENTATION_UNSET;
@@ -1549,16 +1555,16 @@
         // specified; otherwise we prefer to use the orientation of its topmost child that has one
         // specified and fall back on this container's unset or unspecified value as a candidate
         // if none of the children have a better candidate for the orientation.
-        if (mOrientation != SCREEN_ORIENTATION_UNSET
-                && mOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
+        if (getOverrideOrientation() != SCREEN_ORIENTATION_UNSET
+                && getOverrideOrientation() != SCREEN_ORIENTATION_UNSPECIFIED) {
             mLastOrientationSource = this;
-            return mOrientation;
+            return getOverrideOrientation();
         }
 
         for (int i = mChildren.size() - 1; i >= 0; --i) {
             final WindowContainer wc = mChildren.get(i);
 
-            // TODO: Maybe mOrientation should default to SCREEN_ORIENTATION_UNSET vs.
+            // TODO: Maybe mOverrideOrientation should default to SCREEN_ORIENTATION_UNSET vs.
             // SCREEN_ORIENTATION_UNSPECIFIED?
             final int orientation = wc.getOrientation(candidate == SCREEN_ORIENTATION_BEHIND
                     ? SCREEN_ORIENTATION_BEHIND : SCREEN_ORIENTATION_UNSET);
@@ -1590,6 +1596,20 @@
     }
 
     /**
+     * Returns orientation specified on this level of hierarchy without taking children into
+     * account, like {@link #getOrientation} does, allowing subclasses to override. See {@link
+     * ActivityRecord#getOverrideOrientation} for an example.
+     */
+    @ScreenOrientation
+    protected int getOverrideOrientation() {
+        return mOverrideOrientation;
+    }
+
+    protected void setOverrideOrientation(@ScreenOrientation int orientation) {
+        mOverrideOrientation = orientation;
+    }
+
+    /**
      * @return The deepest source which decides the orientation of this window container since the
      *         last time {@link #getOrientation(int) was called.
      */
@@ -2635,7 +2655,7 @@
 
         final long token = proto.start(fieldId);
         super.dumpDebug(proto, CONFIGURATION_CONTAINER, logLevel);
-        proto.write(ORIENTATION, mOrientation);
+        proto.write(ORIENTATION, mOverrideOrientation);
         proto.write(VISIBLE, isVisible);
         writeIdentifierToProto(proto, IDENTIFIER);
         if (mSurfaceAnimator.isAnimating()) {
@@ -3149,7 +3169,7 @@
                             ? activityRecord.getOrganizedTaskFragment()
                             : taskFragment.getOrganizedTaskFragment();
                     if (organizedTf != null && organizedTf.getAnimationParams()
-                            .getAnimationBackgroundColor() != 0) {
+                            .getAnimationBackgroundColor() != DEFAULT_ANIMATION_BACKGROUND_COLOR) {
                         // This window is embedded and has an animation background color set on the
                         // TaskFragment. Pass this color with this window, so the handler can use it
                         // as the animation background color if needed,
@@ -3162,11 +3182,14 @@
                         final Task parentTask = activityRecord != null
                                 ? activityRecord.getTask()
                                 : taskFragment.getTask();
-                        backgroundColorForTransition = ColorUtils.setAlphaComponent(
-                                parentTask.getTaskDescription().getBackgroundColor(), 255);
+                        backgroundColorForTransition = parentTask.getTaskDescription()
+                                .getBackgroundColor();
                     }
                 }
-                animationRunnerBuilder.setTaskBackgroundColor(backgroundColorForTransition);
+                // Set to opaque for animation background to prevent it from exposing the blank
+                // background or content below.
+                animationRunnerBuilder.setTaskBackgroundColor(ColorUtils.setAlphaComponent(
+                        backgroundColorForTransition, 255));
             }
 
             animationRunnerBuilder.build()
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 5065014..8931d80 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -113,6 +113,7 @@
 import static com.android.internal.util.LatencyTracker.ACTION_ROTATE_SCREEN;
 import static com.android.server.LockGuard.INDEX_WINDOW;
 import static com.android.server.LockGuard.installLock;
+import static com.android.server.policy.PhoneWindowManager.TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.ActivityTaskManagerService.POWER_MODE_REASON_CHANGE_DISPLAY;
 import static com.android.server.wm.DisplayContent.IME_TARGET_CONTROL;
@@ -355,6 +356,7 @@
 public class WindowManagerService extends IWindowManager.Stub
         implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowManagerService" : TAG_WM;
+    private static final int TRACE_MAX_SECTION_NAME_LENGTH = 127;
 
     static final int LAYOUT_REPEAT_THRESHOLD = 4;
 
@@ -4170,7 +4172,8 @@
      * <p>Note: this assumes that {@link #mGlobalLock} is held by the caller.
      */
     boolean isIgnoreOrientationRequestDisabled() {
-        return mIsIgnoreOrientationRequestDisabled;
+        return mIsIgnoreOrientationRequestDisabled
+                || !mLetterboxConfiguration.isIgnoreOrientationRequestAllowed();
     }
 
     @Override
@@ -5441,10 +5444,15 @@
 
                 case WAITING_FOR_DRAWN_TIMEOUT: {
                     Runnable callback = null;
-                    final WindowContainer container = (WindowContainer) msg.obj;
+                    final WindowContainer<?> container = (WindowContainer<?>) msg.obj;
                     synchronized (mGlobalLock) {
                         ProtoLog.w(WM_ERROR, "Timeout waiting for drawn: undrawn=%s",
                                 container.mWaitingForDrawn);
+                        if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
+                            for (int i = 0; i < container.mWaitingForDrawn.size(); i++) {
+                                traceEndWaitingForWindowDrawn(container.mWaitingForDrawn.get(i));
+                            }
+                        }
                         container.mWaitingForDrawn.clear();
                         callback = mWaitingForDrawnCallbacks.remove(container);
                     }
@@ -6051,10 +6059,16 @@
                     // Window has been removed or hidden; no draw will now happen, so stop waiting.
                     ProtoLog.w(WM_DEBUG_SCREEN_ON, "Aborted waiting for drawn: %s", win);
                     container.mWaitingForDrawn.remove(win);
+                    if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
+                        traceEndWaitingForWindowDrawn(win);
+                    }
                 } else if (win.hasDrawn()) {
                     // Window is now drawn (and shown).
                     ProtoLog.d(WM_DEBUG_SCREEN_ON, "Window drawn win=%s", win);
                     container.mWaitingForDrawn.remove(win);
+                    if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
+                        traceEndWaitingForWindowDrawn(win);
+                    }
                 }
             }
             if (container.mWaitingForDrawn.isEmpty()) {
@@ -6065,6 +6079,22 @@
         });
     }
 
+    private void traceStartWaitingForWindowDrawn(WindowState window) {
+        final String traceName = TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD + "#"
+                + window.getWindowTag();
+        final String shortenedTraceName = traceName.substring(0, Math.min(
+                TRACE_MAX_SECTION_NAME_LENGTH, traceName.length()));
+        Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, shortenedTraceName, /* cookie= */ 0);
+    }
+
+    private void traceEndWaitingForWindowDrawn(WindowState window) {
+        final String traceName = TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD + "#"
+                + window.getWindowTag();
+        final String shortenedTraceName = traceName.substring(0, Math.min(
+                TRACE_MAX_SECTION_NAME_LENGTH, traceName.length()));
+        Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, shortenedTraceName, /* cookie= */ 0);
+    }
+
     void requestTraversal() {
         mWindowPlacerLocked.requestTraversal();
     }
@@ -7799,7 +7829,7 @@
 
         @Override
         public void waitForAllWindowsDrawn(Runnable callback, long timeout, int displayId) {
-            final WindowContainer container = displayId == INVALID_DISPLAY
+            final WindowContainer<?> container = displayId == INVALID_DISPLAY
                     ? mRoot : mRoot.getDisplayContent(displayId);
             if (container == null) {
                 // The waiting container doesn't exist, no need to wait to run the callback. Run and
@@ -7815,6 +7845,12 @@
                 if (container.mWaitingForDrawn.isEmpty()) {
                     allWindowsDrawn = true;
                 } else {
+                    if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
+                        for (int i = 0; i < container.mWaitingForDrawn.size(); i++) {
+                            traceStartWaitingForWindowDrawn(container.mWaitingForDrawn.get(i));
+                        }
+                    }
+
                     mWaitingForDrawnCallbacks.put(container, callback);
                     mH.sendNewMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, container, timeout);
                     checkDrawnWindowsLocked();
diff --git a/services/core/java/com/android/server/wm/WindowOrientationListener.java b/services/core/java/com/android/server/wm/WindowOrientationListener.java
index 3e165e4..14c816d 100644
--- a/services/core/java/com/android/server/wm/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/wm/WindowOrientationListener.java
@@ -88,14 +88,19 @@
 
     private final Object mLock = new Object();
 
+    @Surface.Rotation
+    private final int mDefaultRotation;
+
     /**
      * Creates a new WindowOrientationListener.
      *
      * @param context for the WindowOrientationListener.
      * @param handler Provides the Looper for receiving sensor updates.
+     * @param defaultRotation Default rotation of the display.
      */
-    public WindowOrientationListener(Context context, Handler handler) {
-        this(context, handler, SensorManager.SENSOR_DELAY_UI);
+    public WindowOrientationListener(Context context, Handler handler,
+            @Surface.Rotation int defaultRotation) {
+        this(context, handler, defaultRotation, SensorManager.SENSOR_DELAY_UI);
     }
 
     /**
@@ -103,7 +108,7 @@
      *
      * @param context for the WindowOrientationListener.
      * @param handler Provides the Looper for receiving sensor updates.
-     * @param wmService WindowManagerService to read the device config from.
+     * @param defaultRotation Default rotation of the display.
      * @param rate at which sensor events are processed (see also
      * {@link android.hardware.SensorManager SensorManager}). Use the default
      * value of {@link android.hardware.SensorManager#SENSOR_DELAY_NORMAL
@@ -111,10 +116,11 @@
      *
      * This constructor is private since no one uses it.
      */
-    private WindowOrientationListener(
-            Context context, Handler handler, int rate) {
+    private WindowOrientationListener(Context context, Handler handler,
+            @Surface.Rotation int defaultRotation, int rate) {
         mContext = context;
         mHandler = handler;
+        mDefaultRotation = defaultRotation;
         mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
         mRate = rate;
         List<Sensor> l = mSensorManager.getSensorList(Sensor.TYPE_DEVICE_ORIENTATION);
@@ -1159,7 +1165,7 @@
                                 "Reusing the last rotation resolution: " + mLastRotationResolution);
                         finalizeRotation(mLastRotationResolution);
                     } else {
-                        finalizeRotation(Surface.ROTATION_0);
+                        finalizeRotation(mDefaultRotation);
                     }
                     return;
                 }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index ae03fbb..52f2b63 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2387,7 +2387,11 @@
         // IME parent may failed to attach to the app during rotating the screen.
         // See DisplayContent#shouldImeAttachedToApp, DisplayContent#isImeControlledByApp
         if (windowConfigChanged) {
-            getDisplayContent().updateImeControlTarget();
+            // If the window was the IME layering target, updates the IME surface parent in case
+            // the IME surface may be wrongly positioned when the window configuration affects the
+            // IME surface association. (e.g. Attach IME surface on the display instead of the
+            // app when the app bounds being letterboxed.)
+            mDisplayContent.updateImeControlTarget(isImeLayeringTarget() /* updateImeParent */);
         }
     }
 
@@ -5675,14 +5679,6 @@
                     && imeTarget.compareTo(this) <= 0;
             return inTokenWithAndAboveImeTarget;
         }
-
-        // The condition is for the system dialog not belonging to any Activity.
-        // (^FLAG_NOT_FOCUSABLE & FLAG_ALT_FOCUSABLE_IM) means the dialog is still focusable but
-        // should be placed above the IME window.
-        if ((mAttrs.flags & (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM))
-                == FLAG_ALT_FOCUSABLE_IM && isTrustedOverlay() && canAddInternalSystemWindow()) {
-            return true;
-        }
         return false;
     }
 
@@ -6033,7 +6029,7 @@
             Slog.i(TAG, "finishDrawing of orientation change: " + this + " " + duration + "ms");
             mOrientationChangeRedrawRequestTime = 0;
         } else if (mActivityRecord != null && mActivityRecord.mRelaunchStartTime != 0
-                && mActivityRecord.findMainWindow() == this) {
+                && mActivityRecord.findMainWindow(false /* includeStartingApp */) == this) {
             final long duration =
                     SystemClock.elapsedRealtime() - mActivityRecord.mRelaunchStartTime;
             Slog.i(TAG, "finishDrawing of relaunch: " + this + " " + duration + "ms");
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index abe48f8..91a1138 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -472,6 +472,14 @@
                     minOccurs="0" maxOccurs="1">
             <xs:annotation name="final"/>
         </xs:element>
+        <xs:element name="defaultRefreshRateInHbmHdr" type="xs:nonNegativeInteger"
+                    minOccurs="0" maxOccurs="1">
+            <xs:annotation name="final"/>
+        </xs:element>
+        <xs:element name="defaultRefreshRateInHbmSunlight" type="xs:nonNegativeInteger"
+                    minOccurs="0" maxOccurs="1">
+            <xs:annotation name="final"/>
+        </xs:element>
         <xs:element name="lowerBlockingZoneConfigs" type="blockingZoneConfig"
                     minOccurs="0" maxOccurs="1">
             <xs:annotation name="final"/>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 2c97af5..1110d86 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -188,10 +188,14 @@
     ctor public RefreshRateConfigs();
     method public final java.math.BigInteger getDefaultPeakRefreshRate();
     method public final java.math.BigInteger getDefaultRefreshRate();
+    method public final java.math.BigInteger getDefaultRefreshRateInHbmHdr();
+    method public final java.math.BigInteger getDefaultRefreshRateInHbmSunlight();
     method public final com.android.server.display.config.BlockingZoneConfig getHigherBlockingZoneConfigs();
     method public final com.android.server.display.config.BlockingZoneConfig getLowerBlockingZoneConfigs();
     method public final void setDefaultPeakRefreshRate(java.math.BigInteger);
     method public final void setDefaultRefreshRate(java.math.BigInteger);
+    method public final void setDefaultRefreshRateInHbmHdr(java.math.BigInteger);
+    method public final void setDefaultRefreshRateInHbmSunlight(java.math.BigInteger);
     method public final void setHigherBlockingZoneConfigs(com.android.server.display.config.BlockingZoneConfig);
     method public final void setLowerBlockingZoneConfigs(com.android.server.display.config.BlockingZoneConfig);
   }
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index a49577b..1aeb0ca 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -2816,6 +2816,12 @@
 
 binder::Status IncrementalService::DataLoaderStub::onStatusChanged(MountId mountId, int newStatus) {
     if (!isValid()) {
+        if (newStatus == IDataLoaderStatusListener::DATA_LOADER_BOUND) {
+            // Async "bound" came to already destroyed stub.
+            // Unbind immediately to avoid invalid stub sitting around in DataLoaderManagerService.
+            mService.mDataLoaderManager->unbindFromDataLoader(mountId);
+            return binder::Status::ok();
+        }
         return binder::Status::
                 fromServiceSpecificError(-EINVAL, "onStatusChange came to invalid DataLoaderStub");
     }
diff --git a/services/tests/mockingservicestests/AndroidManifest.xml b/services/tests/mockingservicestests/AndroidManifest.xml
index 33ac735..ea0481e 100644
--- a/services/tests/mockingservicestests/AndroidManifest.xml
+++ b/services/tests/mockingservicestests/AndroidManifest.xml
@@ -43,6 +43,9 @@
     <!-- needed by TrustManagerServiceTest to access LockSettings' secure storage -->
     <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
 
+    <!-- needed by GameManagerServiceTest because GameManager creates a UidObserver -->
+    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+
     <application android:testOnly="true"
                  android:debuggable="true">
         <uses-library android:name="android.test.runner" />
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 80de823..0f2176f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -205,6 +205,7 @@
     private static final String TAG = AlarmManagerServiceTest.class.getSimpleName();
     private static final int SYSTEM_UI_UID = 12345;
     private static final int TEST_CALLING_USER = UserHandle.getUserId(TEST_CALLING_UID);
+    private static final int TEST_CALLING_UID_2 = TEST_CALLING_UID + 1;
 
     private long mAppStandbyWindow;
     private long mAllowWhileIdleWindow;
@@ -3375,10 +3376,40 @@
             final int type = ((i & 1) == 0) ? ELAPSED_REALTIME : ELAPSED_REALTIME_WAKEUP;
             setTestAlarm(type, mNowElapsedTest + i, getNewMockPendingIntent());
         }
+        for (int i = 0; i < 4; i++) {
+            final int type = ((i & 1) == 0) ? ELAPSED_REALTIME : ELAPSED_REALTIME_WAKEUP;
+            setTestAlarm(
+                    type,
+                    mNowElapsedTest + i,
+                    getNewMockPendingIntent(),
+                    0,
+                    FLAG_STANDALONE,
+                    TEST_CALLING_UID_2);
+        }
         mNowElapsedTest += 100;
         mTestTimer.expire();
 
-        verify(() -> MetricsHelper.pushAlarmBatchDelivered(10, 5));
+        final ArgumentCaptor<int[]> uidsCaptor = ArgumentCaptor.forClass(int[].class);
+        final ArgumentCaptor<int[]> alarmsPerUidCaptor = ArgumentCaptor.forClass(int[].class);
+        final ArgumentCaptor<int[]> wakeupAlarmsPerUidCaptor = ArgumentCaptor.forClass(int[].class);
+
+        verify(() -> MetricsHelper.pushAlarmBatchDelivered(
+                eq(14),
+                eq(7),
+                uidsCaptor.capture(),
+                alarmsPerUidCaptor.capture(),
+                wakeupAlarmsPerUidCaptor.capture()));
+        assertEquals(2, uidsCaptor.getValue().length);
+        assertEquals(2, alarmsPerUidCaptor.getValue().length);
+        assertEquals(2, wakeupAlarmsPerUidCaptor.getValue().length);
+        final int uid1Idx = uidsCaptor.getValue()[0] == TEST_CALLING_UID ? 0 : 1;
+        final int uid2Idx = 1 - uid1Idx;
+        assertEquals(TEST_CALLING_UID, uidsCaptor.getValue()[uid1Idx]);
+        assertEquals(TEST_CALLING_UID_2, uidsCaptor.getValue()[uid2Idx]);
+        assertEquals(10, alarmsPerUidCaptor.getValue()[uid1Idx]);
+        assertEquals(5, wakeupAlarmsPerUidCaptor.getValue()[uid1Idx]);
+        assertEquals(4, alarmsPerUidCaptor.getValue()[uid2Idx]);
+        assertEquals(2, wakeupAlarmsPerUidCaptor.getValue()[uid2Idx]);
     }
 
     @Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
index fa4a9de..2d5f0b0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -29,13 +29,16 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.Manifest;
+import android.app.ActivityManager;
 import android.app.GameManager;
 import android.app.GameModeInfo;
 import android.app.GameState;
@@ -203,6 +206,23 @@
         LocalServices.addService(PowerManagerInternal.class, mMockPowerManager);
     }
 
+    private void mockAppCategory(String packageName, @ApplicationInfo.Category int category)
+            throws Exception {
+        reset(mMockPackageManager);
+        final ApplicationInfo gameApplicationInfo = new ApplicationInfo();
+        gameApplicationInfo.category = category;
+        gameApplicationInfo.packageName = packageName;
+        final PackageInfo pi = new PackageInfo();
+        pi.packageName = packageName;
+        pi.applicationInfo = gameApplicationInfo;
+        final List<PackageInfo> packages = new ArrayList<>();
+        packages.add(pi);
+        when(mMockPackageManager.getInstalledPackagesAsUser(anyInt(), anyInt()))
+            .thenReturn(packages);
+        when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
+            .thenReturn(gameApplicationInfo);
+    }
+
     @After
     public void tearDown() throws Exception {
         LocalServices.removeServiceForTest(PowerManagerInternal.class);
@@ -1597,4 +1617,113 @@
                 ArgumentMatchers.eq(DEFAULT_PACKAGE_UID),
                 ArgumentMatchers.eq(0.0f));
     }
+
+    private GameManagerService createServiceAndStartUser(int userId) {
+        GameManagerService gameManagerService = new GameManagerService(mMockContext,
+                mTestLooper.getLooper());
+        startUser(gameManagerService, userId);
+        return gameManagerService;
+    }
+
+    @Test
+    public void testGamePowerMode_gamePackage() throws Exception {
+        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+        String[] packages = {mPackageName};
+        when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
+        gameManagerService.mUidObserver.onUidStateChanged(
+                DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+        verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, true);
+    }
+
+    @Test
+    public void testGamePowerMode_twoGames() throws Exception {
+        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+        String[] packages1 = {mPackageName};
+        when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages1);
+        String someGamePkg = "some.game";
+        String[] packages2 = {someGamePkg};
+        int somePackageId = DEFAULT_PACKAGE_UID + 1;
+        when(mMockPackageManager.getPackagesForUid(somePackageId)).thenReturn(packages2);
+        HashMap<Integer, Boolean> powerState = new HashMap<>();
+        doAnswer(inv -> powerState.put(inv.getArgument(0), inv.getArgument(1)))
+                .when(mMockPowerManager).setPowerMode(anyInt(), anyBoolean());
+        gameManagerService.mUidObserver.onUidStateChanged(
+                DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+        assertTrue(powerState.get(Mode.GAME));
+        gameManagerService.mUidObserver.onUidStateChanged(
+                DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+        gameManagerService.mUidObserver.onUidStateChanged(
+                somePackageId, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+        assertTrue(powerState.get(Mode.GAME));
+        gameManagerService.mUidObserver.onUidStateChanged(
+                somePackageId, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+        assertFalse(powerState.get(Mode.GAME));
+    }
+
+    @Test
+    public void testGamePowerMode_twoGamesOverlap() throws Exception {
+        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+        String[] packages1 = {mPackageName};
+        when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages1);
+        String someGamePkg = "some.game";
+        String[] packages2 = {someGamePkg};
+        int somePackageId = DEFAULT_PACKAGE_UID + 1;
+        when(mMockPackageManager.getPackagesForUid(somePackageId)).thenReturn(packages2);
+        gameManagerService.mUidObserver.onUidStateChanged(
+                DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+        gameManagerService.mUidObserver.onUidStateChanged(
+                somePackageId, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+        gameManagerService.mUidObserver.onUidStateChanged(
+                DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+        gameManagerService.mUidObserver.onUidStateChanged(
+                somePackageId, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+        verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, true);
+        verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, false);
+    }
+
+    @Test
+    public void testGamePowerMode_released() throws Exception {
+        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+        String[] packages = {mPackageName};
+        when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
+        gameManagerService.mUidObserver.onUidStateChanged(
+                DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+        gameManagerService.mUidObserver.onUidStateChanged(
+                DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+        verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, false);
+    }
+
+    @Test
+    public void testGamePowerMode_noPackage() throws Exception {
+        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+        String[] packages = {};
+        when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
+        gameManagerService.mUidObserver.onUidStateChanged(
+                DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+        verify(mMockPowerManager, times(0)).setPowerMode(Mode.GAME, true);
+    }
+
+    @Test
+    public void testGamePowerMode_notAGamePackage() throws Exception {
+        mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_IMAGE);
+        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+        String[] packages = {"someapp"};
+        when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
+        gameManagerService.mUidObserver.onUidStateChanged(
+                DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+        verify(mMockPowerManager, times(0)).setPowerMode(Mode.GAME, true);
+    }
+
+    @Test
+    public void testGamePowerMode_notAGamePackageNotReleased() throws Exception {
+        mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_IMAGE);
+        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+        String[] packages = {"someapp"};
+        when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
+        gameManagerService.mUidObserver.onUidStateChanged(
+                DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+        gameManagerService.mUidObserver.onUidStateChanged(
+                DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+        verify(mMockPowerManager, times(0)).setPowerMode(Mode.GAME, false);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
index 3c735e3..4915c64 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.biometrics.sensors.fingerprint.aidl;
 
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_VENDOR;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -35,6 +37,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.content.ComponentName;
+import android.hardware.biometrics.BiometricFingerprintConstants;
 import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.common.ICancellationSignal;
 import android.hardware.biometrics.common.OperationContext;
@@ -54,6 +57,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.internal.R;
 import com.android.server.biometrics.log.BiometricContext;
 import com.android.server.biometrics.log.BiometricLogger;
 import com.android.server.biometrics.log.CallbackWithProbe;
@@ -335,6 +339,21 @@
         showHideOverlay(c -> c.onLockoutPermanent());
     }
 
+    @Test
+    public void testPowerPressForwardsErrorMessage() throws RemoteException {
+        final FingerprintAuthenticationClient client = createClient();
+        final int testVendorPowerPressCode = 1;
+        when(mContext.getOrCreateTestableResources().getResources()
+                .getBoolean(R.bool.config_powerPressMapping)).thenReturn(true);
+        when(mContext.getOrCreateTestableResources().getResources()
+                .getInteger(R.integer.config_powerPressCode)).thenReturn(testVendorPowerPressCode);
+
+        client.onError(FINGERPRINT_ERROR_VENDOR, testVendorPowerPressCode);
+
+        verify(mClientMonitorCallbackConverter).onError(anyInt(), anyInt(),
+                eq(BiometricFingerprintConstants.BIOMETRIC_ERROR_POWER_PRESSED), anyInt());
+    }
+
     private void showHideOverlay(Consumer<FingerprintAuthenticationClient> block)
             throws RemoteException {
         final FingerprintAuthenticationClient client = createClient();
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
index 837b553..7e29a76 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
@@ -16,7 +16,7 @@
 
 package com.android.server.biometrics.sensors.fingerprint.aidl;
 
-import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_POWER_PRESSED;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -28,10 +28,10 @@
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.same;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.hardware.biometrics.BiometricFingerprintConstants;
 import android.hardware.biometrics.common.OperationContext;
 import android.hardware.biometrics.fingerprint.ISession;
 import android.hardware.biometrics.fingerprint.PointerContext;
@@ -48,6 +48,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.internal.R;
 import com.android.server.biometrics.log.BiometricContext;
 import com.android.server.biometrics.log.BiometricLogger;
 import com.android.server.biometrics.log.CallbackWithProbe;
@@ -66,7 +67,6 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
-import java.util.ArrayList;
 import java.util.function.Consumer;
 
 @Presubmit
@@ -258,11 +258,16 @@
     @Test
     public void testPowerPressForwardsAcquireMessage() throws RemoteException {
         final FingerprintEnrollClient client = createClient();
-        client.start(mCallback);
-        client.onPowerPressed();
+        final int testVendorPowerPressCode = 1;
+        when(mContext.getOrCreateTestableResources().getResources()
+                .getBoolean(R.bool.config_powerPressMapping)).thenReturn(true);
+        when(mContext.getOrCreateTestableResources().getResources()
+                .getInteger(R.integer.config_powerPressCode)).thenReturn(testVendorPowerPressCode);
+
+        client.onAcquired(FINGERPRINT_ACQUIRED_VENDOR, testVendorPowerPressCode);
 
         verify(mClientMonitorCallbackConverter).onAcquired(anyInt(),
-                eq(FINGERPRINT_ACQUIRED_POWER_PRESSED), anyInt());
+                eq(BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_POWER_PRESSED), anyInt());
     }
 
     private void showHideOverlay(Consumer<FingerprintEnrollClient> block)
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStatePolicyProviderTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStatePolicyProviderTest.java
index 0bd81b7..18dc35c 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStatePolicyProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStatePolicyProviderTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.devicestate;
 
+import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.Assert.assertThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 import static org.testng.Assert.assertThrows;
@@ -24,8 +26,6 @@
 import android.content.res.Resources;
 import android.platform.test.annotations.Presubmit;
 
-import org.hamcrest.Matchers;
-import org.junit.Assert;
 import org.junit.Test;
 
 /**
@@ -39,37 +39,35 @@
 
     @Test
     public void test_emptyPolicyProvider() {
-        Assert.assertThat(DeviceStatePolicy.Provider.fromResources(resourcesWithProvider("")),
-                Matchers.instanceOf(DeviceStatePolicy.DefaultProvider.class));
+        assertThat(DeviceStatePolicy.Provider.fromResources(resourcesWithProvider("")),
+                instanceOf(DeviceStatePolicy.DefaultProvider.class));
     }
 
     @Test
     public void test_nullPolicyProvider() {
-        Assert.assertThat(DeviceStatePolicy.Provider.fromResources(resourcesWithProvider(null)),
-                Matchers.instanceOf(DeviceStatePolicy.DefaultProvider.class));
+        assertThat(DeviceStatePolicy.Provider.fromResources(resourcesWithProvider(null)),
+                instanceOf(DeviceStatePolicy.DefaultProvider.class));
     }
 
     @Test
     public void test_customPolicyProvider() {
-        Assert.assertThat(DeviceStatePolicy.Provider.fromResources(resourcesWithProvider(
-                TestProvider.class.getName())),
-                Matchers.instanceOf(TestProvider.class));
+        assertThat(DeviceStatePolicy.Provider.fromResources(resourcesWithProvider(
+                        TestProvider.class.getName())),
+                instanceOf(TestProvider.class));
     }
 
     @Test
     public void test_badPolicyProvider_notImplementingProviderInterface() {
-        assertThrows(IllegalStateException.class, () -> {
-            DeviceStatePolicy.Provider.fromResources(resourcesWithProvider(
-                    Object.class.getName()));
-        });
+        assertThrows(IllegalStateException.class, () ->
+                DeviceStatePolicy.Provider.fromResources(resourcesWithProvider(
+                        Object.class.getName())));
     }
 
     @Test
-    public void test_badPolicyProvider_doesntExist() {
-        assertThrows(IllegalStateException.class, () -> {
-            DeviceStatePolicy.Provider.fromResources(resourcesWithProvider(
-                    "com.android.devicestate.nonexistent.policy"));
-        });
+    public void test_badPolicyProvider_returnsDefault() {
+        assertThat(DeviceStatePolicy.Provider.fromResources(
+                        resourcesWithProvider("com.android.devicestate.nonexistent.policy")),
+                instanceOf(DeviceStatePolicy.DefaultProvider.class));
     }
 
     private static Resources resourcesWithProvider(String provider) {
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 77e5d1d..8f70617 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -53,6 +53,8 @@
     private static final int DEFAULT_REFRESH_RATE = 120;
     private static final int DEFAULT_HIGH_BLOCKING_ZONE_REFRESH_RATE = 55;
     private static final int DEFAULT_LOW_BLOCKING_ZONE_REFRESH_RATE = 95;
+    private static final int DEFAULT_REFRESH_RATE_IN_HBM_HDR = 90;
+    private static final int DEFAULT_REFRESH_RATE_IN_HBM_SUNLIGHT = 100;
     private static final int[] LOW_BRIGHTNESS_THRESHOLD_OF_PEAK_REFRESH_RATE = new int[]{10, 30};
     private static final int[] LOW_AMBIENT_THRESHOLD_OF_PEAK_REFRESH_RATE = new int[]{1, 21};
     private static final int[] HIGH_BRIGHTNESS_THRESHOLD_OF_PEAK_REFRESH_RATE = new int[]{160};
@@ -156,6 +158,8 @@
         assertEquals(90, mDisplayDeviceConfig.getDefaultHighBlockingZoneRefreshRate());
         assertEquals(85, mDisplayDeviceConfig.getDefaultPeakRefreshRate());
         assertEquals(45, mDisplayDeviceConfig.getDefaultRefreshRate());
+        assertEquals(82, mDisplayDeviceConfig.getDefaultRefreshRateInHbmHdr());
+        assertEquals(83, mDisplayDeviceConfig.getDefaultRefreshRateInHbmSunlight());
         assertArrayEquals(new int[]{45, 55},
                 mDisplayDeviceConfig.getLowDisplayBrightnessThresholds());
         assertArrayEquals(new int[]{50, 60},
@@ -240,6 +244,10 @@
                 DEFAULT_HIGH_BLOCKING_ZONE_REFRESH_RATE);
         assertEquals(mDisplayDeviceConfig.getDefaultPeakRefreshRate(), DEFAULT_PEAK_REFRESH_RATE);
         assertEquals(mDisplayDeviceConfig.getDefaultRefreshRate(), DEFAULT_REFRESH_RATE);
+        assertEquals(mDisplayDeviceConfig.getDefaultRefreshRateInHbmSunlight(),
+                DEFAULT_REFRESH_RATE_IN_HBM_SUNLIGHT);
+        assertEquals(mDisplayDeviceConfig.getDefaultRefreshRateInHbmHdr(),
+                DEFAULT_REFRESH_RATE_IN_HBM_HDR);
         assertArrayEquals(mDisplayDeviceConfig.getLowDisplayBrightnessThresholds(),
                 LOW_BRIGHTNESS_THRESHOLD_OF_PEAK_REFRESH_RATE);
         assertArrayEquals(mDisplayDeviceConfig.getLowAmbientBrightnessThresholds(),
@@ -459,6 +467,8 @@
                 +   "<refreshRate>\n"
                 +       "<defaultRefreshRate>45</defaultRefreshRate>\n"
                 +       "<defaultPeakRefreshRate>85</defaultPeakRefreshRate>\n"
+                +       "<defaultRefreshRateInHbmHdr>82</defaultRefreshRateInHbmHdr>\n"
+                +       "<defaultRefreshRateInHbmSunlight>83</defaultRefreshRateInHbmSunlight>\n"
                 +       "<lowerBlockingZoneConfigs>\n"
                 +           "<defaultRefreshRate>75</defaultRefreshRate>\n"
                 +           "<blockingZoneThreshold>\n"
@@ -578,6 +588,12 @@
         when(mResources.getIntArray(
                 R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate))
                 .thenReturn(HIGH_AMBIENT_THRESHOLD_OF_PEAK_REFRESH_RATE);
+        when(mResources.getInteger(
+            R.integer.config_defaultRefreshRateInHbmHdr))
+            .thenReturn(DEFAULT_REFRESH_RATE_IN_HBM_HDR);
+        when(mResources.getInteger(
+            R.integer.config_defaultRefreshRateInHbmSunlight))
+            .thenReturn(DEFAULT_REFRESH_RATE_IN_HBM_SUNLIGHT);
 
         mDisplayDeviceConfig = DisplayDeviceConfig.create(mContext, true);
     }
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index af39dd4..ff37564 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -1873,6 +1873,10 @@
             .thenReturn(65);
         when(resources.getInteger(R.integer.config_defaultRefreshRateInZone))
             .thenReturn(85);
+        when(resources.getInteger(R.integer.config_defaultRefreshRateInHbmHdr))
+            .thenReturn(95);
+        when(resources.getInteger(R.integer.config_defaultRefreshRateInHbmSunlight))
+            .thenReturn(100);
         when(resources.getIntArray(R.array.config_brightnessThresholdsOfPeakRefreshRate))
             .thenReturn(new int[]{5});
         when(resources.getIntArray(R.array.config_ambientThresholdsOfPeakRefreshRate))
@@ -1883,8 +1887,21 @@
         when(
             resources.getIntArray(R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate))
             .thenReturn(new int[]{7000});
+        when(resources.getInteger(
+            com.android.internal.R.integer.config_displayWhiteBalanceBrightnessFilterHorizon))
+            .thenReturn(3);
+        ArgumentCaptor<TypedValue> valueArgumentCaptor = ArgumentCaptor.forClass(TypedValue.class);
+        doAnswer((Answer<Void>) invocation -> {
+            valueArgumentCaptor.getValue().type = 4;
+            valueArgumentCaptor.getValue().data = 13;
+            return null;
+        }).when(resources).getValue(eq(com.android.internal.R.dimen
+                .config_displayWhiteBalanceBrightnessFilterIntercept),
+                valueArgumentCaptor.capture(), eq(true));
         DisplayModeDirector director =
                 createDirectorFromRefreshRateArray(new float[]{60.0f, 90.0f}, 0);
+        SensorManager sensorManager = createMockSensorManager(createLightSensor());
+        director.start(sensorManager);
         // We don't expect any interaction with DeviceConfig when the director is initialized
         // because we explicitly avoid doing this as this can lead to a latency spike in the
         // startup of DisplayManagerService
@@ -1894,6 +1911,8 @@
                 0.0);
         assertEquals(director.getBrightnessObserver().getRefreshRateInHighZone(), 65);
         assertEquals(director.getBrightnessObserver().getRefreshRateInLowZone(), 85);
+        assertEquals(director.getHbmObserver().getRefreshRateInHbmHdr(), 95);
+        assertEquals(director.getHbmObserver().getRefreshRateInHbmSunlight(), 100);
         assertArrayEquals(director.getBrightnessObserver().getHighDisplayBrightnessThreshold(),
                 new int[]{250});
         assertArrayEquals(director.getBrightnessObserver().getHighAmbientBrightnessThreshold(),
@@ -1903,6 +1922,7 @@
         assertArrayEquals(director.getBrightnessObserver().getLowAmbientBrightnessThreshold(),
                 new int[]{10});
 
+
         // Notify that the default display is updated, such that DisplayDeviceConfig has new values
         DisplayDeviceConfig displayDeviceConfig = mock(DisplayDeviceConfig.class);
         when(displayDeviceConfig.getDefaultLowBlockingZoneRefreshRate()).thenReturn(50);
@@ -1913,6 +1933,8 @@
         when(displayDeviceConfig.getLowAmbientBrightnessThresholds()).thenReturn(new int[]{30});
         when(displayDeviceConfig.getHighDisplayBrightnessThresholds()).thenReturn(new int[]{210});
         when(displayDeviceConfig.getHighAmbientBrightnessThresholds()).thenReturn(new int[]{2100});
+        when(displayDeviceConfig.getDefaultRefreshRateInHbmHdr()).thenReturn(65);
+        when(displayDeviceConfig.getDefaultRefreshRateInHbmSunlight()).thenReturn(75);
         director.defaultDisplayDeviceUpdated(displayDeviceConfig);
 
         assertEquals(director.getSettingsObserver().getDefaultRefreshRate(), 60, 0.0);
@@ -1928,6 +1950,8 @@
                 new int[]{25});
         assertArrayEquals(director.getBrightnessObserver().getLowAmbientBrightnessThreshold(),
                 new int[]{30});
+        assertEquals(director.getHbmObserver().getRefreshRateInHbmHdr(), 65);
+        assertEquals(director.getHbmObserver().getRefreshRateInHbmSunlight(), 75);
 
         // Notify that the default display is updated, such that DeviceConfig has new values
         FakeDeviceConfig config = mInjector.getDeviceConfig();
@@ -1938,7 +1962,8 @@
         config.setLowDisplayBrightnessThresholds(new int[]{10});
         config.setHighDisplayBrightnessThresholds(new int[]{255});
         config.setHighAmbientBrightnessThresholds(new int[]{8000});
-
+        config.setRefreshRateInHbmHdr(70);
+        config.setRefreshRateInHbmSunlight(80);
         director.defaultDisplayDeviceUpdated(displayDeviceConfig);
 
         assertEquals(director.getSettingsObserver().getDefaultRefreshRate(), 60, 0.0);
@@ -1954,6 +1979,8 @@
                 new int[]{10});
         assertArrayEquals(director.getBrightnessObserver().getLowAmbientBrightnessThreshold(),
                 new int[]{20});
+        assertEquals(director.getHbmObserver().getRefreshRateInHbmHdr(), 70);
+        assertEquals(director.getHbmObserver().getRefreshRateInHbmSunlight(), 80);
     }
 
     @Test
@@ -2011,6 +2038,74 @@
                 eq(lightSensorTwo), anyInt(), any(Handler.class));
     }
 
+    @Test
+    public void testAuthenticationPossibleSetsPhysicalRateRangesToMax() throws RemoteException {
+        DisplayModeDirector director =
+                createDirectorFromRefreshRateArray(new float[]{60.0f, 90.0f}, 0);
+        // don't call director.start(createMockSensorManager());
+        // DisplayObserver will reset mSupportedModesByDisplay
+        director.onBootCompleted();
+        ArgumentCaptor<IUdfpsHbmListener> captor =
+                ArgumentCaptor.forClass(IUdfpsHbmListener.class);
+        verify(mStatusBarMock).setUdfpsHbmListener(captor.capture());
+
+        captor.getValue().onAuthenticationPossible(DISPLAY_ID, true);
+
+        Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE);
+        assertThat(vote.refreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
+        assertThat(vote.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90);
+    }
+
+    @Test
+    public void testAuthenticationPossibleUnsetsVote() throws RemoteException {
+        DisplayModeDirector director =
+                createDirectorFromRefreshRateArray(new float[]{60.0f, 90.0f}, 0);
+        director.start(createMockSensorManager());
+        director.onBootCompleted();
+        ArgumentCaptor<IUdfpsHbmListener> captor =
+                ArgumentCaptor.forClass(IUdfpsHbmListener.class);
+        verify(mStatusBarMock).setUdfpsHbmListener(captor.capture());
+        captor.getValue().onAuthenticationPossible(DISPLAY_ID, true);
+        captor.getValue().onAuthenticationPossible(DISPLAY_ID, false);
+
+        Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE);
+        assertNull(vote);
+    }
+
+    @Test
+    public void testUdfpsRequestSetsPhysicalRateRangesToMax() throws RemoteException {
+        DisplayModeDirector director =
+                createDirectorFromRefreshRateArray(new float[]{60.0f, 90.0f}, 0);
+        // don't call director.start(createMockSensorManager());
+        // DisplayObserver will reset mSupportedModesByDisplay
+        director.onBootCompleted();
+        ArgumentCaptor<IUdfpsHbmListener> captor =
+                ArgumentCaptor.forClass(IUdfpsHbmListener.class);
+        verify(mStatusBarMock).setUdfpsHbmListener(captor.capture());
+
+        captor.getValue().onHbmEnabled(DISPLAY_ID);
+
+        Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_UDFPS);
+        assertThat(vote.refreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
+        assertThat(vote.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90);
+    }
+
+    @Test
+    public void testUdfpsRequestUnsetsUnsetsVote() throws RemoteException {
+        DisplayModeDirector director =
+                createDirectorFromRefreshRateArray(new float[]{60.0f, 90.0f}, 0);
+        director.start(createMockSensorManager());
+        director.onBootCompleted();
+        ArgumentCaptor<IUdfpsHbmListener> captor =
+                ArgumentCaptor.forClass(IUdfpsHbmListener.class);
+        verify(mStatusBarMock).setUdfpsHbmListener(captor.capture());
+        captor.getValue().onHbmEnabled(DISPLAY_ID);
+        captor.getValue().onHbmEnabled(DISPLAY_ID);
+
+        Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_UDFPS);
+        assertNull(vote);
+    }
+
     private Temperature getSkinTemp(@Temperature.ThrottlingStatus int status) {
         return new Temperature(30.0f, Temperature.TYPE_SKIN, "test_skin_temp", status);
     }
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
index 650eef0..a7da2417 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.display;
 
+import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.DEFAULT_DISPLAY_GROUP;
 import static android.view.Display.TYPE_EXTERNAL;
@@ -441,6 +442,11 @@
                 /* isOverrideActive= */false,
                 /* isInteractive= */true,
                 /* isBootCompleted= */true));
+        assertFalse(mLogicalDisplayMapper.shouldDeviceBePutToSleep(DEVICE_STATE_CLOSED,
+                INVALID_DEVICE_STATE,
+                /* isOverrideActive= */false,
+                /* isInteractive= */true,
+                /* isBootCompleted= */true));
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/dreams/DreamControllerTest.java b/services/tests/servicestests/src/com/android/server/dreams/DreamControllerTest.java
index 303a370..1ef1197 100644
--- a/services/tests/servicestests/src/com/android/server/dreams/DreamControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/dreams/DreamControllerTest.java
@@ -99,7 +99,24 @@
         mLooper.dispatchAll();
 
         // Verify that dream service is called to attach.
-        verify(mIDreamService).attach(eq(mToken), eq(false) /*doze*/, any());
+        verify(mIDreamService).attach(eq(mToken), eq(false) /*doze*/,
+                eq(false) /*preview*/, any());
+    }
+
+    @Test
+    public void startDream_attachOnServiceConnectedInPreviewMode() throws RemoteException {
+        // Call dream controller to start dreaming.
+        mDreamController.startDream(mToken, mDreamName, true /*isPreview*/, false /*doze*/,
+                0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/);
+
+        // Mock service connected.
+        final ServiceConnection serviceConnection = captureServiceConnection();
+        serviceConnection.onServiceConnected(mDreamName, mIBinder);
+        mLooper.dispatchAll();
+
+        // Verify that dream service is called to attach.
+        verify(mIDreamService).attach(eq(mToken), eq(false) /*doze*/,
+                eq(true) /*preview*/, any());
     }
 
     @Test
@@ -129,7 +146,7 @@
 
         // Mock second dream started.
         verify(newDreamService).attach(eq(newToken), eq(false) /*doze*/,
-                mRemoteCallbackCaptor.capture());
+                eq(false) /*preview*/, mRemoteCallbackCaptor.capture());
         mRemoteCallbackCaptor.getValue().sendResult(null /*data*/);
         mLooper.dispatchAll();
 
diff --git a/services/tests/servicestests/src/com/android/server/dreams/DreamOverlayServiceTest.java b/services/tests/servicestests/src/com/android/server/dreams/DreamOverlayServiceTest.java
new file mode 100644
index 0000000..6c73f71
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/dreams/DreamOverlayServiceTest.java
@@ -0,0 +1,175 @@
+/*
+ * 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.server.dreams;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.service.dreams.DreamOverlayService;
+import android.service.dreams.IDreamOverlay;
+import android.service.dreams.IDreamOverlayCallback;
+import android.service.dreams.IDreamOverlayClient;
+import android.service.dreams.IDreamOverlayClientCallback;
+import android.view.WindowManager;
+
+import androidx.annotation.NonNull;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * A collection of tests to exercise {@link DreamOverlayService}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DreamOverlayServiceTest {
+    private static final ComponentName FIRST_DREAM_COMPONENT =
+            ComponentName.unflattenFromString("com.foo.bar/.DreamService");
+    private static final ComponentName SECOND_DREAM_COMPONENT =
+            ComponentName.unflattenFromString("com.foo.baz/.DreamService");
+
+    @Mock
+    WindowManager.LayoutParams mLayoutParams;
+
+    @Mock
+    IDreamOverlayCallback mOverlayCallback;
+
+    /**
+     * {@link TestDreamOverlayService} is a simple {@link DreamOverlayService} implementation for
+     * tracking interactions across {@link IDreamOverlay} binder interface. The service reports
+     * interactions to a {@link Monitor} instance provided at construction.
+     */
+    private static class TestDreamOverlayService extends DreamOverlayService {
+        /**
+         * An interface implemented to be informed when the corresponding methods in
+         * {@link TestDreamOverlayService} are invoked.
+         */
+        interface Monitor {
+            void onStartDream();
+            void onEndDream();
+            void onWakeUp();
+        }
+
+        private final Monitor mMonitor;
+
+        TestDreamOverlayService(Monitor monitor) {
+            super();
+            mMonitor = monitor;
+        }
+
+        @Override
+        public void onStartDream(@NonNull WindowManager.LayoutParams layoutParams) {
+            mMonitor.onStartDream();
+        }
+
+        @Override
+        public void onEndDream() {
+            mMonitor.onEndDream();
+            super.onEndDream();
+        }
+
+        @Override
+        public void onWakeUp(@NonNull Runnable onCompleteCallback) {
+            mMonitor.onWakeUp();
+            super.onWakeUp(onCompleteCallback);
+        }
+    }
+
+    /**
+     * A {@link IDreamOverlayClientCallback} implementation that captures the requested client.
+     */
+    private static class OverlayClientCallback extends IDreamOverlayClientCallback.Stub {
+        public IDreamOverlayClient retrievedClient;
+        @Override
+        public void onDreamOverlayClient(IDreamOverlayClient client) throws RemoteException {
+            retrievedClient = client;
+        }
+    }
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    /**
+     * Verifies that only the currently started dream is able to affect the overlay.
+     */
+    @Test
+    public void testOverlayClientInteraction() throws RemoteException {
+        final TestDreamOverlayService.Monitor monitor = Mockito.mock(
+                TestDreamOverlayService.Monitor.class);
+        final TestDreamOverlayService service = new TestDreamOverlayService(monitor);
+        final IBinder binder = service.onBind(new Intent());
+        final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(binder);
+
+        // Create two overlay clients and ensure they are unique.
+        final IDreamOverlayClient firstClient = getClient(overlay);
+        assertThat(firstClient).isNotNull();
+
+        final IDreamOverlayClient secondClient = getClient(overlay);
+        assertThat(secondClient).isNotNull();
+
+        assertThat(firstClient).isNotEqualTo(secondClient);
+
+        // Start a dream with the first client and ensure the dream is now active from the
+        // overlay's perspective.
+        firstClient.startDream(mLayoutParams, mOverlayCallback,
+                FIRST_DREAM_COMPONENT.flattenToString(), false);
+
+
+        verify(monitor).onStartDream();
+        assertThat(service.getDreamComponent()).isEqualTo(FIRST_DREAM_COMPONENT);
+
+        Mockito.clearInvocations(monitor);
+
+        // Start a dream from the second client and verify that the overlay has both cycled to
+        // the new dream (ended/started).
+        secondClient.startDream(mLayoutParams, mOverlayCallback,
+                SECOND_DREAM_COMPONENT.flattenToString(), false);
+
+        verify(monitor).onEndDream();
+        verify(monitor).onStartDream();
+        assertThat(service.getDreamComponent()).isEqualTo(SECOND_DREAM_COMPONENT);
+
+        Mockito.clearInvocations(monitor);
+
+        // Verify that interactions with the first, now inactive client don't affect the overlay.
+        firstClient.endDream();
+        verify(monitor, never()).onEndDream();
+
+        firstClient.wakeUp();
+        verify(monitor, never()).onWakeUp();
+    }
+
+    private static IDreamOverlayClient getClient(IDreamOverlay overlay) throws RemoteException {
+        final OverlayClientCallback callback = new OverlayClientCallback();
+        overlay.getClient(callback);
+        return callback.retrievedClient;
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
index 3de65c1..1b3a199 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
@@ -132,6 +132,19 @@
         assertThat(mFinalizedRotation).isEqualTo(DEFAULT_SENSOR_ROTATION);
     }
 
+    @Test
+    public void testOnSensorChanged_screenLocked_doNotCallRotationResolverReturnDefaultRotation() {
+        mWindowOrientationListener = new TestableWindowOrientationListener(mMockContext,
+                mHandler, /* defaultRotation */ Surface.ROTATION_180);
+        mWindowOrientationListener.mRotationResolverService = mFakeRotationResolverInternal;
+        mWindowOrientationListener.mIsScreenLocked = true;
+
+        mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent);
+
+        assertThat(mWindowOrientationListener.mIsOnProposedRotationChangedCalled).isFalse();
+        assertThat(mFinalizedRotation).isEqualTo(Surface.ROTATION_180);
+    }
+
     static final class TestableRotationResolver extends RotationResolverInternal {
         @Surface.Rotation
         RotationResolverCallbackInternal mCallback;
@@ -166,21 +179,17 @@
         }
     }
 
-    @Test
-    public void testOnSensorChanged_inLockScreen_doNotCallRotationResolver() {
-        mWindowOrientationListener.mIsScreenLocked = true;
-
-        mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent);
-
-        assertThat(mWindowOrientationListener.mIsOnProposedRotationChangedCalled).isFalse();
-    }
-
     final class TestableWindowOrientationListener extends WindowOrientationListener {
         private boolean mIsOnProposedRotationChangedCalled = false;
         private boolean mIsScreenLocked;
 
         TestableWindowOrientationListener(Context context, Handler handler) {
-            super(context, handler);
+            this(context, handler, Surface.ROTATION_0);
+        }
+
+        TestableWindowOrientationListener(Context context, Handler handler,
+                @Surface.Rotation int defaultRotation) {
+            super(context, handler, defaultRotation);
             this.mOrientationJudge = new OrientationSensorJudge();
         }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 7986043..8b1384e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -1582,6 +1582,55 @@
     }
 
     @Test
+    public void testSetComponentState_differentUsers() throws Exception {
+        Context context = mock(Context.class);
+        PackageManager pm = mock(PackageManager.class);
+        ApplicationInfo ai = new ApplicationInfo();
+        ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
+
+        when(context.getPackageName()).thenReturn(mContext.getPackageName());
+        when(context.getUserId()).thenReturn(mContext.getUserId());
+        when(context.getPackageManager()).thenReturn(pm);
+        when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai);
+
+        ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm,
+                APPROVAL_BY_COMPONENT);
+        ComponentName cn = ComponentName.unflattenFromString("a/a");
+
+        addExpectedServices(service, Arrays.asList("a"), mZero.id);
+        addExpectedServices(service, Arrays.asList("a"), mTen.id);
+        when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> {
+            Object[] args = invocation.getArguments();
+            ServiceConnection sc = (ServiceConnection) args[1];
+            sc.onServiceConnected(cn, mock(IBinder.class));
+            return true;
+        });
+        service.addApprovedList("a/a", 0, true);
+        service.addApprovedList("a/a", 10, false);
+
+        service.registerService(cn, mZero.id);
+        assertTrue(service.isBound(cn, mZero.id));
+
+        service.onUserSwitched(mTen.id);
+        assertFalse(service.isBound(cn, mZero.id));
+        service.registerService(cn, mTen.id);
+        assertTrue(service.isBound(cn, mTen.id));
+
+        service.setComponentState(cn, mTen.id, false);
+        assertFalse(service.isBound(cn, mZero.id));
+        assertFalse(service.isBound(cn, mTen.id));
+
+        // Service should be rebound on user 0, since it was only disabled for user 10.
+        service.onUserSwitched(mZero.id);
+        assertTrue(service.isBound(cn, mZero.id));
+        assertFalse(service.isBound(cn, mTen.id));
+
+        // Service should stay unbound on going back to user 10.
+        service.onUserSwitched(mTen.id);
+        assertFalse(service.isBound(cn, mZero.id));
+        assertFalse(service.isBound(cn, mTen.id));
+    }
+    @Test
     public void testOnPackagesChanged_nullValuesPassed_noNullPointers() {
         for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
             ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
@@ -1847,7 +1896,7 @@
     }
 
     private void addExpectedServices(final ManagedServices service, final List<String> packages,
-            int userId) {
+            int userId) throws Exception {
         ManagedServices.Config config = service.getConfig();
         when(mPm.queryIntentServicesAsUser(any(), anyInt(), eq(userId))).
                 thenAnswer(new Answer<List<ResolveInfo>>() {
@@ -1876,6 +1925,20 @@
                         return new ArrayList<>();
                     }
                 });
+
+        when(mIpm.getServiceInfo(any(), anyLong(), anyInt())).thenAnswer(
+                (Answer<ServiceInfo>) invocation -> {
+                    ComponentName invocationCn = invocation.getArgument(0);
+                    if (invocationCn != null && packages.contains(invocationCn.getPackageName())) {
+                        ServiceInfo serviceInfo = new ServiceInfo();
+                        serviceInfo.packageName = invocationCn.getPackageName();
+                        serviceInfo.name = invocationCn.getClassName();
+                        serviceInfo.permission = service.getConfig().bindPermission;
+                        return serviceInfo;
+                    }
+                    return null;
+                }
+        );
     }
 
     private List<String> stringToList(String list) {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 3f3b052..96e2a09 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -1645,6 +1645,46 @@
     }
 
     @Test
+    public void testEnqueueNotificationWithTag_nullAction_fixed() throws Exception {
+        Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .addAction(new Notification.Action.Builder(null, "one", null).build())
+                .addAction(new Notification.Action.Builder(null, "two", null).build())
+                .addAction(new Notification.Action.Builder(null, "three", null).build())
+                .build();
+        n.actions[1] = null;
+
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0, n, 0);
+        waitForIdle();
+
+        StatusBarNotification[] posted = mBinderService.getActiveNotifications(PKG);
+        assertThat(posted).hasLength(1);
+        assertThat(posted[0].getNotification().actions).hasLength(2);
+        assertThat(posted[0].getNotification().actions[0].title.toString()).isEqualTo("one");
+        assertThat(posted[0].getNotification().actions[1].title.toString()).isEqualTo("three");
+    }
+
+    @Test
+    public void testEnqueueNotificationWithTag_allNullActions_fixed() throws Exception {
+        Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .addAction(new Notification.Action.Builder(null, "one", null).build())
+                .addAction(new Notification.Action.Builder(null, "two", null).build())
+                .build();
+        n.actions[0] = null;
+        n.actions[1] = null;
+
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0, n, 0);
+        waitForIdle();
+
+        StatusBarNotification[] posted = mBinderService.getActiveNotifications(PKG);
+        assertThat(posted).hasLength(1);
+        assertThat(posted[0].getNotification().actions).isNull();
+    }
+
+    @Test
     public void testCancelNonexistentNotification() throws Exception {
         mBinderService.cancelNotificationWithTag(PKG, PKG,
                 "testCancelNonexistentNotification", 0, 0);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java
index 86732c9..2a28ae2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java
@@ -16,13 +16,12 @@
 
 package com.android.server.wm;
 
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
 
 import android.content.Context;
 import android.content.res.Resources;
@@ -32,9 +31,10 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.R;
+
 import org.junit.Before;
 import org.junit.Test;
-import org.mockito.ArgumentCaptor;
 
 import java.util.function.Consumer;
 
@@ -48,92 +48,76 @@
 @Presubmit
 public class DeviceStateControllerTests {
 
-    private DeviceStateController.FoldStateListener mFoldStateListener;
     private DeviceStateController mTarget;
     private DeviceStateControllerBuilder mBuilder;
 
     private Context mMockContext;
-    private Handler mMockHandler;
-    private Resources mMockRes;
     private DeviceStateManager mMockDeviceStateManager;
-
-    private Consumer<DeviceStateController.FoldState> mDelegate;
-    private DeviceStateController.FoldState mCurrentState = DeviceStateController.FoldState.UNKNOWN;
+    private DeviceStateController.DeviceState mCurrentState =
+            DeviceStateController.DeviceState.UNKNOWN;
 
     @Before
     public void setUp() {
         mBuilder = new DeviceStateControllerBuilder();
-        mCurrentState = DeviceStateController.FoldState.UNKNOWN;
+        mCurrentState = DeviceStateController.DeviceState.UNKNOWN;
     }
 
-    private void initialize(boolean supportFold, boolean supportHalfFold) throws Exception {
+    private void initialize(boolean supportFold, boolean supportHalfFold) {
         mBuilder.setSupportFold(supportFold, supportHalfFold);
-        mDelegate = (newFoldState) -> {
+        Consumer<DeviceStateController.DeviceState> delegate = (newFoldState) -> {
             mCurrentState = newFoldState;
         };
-        mBuilder.setDelegate(mDelegate);
+        mBuilder.setDelegate(delegate);
         mBuilder.build();
-        verifyFoldStateListenerRegistration(1);
+        verify(mMockDeviceStateManager).registerCallback(any(), any());
     }
 
     @Test
-    public void testInitialization() throws Exception {
+    public void testInitialization() {
         initialize(true /* supportFold */, true /* supportHalfFolded */);
-        mFoldStateListener.onStateChanged(mUnfoldedStates[0]);
-        assertEquals(mCurrentState, DeviceStateController.FoldState.OPEN);
+        mTarget.onStateChanged(mOpenDeviceStates[0]);
+        assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState);
     }
 
     @Test
-    public void testInitializationWithNoFoldSupport() throws Exception {
+    public void testInitializationWithNoFoldSupport() {
         initialize(false /* supportFold */, false /* supportHalfFolded */);
-        mFoldStateListener.onStateChanged(mFoldedStates[0]);
+        mTarget.onStateChanged(mFoldedStates[0]);
         // Note that the folded state is ignored.
-        assertEquals(mCurrentState, DeviceStateController.FoldState.OPEN);
+        assertEquals(DeviceStateController.DeviceState.UNKNOWN, mCurrentState);
     }
 
     @Test
-    public void testWithFoldSupported() throws Exception {
+    public void testWithFoldSupported() {
         initialize(true /* supportFold */, false /* supportHalfFolded */);
-        mFoldStateListener.onStateChanged(mUnfoldedStates[0]);
-        assertEquals(mCurrentState, DeviceStateController.FoldState.OPEN);
-        mFoldStateListener.onStateChanged(mFoldedStates[0]);
-        assertEquals(mCurrentState, DeviceStateController.FoldState.FOLDED);
-        mFoldStateListener.onStateChanged(mHalfFoldedStates[0]);
-        assertEquals(mCurrentState, DeviceStateController.FoldState.OPEN); // Ignored
+        mTarget.onStateChanged(mOpenDeviceStates[0]);
+        assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState);
+        mTarget.onStateChanged(mFoldedStates[0]);
+        assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState);
+        mTarget.onStateChanged(mHalfFoldedStates[0]);
+        assertEquals(DeviceStateController.DeviceState.UNKNOWN, mCurrentState); // Ignored
     }
 
     @Test
-    public void testWithHalfFoldSupported() throws Exception {
+    public void testWithHalfFoldSupported() {
         initialize(true /* supportFold */, true /* supportHalfFolded */);
-        mFoldStateListener.onStateChanged(mUnfoldedStates[0]);
-        assertEquals(mCurrentState, DeviceStateController.FoldState.OPEN);
-        mFoldStateListener.onStateChanged(mFoldedStates[0]);
-        assertEquals(mCurrentState, DeviceStateController.FoldState.FOLDED);
-        mFoldStateListener.onStateChanged(mHalfFoldedStates[0]);
-        assertEquals(mCurrentState, DeviceStateController.FoldState.HALF_FOLDED);
+        mTarget.onStateChanged(mOpenDeviceStates[0]);
+        assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState);
+        mTarget.onStateChanged(mFoldedStates[0]);
+        assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState);
+        mTarget.onStateChanged(mHalfFoldedStates[0]);
+        assertEquals(DeviceStateController.DeviceState.HALF_FOLDED, mCurrentState);
     }
 
-
     private final int[] mFoldedStates = {0};
-    private final int[] mUnfoldedStates = {1};
+    private final int[] mOpenDeviceStates = {1};
     private final int[] mHalfFoldedStates = {2};
-
-
-    private void verifyFoldStateListenerRegistration(int numOfInvocation) {
-        final ArgumentCaptor<DeviceStateController.FoldStateListener> listenerCaptor =
-                ArgumentCaptor.forClass(DeviceStateController.FoldStateListener.class);
-        verify(mMockDeviceStateManager, times(numOfInvocation)).registerCallback(
-                any(),
-                listenerCaptor.capture());
-        if (numOfInvocation > 0) {
-            mFoldStateListener = listenerCaptor.getValue();
-        }
-    }
+    private final int[] mRearDisplayStates = {3};
 
     private class DeviceStateControllerBuilder {
         private boolean mSupportFold = false;
         private boolean mSupportHalfFold = false;
-        private Consumer<DeviceStateController.FoldState> mDelegate;
+        private Consumer<DeviceStateController.DeviceState> mDelegate;
 
         DeviceStateControllerBuilder setSupportFold(
                 boolean supportFold, boolean supportHalfFold) {
@@ -143,34 +127,44 @@
         }
 
         DeviceStateControllerBuilder setDelegate(
-                Consumer<DeviceStateController.FoldState> delegate) {
+                Consumer<DeviceStateController.DeviceState> delegate) {
             mDelegate = delegate;
             return this;
         }
 
         private void mockFold(boolean enableFold, boolean enableHalfFold) {
+            if (enableFold || enableHalfFold) {
+                when(mMockContext.getResources()
+                        .getIntArray(R.array.config_openDeviceStates))
+                        .thenReturn(mOpenDeviceStates);
+                when(mMockContext.getResources()
+                        .getIntArray(R.array.config_rearDisplayDeviceStates))
+                        .thenReturn(mRearDisplayStates);
+            }
+
             if (enableFold) {
-                when(mMockContext.getResources().getIntArray(
-                        com.android.internal.R.array.config_foldedDeviceStates))
+                when(mMockContext.getResources()
+                        .getIntArray(R.array.config_foldedDeviceStates))
                         .thenReturn(mFoldedStates);
             }
             if (enableHalfFold) {
-                when(mMockContext.getResources().getIntArray(
-                        com.android.internal.R.array.config_halfFoldedDeviceStates))
+                when(mMockContext.getResources()
+                        .getIntArray(R.array.config_halfFoldedDeviceStates))
                         .thenReturn(mHalfFoldedStates);
             }
         }
 
-        private void build() throws Exception {
+        private void build() {
             mMockContext = mock(Context.class);
-            mMockRes = mock(Resources.class);
-            when(mMockContext.getResources()).thenReturn((mMockRes));
             mMockDeviceStateManager = mock(DeviceStateManager.class);
             when(mMockContext.getSystemService(DeviceStateManager.class))
                     .thenReturn(mMockDeviceStateManager);
+            Resources mockRes = mock(Resources.class);
+            when(mMockContext.getResources()).thenReturn((mockRes));
             mockFold(mSupportFold, mSupportHalfFold);
-            mMockHandler = mock(Handler.class);
-            mTarget = new DeviceStateController(mMockContext, mMockHandler, mDelegate);
+            Handler mockHandler = mock(Handler.class);
+            mTarget = new DeviceStateController(mMockContext, mockHandler);
+            mTarget.registerDeviceStateCallback(mDelegate);
         }
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index b0639bf..dc12469 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1736,7 +1736,7 @@
 
         // No need to apply rotation if the display ignores orientation request.
         doCallRealMethod().when(displayContent).rotationForActivityInDifferentOrientation(any());
-        pinnedActivity.mOrientation = SCREEN_ORIENTATION_LANDSCAPE;
+        pinnedActivity.setOverrideOrientation(SCREEN_ORIENTATION_LANDSCAPE);
         displayContent.setIgnoreOrientationRequest(true);
         assertEquals(WindowConfiguration.ROTATION_UNDEFINED,
                 displayContent.rotationForActivityInDifferentOrientation(pinnedActivity));
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
index 45b30b2..c2b3783 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
 import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
@@ -30,7 +31,9 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 
 import static org.junit.Assert.assertEquals;
@@ -58,6 +61,8 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.R;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -128,6 +133,95 @@
     }
 
     @Test
+    public void testOpenedCameraInSplitScreen_showToast() {
+        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+        spyOn(mTask);
+        spyOn(mDisplayRotationCompatPolicy);
+        doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mActivity).getWindowingMode();
+        doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mTask).getWindowingMode();
+
+        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+
+        verify(mDisplayRotationCompatPolicy).showToast(
+                R.string.display_rotation_camera_compat_toast_in_split_screen);
+    }
+
+    @Test
+    public void testOpenedCameraInSplitScreen_orientationNotFixed_doNotShowToast() {
+        configureActivity(SCREEN_ORIENTATION_UNSPECIFIED);
+        spyOn(mTask);
+        spyOn(mDisplayRotationCompatPolicy);
+        doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mActivity).getWindowingMode();
+        doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mTask).getWindowingMode();
+
+        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+
+        verify(mDisplayRotationCompatPolicy, never()).showToast(
+                R.string.display_rotation_camera_compat_toast_in_split_screen);
+    }
+
+    @Test
+    public void testOnScreenRotationAnimationFinished_treatmentNotEnabled_doNotShowToast() {
+        when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled(
+                    /* checkDeviceConfig */ anyBoolean()))
+                .thenReturn(false);
+        spyOn(mDisplayRotationCompatPolicy);
+
+        mDisplayRotationCompatPolicy.onScreenRotationAnimationFinished();
+
+        verify(mDisplayRotationCompatPolicy, never()).showToast(
+                R.string.display_rotation_camera_compat_toast_after_rotation);
+    }
+
+    @Test
+    public void testOnScreenRotationAnimationFinished_noOpenCamera_doNotShowToast() {
+        spyOn(mDisplayRotationCompatPolicy);
+
+        mDisplayRotationCompatPolicy.onScreenRotationAnimationFinished();
+
+        verify(mDisplayRotationCompatPolicy, never()).showToast(
+                R.string.display_rotation_camera_compat_toast_after_rotation);
+    }
+
+    @Test
+    public void testOnScreenRotationAnimationFinished_notFullscreen_doNotShowToast() {
+        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+        doReturn(true).when(mActivity).inMultiWindowMode();
+        spyOn(mDisplayRotationCompatPolicy);
+
+        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+
+        mDisplayRotationCompatPolicy.onScreenRotationAnimationFinished();
+
+        verify(mDisplayRotationCompatPolicy, never()).showToast(
+                R.string.display_rotation_camera_compat_toast_after_rotation);
+    }
+
+    @Test
+    public void testOnScreenRotationAnimationFinished_orientationNotFixed_doNotShowToast() {
+        configureActivity(SCREEN_ORIENTATION_UNSPECIFIED);
+        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+        spyOn(mDisplayRotationCompatPolicy);
+
+        mDisplayRotationCompatPolicy.onScreenRotationAnimationFinished();
+
+        verify(mDisplayRotationCompatPolicy, never()).showToast(
+                R.string.display_rotation_camera_compat_toast_after_rotation);
+    }
+
+    @Test
+    public void testOnScreenRotationAnimationFinished_showToast() {
+        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+        spyOn(mDisplayRotationCompatPolicy);
+
+        mDisplayRotationCompatPolicy.onScreenRotationAnimationFinished();
+
+        verify(mDisplayRotationCompatPolicy).showToast(
+                R.string.display_rotation_camera_compat_toast_after_rotation);
+    }
+
+    @Test
     public void testTreatmentNotEnabled_noForceRotationOrRefresh() throws Exception {
         when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled(
                     /* checkDeviceConfig */ anyBoolean()))
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index 4ce43e1..ed2b0a3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -56,6 +56,7 @@
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
+import android.hardware.devicestate.DeviceStateManager;
 import android.os.PowerManagerInternal;
 import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
@@ -111,6 +112,7 @@
     private ContentResolver mMockResolver;
     private FakeSettingsProvider mFakeSettingsProvider;
     private StatusBarManagerInternal mMockStatusBarManagerInternal;
+    private DeviceStateManager mMockDeviceStateManager;
 
     // Fields below are callbacks captured from test target.
     private ContentObserver mShowRotationSuggestionsObserver;
@@ -120,6 +122,7 @@
 
     private DisplayRotationBuilder mBuilder;
 
+    private DeviceStateController mDeviceStateController;
     private DisplayRotation mTarget;
 
     @BeforeClass
@@ -484,6 +487,34 @@
                 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
     }
 
+    @Test
+    public void testReverseRotation() throws Exception {
+        mBuilder.build();
+        configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+        when(mDeviceStateController.shouldReverseRotationDirectionAroundZAxis()).thenReturn(true);
+
+        thawRotation();
+
+        enableOrientationSensor();
+
+        mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
+        assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
+                SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+
+        mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_270));
+        assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
+                SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+
+        mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_0));
+        assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(
+                SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+
+        mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
+        assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation(
+                SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_180));
+    }
+
     private boolean waitForUiHandler() {
         final CountDownLatch latch = new CountDownLatch(1);
         UiThread.getHandler().post(latch::countDown);
@@ -705,7 +736,7 @@
 
         enableOrientationSensor();
 
-        mTarget.foldStateChanged(DeviceStateController.FoldState.OPEN);
+        mTarget.foldStateChanged(DeviceStateController.DeviceState.OPEN);
         freezeRotation(Surface.ROTATION_270);
 
         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_0));
@@ -715,7 +746,7 @@
                 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
 
         // ... until half-fold
-        mTarget.foldStateChanged(DeviceStateController.FoldState.HALF_FOLDED);
+        mTarget.foldStateChanged(DeviceStateController.DeviceState.HALF_FOLDED);
         assertTrue(waitForUiHandler());
         verify(sMockWm).updateRotation(false, false);
         assertTrue(waitForUiHandler());
@@ -723,7 +754,7 @@
                 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
 
         // ... then transition back to flat
-        mTarget.foldStateChanged(DeviceStateController.FoldState.OPEN);
+        mTarget.foldStateChanged(DeviceStateController.DeviceState.OPEN);
         assertTrue(waitForUiHandler());
         verify(sMockWm, atLeast(1)).updateRotation(false, false);
         assertTrue(waitForUiHandler());
@@ -1097,8 +1128,14 @@
 
             mMockDisplayWindowSettings = mock(DisplayWindowSettings.class);
 
+            mMockDeviceStateManager = mock(DeviceStateManager.class);
+            when(mMockContext.getSystemService(eq(DeviceStateManager.class)))
+                    .thenReturn(mMockDeviceStateManager);
+
+            mDeviceStateController = mock(DeviceStateController.class);
             mTarget = new DisplayRotation(sMockWm, mMockDisplayContent, mMockDisplayAddress,
-                    mMockDisplayPolicy, mMockDisplayWindowSettings, mMockContext, new Object()) {
+                    mMockDisplayPolicy, mMockDisplayWindowSettings, mMockContext, new Object(),
+                    mDeviceStateController) {
                 @Override
                 DisplayRotationImmersiveAppCompatPolicy initImmersiveAppCompatPolicy(
                         WindowManagerService service, DisplayContent displayContent) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java
index ead1a86..e1fc0cf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java
@@ -18,7 +18,6 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
-import static com.android.server.wm.LetterboxConfiguration.DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS;
 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER;
 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT;
 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT;
@@ -26,8 +25,6 @@
 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER;
 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
@@ -37,7 +34,6 @@
 
 import android.content.Context;
 import android.platform.test.annotations.Presubmit;
-import android.provider.DeviceConfig;
 
 import androidx.test.filters.SmallTest;
 
@@ -51,7 +47,7 @@
  * Tests for the {@link LetterboxConfiguration} class.
  *
  * Build/Install/Run:
- *  atest WmTests:LetterboxConfigurationTests
+ *  atest WmTests:LetterboxConfigurationTest
  */
 @SmallTest
 @Presubmit
@@ -233,34 +229,6 @@
                 LetterboxConfiguration::movePositionForVerticalReachabilityToNextBottomStop);
     }
 
-    @Test
-    public void testIsCompatFakeFocusEnabledOnDevice() {
-        boolean wasFakeFocusEnabled = DeviceConfig
-                .getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
-                DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS, false);
-
-        // Set runtime flag to true and build time flag to false
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
-                DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS, "true", false);
-        mLetterboxConfiguration.setIsCompatFakeFocusEnabled(false);
-        assertFalse(mLetterboxConfiguration.isCompatFakeFocusEnabledOnDevice());
-
-        // Set runtime flag to false and build time flag to true
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
-                DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS, "false", false);
-        mLetterboxConfiguration.setIsCompatFakeFocusEnabled(true);
-        assertFalse(mLetterboxConfiguration.isCompatFakeFocusEnabledOnDevice());
-
-        // Set runtime flag to true so that both are enabled
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
-                DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS, "true", false);
-        assertTrue(mLetterboxConfiguration.isCompatFakeFocusEnabledOnDevice());
-
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
-                DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS, Boolean.toString(wasFakeFocusEnabled),
-                false);
-    }
-
     private void assertForHorizontalMove(int from, int expected, int expectedTime,
             boolean halfFoldPose, BiConsumer<LetterboxConfiguration, Boolean> move) {
         // We are in the current position
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index 5e087f0..656c07b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -16,33 +16,65 @@
 
 package com.android.server.wm;
 
+import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION;
 import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION;
 import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH;
 import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
+import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS;
 import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION;
+import static android.content.pm.ActivityInfo.OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE;
+import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA;
+import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR;
+import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT;
+import static android.content.pm.ActivityInfo.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION;
 import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH;
 import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS;
 import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyString;
 
+import android.annotation.Nullable;
 import android.compat.testing.PlatformCompatChangeRule;
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.Property;
+import android.content.res.Resources;
+import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
+import android.view.InsetsSource;
+import android.view.InsetsState;
+import android.view.RoundedCorner;
+import android.view.RoundedCorners;
+import android.view.WindowManager;
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.R;
+
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
 import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
 
 import org.junit.Before;
@@ -61,14 +93,24 @@
 @Presubmit
 @RunWith(WindowTestRunner.class)
 public class LetterboxUiControllerTest extends WindowTestsBase {
+    private static final int TASKBAR_COLLAPSED_HEIGHT = 10;
+    private static final int TASKBAR_EXPANDED_HEIGHT = 20;
+    private static final int SCREEN_WIDTH = 200;
+    private static final int SCREEN_HEIGHT = 100;
+    private static final Rect TASKBAR_COLLAPSED_BOUNDS = new Rect(0,
+            SCREEN_HEIGHT - TASKBAR_COLLAPSED_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT);
+    private static final Rect TASKBAR_EXPANDED_BOUNDS = new Rect(0,
+            SCREEN_HEIGHT - TASKBAR_EXPANDED_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT);
 
     @Rule
     public TestRule compatChangeRule = new PlatformCompatChangeRule();
 
     private ActivityRecord mActivity;
+    private Task mTask;
     private DisplayContent mDisplayContent;
     private LetterboxUiController mController;
     private LetterboxConfiguration mLetterboxConfiguration;
+    private final Rect mLetterboxedPortraitTaskBounds = new Rect();
 
     @Before
     public void setUp() throws Exception {
@@ -308,6 +350,389 @@
         assertTrue(mController.shouldForceRotateForCameraCompat());
     }
 
+    @Test
+    public void testGetCropBoundsIfNeeded_noCrop() {
+        final InsetsSource taskbar = new InsetsSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
+        final WindowState mainWindow = mockForGetCropBoundsAndRoundedCorners(taskbar);
+
+        // Do not apply crop if taskbar is collapsed
+        taskbar.setFrame(TASKBAR_COLLAPSED_BOUNDS);
+        assertNull(mController.getExpandedTaskbarOrNull(mainWindow));
+
+        mLetterboxedPortraitTaskBounds.set(SCREEN_WIDTH / 4, SCREEN_HEIGHT / 4,
+                SCREEN_WIDTH - SCREEN_WIDTH / 4, SCREEN_HEIGHT - SCREEN_HEIGHT / 4);
+
+        final Rect noCrop = mController.getCropBoundsIfNeeded(mainWindow);
+        assertNotEquals(null, noCrop);
+        assertEquals(0, noCrop.left);
+        assertEquals(0, noCrop.top);
+        assertEquals(mLetterboxedPortraitTaskBounds.width(), noCrop.right);
+        assertEquals(mLetterboxedPortraitTaskBounds.height(), noCrop.bottom);
+    }
+
+    @Test
+    public void testGetCropBoundsIfNeeded_appliesCrop() {
+        final InsetsSource taskbar = new InsetsSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
+        final WindowState mainWindow = mockForGetCropBoundsAndRoundedCorners(taskbar);
+
+        // Apply crop if taskbar is expanded
+        taskbar.setFrame(TASKBAR_EXPANDED_BOUNDS);
+        assertNotNull(mController.getExpandedTaskbarOrNull(mainWindow));
+
+        mLetterboxedPortraitTaskBounds.set(SCREEN_WIDTH / 4, 0, SCREEN_WIDTH - SCREEN_WIDTH / 4,
+                SCREEN_HEIGHT);
+
+        final Rect crop = mController.getCropBoundsIfNeeded(mainWindow);
+        assertNotEquals(null, crop);
+        assertEquals(0, crop.left);
+        assertEquals(0, crop.top);
+        assertEquals(mLetterboxedPortraitTaskBounds.width(), crop.right);
+        assertEquals(mLetterboxedPortraitTaskBounds.height() - TASKBAR_EXPANDED_HEIGHT,
+                crop.bottom);
+    }
+
+    @Test
+    public void testGetCropBoundsIfNeeded_appliesCropWithSizeCompatScaling() {
+        final InsetsSource taskbar = new InsetsSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
+        final WindowState mainWindow = mockForGetCropBoundsAndRoundedCorners(taskbar);
+        final float scaling = 2.0f;
+
+        // Apply crop if taskbar is expanded
+        taskbar.setFrame(TASKBAR_EXPANDED_BOUNDS);
+        assertNotNull(mController.getExpandedTaskbarOrNull(mainWindow));
+        // With SizeCompat scaling
+        doReturn(true).when(mActivity).inSizeCompatMode();
+        mainWindow.mInvGlobalScale = scaling;
+
+        mLetterboxedPortraitTaskBounds.set(SCREEN_WIDTH / 4, 0, SCREEN_WIDTH - SCREEN_WIDTH / 4,
+                SCREEN_HEIGHT);
+
+        final int appWidth = mLetterboxedPortraitTaskBounds.width();
+        final int appHeight = mLetterboxedPortraitTaskBounds.height();
+
+        final Rect crop = mController.getCropBoundsIfNeeded(mainWindow);
+        assertNotEquals(null, crop);
+        assertEquals(0, crop.left);
+        assertEquals(0, crop.top);
+        assertEquals((int) (appWidth * scaling), crop.right);
+        assertEquals((int) ((appHeight - TASKBAR_EXPANDED_HEIGHT) * scaling), crop.bottom);
+    }
+
+    @Test
+    public void testGetRoundedCornersRadius_withRoundedCornersFromInsets() {
+        final float invGlobalScale = 0.5f;
+        final int expectedRadius = 7;
+        final int configurationRadius = 15;
+
+        final WindowState mainWindow = mockForGetCropBoundsAndRoundedCorners(/*taskbar=*/ null);
+        mainWindow.mInvGlobalScale = invGlobalScale;
+        final InsetsState insets = mainWindow.getInsetsState();
+
+        RoundedCorners roundedCorners = new RoundedCorners(
+                /*topLeft=*/ null,
+                /*topRight=*/ null,
+                /*bottomRight=*/ new RoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT,
+                    configurationRadius, /*centerX=*/ 1, /*centerY=*/ 1),
+                /*bottomLeft=*/ new RoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT,
+                    configurationRadius * 2 /*2 is to test selection of the min radius*/,
+                    /*centerX=*/ 1, /*centerY=*/ 1)
+        );
+        doReturn(roundedCorners).when(insets).getRoundedCorners();
+        mLetterboxConfiguration.setLetterboxActivityCornersRadius(-1);
+
+        assertEquals(expectedRadius, mController.getRoundedCornersRadius(mainWindow));
+    }
+
+    @Test
+    public void testGetRoundedCornersRadius_withLetterboxActivityCornersRadius() {
+        final float invGlobalScale = 0.5f;
+        final int expectedRadius = 7;
+        final int configurationRadius = 15;
+
+        final WindowState mainWindow = mockForGetCropBoundsAndRoundedCorners(/*taskbar=*/ null);
+        mainWindow.mInvGlobalScale = invGlobalScale;
+        mLetterboxConfiguration.setLetterboxActivityCornersRadius(configurationRadius);
+
+        assertEquals(expectedRadius, mController.getRoundedCornersRadius(mainWindow));
+
+    }
+
+    @Test
+    public void testGetRoundedCornersRadius_noScalingApplied() {
+        final int configurationRadius = 15;
+
+        final WindowState mainWindow = mockForGetCropBoundsAndRoundedCorners(/*taskbar=*/ null);
+        mLetterboxConfiguration.setLetterboxActivityCornersRadius(configurationRadius);
+
+        mainWindow.mInvGlobalScale = -1f;
+        assertEquals(configurationRadius, mController.getRoundedCornersRadius(mainWindow));
+
+        mainWindow.mInvGlobalScale = 0f;
+        assertEquals(configurationRadius, mController.getRoundedCornersRadius(mainWindow));
+
+        mainWindow.mInvGlobalScale = 1f;
+        assertEquals(configurationRadius, mController.getRoundedCornersRadius(mainWindow));
+    }
+
+    private WindowState mockForGetCropBoundsAndRoundedCorners(@Nullable InsetsSource taskbar) {
+        final WindowState mainWindow = mock(WindowState.class);
+        final InsetsState insets = mock(InsetsState.class);
+        final Resources resources = mWm.mContext.getResources();
+        final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams();
+
+        mainWindow.mInvGlobalScale = 1f;
+        spyOn(resources);
+        spyOn(mActivity);
+
+        if (taskbar != null) {
+            taskbar.setVisible(true);
+            doReturn(taskbar).when(insets).peekSource(taskbar.getType());
+        }
+        doReturn(mLetterboxedPortraitTaskBounds).when(mActivity).getBounds();
+        doReturn(true).when(mActivity).isVisible();
+        doReturn(true).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio();
+        doReturn(insets).when(mainWindow).getInsetsState();
+        doReturn(attrs).when(mainWindow).getAttrs();
+        doReturn(true).when(mainWindow).isDrawn();
+        doReturn(true).when(mainWindow).isOnScreen();
+        doReturn(false).when(mainWindow).isLetterboxedForDisplayCutout();
+        doReturn(true).when(mainWindow).areAppWindowBoundsLetterboxed();
+        doReturn(true).when(mLetterboxConfiguration).isLetterboxActivityCornersRounded();
+        doReturn(TASKBAR_EXPANDED_HEIGHT).when(resources).getDimensionPixelSize(
+                R.dimen.taskbar_frame_height);
+
+        // Need to reinitialise due to the change in resources getDimensionPixelSize output.
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        return mainWindow;
+    }
+
+    // overrideOrientationIfNeeded
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT})
+    public void testOverrideOrientationIfNeeded_portraitOverrideEnabled_returnsPortrait()
+            throws Exception {
+        assertEquals(mController.overrideOrientationIfNeeded(
+                /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_PORTRAIT);
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR})
+    public void testOverrideOrientationIfNeeded_portraitOverrideEnabled_returnsNosensor() {
+        assertEquals(mController.overrideOrientationIfNeeded(
+                /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_NOSENSOR);
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR})
+    public void testOverrideOrientationIfNeeded_nosensorOverride_orientationFixed_returnsUnchanged() {
+        assertEquals(mController.overrideOrientationIfNeeded(
+                /* candidate */ SCREEN_ORIENTATION_PORTRAIT), SCREEN_ORIENTATION_PORTRAIT);
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE})
+    public void testOverrideOrientationIfNeeded_reverseLandscapeOverride_orientationPortraitOrUndefined_returnsUnchanged() {
+        assertEquals(mController.overrideOrientationIfNeeded(
+                /* candidate */ SCREEN_ORIENTATION_PORTRAIT), SCREEN_ORIENTATION_PORTRAIT);
+        assertEquals(mController.overrideOrientationIfNeeded(
+                /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_UNSPECIFIED);
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE})
+    public void testOverrideOrientationIfNeeded_reverseLandscapeOverride_orientationLandscape_returnsReverseLandscape() {
+        assertEquals(mController.overrideOrientationIfNeeded(
+                /* candidate */ SCREEN_ORIENTATION_LANDSCAPE),
+                SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT})
+    public void testOverrideOrientationIfNeeded_portraitOverride_orientationFixed_returnsUnchanged() {
+        assertEquals(mController.overrideOrientationIfNeeded(
+                /* candidate */ SCREEN_ORIENTATION_NOSENSOR), SCREEN_ORIENTATION_NOSENSOR);
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT, OVERRIDE_ANY_ORIENTATION})
+    public void testOverrideOrientationIfNeeded_portraitAndIgnoreFixedOverrides_returnsPortrait() {
+        assertEquals(mController.overrideOrientationIfNeeded(
+                /* candidate */ SCREEN_ORIENTATION_NOSENSOR), SCREEN_ORIENTATION_PORTRAIT);
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR, OVERRIDE_ANY_ORIENTATION})
+    public void testOverrideOrientationIfNeeded_noSensorAndIgnoreFixedOverrides_returnsNosensor() {
+        assertEquals(mController.overrideOrientationIfNeeded(
+                /* candidate */ SCREEN_ORIENTATION_PORTRAIT), SCREEN_ORIENTATION_NOSENSOR);
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT})
+    public void testOverrideOrientationIfNeeded_propertyIsFalse_returnsUnchanged()
+            throws Exception {
+        mockThatProperty(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE, /* value */ false);
+
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        assertEquals(mController.overrideOrientationIfNeeded(
+                /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_UNSPECIFIED);
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT,
+            OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA})
+    public void testOverrideOrientationIfNeeded_whenCameraNotActive_returnsUnchanged() {
+        doReturn(true).when(mLetterboxConfiguration).isCameraCompatTreatmentEnabled(anyBoolean());
+
+        // Recreate DisplayContent with DisplayRotationCompatPolicy
+        mActivity = setUpActivityWithComponent();
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        spyOn(mDisplayContent.mDisplayRotationCompatPolicy);
+        doReturn(false).when(mDisplayContent.mDisplayRotationCompatPolicy)
+                .isActivityEligibleForOrientationOverride(eq(mActivity));
+
+        assertEquals(mController.overrideOrientationIfNeeded(
+                /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_UNSPECIFIED);
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT,
+            OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA})
+    public void testOverrideOrientationIfNeeded_whenCameraActive_returnsPortrait() {
+        doReturn(true).when(mLetterboxConfiguration).isCameraCompatTreatmentEnabled(anyBoolean());
+
+        // Recreate DisplayContent with DisplayRotationCompatPolicy
+        mActivity = setUpActivityWithComponent();
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        spyOn(mDisplayContent.mDisplayRotationCompatPolicy);
+        doReturn(true).when(mDisplayContent.mDisplayRotationCompatPolicy)
+                .isActivityEligibleForOrientationOverride(eq(mActivity));
+
+        assertEquals(mController.overrideOrientationIfNeeded(
+                /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_PORTRAIT);
+    }
+
+    // shouldUseDisplayLandscapeNaturalOrientation
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION})
+    public void testShouldUseDisplayLandscapeNaturalOrientation_override_returnsTrue() {
+        prepareActivityThatShouldUseDisplayLandscapeNaturalOrientation();
+        assertTrue(mController.shouldUseDisplayLandscapeNaturalOrientation());
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION})
+    public void testShouldUseDisplayLandscapeNaturalOrientation_overrideAndFalseProperty_returnsFalse()
+            throws Exception {
+        mockThatProperty(PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE, /* value */ false);
+
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        prepareActivityThatShouldUseDisplayLandscapeNaturalOrientation();
+        assertFalse(mController.shouldUseDisplayLandscapeNaturalOrientation());
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION})
+    public void testShouldUseDisplayLandscapeNaturalOrientation_portraitNaturalOrientation_returnsFalse() {
+        prepareActivityThatShouldUseDisplayLandscapeNaturalOrientation();
+        doReturn(ORIENTATION_PORTRAIT).when(mDisplayContent).getNaturalOrientation();
+
+        assertFalse(mController.shouldUseDisplayLandscapeNaturalOrientation());
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION})
+    public void testShouldUseDisplayLandscapeNaturalOrientation_disabledIgnoreOrientationRequest_returnsFalse() {
+        prepareActivityThatShouldUseDisplayLandscapeNaturalOrientation();
+        mDisplayContent.setIgnoreOrientationRequest(false);
+
+        assertFalse(mController.shouldUseDisplayLandscapeNaturalOrientation());
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION})
+    public void testShouldUseDisplayLandscapeNaturalOrientation_inMultiWindowMode_returnsFalse() {
+        prepareActivityThatShouldUseDisplayLandscapeNaturalOrientation();
+
+        spyOn(mTask);
+        doReturn(true).when(mTask).inMultiWindowMode();
+
+        assertFalse(mController.shouldUseDisplayLandscapeNaturalOrientation());
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testShouldSendFakeFocus_overrideEnabled_returnsTrue() {
+        doReturn(true).when(mLetterboxConfiguration).isCompatFakeFocusEnabled();
+
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        assertTrue(mController.shouldSendFakeFocus());
+    }
+
+    @Test
+    @DisableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testShouldSendFakeFocus_overrideDisabled_returnsFalse() {
+        doReturn(true).when(mLetterboxConfiguration).isCompatFakeFocusEnabled();
+
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        assertFalse(mController.shouldSendFakeFocus());
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testIsCompatFakeFocusEnabled_propertyDisabledAndOverrideEnabled_fakeFocusDisabled()
+            throws Exception {
+        doReturn(true).when(mLetterboxConfiguration).isCompatFakeFocusEnabled();
+        mockThatProperty(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS, /* value */ false);
+
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        assertFalse(mController.shouldSendFakeFocus());
+    }
+
+    @Test
+    @DisableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testIsCompatFakeFocusEnabled_propertyEnabled_noOverride_fakeFocusEnabled()
+            throws Exception {
+        doReturn(true).when(mLetterboxConfiguration).isCompatFakeFocusEnabled();
+        mockThatProperty(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS, /* value */ true);
+
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        assertTrue(mController.shouldSendFakeFocus());
+    }
+
+    @Test
+    public void testIsCompatFakeFocusEnabled_propertyDisabled_fakeFocusDisabled()
+            throws Exception {
+        doReturn(true).when(mLetterboxConfiguration).isCompatFakeFocusEnabled();
+        mockThatProperty(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS, /* value */ false);
+
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        assertFalse(mController.shouldSendFakeFocus());
+    }
+
+    @Test
+    public void testIsCompatFakeFocusEnabled_propertyEnabled_fakeFocusEnabled()
+            throws Exception {
+        doReturn(true).when(mLetterboxConfiguration).isCompatFakeFocusEnabled();
+        mockThatProperty(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS, /* value */ true);
+
+        mController = new LetterboxUiController(mWm, mActivity);
+
+        assertTrue(mController.shouldSendFakeFocus());
+    }
+
     private void mockThatProperty(String propertyName, boolean value) throws Exception {
         Property property = new Property(propertyName, /* value */ value, /* packageName */ "",
                  /* className */ "");
@@ -316,6 +741,12 @@
         doReturn(property).when(pm).getProperty(eq(propertyName), anyString());
     }
 
+    private void prepareActivityThatShouldUseDisplayLandscapeNaturalOrientation() {
+        spyOn(mDisplayContent);
+        doReturn(ORIENTATION_LANDSCAPE).when(mDisplayContent).getNaturalOrientation();
+        mDisplayContent.setIgnoreOrientationRequest(true);
+    }
+
     private void prepareActivityThatShouldIgnoreRequestedOrientationDuringRelaunch() {
         doReturn(true).when(mLetterboxConfiguration)
                 .isPolicyForIgnoringRequestedOrientationEnabled();
@@ -325,10 +756,10 @@
     private ActivityRecord setUpActivityWithComponent() {
         mDisplayContent = new TestDisplayContent
                 .Builder(mAtm, /* dw */ 1000, /* dh */ 2000).build();
-        Task task = new TaskBuilder(mSupervisor).setDisplay(mDisplayContent).build();
+        mTask = new TaskBuilder(mSupervisor).setDisplay(mDisplayContent).build();
         final ActivityRecord activity = new ActivityBuilder(mAtm)
                 .setOnTop(true)
-                .setTask(task)
+                .setTask(mTask)
                 // Set the component to be that of the test class in order to enable compat changes
                 .setComponent(ComponentName.createRelative(mContext,
                         com.android.server.wm.LetterboxUiControllerTest.class.getName()))
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 4808474..06e3854 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -468,7 +468,7 @@
         mWm.setRecentsAnimationController(mController);
         spyOn(mDisplayContent.mFixedRotationTransitionListener);
         final ActivityRecord recents = mock(ActivityRecord.class);
-        recents.mOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+        recents.setOverrideOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
         doReturn(ORIENTATION_PORTRAIT).when(recents)
                 .getRequestedConfigurationOrientation(anyBoolean());
         mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recents);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
index 9d2eb26..63797778 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
@@ -21,7 +21,9 @@
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import android.os.Parcel;
@@ -258,6 +260,14 @@
         assertEquals(0, mPolicy.getPreferredRefreshRate(overrideWindow), FLOAT_TOLERANCE);
         assertEquals(0, mPolicy.getPreferredMinRefreshRate(overrideWindow), FLOAT_TOLERANCE);
         assertEquals(0, mPolicy.getPreferredMaxRefreshRate(overrideWindow), FLOAT_TOLERANCE);
+
+        // If there will be display size change when switching from preferred mode to default mode,
+        // then keep the current preferred mode during animating.
+        mDisplayInfo = spy(mDisplayInfo);
+        final Mode defaultMode = new Mode(4321 /* width */, 1234 /* height */, LOW_REFRESH_RATE);
+        doReturn(defaultMode).when(mDisplayInfo).getDefaultMode();
+        mPolicy = new RefreshRatePolicy(mWm, mDisplayInfo, mDenylist);
+        assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(overrideWindow));
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index de0999f..de84655 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -16,8 +16,10 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
@@ -53,8 +55,6 @@
 import static com.android.server.wm.ActivityRecord.State.RESUMED;
 import static com.android.server.wm.ActivityRecord.State.STOPPED;
 import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
-import static com.android.server.wm.LetterboxConfiguration.PROPERTY_COMPAT_FAKE_FOCUS_OPT_IN;
-import static com.android.server.wm.LetterboxConfiguration.PROPERTY_COMPAT_FAKE_FOCUS_OPT_OUT;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -98,7 +98,7 @@
 import com.android.internal.policy.SystemBarUtils;
 import com.android.internal.statusbar.LetterboxDetails;
 import com.android.server.statusbar.StatusBarManagerInternal;
-import com.android.server.wm.DeviceStateController.FoldState;
+import com.android.server.wm.DeviceStateController.DeviceState;
 
 import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
 import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
@@ -255,6 +255,51 @@
     }
 
     @Test
+    public void testCheckOpaqueIsLetterboxedWhenStrategyIsApplied() {
+        mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
+        setUpDisplaySizeWithApp(2000, 1000);
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        // Translucent Activity
+        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
+                .setLaunchedFromUid(mActivity.getUid())
+                .build();
+        doReturn(false).when(translucentActivity).fillsParent();
+        spyOn(mActivity);
+        mTask.addChild(translucentActivity);
+        verify(mActivity).isFinishing();
+    }
+
+    @Test
+    public void testTranslucentActivitiesWhenUnfolding() {
+        mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
+        setUpDisplaySizeWithApp(2800, 1400);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mActivity.mWmService.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(
+                1.0f /*letterboxVerticalPositionMultiplier*/);
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+        // We launch a transparent activity
+        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
+                .setLaunchedFromUid(mActivity.getUid())
+                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
+                .build();
+        doReturn(false).when(translucentActivity).fillsParent();
+        mTask.addChild(translucentActivity);
+
+        mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        spyOn(mActivity);
+
+        // Halffold
+        setFoldablePosture(translucentActivity, true /* isHalfFolded */, false /* isTabletop */);
+        verify(mActivity).recomputeConfiguration();
+        clearInvocations(mActivity);
+
+        // Unfold
+        setFoldablePosture(translucentActivity, false /* isHalfFolded */, false /* isTabletop */);
+        verify(mActivity).recomputeConfiguration();
+    }
+
+    @Test
     public void testRestartProcessIfVisible() {
         setUpDisplaySizeWithApp(1000, 2500);
         doNothing().when(mSupervisor).scheduleRestartTimeout(mActivity);
@@ -457,7 +502,7 @@
 
         spyOn(mActivity.mLetterboxUiController);
         doReturn(true).when(mActivity.mLetterboxUiController)
-                .isSurfaceReadyAndVisible(any());
+                .isSurfaceVisible(any());
 
         assertTrue(mActivity.mLetterboxUiController.shouldShowLetterboxUi(
                 mActivity.findMainWindow()));
@@ -1389,6 +1434,65 @@
     }
 
     @Test
+    public void testGetLetterboxInnerBounds_noScalingApplied() {
+        // Set up a display in portrait and ignoring orientation request.
+        final int dw = 1400;
+        final int dh = 2800;
+        setUpDisplaySizeWithApp(dw, dh);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+        // Rotate display to landscape.
+        rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+
+        // Portrait fixed app without max aspect.
+        prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_LANDSCAPE);
+
+        // Need a window to call adjustBoundsForTaskbar with.
+        addWindowToActivity(mActivity);
+
+        // App should launch in fullscreen.
+        assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+        assertFalse(mActivity.inSizeCompatMode());
+
+        // Activity inherits max bounds from TaskDisplayArea.
+        assertMaxBoundsInheritDisplayAreaBounds();
+
+        // Rotate display to portrait.
+        rotateDisplay(mActivity.mDisplayContent, ROTATION_0);
+
+        final Rect rotatedDisplayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+        final Rect rotatedActivityBounds = new Rect(mActivity.getBounds());
+        assertTrue(rotatedDisplayBounds.width() < rotatedDisplayBounds.height());
+
+        // App should be in size compat.
+        assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+        assertScaled();
+        assertThat(mActivity.inSizeCompatMode()).isTrue();
+        assertActivityMaxBoundsSandboxed();
+
+
+	final int scale = dh / dw;
+
+        // App bounds should be dh / scale x dw / scale
+        assertEquals(dw, rotatedDisplayBounds.width());
+        assertEquals(dh, rotatedDisplayBounds.height());
+
+        assertEquals(dh / scale, rotatedActivityBounds.width());
+        assertEquals(dw / scale, rotatedActivityBounds.height());
+
+        // Compute the frames of the window and invoke {@link ActivityRecord#layoutLetterbox}.
+        mActivity.mRootWindowContainer.performSurfacePlacement();
+
+        LetterboxDetails letterboxDetails = mActivity.mLetterboxUiController.getLetterboxDetails();
+
+        assertEquals(dh / scale, letterboxDetails.getLetterboxInnerBounds().width());
+        assertEquals(dw / scale, letterboxDetails.getLetterboxInnerBounds().height());
+
+        assertEquals(dw, letterboxDetails.getLetterboxFullBounds().width());
+        assertEquals(dh, letterboxDetails.getLetterboxFullBounds().height());
+    }
+
+    @Test
     public void testLaunchWithFixedRotationTransform() {
         final int dw = 1000;
         final int dh = 2500;
@@ -1876,6 +1980,43 @@
     }
 
     @Test
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_RESPECT_REQUESTED_ORIENTATION})
+    public void testOverrideRespectRequestedOrientationIsEnabled_orientationIsRespected() {
+        // Set up a display in landscape
+        setUpDisplaySizeWithApp(2800, 1400);
+
+        final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */ false,
+                RESIZE_MODE_UNRESIZEABLE, SCREEN_ORIENTATION_PORTRAIT);
+        activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+        // Display should be rotated.
+        assertEquals(SCREEN_ORIENTATION_PORTRAIT, activity.mDisplayContent.getOrientation());
+
+        // No size compat mode
+        assertFalse(activity.inSizeCompatMode());
+    }
+
+    @Test
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_RESPECT_REQUESTED_ORIENTATION})
+    public void testOverrideRespectRequestedOrientationIsEnabled_multiWindow_orientationIgnored() {
+        // Set up a display in landscape
+        setUpDisplaySizeWithApp(2800, 1400);
+
+        final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */ false,
+                RESIZE_MODE_UNRESIZEABLE, SCREEN_ORIENTATION_PORTRAIT);
+        TaskFragment taskFragment = activity.getTaskFragment();
+        spyOn(taskFragment);
+        activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        doReturn(WINDOWING_MODE_MULTI_WINDOW).when(taskFragment).getWindowingMode();
+
+        // Display should not be rotated.
+        assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, activity.mDisplayContent.getOrientation());
+
+        // No size compat mode
+        assertFalse(activity.inSizeCompatMode());
+    }
+
+    @Test
     public void testSplitAspectRatioForUnresizableLandscapeApps() {
         // Set up a display in portrait and ignoring orientation request.
         int screenWidth = 1400;
@@ -2313,6 +2454,29 @@
     }
 
     @Test
+    public void testDisplayIgnoreOrientationRequest_disabledViaDeviceConfig_orientationRespected() {
+        // Set up a display in landscape
+        setUpDisplaySizeWithApp(2800, 1400);
+
+        final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */ false,
+                RESIZE_MODE_UNRESIZEABLE, SCREEN_ORIENTATION_PORTRAIT);
+        activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+        spyOn(activity.mWmService.mLetterboxConfiguration);
+        doReturn(true).when(activity.mWmService.mLetterboxConfiguration)
+                .isIgnoreOrientationRequestAllowed();
+
+        // Display should not be rotated.
+        assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, activity.mDisplayContent.getOrientation());
+
+        doReturn(false).when(activity.mWmService.mLetterboxConfiguration)
+                .isIgnoreOrientationRequestAllowed();
+
+        // Display should be rotated.
+        assertEquals(SCREEN_ORIENTATION_PORTRAIT, activity.mDisplayContent.getOrientation());
+    }
+
+    @Test
     public void testSandboxDisplayApis_unresizableAppNotSandboxed() {
         // Set up a display in landscape with an unresizable app.
         setUpDisplaySizeWithApp(2500, 1000);
@@ -2821,7 +2985,7 @@
         mActivity.mRootWindowContainer.performSurfacePlacement();
 
         final ArgumentCaptor<Rect> cropCapturer = ArgumentCaptor.forClass(Rect.class);
-        verify(mTransaction, times(2)).setWindowCrop(
+        verify(mTransaction, times(2)).setCrop(
                 eq(w1.getSurfaceControl()),
                 cropCapturer.capture()
         );
@@ -2910,6 +3074,39 @@
     }
 
     @Test
+    public void testUpdateResolvedBoundsHorizontalPosition_leftInsets_appCentered() {
+        // Set up folded display
+        final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1100, 2100)
+                .setCanRotate(true)
+                .build();
+        display.setIgnoreOrientationRequest(true);
+        final DisplayPolicy policy = display.getDisplayPolicy();
+        DisplayPolicy.DecorInsets.Info decorInfo = policy.getDecorInsetsInfo(ROTATION_90,
+                display.mBaseDisplayHeight, display.mBaseDisplayWidth);
+        decorInfo.mNonDecorInsets.set(130, 0,  60, 0);
+        spyOn(policy);
+        doReturn(decorInfo).when(policy).getDecorInsetsInfo(ROTATION_90,
+                display.mBaseDisplayHeight, display.mBaseDisplayWidth);
+        mWm.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(0.5f);
+
+        setUpApp(display);
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+        // Resize the display to simulate unfolding in portrait
+        resizeDisplay(mTask.mDisplayContent, 2200, 1800);
+        assertTrue(mActivity.inSizeCompatMode());
+
+        // Simulate real display not taking non-decor insets into consideration
+        display.getWindowConfiguration().setAppBounds(0, 0, 2200, 1800);
+
+        // Rotate display to landscape
+        rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+
+        // App is centered
+        assertEquals(mActivity.getBounds(), new Rect(350, 50, 1450, 2150));
+    }
+
+    @Test
     public void testUpdateResolvedBoundsHorizontalPosition_left() {
         // Display configured as (2800, 1400).
         assertHorizontalPositionForDifferentDisplayConfigsForPortraitActivity(
@@ -3055,6 +3252,20 @@
     }
 
     @Test
+    public void testApplyAspectRatio_containingRatioAlmostEqualToMaxRatio_boundsUnchanged() {
+        setUpDisplaySizeWithApp(1981, 2576);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mWm.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(0.5f);
+
+        final Rect originalBounds = new Rect(mActivity.getBounds());
+        prepareUnresizable(mActivity, 1.3f, SCREEN_ORIENTATION_UNSPECIFIED);
+
+        // The containing aspect ratio is now 1.3003534, while the desired aspect ratio is 1.3. The
+        // bounds of the activity should not be changed as the difference is too small
+        assertEquals(mActivity.getBounds(), originalBounds);
+    }
+
+    @Test
     public void testUpdateResolvedBoundsHorizontalPosition_activityFillParentWidth() {
         // When activity width equals parent width, multiplier shouldn't have any effect.
         assertHorizontalPositionForDifferentDisplayConfigsForLandscapeActivity(
@@ -3066,6 +3277,39 @@
     }
 
     @Test
+    public void testUpdateResolvedBoundsVerticalPosition_topInsets_appCentered() {
+        // Set up folded display
+        final DisplayContent display = new TestDisplayContent.Builder(mAtm, 2100, 1100)
+                .setCanRotate(true)
+                .build();
+        display.setIgnoreOrientationRequest(true);
+        final DisplayPolicy policy = display.getDisplayPolicy();
+        DisplayPolicy.DecorInsets.Info decorInfo = policy.getDecorInsetsInfo(ROTATION_90,
+                display.mBaseDisplayHeight, display.mBaseDisplayWidth);
+        decorInfo.mNonDecorInsets.set(0, 130,  0, 60);
+        spyOn(policy);
+        doReturn(decorInfo).when(policy).getDecorInsetsInfo(ROTATION_90,
+                display.mBaseDisplayHeight, display.mBaseDisplayWidth);
+        mWm.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(0.5f);
+
+        setUpApp(display);
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
+
+        // Resize the display to simulate unfolding in portrait
+        resizeDisplay(mTask.mDisplayContent, 1800, 2200);
+        assertTrue(mActivity.inSizeCompatMode());
+
+        // Simulate real display not taking non-decor insets into consideration
+        display.getWindowConfiguration().setAppBounds(0, 0, 1800, 2200);
+
+        // Rotate display to landscape
+        rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+
+        // App is centered
+        assertEquals(mActivity.getBounds(), new Rect(50, 350, 2150, 1450));
+    }
+
+    @Test
     public void testUpdateResolvedBoundsVerticalPosition_top() {
         // Display configured as (1400, 2800).
         assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity(
@@ -3183,14 +3427,20 @@
 
     }
 
-    private void setFoldablePosture(boolean isHalfFolded, boolean isTabletop) {
-        final DisplayRotation r = mActivity.mDisplayContent.getDisplayRotation();
+    private void setFoldablePosture(ActivityRecord activity, boolean isHalfFolded,
+            boolean isTabletop) {
+        final DisplayRotation r = activity.mDisplayContent.getDisplayRotation();
         doReturn(isHalfFolded).when(r).isDisplaySeparatingHinge();
-        doReturn(false).when(r).isDeviceInPosture(any(FoldState.class), anyBoolean());
+        doReturn(false).when(r).isDeviceInPosture(any(DeviceState.class), anyBoolean());
         if (isHalfFolded) {
-            doReturn(true).when(r).isDeviceInPosture(FoldState.HALF_FOLDED, isTabletop);
+            doReturn(true).when(r)
+                    .isDeviceInPosture(DeviceState.HALF_FOLDED, isTabletop);
         }
-        mActivity.recomputeConfiguration();
+        activity.recomputeConfiguration();
+    }
+
+    private void setFoldablePosture(boolean isHalfFolded, boolean isTabletop) {
+        setFoldablePosture(mActivity, isHalfFolded, isTabletop);
     }
 
     @Test
@@ -3535,7 +3785,8 @@
         assertEquals(newDensity, mActivity.getConfiguration().densityDpi);
     }
 
-    private ActivityRecord setUpActivityForCompatFakeFocusTest() {
+    @Test
+    public void testShouldSendFakeFocus_compatFakeFocusEnabled() {
         final ActivityRecord activity = new ActivityBuilder(mAtm)
                 .setCreateTask(true)
                 .setOnTop(true)
@@ -3544,69 +3795,40 @@
                         com.android.server.wm.SizeCompatTests.class.getName()))
                 .build();
         final Task task = activity.getTask();
+        spyOn(activity.mLetterboxUiController);
+        doReturn(true).when(activity.mLetterboxUiController).shouldSendFakeFocus();
+
         task.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
-        spyOn(activity.mWmService.mLetterboxConfiguration);
-        doReturn(true).when(activity.mWmService.mLetterboxConfiguration)
-                .isCompatFakeFocusEnabledOnDevice();
-        return activity;
-    }
-
-    @Test
-    @EnableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
-    public void testShouldSendFakeFocus_overrideEnabled_returnsTrue() {
-        ActivityRecord activity = setUpActivityForCompatFakeFocusTest();
-
         assertTrue(activity.shouldSendCompatFakeFocus());
-    }
 
-    @Test
-    @DisableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
-    public void testShouldSendFakeFocus_overrideDisabled_returnsFalse() {
-        ActivityRecord activity = setUpActivityForCompatFakeFocusTest();
+        task.setWindowingMode(WINDOWING_MODE_PINNED);
+        assertFalse(activity.shouldSendCompatFakeFocus());
 
+        task.setWindowingMode(WINDOWING_MODE_FREEFORM);
         assertFalse(activity.shouldSendCompatFakeFocus());
     }
 
     @Test
-    @EnableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
-    public void testIsCompatFakeFocusEnabled_optOutPropertyAndOverrideEnabled_fakeFocusDisabled() {
-        ActivityRecord activity = setUpActivityForCompatFakeFocusTest();
-        doReturn(true).when(activity.mWmService.mLetterboxConfiguration)
-                .getPackageManagerProperty(any(), eq(PROPERTY_COMPAT_FAKE_FOCUS_OPT_OUT));
+    public void testShouldSendFakeFocus_compatFakeFocusDisabled() {
+        final ActivityRecord activity = new ActivityBuilder(mAtm)
+                .setCreateTask(true)
+                .setOnTop(true)
+                // Set the component to be that of the test class in order to enable compat changes
+                .setComponent(ComponentName.createRelative(mContext,
+                        com.android.server.wm.SizeCompatTests.class.getName()))
+                .build();
+        final Task task = activity.getTask();
+        spyOn(activity.mLetterboxUiController);
+        doReturn(false).when(activity.mLetterboxUiController).shouldSendFakeFocus();
 
-        assertFalse(activity.mWmService.mLetterboxConfiguration
-                .isCompatFakeFocusEnabled(activity.info));
-    }
+        task.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        assertFalse(activity.shouldSendCompatFakeFocus());
 
-    @Test
-    @DisableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
-    public void testIsCompatFakeFocusEnabled_optInPropertyEnabled_noOverride_fakeFocusEnabled() {
-        ActivityRecord activity = setUpActivityForCompatFakeFocusTest();
-        doReturn(true).when(activity.mWmService.mLetterboxConfiguration)
-                .getPackageManagerProperty(any(), eq(PROPERTY_COMPAT_FAKE_FOCUS_OPT_IN));
+        task.setWindowingMode(WINDOWING_MODE_PINNED);
+        assertFalse(activity.shouldSendCompatFakeFocus());
 
-        assertTrue(activity.mWmService.mLetterboxConfiguration
-                .isCompatFakeFocusEnabled(activity.info));
-    }
-
-    @Test
-    public void testIsCompatFakeFocusEnabled_optOutPropertyEnabled_fakeFocusDisabled() {
-        ActivityRecord activity = setUpActivityForCompatFakeFocusTest();
-        doReturn(true).when(activity.mWmService.mLetterboxConfiguration)
-                .getPackageManagerProperty(any(), eq(PROPERTY_COMPAT_FAKE_FOCUS_OPT_OUT));
-
-        assertFalse(activity.mWmService.mLetterboxConfiguration
-                .isCompatFakeFocusEnabled(activity.info));
-    }
-
-    @Test
-    public void testIsCompatFakeFocusEnabled_optInPropertyEnabled_fakeFocusEnabled() {
-        ActivityRecord activity = setUpActivityForCompatFakeFocusTest();
-        doReturn(true).when(activity.mWmService.mLetterboxConfiguration)
-                .getPackageManagerProperty(any(), eq(PROPERTY_COMPAT_FAKE_FOCUS_OPT_IN));
-
-        assertTrue(activity.mWmService.mLetterboxConfiguration
-                .isCompatFakeFocusEnabled(activity.info));
+        task.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        assertFalse(activity.shouldSendCompatFakeFocus());
     }
 
     private int getExpectedSplitSize(int dimensionToSplit) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index d31ae6a..83be4f0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -18,6 +18,7 @@
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+import static android.view.Surface.ROTATION_0;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
@@ -78,6 +79,12 @@
         final InputMonitor inputMonitor = getInputMonitor();
         spyOn(inputMonitor);
         doNothing().when(inputMonitor).resumeDispatchingLw(any());
+
+        // For devices that set the sysprop ro.bootanim.set_orientation_<display_id>
+        // See DisplayRotation#readDefaultDisplayRotation for context.
+        // Without that, meaning of height and width in context of the tests can be swapped if
+        // the default rotation is 90 or 270.
+        displayRotation.setRotation(ROTATION_0);
     }
 
     public static class Builder {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index d2cb7ba..e2db2e6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -222,7 +222,7 @@
     }
 
     @Override
-    public void onKeyguardOccludedChangedLw(boolean occluded) {
+    public void onKeyguardOccludedChangedLw(boolean occluded, boolean waitAppTransition) {
     }
 
     public void setSafeMode(boolean safeMode) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index ed7d123..2446fc4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -1557,7 +1557,7 @@
 
         @Override
         int getOrientation() {
-            return getOrientation(super.mOrientation);
+            return getOrientation(super.getOverrideOrientation());
         }
 
         @Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index fd3776f..219f441 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -42,7 +42,6 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
-import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
@@ -975,19 +974,6 @@
         assertFalse(sameTokenWindow.needsRelativeLayeringToIme());
     }
 
-    @UseTestDisplay(addWindows = {W_ACTIVITY, W_INPUT_METHOD})
-    @Test
-    public void testNeedsRelativeLayeringToIme_systemDialog() {
-        WindowState systemDialogWindow = createWindow(null, TYPE_SECURE_SYSTEM_OVERLAY,
-                mDisplayContent,
-                "SystemDialog", true);
-        mDisplayContent.setImeLayeringTarget(mAppWindow);
-        mAppWindow.getRootTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
-        makeWindowVisible(mImeWindow);
-        systemDialogWindow.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM;
-        assertTrue(systemDialogWindow.needsRelativeLayeringToIme());
-    }
-
     @Test
     public void testSetFreezeInsetsState() {
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
@@ -1138,7 +1124,9 @@
         spyOn(app.getDisplayContent());
         app.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
 
-        verify(app.getDisplayContent()).updateImeControlTarget();
+        // Expect updateImeParent will be invoked when the configuration of the IME control
+        // target has changed.
+        verify(app.getDisplayContent()).updateImeControlTarget(eq(true) /* updateImeParent */);
         assertEquals(mAppWindow, mDisplayContent.getImeTarget(IME_TARGET_CONTROL).getWindow());
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index 7959d82..77fca45 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -22,7 +22,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
@@ -32,7 +31,6 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
-import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
@@ -545,28 +543,4 @@
         assertZOrderGreaterThan(mTransaction, popupWindow.getSurfaceControl(),
                 mDisplayContent.getImeContainer().getSurfaceControl());
     }
-
-    @Test
-    public void testSystemDialogWindow_expectHigherThanIme_inMultiWindow() {
-        // Simulate the app window is in multi windowing mode and being IME target
-        mAppWindow.getConfiguration().windowConfiguration.setWindowingMode(
-                WINDOWING_MODE_MULTI_WINDOW);
-        mDisplayContent.setImeLayeringTarget(mAppWindow);
-        mDisplayContent.setImeInputTarget(mAppWindow);
-        makeWindowVisible(mImeWindow);
-
-        // Create a popupWindow
-        final WindowState systemDialogWindow = createWindow(null, TYPE_SECURE_SYSTEM_OVERLAY,
-                mDisplayContent, "SystemDialog", true);
-        systemDialogWindow.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM;
-        spyOn(systemDialogWindow);
-
-        mDisplayContent.assignChildLayers(mTransaction);
-
-        // Verify the surface layer of the popupWindow should higher than IME
-        verify(systemDialogWindow).needsRelativeLayeringToIme();
-        assertThat(systemDialogWindow.needsRelativeLayeringToIme()).isTrue();
-        assertZOrderGreaterThan(mTransaction, systemDialogWindow.getSurfaceControl(),
-                mDisplayContent.getImeContainer().getSurfaceControl());
-    }
 }
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 42a5af7..17c354a 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -36,6 +36,7 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -106,6 +107,12 @@
         return false;
     }
 
+    /**
+     * List of connected MIDI devices
+     */
+    private final HashMap<String, UsbMidiDevice>
+            mMidiDevices = new HashMap<String, UsbMidiDevice>();
+
     // UsbMidiDevice for USB peripheral mode (gadget) device
     private UsbMidiDevice mPeripheralMidiDevice = null;
 
@@ -249,6 +256,8 @@
             }
         }
 
+        addMidiDevice(deviceAddress, usbDevice, parser, cardRec);
+
         logDevices("deviceAdded()");
 
         if (DEBUG) {
@@ -256,6 +265,54 @@
         }
     }
 
+    private void addMidiDevice(String deviceAddress, UsbDevice usbDevice,
+            UsbDescriptorParser parser, AlsaCardsParser.AlsaCardRecord cardRec) {
+        boolean hasMidi = parser.hasMIDIInterface();
+        // UsbHostManager will create UsbDirectMidiDevices instead if MIDI 2 is supported.
+        boolean hasMidi2 = parser.containsUniversalMidiDeviceEndpoint();
+        if (DEBUG) {
+            Slog.d(TAG, "hasMidi: " + hasMidi + " mHasMidiFeature:" + mHasMidiFeature);
+            Slog.d(TAG, "hasMidi2: " + hasMidi2);
+        }
+        if (mHasMidiFeature && hasMidi && !hasMidi2) {
+            Bundle properties = new Bundle();
+            String manufacturer = usbDevice.getManufacturerName();
+            String product = usbDevice.getProductName();
+            String version = usbDevice.getVersion();
+            String name;
+            if (manufacturer == null || manufacturer.isEmpty()) {
+                name = product;
+            } else if (product == null || product.isEmpty()) {
+                name = manufacturer;
+            } else {
+                name = manufacturer + " " + product;
+            }
+            properties.putString(MidiDeviceInfo.PROPERTY_NAME, name);
+            properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, manufacturer);
+            properties.putString(MidiDeviceInfo.PROPERTY_PRODUCT, product);
+            properties.putString(MidiDeviceInfo.PROPERTY_VERSION, version);
+            properties.putString(MidiDeviceInfo.PROPERTY_SERIAL_NUMBER,
+                    usbDevice.getSerialNumber());
+            properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, cardRec.getCardNum());
+            properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, 0 /*deviceNum*/);
+            properties.putParcelable(MidiDeviceInfo.PROPERTY_USB_DEVICE, usbDevice);
+
+            int numLegacyMidiInputs = parser.calculateNumLegacyMidiInputs();
+            int numLegacyMidiOutputs = parser.calculateNumLegacyMidiOutputs();
+            if (DEBUG) {
+                Slog.d(TAG, "numLegacyMidiInputs: " + numLegacyMidiInputs);
+                Slog.d(TAG, "numLegacyMidiOutputs:" + numLegacyMidiOutputs);
+            }
+
+            UsbMidiDevice usbMidiDevice = UsbMidiDevice.create(mContext, properties,
+                    cardRec.getCardNum(), 0 /*device*/, numLegacyMidiInputs,
+                    numLegacyMidiOutputs);
+            if (usbMidiDevice != null) {
+                mMidiDevices.put(deviceAddress, usbMidiDevice);
+            }
+        }
+    }
+
     /* package */ synchronized void usbDeviceRemoved(String deviceAddress/*UsbDevice usbDevice*/) {
         if (DEBUG) {
             Slog.d(TAG, "deviceRemoved(" + deviceAddress + ")");
@@ -269,6 +326,13 @@
             selectDefaultDevice(); // if there any external devices left, select one of them
         }
 
+        // MIDI
+        UsbMidiDevice usbMidiDevice = mMidiDevices.remove(deviceAddress);
+        if (usbMidiDevice != null) {
+            Slog.i(TAG, "USB MIDI Device Removed: " + deviceAddress);
+            IoUtils.closeQuietly(usbMidiDevice);
+        }
+
         logDevices("usbDeviceRemoved()");
 
     }
@@ -324,6 +388,12 @@
             usbAlsaDevice.dump(dump, "alsa_devices", UsbAlsaManagerProto.ALSA_DEVICES);
         }
 
+        for (String deviceAddr : mMidiDevices.keySet()) {
+            // A UsbMidiDevice does not have a handle to the UsbDevice anymore
+            mMidiDevices.get(deviceAddr).dump(deviceAddr, dump, "midi_devices",
+                    UsbAlsaManagerProto.MIDI_DEVICES);
+        }
+
         dump.end(token);
     }
 
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index f389276..b3eb285 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -444,14 +444,19 @@
                         } else {
                             Slog.e(TAG, "Universal Midi Device is null.");
                         }
-                    }
-                    if (parser.containsLegacyMidiDeviceEndpoint()) {
-                        UsbDirectMidiDevice midiDevice = UsbDirectMidiDevice.create(mContext,
-                                newDevice, parser, false, uniqueUsbDeviceIdentifier);
-                        if (midiDevice != null) {
-                            midiDevices.add(midiDevice);
-                        } else {
-                            Slog.e(TAG, "Legacy Midi Device is null.");
+
+                        // Use UsbDirectMidiDevice only if this supports MIDI 2.0 as well.
+                        // ALSA removes the audio sound card if MIDI interfaces are removed.
+                        // This means that as long as ALSA is used for audio, MIDI 1.0 USB
+                        // devices should use the ALSA path for MIDI.
+                        if (parser.containsLegacyMidiDeviceEndpoint()) {
+                            midiDevice = UsbDirectMidiDevice.create(mContext,
+                                    newDevice, parser, false, uniqueUsbDeviceIdentifier);
+                            if (midiDevice != null) {
+                                midiDevices.add(midiDevice);
+                            } else {
+                                Slog.e(TAG, "Legacy Midi Device is null.");
+                            }
                         }
                     }
 
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
index 3f2d8c8..c6ea228 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
@@ -79,6 +79,10 @@
         mInterfaceDescriptors.add(interfaceDesc);
     }
 
+    ArrayList<UsbInterfaceDescriptor> getInterfaceDescriptors() {
+        return mInterfaceDescriptors;
+    }
+
     private boolean isAudioInterface(UsbInterfaceDescriptor descriptor) {
         return descriptor.getUsbClass() == UsbDescriptor.CLASSID_AUDIO
                 && descriptor.getUsbSubclass() == UsbDescriptor.AUDIO_AUDIOSTREAMING;
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index cd6ea68..626ce89 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -40,6 +40,7 @@
     private UsbDeviceDescriptor mDeviceDescriptor;
     private UsbConfigDescriptor mCurConfigDescriptor;
     private UsbInterfaceDescriptor mCurInterfaceDescriptor;
+    private UsbEndpointDescriptor mCurEndpointDescriptor;
 
     // The AudioClass spec implemented by the AudioClass Interfaces
     // This may well be different than the overall USB Spec.
@@ -165,7 +166,7 @@
                 break;
 
             case UsbDescriptor.DESCRIPTORTYPE_ENDPOINT:
-                descriptor = new UsbEndpointDescriptor(length, type);
+                descriptor = mCurEndpointDescriptor = new UsbEndpointDescriptor(length, type);
                 if (mCurInterfaceDescriptor != null) {
                     mCurInterfaceDescriptor.addEndpointDescriptor(
                             (UsbEndpointDescriptor) descriptor);
@@ -265,6 +266,9 @@
                                     + Integer.toHexString(subClass));
                             break;
                     }
+                    if (mCurEndpointDescriptor != null && descriptor != null) {
+                        mCurEndpointDescriptor.setClassSpecificEndpointDescriptor(descriptor);
+                    }
                 }
                 break;
 
@@ -798,6 +802,84 @@
     /**
      * @hide
      */
+    private int calculateNumLegacyMidiPorts(boolean isOutput) {
+        // Only look at the first config.
+        UsbConfigDescriptor configDescriptor = null;
+        for (UsbDescriptor descriptor : mDescriptors) {
+            if (descriptor.getType() == UsbDescriptor.DESCRIPTORTYPE_CONFIG) {
+                if (descriptor instanceof UsbConfigDescriptor) {
+                    configDescriptor = (UsbConfigDescriptor) descriptor;
+                    break;
+                } else {
+                    Log.w(TAG, "Unrecognized Config l: " + descriptor.getLength()
+                            + " t:0x" + Integer.toHexString(descriptor.getType()));
+                }
+            }
+        }
+        if (configDescriptor == null) {
+            Log.w(TAG, "Config not found");
+            return 0;
+        }
+
+        ArrayList<UsbInterfaceDescriptor> legacyMidiInterfaceDescriptors =
+                new ArrayList<UsbInterfaceDescriptor>();
+        for (UsbInterfaceDescriptor interfaceDescriptor
+                : configDescriptor.getInterfaceDescriptors()) {
+            if (interfaceDescriptor.getUsbClass() == UsbDescriptor.CLASSID_AUDIO) {
+                if (interfaceDescriptor.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) {
+                    UsbDescriptor midiHeaderDescriptor =
+                            interfaceDescriptor.getMidiHeaderInterfaceDescriptor();
+                    if (midiHeaderDescriptor != null) {
+                        if (midiHeaderDescriptor instanceof UsbMSMidiHeader) {
+                            UsbMSMidiHeader midiHeader =
+                                    (UsbMSMidiHeader) midiHeaderDescriptor;
+                            if (midiHeader.getMidiStreamingClass() == MS_MIDI_1_0) {
+                                legacyMidiInterfaceDescriptors.add(interfaceDescriptor);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        int count = 0;
+        for (UsbInterfaceDescriptor interfaceDescriptor : legacyMidiInterfaceDescriptors) {
+            for (int i = 0; i < interfaceDescriptor.getNumEndpoints(); i++) {
+                UsbEndpointDescriptor endpoint =
+                        interfaceDescriptor.getEndpointDescriptor(i);
+                // 0 is output, 1 << 7 is input.
+                if ((endpoint.getDirection() == 0) == isOutput) {
+                    UsbDescriptor classSpecificEndpointDescriptor =
+                            endpoint.getClassSpecificEndpointDescriptor();
+                    if (classSpecificEndpointDescriptor != null
+                            && (classSpecificEndpointDescriptor instanceof UsbACMidi10Endpoint)) {
+                        UsbACMidi10Endpoint midiEndpoint =
+                                (UsbACMidi10Endpoint) classSpecificEndpointDescriptor;
+                        count += midiEndpoint.getNumJacks();
+                    }
+                }
+            }
+        }
+        return count;
+    }
+
+    /**
+     * @hide
+     */
+    public int calculateNumLegacyMidiInputs() {
+        return calculateNumLegacyMidiPorts(false /*isOutput*/);
+    }
+
+    /**
+     * @hide
+     */
+    public int calculateNumLegacyMidiOutputs() {
+        return calculateNumLegacyMidiPorts(true /*isOutput*/);
+    }
+
+    /**
+     * @hide
+     */
     public float getInputHeadsetProbability() {
         if (hasMIDIInterface()) {
             return 0.0f;
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
index ab07ce7..1f448ac 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
@@ -79,6 +79,8 @@
     private byte mRefresh;
     private byte mSyncAddress;
 
+    private UsbDescriptor mClassSpecificEndpointDescriptor;
+
     public UsbEndpointDescriptor(int length, byte type) {
         super(length, type);
         mHierarchyLevel = 4;
@@ -112,6 +114,14 @@
         return mEndpointAddress & UsbEndpointDescriptor.MASK_ENDPOINT_DIRECTION;
     }
 
+    void setClassSpecificEndpointDescriptor(UsbDescriptor descriptor) {
+        mClassSpecificEndpointDescriptor = descriptor;
+    }
+
+    UsbDescriptor getClassSpecificEndpointDescriptor() {
+        return mClassSpecificEndpointDescriptor;
+    }
+
     /**
     * Returns a UsbEndpoint that this UsbEndpointDescriptor is describing.
     */
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
index aacc17a4..3361502 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
@@ -113,4 +113,18 @@
         }
         return false
     }
+
+    fun toggleFixPortraitOrientation(wmHelper: WindowManagerStateHelper) {
+        val button = uiDevice.wait(Until.findObject(By.res(getPackage(),
+                "toggle_fixed_portrait_btn")), FIND_TIMEOUT)
+        require(button != null) {
+            "Button not found, this usually happens when the device " +
+                    "was left in an unknown state (e.g. Screen turned off)"
+        }
+        button.click()
+        mInstrumentation.waitForIdleSync()
+        // Ensure app relaunching transition finish and the IME has shown
+        wmHelper.waitForAppTransitionIdle()
+        wmHelper.waitImeShown()
+    }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
new file mode 100644
index 0000000..3b3bce6
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
@@ -0,0 +1,129 @@
+/*
+ * 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.server.wm.flicker.ime
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
+import android.view.Surface
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerBuilderProvider
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.annotation.Group2
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
+import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.traces.region.RegionSubject
+import com.android.server.wm.traces.common.FlickerComponentName
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test IME window shown on the app with fixing portrait orientation.
+ * To run this test: `atest FlickerTests:OpenImeWindowToFixedPortraitAppTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group2
+class OpenImeWindowToFixedPortraitAppTest (private val testSpec: FlickerTestParameter) {
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
+
+    @FlickerBuilderProvider
+    fun buildFlicker(): FlickerBuilder {
+        return FlickerBuilder(instrumentation).apply {
+            setup {
+                eachRun {
+                    testApp.launchViaIntent(wmHelper)
+                    testApp.openIME(device, wmHelper)
+                    // Enable letterbox when the app calls setRequestedOrientation
+                    device.executeShellCommand("cmd window set-ignore-orientation-request true")
+                }
+            }
+            transitions {
+                testApp.toggleFixPortraitOrientation(wmHelper)
+            }
+            teardown {
+                eachRun {
+                    testApp.exit()
+                    device.executeShellCommand("cmd window set-ignore-orientation-request false")
+                }
+            }
+        }
+    }
+
+    @Postsubmit
+    @Test
+    fun imeLayerVisibleStart() {
+        testSpec.assertLayersStart {
+            this.isVisible(FlickerComponentName.IME)
+        }
+    }
+
+    @Postsubmit
+    @Test
+    fun imeLayerExistsEnd() {
+        testSpec.assertLayersEnd {
+            this.isVisible(FlickerComponentName.IME)
+        }
+    }
+
+    @Postsubmit
+    @Test
+    fun imeLayerVisibleRegionKeepsTheSame() {
+        var imeLayerVisibleRegionBeforeTransition: RegionSubject? = null
+        testSpec.assertLayersStart {
+            imeLayerVisibleRegionBeforeTransition = this.visibleRegion(FlickerComponentName.IME)
+        }
+        testSpec.assertLayersEnd {
+            this.visibleRegion(FlickerComponentName.IME)
+                    .coversExactly(imeLayerVisibleRegionBeforeTransition!!.region)
+        }
+    }
+
+    @Postsubmit
+    @Test
+    fun appWindowWithLetterboxCoversExactlyOnScreen() {
+        val displayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
+        testSpec.assertLayersEnd {
+            this.visibleRegion(testApp.component, FlickerComponentName.LETTERBOX)
+                    .coversExactly(displayBounds)
+        }
+    }
+
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): Collection<FlickerTestParameter> {
+            return FlickerTestParameterFactory.getInstance()
+                    .getConfigNonRotationTests(
+                            supportedRotations = listOf(Surface.ROTATION_90, Surface.ROTATION_270),
+                            supportedNavigationModes = listOf(
+                                    WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
+                                    WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+                            )
+                    )
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index b8ef195..efd80f2 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -45,7 +45,7 @@
              android:theme="@style/CutoutShortEdges"
              android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivityAutoFocus"
              android:windowSoftInputMode="stateVisible"
-             android:configChanges="orientation|screenSize"
+             android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
              android:label="ImeAppAutoFocus"
              android:exported="true">
             <intent-filter>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
index baaf707..e71fe80 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
@@ -26,14 +26,27 @@
               android:layout_width="match_parent"
 	      android:imeOptions="flagNoExtractUi"
               android:inputType="text"/>
-    <Button
-        android:id="@+id/finish_activity_btn"
+    <LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:text="Finish activity" />
-    <Button
-        android:id="@+id/start_dialog_themed_activity_btn"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:text="Start dialog themed activity" />
+        android:layout_height="match_parent"
+        android:orientation="horizontal">
+        <Button
+            android:id="@+id/finish_activity_btn"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Finish activity" />
+        <Button
+            android:id="@+id/start_dialog_themed_activity_btn"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Dialog activity" />
+        <ToggleButton
+            android:id="@+id/toggle_fixed_portrait_btn"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textOn="Portrait (On)"
+            android:textOff="Portrait (Off)"
+        />
+    </LinearLayout>
 </LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java
index bb200f1..7ee8deb 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java
@@ -16,21 +16,29 @@
 
 package com.android.server.wm.flicker.testapp;
 
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
 import android.content.Intent;
 import android.widget.Button;
 import android.widget.EditText;
+import android.widget.ToggleButton;
 
 public class ImeActivityAutoFocus extends ImeActivity {
-
     @Override
     protected void onStart() {
         super.onStart();
 
-        EditText editTextField = findViewById(R.id.plain_text_input);
-        editTextField.requestFocus();
-
         Button startThemedActivityButton = findViewById(R.id.start_dialog_themed_activity_btn);
         startThemedActivityButton.setOnClickListener(
                 button -> startActivity(new Intent(this, DialogThemedActivity.class)));
+
+        ToggleButton toggleFixedPortraitButton = findViewById(R.id.toggle_fixed_portrait_btn);
+        toggleFixedPortraitButton.setOnCheckedChangeListener(
+                (button, isChecked) -> setRequestedOrientation(
+                        isChecked ? SCREEN_ORIENTATION_PORTRAIT : SCREEN_ORIENTATION_UNSPECIFIED));
+
+        EditText editTextField = findViewById(R.id.plain_text_input);
+        editTextField.requestFocus();
     }
 }
diff --git a/tests/testables/src/android/testing/TestableSettingsProvider.java b/tests/testables/src/android/testing/TestableSettingsProvider.java
index fd92c65..c6f18fd 100644
--- a/tests/testables/src/android/testing/TestableSettingsProvider.java
+++ b/tests/testables/src/android/testing/TestableSettingsProvider.java
@@ -49,14 +49,15 @@
     }
 
     void clearValuesAndCheck(Context context) {
-        int userId = UserHandle.myUserId();
-        mValues.put(key("global", MY_UNIQUE_KEY, userId), MY_UNIQUE_KEY);
-        mValues.put(key("secure", MY_UNIQUE_KEY, userId), MY_UNIQUE_KEY);
-        mValues.put(key("system", MY_UNIQUE_KEY, userId), MY_UNIQUE_KEY);
-
+        // Ensure we swapped over to use TestableSettingsProvider
         Settings.Global.clearProviderForTest();
         Settings.Secure.clearProviderForTest();
         Settings.System.clearProviderForTest();
+
+        // putString will eventually invoking the mocked call() method and update mValues
+        Settings.Global.putString(context.getContentResolver(), MY_UNIQUE_KEY, MY_UNIQUE_KEY);
+        Settings.Secure.putString(context.getContentResolver(), MY_UNIQUE_KEY, MY_UNIQUE_KEY);
+        Settings.System.putString(context.getContentResolver(), MY_UNIQUE_KEY, MY_UNIQUE_KEY);
         // Verify that if any test is using TestableContext, they all have the correct settings
         // provider.
         assertEquals("Incorrect settings provider, test using incorrect Context?", MY_UNIQUE_KEY,
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
index 8ea43abf..34e8edb 100644
--- a/tools/aapt2/SdkConstants.cpp
+++ b/tools/aapt2/SdkConstants.cpp
@@ -27,7 +27,7 @@
 
 static ApiVersion sDevelopmentSdkLevel = 10000;
 static const auto sDevelopmentSdkCodeNames =
-    std::unordered_set<StringPiece>({"Q", "R", "S", "Sv2", "Tiramisu"});
+    std::unordered_set<StringPiece>({"Q", "R", "S", "Sv2", "Tiramisu", "UpsideDownCake"});
 
 static const std::vector<std::pair<uint16_t, ApiVersion>> sAttrIdMap = {
     {0x021c, 1},