Merge "Open USI details page from USI low battery notification." into tm-qpr-dev
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 70a23cd..93c0c4d 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1139,7 +1139,6 @@
   public final class CameraManager {
     method public String[] getCameraIdListNoLazy() throws android.hardware.camera2.CameraAccessException;
     method @RequiresPermission(allOf={android.Manifest.permission.SYSTEM_CAMERA, android.Manifest.permission.CAMERA}) public void openCamera(@NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraDevice.StateCallback) throws android.hardware.camera2.CameraAccessException;
-    field public static final long OVERRIDE_FRONT_CAMERA_APP_COMPAT = 250678880L; // 0xef10e60L
   }
 
   public abstract static class CameraManager.AvailabilityCallback {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 4a4ba63..bab2061 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6787,6 +6787,8 @@
      * {@link #ENCRYPTION_STATUS_UNSUPPORTED}, {@link #ENCRYPTION_STATUS_INACTIVE},
      * {@link #ENCRYPTION_STATUS_ACTIVATING}, {@link #ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY},
      * {@link #ENCRYPTION_STATUS_ACTIVE}, or {@link #ENCRYPTION_STATUS_ACTIVE_PER_USER}.
+     *
+     * @throws SecurityException if called on a parent instance.
      */
     public int getStorageEncryptionStatus() {
         throwIfParentInstance("getStorageEncryptionStatus");
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 2ea0d82..a320f1e 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -11488,7 +11488,7 @@
     private void toUriInner(StringBuilder uri, String scheme, String defAction,
             String defPackage, int flags) {
         if (scheme != null) {
-            uri.append("scheme=").append(scheme).append(';');
+            uri.append("scheme=").append(Uri.encode(scheme)).append(';');
         }
         if (mAction != null && !mAction.equals(defAction)) {
             uri.append("action=").append(Uri.encode(mAction)).append(';');
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 5291d2b..7ccf07a 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -29,7 +29,6 @@
 import android.annotation.SdkConstant.SdkConstantType;
 import android.app.ActivityThread;
 import android.app.AppOpsManager;
-import android.app.compat.CompatChanges;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.graphics.ImageFormat;
@@ -47,7 +46,6 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.SystemProperties;
 import android.renderscript.Allocation;
 import android.renderscript.Element;
 import android.renderscript.RSIllegalArgumentException;
@@ -284,14 +282,6 @@
      */
     public native static int getNumberOfCameras();
 
-    private static final boolean sLandscapeToPortrait =
-            SystemProperties.getBoolean(CameraManager.LANDSCAPE_TO_PORTRAIT_PROP, false);
-
-    private static boolean shouldOverrideToPortrait() {
-        return CompatChanges.isChangeEnabled(CameraManager.OVERRIDE_FRONT_CAMERA_APP_COMPAT)
-                && sLandscapeToPortrait;
-    }
-
     /**
      * Returns the information about a particular camera.
      * If {@link #getNumberOfCameras()} returns N, the valid id is 0 to N-1.
@@ -301,7 +291,8 @@
      *    low-level failure).
      */
     public static void getCameraInfo(int cameraId, CameraInfo cameraInfo) {
-        boolean overrideToPortrait = shouldOverrideToPortrait();
+        boolean overrideToPortrait = CameraManager.shouldOverrideToPortrait(
+                ActivityThread.currentApplication().getApplicationContext());
 
         _getCameraInfo(cameraId, overrideToPortrait, cameraInfo);
         IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
@@ -498,7 +489,8 @@
             mEventHandler = null;
         }
 
-        boolean overrideToPortrait = shouldOverrideToPortrait();
+        boolean overrideToPortrait = CameraManager.shouldOverrideToPortrait(
+                ActivityThread.currentApplication().getApplicationContext());
         return native_setup(new WeakReference<Camera>(this), cameraId,
                 ActivityThread.currentOpPackageName(), overrideToPortrait);
     }
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index be99f0f..5e2b40c 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -115,8 +115,14 @@
     @ChangeId
     @Overridable
     @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.BASE)
-    @TestApi
-    public static final long OVERRIDE_FRONT_CAMERA_APP_COMPAT = 250678880L;
+    public static final long OVERRIDE_CAMERA_LANDSCAPE_TO_PORTRAIT = 250678880L;
+
+    /**
+     * Package-level opt in/out for the above.
+     * @hide
+     */
+    public static final String PROPERTY_COMPAT_OVERRIDE_LANDSCAPE_TO_PORTRAIT =
+            "android.camera.PROPERTY_COMPAT_OVERRIDE_LANDSCAPE_TO_PORTRAIT";
 
     /**
      * System property for allowing the above
@@ -602,7 +608,7 @@
             try {
                 Size displaySize = getDisplaySize();
 
-                boolean overrideToPortrait = shouldOverrideToPortrait();
+                boolean overrideToPortrait = shouldOverrideToPortrait(mContext);
                 CameraMetadataNative info = cameraService.getCameraCharacteristics(cameraId,
                         mContext.getApplicationInfo().targetSdkVersion, overrideToPortrait);
                 try {
@@ -722,7 +728,7 @@
                         "Camera service is currently unavailable");
                 }
 
-                boolean overrideToPortrait = shouldOverrideToPortrait();
+                boolean overrideToPortrait = shouldOverrideToPortrait(mContext);
                 cameraUser = cameraService.connectDevice(callbacks, cameraId,
                     mContext.getOpPackageName(), mContext.getAttributionTag(), uid,
                     oomScoreOffset, mContext.getApplicationInfo().targetSdkVersion,
@@ -1154,9 +1160,26 @@
         return CameraManagerGlobal.get().getTorchStrengthLevel(cameraId);
     }
 
-    private static boolean shouldOverrideToPortrait() {
-        return CompatChanges.isChangeEnabled(OVERRIDE_FRONT_CAMERA_APP_COMPAT)
-                && CameraManagerGlobal.sLandscapeToPortrait;
+    /**
+     * @hide
+     */
+    public static boolean shouldOverrideToPortrait(@Nullable Context context) {
+        if (!CameraManagerGlobal.sLandscapeToPortrait) {
+            return false;
+        }
+
+        if (context != null) {
+            PackageManager packageManager = context.getPackageManager();
+
+            try {
+                return packageManager.getProperty(context.getOpPackageName(),
+                            PROPERTY_COMPAT_OVERRIDE_LANDSCAPE_TO_PORTRAIT).getBoolean();
+            } catch (PackageManager.NameNotFoundException e) {
+                // No such property
+            }
+        }
+
+        return CompatChanges.isChangeEnabled(OVERRIDE_CAMERA_LANDSCAPE_TO_PORTRAIT);
     }
 
     /**
@@ -2313,6 +2336,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
 
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index a6c79b3..0c2468e 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -87,6 +87,7 @@
 
     // TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
     private ICameraDeviceUserWrapper mRemoteDevice;
+    private boolean mRemoteDeviceInit = false;
 
     // Lock to synchronize cross-thread access to device public interface
     final Object mInterfaceLock = new Object(); // access from this class and Session only!
@@ -338,6 +339,8 @@
 
             mDeviceExecutor.execute(mCallOnOpened);
             mDeviceExecutor.execute(mCallOnUnconfigured);
+
+            mRemoteDeviceInit = true;
         }
     }
 
@@ -1754,8 +1757,8 @@
         }
 
         synchronized(mInterfaceLock) {
-            if (mRemoteDevice == null) {
-                return; // Camera already closed
+            if (mRemoteDevice == null && mRemoteDeviceInit) {
+                return; // Camera already closed, user is not interested in errors anymore.
             }
 
             // Redirect device callback to the offline session in case we are in the middle
diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java
index dba1a5e..6a667fe 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManager.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManager.java
@@ -251,6 +251,10 @@
         @Nullable
         private Boolean lastResult;
 
+        public FoldStateListener(Context context) {
+            this(context, folded -> {});
+        }
+
         public FoldStateListener(Context context, Consumer<Boolean> listener) {
             mFoldedDeviceStates = context.getResources().getIntArray(
                     com.android.internal.R.array.config_foldedDeviceStates);
@@ -266,5 +270,10 @@
                 mDelegate.accept(folded);
             }
         }
+
+        @Nullable
+        public Boolean getFolded() {
+            return lastResult;
+        }
     }
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 94a6382..b21187a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9819,11 +9819,10 @@
                 "fingerprint_side_fps_auth_downtime";
 
         /**
-         * Whether or not a SFPS device is required to be interactive for auth to unlock the device.
+         * Whether or not a SFPS device is enabling the performant auth setting.
          * @hide
          */
-        public static final String SFPS_REQUIRE_SCREEN_ON_TO_AUTH_ENABLED =
-                "sfps_require_screen_on_to_auth_enabled";
+        public static final String SFPS_PERFORMANT_AUTH_ENABLED = "sfps_performant_auth_enabled";
 
         /**
          * Whether or not debugging is enabled.
diff --git a/core/java/android/text/TextShaper.java b/core/java/android/text/TextShaper.java
index a1d6cc8..6da0b63 100644
--- a/core/java/android/text/TextShaper.java
+++ b/core/java/android/text/TextShaper.java
@@ -173,7 +173,7 @@
     private TextShaper() {}
 
     /**
-     * An consumer interface for accepting text shape result.
+     * A consumer interface for accepting text shape result.
      */
     public interface GlyphsConsumer {
         /**
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index d067d4b..497f066 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -66,8 +66,7 @@
 import java.util.function.Consumer;
 
 /**
- * <p>The {@link ContentCaptureManager} provides additional ways for for apps to
- * integrate with the content capture subsystem.
+ * <p>Provides additional ways for apps to integrate with the content capture subsystem.
  *
  * <p>Content capture provides real-time, continuous capture of application activity, display and
  * events to an intelligence service that is provided by the Android system. The intelligence
diff --git a/core/java/android/webkit/WebResourceError.java b/core/java/android/webkit/WebResourceError.java
index 11f1b6f1..4c87489 100644
--- a/core/java/android/webkit/WebResourceError.java
+++ b/core/java/android/webkit/WebResourceError.java
@@ -19,7 +19,7 @@
 import android.annotation.SystemApi;
 
 /**
- * Encapsulates information about errors occured during loading of web resources. See
+ * Encapsulates information about errors that occurred during loading of web resources. See
  * {@link WebViewClient#onReceivedError(WebView, WebResourceRequest, WebResourceError) WebViewClient.onReceivedError(WebView, WebResourceRequest, WebResourceError)}
  */
 public abstract class WebResourceError {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index af31391..6230d22 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -17,6 +17,7 @@
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
+import static com.android.wm.shell.bubbles.Bubble.KEY_APP_BUBBLE;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_DATA;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
@@ -684,7 +685,8 @@
         if (bubble.getPendingIntentCanceled()
                 || !(reason == Bubbles.DISMISS_AGED
                 || reason == Bubbles.DISMISS_USER_GESTURE
-                || reason == Bubbles.DISMISS_RELOAD_FROM_DISK)) {
+                || reason == Bubbles.DISMISS_RELOAD_FROM_DISK)
+                || KEY_APP_BUBBLE.equals(bubble.getKey())) {
             return;
         }
         if (DEBUG_BUBBLE_DATA) {
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 8ddc3c04..1488469 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
@@ -605,9 +605,19 @@
             float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         if (options1 == null) options1 = new Bundle();
+        if (taskId2 == INVALID_TASK_ID) {
+            // Launching a solo task.
+            ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
+            activityOptions.update(ActivityOptions.makeRemoteAnimation(adapter));
+            options1 = activityOptions.toBundle();
+            addActivityOptions(options1, null /* launchTarget */);
+            wct.startTask(taskId1, options1);
+            mSyncQueue.queue(wct);
+            return;
+        }
+
         addActivityOptions(options1, mSideStage);
         wct.startTask(taskId1, options1);
-
         startWithLegacyTransition(wct, taskId2, options2, splitPosition, splitRatio, adapter,
                 instanceId);
     }
@@ -632,9 +642,19 @@
             InstanceId instanceId) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         if (options1 == null) options1 = new Bundle();
+        if (taskId == INVALID_TASK_ID) {
+            // Launching a solo task.
+            ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
+            activityOptions.update(ActivityOptions.makeRemoteAnimation(adapter));
+            options1 = activityOptions.toBundle();
+            addActivityOptions(options1, null /* launchTarget */);
+            wct.sendPendingIntent(pendingIntent, fillInIntent, options1);
+            mSyncQueue.queue(wct);
+            return;
+        }
+
         addActivityOptions(options1, mSideStage);
         wct.sendPendingIntent(pendingIntent, fillInIntent, options1);
-
         startWithLegacyTransition(wct, taskId, options2, splitPosition, splitRatio, adapter,
                 instanceId);
     }
@@ -696,6 +716,34 @@
         mShouldUpdateRecents = false;
         mIsSplitEntering = true;
 
+        setSideStagePosition(sidePosition, wct);
+        if (!mMainStage.isActive()) {
+            mMainStage.activate(wct, false /* reparent */);
+        }
+
+        if (mainOptions == null) mainOptions = new Bundle();
+        addActivityOptions(mainOptions, mMainStage);
+        mainOptions = wrapAsSplitRemoteAnimation(adapter, mainOptions);
+
+        updateWindowBounds(mSplitLayout, wct);
+        if (mainTaskId == INVALID_TASK_ID) {
+            wct.sendPendingIntent(mainPendingIntent, mainFillInIntent, mainOptions);
+        } else {
+            wct.startTask(mainTaskId, mainOptions);
+        }
+
+        wct.reorder(mRootTaskInfo.token, true);
+        wct.setForceTranslucent(mRootTaskInfo.token, false);
+
+        mSyncQueue.queue(wct);
+        mSyncQueue.runInSync(t -> {
+            setDividerVisibility(true, t);
+        });
+
+        setEnterInstanceId(instanceId);
+    }
+
+    private Bundle wrapAsSplitRemoteAnimation(RemoteAnimationAdapter adapter, Bundle options) {
         final WindowContainerTransaction evictWct = new WindowContainerTransaction();
         if (isSplitScreenVisible()) {
             mMainStage.evictAllChildren(evictWct);
@@ -739,37 +787,9 @@
         };
         RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(
                 wrapper, adapter.getDuration(), adapter.getStatusBarTransitionDelay());
-
-        if (mainOptions == null) {
-            mainOptions = ActivityOptions.makeRemoteAnimation(wrappedAdapter).toBundle();
-        } else {
-            ActivityOptions mainActivityOptions = ActivityOptions.fromBundle(mainOptions);
-            mainActivityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
-            mainOptions = mainActivityOptions.toBundle();
-        }
-
-        setSideStagePosition(sidePosition, wct);
-        if (!mMainStage.isActive()) {
-            mMainStage.activate(wct, false /* reparent */);
-        }
-
-        if (mainOptions == null) mainOptions = new Bundle();
-        addActivityOptions(mainOptions, mMainStage);
-        updateWindowBounds(mSplitLayout, wct);
-        if (mainTaskId == INVALID_TASK_ID) {
-            wct.sendPendingIntent(mainPendingIntent, mainFillInIntent, mainOptions);
-        } else {
-            wct.startTask(mainTaskId, mainOptions);
-        }
-        wct.reorder(mRootTaskInfo.token, true);
-        wct.setForceTranslucent(mRootTaskInfo.token, false);
-
-        mSyncQueue.queue(wct);
-        mSyncQueue.runInSync(t -> {
-            setDividerVisibility(true, t);
-        });
-
-        setEnterInstanceId(instanceId);
+        ActivityOptions activityOptions = ActivityOptions.fromBundle(options);
+        activityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
+        return activityOptions.toBundle();
     }
 
     private void setEnterInstanceId(InstanceId instanceId) {
@@ -1228,8 +1248,10 @@
         return SPLIT_POSITION_UNDEFINED;
     }
 
-    private void addActivityOptions(Bundle opts, StageTaskListener stage) {
-        opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, stage.mRootTaskInfo.token);
+    private void addActivityOptions(Bundle opts, @Nullable StageTaskListener launchTarget) {
+        if (launchTarget != null) {
+            opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, launchTarget.mRootTaskInfo.token);
+        }
         // Put BAL flags to avoid activity start aborted. Otherwise, flows like shortcut to split
         // will be canceled.
         opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, true);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index e6711ac..8b025cd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -16,6 +16,8 @@
 
 package com.android.wm.shell.bubbles;
 
+import static com.android.wm.shell.bubbles.Bubble.KEY_APP_BUBBLE;
+
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
@@ -32,6 +34,7 @@
 
 import android.app.Notification;
 import android.app.PendingIntent;
+import android.content.Intent;
 import android.content.LocusId;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
@@ -94,6 +97,7 @@
     private Bubble mBubbleInterruptive;
     private Bubble mBubbleDismissed;
     private Bubble mBubbleLocusId;
+    private Bubble mAppBubble;
 
     private BubbleData mBubbleData;
     private TestableBubblePositioner mPositioner;
@@ -178,6 +182,11 @@
                 mBubbleMetadataFlagListener,
                 mPendingIntentCanceledListener,
                 mMainExecutor);
+
+        Intent appBubbleIntent = new Intent(mContext, BubblesTestActivity.class);
+        appBubbleIntent.setPackage(mContext.getPackageName());
+        mAppBubble = new Bubble(appBubbleIntent, new UserHandle(1), mMainExecutor);
+
         mPositioner = new TestableBubblePositioner(mContext,
                 mock(WindowManager.class));
         mBubbleData = new BubbleData(getContext(), mBubbleLogger, mPositioner,
@@ -1089,6 +1098,18 @@
         assertOverflowChangedTo(ImmutableList.of());
     }
 
+    @Test
+    public void test_removeAppBubble_skipsOverflow() {
+        mBubbleData.notificationEntryUpdated(mAppBubble, true /* suppressFlyout*/,
+                false /* showInShade */);
+        assertThat(mBubbleData.getBubbleInStackWithKey(KEY_APP_BUBBLE)).isEqualTo(mAppBubble);
+
+        mBubbleData.dismissBubbleWithKey(KEY_APP_BUBBLE, Bubbles.DISMISS_USER_GESTURE);
+
+        assertThat(mBubbleData.getOverflowBubbleWithKey(KEY_APP_BUBBLE)).isNull();
+        assertThat(mBubbleData.getBubbleInStackWithKey(KEY_APP_BUBBLE)).isNull();
+    }
+
     private void verifyUpdateReceived() {
         verify(mListener).applyUpdate(mUpdateCaptor.capture());
         reset(mListener);
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 1b0b6b4..211030a 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -123,7 +123,7 @@
         Settings.Secure.FINGERPRINT_SIDE_FPS_BP_POWER_WINDOW,
         Settings.Secure.FINGERPRINT_SIDE_FPS_ENROLL_TAP_WINDOW,
         Settings.Secure.FINGERPRINT_SIDE_FPS_AUTH_DOWNTIME,
-        Settings.Secure.SFPS_REQUIRE_SCREEN_ON_TO_AUTH_ENABLED,
+        Settings.Secure.SFPS_PERFORMANT_AUTH_ENABLED,
         Settings.Secure.ACTIVE_UNLOCK_ON_WAKE,
         Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT,
         Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 4fa490f..0539f09 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -178,7 +178,7 @@
         VALIDATORS.put(Secure.FINGERPRINT_SIDE_FPS_ENROLL_TAP_WINDOW,
                 NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.FINGERPRINT_SIDE_FPS_AUTH_DOWNTIME, NON_NEGATIVE_INTEGER_VALIDATOR);
-        VALIDATORS.put(Secure.SFPS_REQUIRE_SCREEN_ON_TO_AUTH_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.SFPS_PERFORMANT_AUTH_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.SHOW_MEDIA_WHEN_BYPASSING, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.FACE_UNLOCK_APP_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, BOOLEAN_VALIDATOR);
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 810dd33..6800917 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -411,7 +411,6 @@
 
         <service android:name=".screenshot.ScreenshotCrossProfileService"
                  android:permission="com.android.systemui.permission.SELF"
-                 android:process=":screenshot_cross_profile"
                  android:exported="false" />
 
         <service android:name=".screenrecord.RecordingService" />
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
index 2b7bdc2..c772c96 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
@@ -27,7 +27,7 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    androidprv:layout_maxWidth="@dimen/keyguard_security_width"
+    androidprv:layout_maxWidth="@dimen/biometric_auth_pattern_view_max_size"
     android:layout_gravity="center_horizontal|bottom"
     android:clipChildren="false"
     android:clipToPadding="false">
diff --git a/packages/SystemUI/res-keyguard/values-sw540dp-port/dimens.xml b/packages/SystemUI/res-keyguard/values-sw540dp-port/dimens.xml
deleted file mode 100644
index a3c37e4..0000000
--- a/packages/SystemUI/res-keyguard/values-sw540dp-port/dimens.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* //device/apps/common/assets/res/any/dimens.xml
-**
-** Copyright 2013, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT 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>
-    <!-- Height of the sliding KeyguardSecurityContainer
-        (includes 2x keyguard_security_view_top_margin) -->
-    <dimen name="keyguard_security_height">550dp</dimen>
-</resources>
diff --git a/packages/SystemUI/res-keyguard/values-sw720dp/dimens.xml b/packages/SystemUI/res-keyguard/values-sw720dp/dimens.xml
index 1dc61c5..b7a1bb4 100644
--- a/packages/SystemUI/res-keyguard/values-sw720dp/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values-sw720dp/dimens.xml
@@ -17,10 +17,5 @@
 */
 -->
 <resources>
-
-    <!-- Height of the sliding KeyguardSecurityContainer
-         (includes 2x keyguard_security_view_top_margin) -->
-    <dimen name="keyguard_security_height">470dp</dimen>
-
     <dimen name="widget_big_font_size">100dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index c5ffdc0..6cc5b9d 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -29,9 +29,6 @@
          (includes 2x keyguard_security_view_top_margin) -->
     <dimen name="keyguard_security_height">420dp</dimen>
 
-    <!-- Max Height of the sliding KeyguardSecurityContainer
-         (includes 2x keyguard_security_view_top_margin) -->
-
     <!-- pin/password field max height -->
     <dimen name="keyguard_password_height">80dp</dimen>
 
diff --git a/packages/SystemUI/res/drawable/overlay_badge_background.xml b/packages/SystemUI/res/drawable/overlay_badge_background.xml
index 857632e..53122c1 100644
--- a/packages/SystemUI/res/drawable/overlay_badge_background.xml
+++ b/packages/SystemUI/res/drawable/overlay_badge_background.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2020 The Android Open Source Project
+  ~ 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.
@@ -14,8 +14,11 @@
   ~ 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="oval">
-    <solid android:color="?androidprv:attr/colorSurface"/>
-</shape>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="48dp"
+        android:height="48dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+    <path
+        android:pathData="M0,0M48,48"/>
+</vector>
diff --git a/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml b/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml
index a3dd334..3505a3e 100644
--- a/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml
+++ b/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml
@@ -71,8 +71,8 @@
         <com.android.internal.widget.LockPatternView
             android:id="@+id/lockPattern"
             android:layout_gravity="center"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"/>
+            android:layout_width="@dimen/biometric_auth_pattern_view_size"
+            android:layout_height="@dimen/biometric_auth_pattern_view_size"/>
 
         <TextView
             android:id="@+id/error"
diff --git a/packages/SystemUI/res/layout/auth_credential_pattern_view.xml b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
index 4af9970..147ea82 100644
--- a/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
+++ b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
@@ -67,8 +67,8 @@
         <com.android.internal.widget.LockPatternView
             android:id="@+id/lockPattern"
             android:layout_gravity="center"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"/>
+            android:layout_width="@dimen/biometric_auth_pattern_view_size"
+            android:layout_height="@dimen/biometric_auth_pattern_view_size"/>
 
         <TextView
             android:id="@+id/error"
diff --git a/packages/SystemUI/res/layout/chipbar.xml b/packages/SystemUI/res/layout/chipbar.xml
index bc97e51..8cf4f4d 100644
--- a/packages/SystemUI/res/layout/chipbar.xml
+++ b/packages/SystemUI/res/layout/chipbar.xml
@@ -23,6 +23,7 @@
     android:layout_width="wrap_content"
     android:layout_height="wrap_content">
 
+    <!-- Extra marginBottom to give room for the drop shadow. -->
     <LinearLayout
         android:id="@+id/chipbar_inner"
         android:orientation="horizontal"
@@ -33,6 +34,8 @@
         android:layout_marginTop="20dp"
         android:layout_marginStart="@dimen/notification_side_paddings"
         android:layout_marginEnd="@dimen/notification_side_paddings"
+        android:translationZ="4dp"
+        android:layout_marginBottom="8dp"
         android:clipToPadding="false"
         android:gravity="center_vertical"
         android:alpha="0.0"
diff --git a/packages/SystemUI/res/layout/clipboard_overlay.xml b/packages/SystemUI/res/layout/clipboard_overlay.xml
index 9134f96..eec3b11 100644
--- a/packages/SystemUI/res/layout/clipboard_overlay.xml
+++ b/packages/SystemUI/res/layout/clipboard_overlay.xml
@@ -32,26 +32,26 @@
         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"
+        android:layout_marginBottom="@dimen/overlay_action_container_margin_bottom"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="@+id/actions_container"
-        app:layout_constraintEnd_toEndOf="@+id/actions_container"/>
+        app:layout_constraintEnd_toEndOf="@+id/actions_container"
+        app:layout_constraintBottom_toBottomOf="parent"/>
     <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:paddingEnd="@dimen/overlay_action_container_padding_end"
         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">
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintBottom_toBottomOf="@id/actions_container_background">
         <LinearLayout
             android:id="@+id/actions"
             android:layout_width="wrap_content"
@@ -69,44 +69,30 @@
         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:layout_marginStart="@dimen/overlay_preview_container_margin"
+        android:layout_marginTop="@dimen/overlay_border_width_neg"
+        android:layout_marginEnd="@dimen/overlay_border_width_neg"
+        android:layout_marginBottom="@dimen/overlay_preview_container_margin"
         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"/>
+        android:background="@drawable/overlay_border"
+        app:layout_constraintStart_toStartOf="@id/actions_container_background"
+        app:layout_constraintTop_toTopOf="@id/clipboard_preview"
+        app:layout_constraintEnd_toEndOf="@id/clipboard_preview"
+        app:layout_constraintBottom_toBottomOf="@id/actions_container_background"/>
     <FrameLayout
         android:id="@+id/clipboard_preview"
+        android:layout_width="@dimen/clipboard_preview_size"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/overlay_border_width"
+        android:layout_marginBottom="@dimen/overlay_border_width"
+        android:layout_gravity="center"
         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_constraintHorizontal_bias="0"
-        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">
+        app:layout_constraintBottom_toBottomOf="@id/preview_border">
         <TextView android:id="@+id/text_preview"
                   android:textFontWeight="500"
                   android:padding="8dp"
diff --git a/packages/SystemUI/res/layout/combined_qs_header.xml b/packages/SystemUI/res/layout/combined_qs_header.xml
index a565988..d689828 100644
--- a/packages/SystemUI/res/layout/combined_qs_header.xml
+++ b/packages/SystemUI/res/layout/combined_qs_header.xml
@@ -148,9 +148,4 @@
         <include layout="@layout/ongoing_privacy_chip"/>
     </FrameLayout>
 
-    <Space
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        android:id="@+id/space"
-    />
 </com.android.systemui.util.NoRemeasureMotionLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml b/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
index 9add32c..885e5e2 100644
--- a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
@@ -57,6 +57,7 @@
             android:layout_width="@dimen/dream_overlay_status_bar_icon_size"
             android:layout_height="match_parent"
             android:layout_marginStart="@dimen/dream_overlay_status_icon_margin"
+            android:layout_marginTop="@dimen/dream_overlay_status_bar_marginTop"
             android:src="@drawable/ic_alarm"
             android:tint="@android:color/white"
             android:visibility="gone"
@@ -67,6 +68,7 @@
             android:layout_width="@dimen/dream_overlay_status_bar_icon_size"
             android:layout_height="match_parent"
             android:layout_marginStart="@dimen/dream_overlay_status_icon_margin"
+            android:layout_marginTop="@dimen/dream_overlay_status_bar_marginTop"
             android:src="@drawable/ic_qs_dnd_on"
             android:tint="@android:color/white"
             android:visibility="gone"
@@ -77,6 +79,7 @@
             android:layout_width="@dimen/dream_overlay_status_bar_icon_size"
             android:layout_height="match_parent"
             android:layout_marginStart="@dimen/dream_overlay_status_icon_margin"
+            android:layout_marginTop="@dimen/dream_overlay_status_bar_marginTop"
             android:src="@drawable/ic_signal_wifi_off"
             android:visibility="gone"
             android:contentDescription="@string/dream_overlay_status_bar_wifi_off" />
diff --git a/packages/SystemUI/res/layout/screenshot_static.xml b/packages/SystemUI/res/layout/screenshot_static.xml
index e4e0bd4..496eb6e 100644
--- a/packages/SystemUI/res/layout/screenshot_static.xml
+++ b/packages/SystemUI/res/layout/screenshot_static.xml
@@ -27,26 +27,26 @@
         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"
+        android:layout_marginBottom="@dimen/overlay_action_container_margin_bottom"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="@+id/actions_container"
-        app:layout_constraintEnd_toEndOf="@+id/actions_container"/>
+        app:layout_constraintEnd_toEndOf="@+id/actions_container"
+        app:layout_constraintBottom_toTopOf="@id/screenshot_message_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:layout_marginBottom="4dp"
-        android:paddingEnd="@dimen/overlay_action_container_padding_right"
+        android:paddingEnd="@dimen/overlay_action_container_padding_end"
         android:paddingVertical="@dimen/overlay_action_container_padding_vertical"
         android:elevation="4dp"
         android:scrollbars="none"
         app:layout_constraintHorizontal_bias="0"
         app:layout_constraintWidth_percent="1.0"
         app:layout_constraintWidth_max="wrap"
-        app:layout_constraintBottom_toTopOf="@id/screenshot_message_container"
         app:layout_constraintStart_toEndOf="@+id/screenshot_preview_border"
-        app:layout_constraintEnd_toEndOf="parent">
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintBottom_toBottomOf="@id/actions_container_background">
         <LinearLayout
             android:id="@+id/screenshot_actions"
             android:layout_width="wrap_content"
@@ -64,35 +64,24 @@
         android:id="@+id/screenshot_preview_border"
         android:layout_width="0dp"
         android:layout_height="0dp"
-        android:layout_marginStart="@dimen/overlay_offset_x"
-        android:layout_marginBottom="12dp"
+        android:layout_marginStart="@dimen/overlay_preview_container_margin"
+        android:layout_marginTop="@dimen/overlay_border_width_neg"
+        android:layout_marginEnd="@dimen/overlay_border_width_neg"
+        android:layout_marginBottom="@dimen/overlay_preview_container_margin"
         android:elevation="7dp"
         android:alpha="0"
         android:background="@drawable/overlay_border"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintBottom_toTopOf="@id/screenshot_message_container"
-        app:layout_constraintEnd_toEndOf="@id/screenshot_preview_end"
-        app:layout_constraintTop_toTopOf="@id/screenshot_preview_top"/>
-    <androidx.constraintlayout.widget.Barrier
-        android:id="@+id/screenshot_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="screenshot_preview"/>
-    <androidx.constraintlayout.widget.Barrier
-        android:id="@+id/screenshot_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="screenshot_preview"/>
+        app:layout_constraintStart_toStartOf="@id/actions_container_background"
+        app:layout_constraintTop_toTopOf="@id/screenshot_preview"
+        app:layout_constraintEnd_toEndOf="@id/screenshot_preview"
+        app:layout_constraintBottom_toBottomOf="@id/actions_container_background"/>
     <ImageView
         android:id="@+id/screenshot_preview"
         android:visibility="invisible"
         android:layout_width="@dimen/overlay_x_scale"
-        android:layout_margin="@dimen/overlay_border_width"
         android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/overlay_border_width"
+        android:layout_marginBottom="@dimen/overlay_border_width"
         android:layout_gravity="center"
         android:elevation="7dp"
         android:contentDescription="@string/screenshot_edit_description"
@@ -100,20 +89,14 @@
         android:background="@drawable/overlay_preview_background"
         android:adjustViewBounds="true"
         android:clickable="true"
-        app:layout_constraintHorizontal_bias="0"
-        app:layout_constraintBottom_toBottomOf="@id/screenshot_preview_border"
         app:layout_constraintStart_toStartOf="@id/screenshot_preview_border"
-        app:layout_constraintEnd_toEndOf="@id/screenshot_preview_border"
-        app:layout_constraintTop_toTopOf="@id/screenshot_preview_border"/>
+        app:layout_constraintBottom_toBottomOf="@id/screenshot_preview_border"/>
     <ImageView
         android:id="@+id/screenshot_badge"
-        android:layout_width="24dp"
-        android:layout_height="24dp"
-        android:padding="4dp"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
         android:visibility="gone"
-        android:background="@drawable/overlay_badge_background"
         android:elevation="8dp"
-        android:src="@drawable/overlay_cancel"
         app:layout_constraintBottom_toBottomOf="@id/screenshot_preview_border"
         app:layout_constraintEnd_toEndOf="@id/screenshot_preview_border"/>
     <FrameLayout
@@ -150,7 +133,7 @@
         android:layout_height="wrap_content"
         android:layout_marginHorizontal="@dimen/overlay_action_container_margin_horizontal"
         android:layout_marginVertical="4dp"
-        android:paddingHorizontal="@dimen/overlay_action_container_padding_right"
+        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"
diff --git a/packages/SystemUI/res/layout/user_switcher_fullscreen.xml b/packages/SystemUI/res/layout/user_switcher_fullscreen.xml
index fa9d739..7eaed43 100644
--- a/packages/SystemUI/res/layout/user_switcher_fullscreen.xml
+++ b/packages/SystemUI/res/layout/user_switcher_fullscreen.xml
@@ -46,7 +46,7 @@
           app:layout_constraintEnd_toEndOf="parent"
           app:flow_horizontalBias="0.5"
           app:flow_verticalAlign="center"
-          app:flow_wrapMode="chain"
+          app:flow_wrapMode="chain2"
           app:flow_horizontalGap="@dimen/user_switcher_fullscreen_horizontal_gap"
           app:flow_verticalGap="44dp"
           app:flow_horizontalStyle="packed"/>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 49ef330..fff2544 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -40,6 +40,10 @@
     <dimen name="biometric_dialog_button_negative_max_width">140dp</dimen>
     <dimen name="biometric_dialog_button_positive_max_width">116dp</dimen>
 
+    <!-- Lock pattern view size, align sysui biometric_auth_pattern_view_size -->
+    <dimen name="biometric_auth_pattern_view_size">248dp</dimen>
+    <dimen name="biometric_auth_pattern_view_max_size">348dp</dimen>
+
     <dimen name="global_actions_power_dialog_item_height">130dp</dimen>
     <dimen name="global_actions_power_dialog_item_bottom_margin">35dp</dimen>
 
diff --git a/packages/SystemUI/res/values-land/styles.xml b/packages/SystemUI/res/values-land/styles.xml
index aefd998..a0e721e 100644
--- a/packages/SystemUI/res/values-land/styles.xml
+++ b/packages/SystemUI/res/values-land/styles.xml
@@ -29,11 +29,11 @@
 
     <style name="AuthCredentialPatternContainerStyle">
         <item name="android:gravity">center</item>
-        <item name="android:maxHeight">320dp</item>
-        <item name="android:maxWidth">320dp</item>
-        <item name="android:minHeight">200dp</item>
-        <item name="android:minWidth">200dp</item>
-        <item name="android:paddingHorizontal">60dp</item>
+        <item name="android:maxHeight">@dimen/biometric_auth_pattern_view_max_size</item>
+        <item name="android:maxWidth">@dimen/biometric_auth_pattern_view_max_size</item>
+        <item name="android:minHeight">@dimen/biometric_auth_pattern_view_size</item>
+        <item name="android:minWidth">@dimen/biometric_auth_pattern_view_size</item>
+        <item name="android:paddingHorizontal">32dp</item>
         <item name="android:paddingVertical">20dp</item>
     </style>
 
diff --git a/packages/SystemUI/res/values-sw360dp/dimens.xml b/packages/SystemUI/res/values-sw360dp/dimens.xml
index 65ca70b..03365b3 100644
--- a/packages/SystemUI/res/values-sw360dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw360dp/dimens.xml
@@ -25,5 +25,8 @@
 
     <!-- Home Controls -->
     <dimen name="global_actions_side_margin">12dp</dimen>
+
+    <!-- Biometric Auth pattern view size, better to align keyguard_security_width -->
+    <dimen name="biometric_auth_pattern_view_size">298dp</dimen>
 </resources>
 
diff --git a/packages/SystemUI/res/values-sw392dp-land/dimens.xml b/packages/SystemUI/res/values-sw392dp-land/dimens.xml
new file mode 100644
index 0000000..1e26a69
--- /dev/null
+++ b/packages/SystemUI/res/values-sw392dp-land/dimens.xml
@@ -0,0 +1,21 @@
+<!--
+  ~ 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.
+  -->
+
+<resources>
+    <!-- Lock pattern view size, align sysui biometric_auth_pattern_view_size -->
+    <dimen name="biometric_auth_pattern_view_size">248dp</dimen>
+    <dimen name="biometric_auth_pattern_view_max_size">248dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-sw392dp/dimens.xml b/packages/SystemUI/res/values-sw392dp/dimens.xml
index 78279ca..96af3c1 100644
--- a/packages/SystemUI/res/values-sw392dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw392dp/dimens.xml
@@ -24,5 +24,8 @@
 
     <!-- Home Controls -->
     <dimen name="global_actions_side_margin">16dp</dimen>
+
+    <!-- Biometric Auth pattern view size, better to align keyguard_security_width -->
+    <dimen name="biometric_auth_pattern_view_size">298dp</dimen>
 </resources>
 
diff --git a/packages/SystemUI/res/values-sw410dp-land/dimens.xml b/packages/SystemUI/res/values-sw410dp-land/dimens.xml
new file mode 100644
index 0000000..c4d9b9b
--- /dev/null
+++ b/packages/SystemUI/res/values-sw410dp-land/dimens.xml
@@ -0,0 +1,21 @@
+<!--
+  ~ 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.
+  -->
+
+<resources>
+    <!-- Lock pattern view size, align sysui biometric_auth_pattern_view_size -->
+    <dimen name="biometric_auth_pattern_view_size">248dp</dimen>
+    <dimen name="biometric_auth_pattern_view_max_size">348dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-sw410dp/dimens.xml b/packages/SystemUI/res/values-sw410dp/dimens.xml
index 7da47e5..ff6e005 100644
--- a/packages/SystemUI/res/values-sw410dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw410dp/dimens.xml
@@ -27,4 +27,6 @@
     <dimen name="global_actions_grid_item_side_margin">12dp</dimen>
     <dimen name="global_actions_grid_item_height">72dp</dimen>
 
+    <!-- Biometric Auth pattern view size, better to align keyguard_security_width -->
+    <dimen name="biometric_auth_pattern_view_size">348dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values-sw600dp-land/styles.xml b/packages/SystemUI/res/values-sw600dp-land/styles.xml
index 8148d3d..c535c64 100644
--- a/packages/SystemUI/res/values-sw600dp-land/styles.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/styles.xml
@@ -18,10 +18,10 @@
 
     <style name="AuthCredentialPatternContainerStyle">
         <item name="android:gravity">center</item>
-        <item name="android:maxHeight">420dp</item>
-        <item name="android:maxWidth">420dp</item>
-        <item name="android:minHeight">200dp</item>
-        <item name="android:minWidth">200dp</item>
+        <item name="android:maxHeight">@dimen/biometric_auth_pattern_view_max_size</item>
+        <item name="android:maxWidth">@dimen/biometric_auth_pattern_view_max_size</item>
+        <item name="android:minHeight">@dimen/biometric_auth_pattern_view_size</item>
+        <item name="android:minWidth">@dimen/biometric_auth_pattern_view_size</item>
         <item name="android:paddingHorizontal">120dp</item>
         <item name="android:paddingVertical">40dp</item>
     </style>
diff --git a/packages/SystemUI/res/values-sw600dp-port/styles.xml b/packages/SystemUI/res/values-sw600dp-port/styles.xml
index 771de08..32eefa7 100644
--- a/packages/SystemUI/res/values-sw600dp-port/styles.xml
+++ b/packages/SystemUI/res/values-sw600dp-port/styles.xml
@@ -26,10 +26,10 @@
 
     <style name="AuthCredentialPatternContainerStyle">
         <item name="android:gravity">center</item>
-        <item name="android:maxHeight">420dp</item>
-        <item name="android:maxWidth">420dp</item>
-        <item name="android:minHeight">200dp</item>
-        <item name="android:minWidth">200dp</item>
+        <item name="android:maxHeight">@dimen/biometric_auth_pattern_view_max_size</item>
+        <item name="android:maxWidth">@dimen/biometric_auth_pattern_view_max_size</item>
+        <item name="android:minHeight">@dimen/biometric_auth_pattern_view_size</item>
+        <item name="android:minWidth">@dimen/biometric_auth_pattern_view_size</item>
         <item name="android:paddingHorizontal">180dp</item>
         <item name="android:paddingVertical">80dp</item>
     </style>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 599bf30..9bc0dde 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -92,4 +92,6 @@
     <dimen name="lockscreen_shade_status_bar_transition_distance">@dimen/lockscreen_shade_full_transition_distance</dimen>
     <dimen name="lockscreen_shade_keyguard_transition_distance">@dimen/lockscreen_shade_media_transition_distance</dimen>
 
+    <!-- Biometric Auth pattern view size, better to align keyguard_security_width -->
+    <dimen name="biometric_auth_pattern_view_size">348dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values-sw720dp-land/styles.xml b/packages/SystemUI/res/values-sw720dp-land/styles.xml
index f9ed67d..6a70ebd 100644
--- a/packages/SystemUI/res/values-sw720dp-land/styles.xml
+++ b/packages/SystemUI/res/values-sw720dp-land/styles.xml
@@ -18,10 +18,10 @@
 
     <style name="AuthCredentialPatternContainerStyle">
         <item name="android:gravity">center</item>
-        <item name="android:maxHeight">420dp</item>
-        <item name="android:maxWidth">420dp</item>
-        <item name="android:minHeight">200dp</item>
-        <item name="android:minWidth">200dp</item>
+        <item name="android:maxHeight">@dimen/biometric_auth_pattern_view_max_size</item>
+        <item name="android:maxWidth">@dimen/biometric_auth_pattern_view_max_size</item>
+        <item name="android:minHeight">@dimen/biometric_auth_pattern_view_size</item>
+        <item name="android:minWidth">@dimen/biometric_auth_pattern_view_size</item>
         <item name="android:paddingHorizontal">120dp</item>
         <item name="android:paddingVertical">40dp</item>
     </style>
diff --git a/packages/SystemUI/res/values-sw720dp-port/styles.xml b/packages/SystemUI/res/values-sw720dp-port/styles.xml
index 78d299c..0a46e08 100644
--- a/packages/SystemUI/res/values-sw720dp-port/styles.xml
+++ b/packages/SystemUI/res/values-sw720dp-port/styles.xml
@@ -26,10 +26,10 @@
 
     <style name="AuthCredentialPatternContainerStyle">
         <item name="android:gravity">center</item>
-        <item name="android:maxHeight">420dp</item>
-        <item name="android:maxWidth">420dp</item>
-        <item name="android:minHeight">200dp</item>
-        <item name="android:minWidth">200dp</item>
+        <item name="android:maxHeight">@dimen/biometric_auth_pattern_view_max_size</item>
+        <item name="android:maxWidth">@dimen/biometric_auth_pattern_view_max_size</item>
+        <item name="android:minHeight">@dimen/biometric_auth_pattern_view_size</item>
+        <item name="android:minWidth">@dimen/biometric_auth_pattern_view_size</item>
         <item name="android:paddingHorizontal">240dp</item>
         <item name="android:paddingVertical">120dp</item>
     </style>
diff --git a/packages/SystemUI/res/values-sw720dp/dimens.xml b/packages/SystemUI/res/values-sw720dp/dimens.xml
index 0705017..927059a 100644
--- a/packages/SystemUI/res/values-sw720dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp/dimens.xml
@@ -22,5 +22,8 @@
     <dimen name="controls_padding_horizontal">75dp</dimen>
 
     <dimen name="large_screen_shade_header_height">56dp</dimen>
+
+    <!-- Biometric Auth pattern view size, better to align keyguard_security_width -->
+    <dimen name="biometric_auth_pattern_view_size">348dp</dimen>
 </resources>
 
diff --git a/packages/SystemUI/res/values-sw800dp/dimens.xml b/packages/SystemUI/res/values-sw800dp/dimens.xml
new file mode 100644
index 0000000..0d82217
--- /dev/null
+++ b/packages/SystemUI/res/values-sw800dp/dimens.xml
@@ -0,0 +1,24 @@
+<?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.
+  -->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources>
+
+    <!-- Biometric Auth pattern view size, better to align keyguard_security_width -->
+    <dimen name="biometric_auth_pattern_view_size">348dp</dimen>
+</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 077ef0f..e8a5e7e 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -668,6 +668,16 @@
         <item>17</item> <!-- WAKE_REASON_BIOMETRIC -->
     </integer-array>
 
+    <!-- Whether to support posture listening for face auth, default is 0(DEVICE_POSTURE_UNKNOWN)
+         means systemui will try listening on all postures.
+         0 : DEVICE_POSTURE_UNKNOWN
+         1 : DEVICE_POSTURE_CLOSED
+         2 : DEVICE_POSTURE_HALF_OPENED
+         3 : DEVICE_POSTURE_OPENED
+         4 : DEVICE_POSTURE_FLIPPED
+    -->
+    <integer name="config_face_auth_supported_posture">0</integer>
+
     <!-- Whether the communal service should be enabled -->
     <bool name="config_communalServiceEnabled">false</bool>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 6841bf8..f5b25f3 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -334,15 +334,22 @@
     <dimen name="overlay_action_chip_spacing">8dp</dimen>
     <dimen name="overlay_action_chip_text_size">14sp</dimen>
     <dimen name="overlay_offset_x">16dp</dimen>
+    <!-- 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_bg_protection_height">242dp</dimen>
     <dimen name="overlay_action_container_corner_radius">18dp</dimen>
     <dimen name="overlay_action_container_padding_vertical">4dp</dimen>
     <dimen name="overlay_action_container_padding_right">8dp</dimen>
+    <dimen name="overlay_action_container_padding_end">8dp</dimen>
     <dimen name="overlay_dismiss_button_tappable_size">48dp</dimen>
     <dimen name="overlay_dismiss_button_margin">8dp</dimen>
+    <!-- must be kept aligned with overlay_border_width_neg, below;
+         overlay_border_width = overlay_border_width_neg * -1 -->
     <dimen name="overlay_border_width">4dp</dimen>
-    <!-- need a negative margin for some of the constraints. should be overlay_border_width * -1 -->
+    <!-- some constraints use a negative margin. must be aligned with overlay_border_width, above;
+         overlay_border_width_neg = overlay_border_width * -1 -->
     <dimen name="overlay_border_width_neg">-4dp</dimen>
 
     <dimen name="clipboard_preview_size">@dimen/overlay_x_scale</dimen>
@@ -966,6 +973,10 @@
     <!-- Biometric Auth Credential values -->
     <dimen name="biometric_auth_icon_size">48dp</dimen>
 
+    <!-- Biometric Auth pattern view size, better to align keyguard_security_width -->
+    <dimen name="biometric_auth_pattern_view_size">348dp</dimen>
+    <dimen name="biometric_auth_pattern_view_max_size">348dp</dimen>
+
     <!-- Starting text size in sp of batteryLevel for wireless charging animation -->
     <item name="wireless_charging_anim_battery_level_text_size_start" format="float" type="dimen">
         0
@@ -1030,8 +1041,6 @@
 
     <dimen name="ongoing_appops_dialog_side_padding">16dp</dimen>
 
-    <!-- Size of the RAT type for CellularTile -->
-
     <!-- Size of media cards in the QSPanel carousel -->
     <dimen name="qs_media_padding">16dp</dimen>
     <dimen name="qs_media_album_radius">14dp</dimen>
@@ -1623,6 +1632,8 @@
     <dimen name="dream_overlay_status_bar_ambient_text_shadow_dx">0.5dp</dimen>
     <dimen name="dream_overlay_status_bar_ambient_text_shadow_dy">0.5dp</dimen>
     <dimen name="dream_overlay_status_bar_ambient_text_shadow_radius">2dp</dimen>
+    <dimen name="dream_overlay_icon_inset_dimen">0dp</dimen>
+    <dimen name="dream_overlay_status_bar_marginTop">22dp</dimen>
 
     <!-- Default device corner radius, used for assist UI -->
     <dimen name="config_rounded_mask_size">0px</dimen>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index b11b6d6..9846fc2 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -251,11 +251,12 @@
 
     <style name="AuthCredentialPatternContainerStyle">
         <item name="android:gravity">center</item>
-        <item name="android:maxHeight">420dp</item>
-        <item name="android:maxWidth">420dp</item>
-        <item name="android:minHeight">200dp</item>
-        <item name="android:minWidth">200dp</item>
-        <item name="android:padding">20dp</item>
+        <item name="android:maxHeight">@dimen/biometric_auth_pattern_view_max_size</item>
+        <item name="android:maxWidth">@dimen/biometric_auth_pattern_view_max_size</item>
+        <item name="android:minHeight">@dimen/biometric_auth_pattern_view_size</item>
+        <item name="android:minWidth">@dimen/biometric_auth_pattern_view_size</item>
+        <item name="android:paddingHorizontal">32dp</item>
+        <item name="android:paddingVertical">20dp</item>
     </style>
 
     <style name="AuthCredentialPinPasswordContainerStyle">
diff --git a/packages/SystemUI/res/xml/qs_header.xml b/packages/SystemUI/res/xml/qs_header.xml
index eca2b2a..d97031f 100644
--- a/packages/SystemUI/res/xml/qs_header.xml
+++ b/packages/SystemUI/res/xml/qs_header.xml
@@ -56,13 +56,9 @@
         <Layout
             android:layout_width="wrap_content"
             android:layout_height="@dimen/new_qs_header_non_clickable_element_height"
-            app:layout_constrainedWidth="true"
             app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintEnd_toStartOf="@id/space"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintTop_toBottomOf="@id/carrier_group"
-            app:layout_constraintHorizontal_bias="0"
-            app:layout_constraintHorizontal_chainStyle="spread_inside"
         />
     </Constraint>
 
@@ -87,39 +83,27 @@
     <Constraint
         android:id="@+id/statusIcons">
         <Layout
-            android:layout_width="wrap_content"
+            android:layout_width="0dp"
             android:layout_height="@dimen/new_qs_header_non_clickable_element_height"
-            app:layout_constraintStart_toEndOf="@id/space"
+            app:layout_constraintWidth_default="wrap"
+            app:layout_constraintStart_toEndOf="@id/date"
             app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon"
             app:layout_constraintTop_toTopOf="@id/date"
-            app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintHorizontal_bias="1"
+            app:layout_constraintBottom_toBottomOf="@id/date"
             />
     </Constraint>
 
     <Constraint
         android:id="@+id/batteryRemainingIcon">
         <Layout
-            android:layout_width="wrap_content"
+            android:layout_width="0dp"
             android:layout_height="@dimen/new_qs_header_non_clickable_element_height"
+            app:layout_constraintWidth_default="wrap"
             app:layout_constraintHeight_min="@dimen/new_qs_header_non_clickable_element_height"
-            app:layout_constraintStart_toEndOf="@id/statusIcons"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintTop_toTopOf="@id/date"
-            app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintHorizontal_bias="1"
-            app:layout_constraintHorizontal_chainStyle="spread_inside"
+            app:layout_constraintBottom_toBottomOf="@id/date"
             />
     </Constraint>
 
-
-    <Constraint
-        android:id="@id/space">
-        <Layout
-            android:layout_width="0dp"
-            android:layout_height="0dp"
-            app:layout_constraintStart_toEndOf="@id/date"
-            app:layout_constraintEnd_toStartOf="@id/statusIcons"
-            />
-    </Constraint>
 </ConstraintSet>
\ No newline at end of file
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 25d2721..9b73cc3 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
@@ -48,48 +48,28 @@
         val drawableInsetSize: Int
         try {
             val keyShadowBlur =
-                attributes.getDimensionPixelSize(R.styleable.DoubleShadowTextView_keyShadowBlur, 0)
+                attributes.getDimension(R.styleable.DoubleShadowTextView_keyShadowBlur, 0f)
             val keyShadowOffsetX =
-                attributes.getDimensionPixelSize(
-                    R.styleable.DoubleShadowTextView_keyShadowOffsetX,
-                    0
-                )
+                attributes.getDimension(R.styleable.DoubleShadowTextView_keyShadowOffsetX, 0f)
             val keyShadowOffsetY =
-                attributes.getDimensionPixelSize(
-                    R.styleable.DoubleShadowTextView_keyShadowOffsetY,
-                    0
-                )
+                attributes.getDimension(R.styleable.DoubleShadowTextView_keyShadowOffsetY, 0f)
             val keyShadowAlpha =
                 attributes.getFloat(R.styleable.DoubleShadowTextView_keyShadowAlpha, 0f)
             mKeyShadowInfo =
-                ShadowInfo(
-                    keyShadowBlur.toFloat(),
-                    keyShadowOffsetX.toFloat(),
-                    keyShadowOffsetY.toFloat(),
-                    keyShadowAlpha
-                )
+                ShadowInfo(keyShadowBlur, keyShadowOffsetX, keyShadowOffsetY, keyShadowAlpha)
             val ambientShadowBlur =
-                attributes.getDimensionPixelSize(
-                    R.styleable.DoubleShadowTextView_ambientShadowBlur,
-                    0
-                )
+                attributes.getDimension(R.styleable.DoubleShadowTextView_ambientShadowBlur, 0f)
             val ambientShadowOffsetX =
-                attributes.getDimensionPixelSize(
-                    R.styleable.DoubleShadowTextView_ambientShadowOffsetX,
-                    0
-                )
+                attributes.getDimension(R.styleable.DoubleShadowTextView_ambientShadowOffsetX, 0f)
             val ambientShadowOffsetY =
-                attributes.getDimensionPixelSize(
-                    R.styleable.DoubleShadowTextView_ambientShadowOffsetY,
-                    0
-                )
+                attributes.getDimension(R.styleable.DoubleShadowTextView_ambientShadowOffsetY, 0f)
             val ambientShadowAlpha =
                 attributes.getFloat(R.styleable.DoubleShadowTextView_ambientShadowAlpha, 0f)
             mAmbientShadowInfo =
                 ShadowInfo(
-                    ambientShadowBlur.toFloat(),
-                    ambientShadowOffsetX.toFloat(),
-                    ambientShadowOffsetY.toFloat(),
+                    ambientShadowBlur,
+                    ambientShadowOffsetX,
+                    ambientShadowOffsetY,
                     ambientShadowAlpha
                 )
             drawableSize =
diff --git a/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt b/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
index 5bb9367..e0cf7b6 100644
--- a/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
+++ b/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
@@ -50,6 +50,7 @@
 import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_VISIBILITY_CHANGED
 import com.android.keyguard.InternalFaceAuthReasons.NON_STRONG_BIOMETRIC_ALLOWED_CHANGED
 import com.android.keyguard.InternalFaceAuthReasons.OCCLUDING_APP_REQUESTED
+import com.android.keyguard.InternalFaceAuthReasons.POSTURE_CHANGED
 import com.android.keyguard.InternalFaceAuthReasons.PRIMARY_BOUNCER_SHOWN
 import com.android.keyguard.InternalFaceAuthReasons.PRIMARY_BOUNCER_SHOWN_OR_WILL_BE_SHOWN
 import com.android.keyguard.InternalFaceAuthReasons.RETRY_AFTER_HW_UNAVAILABLE
@@ -126,6 +127,7 @@
     const val STRONG_AUTH_ALLOWED_CHANGED = "Face auth stopped because strong auth allowed changed"
     const val NON_STRONG_BIOMETRIC_ALLOWED_CHANGED =
         "Face auth stopped because non strong biometric allowed changed"
+    const val POSTURE_CHANGED = "Face auth started/stopped due to device posture changed."
 }
 
 /**
@@ -173,6 +175,7 @@
             return PowerManager.wakeReasonToString(extraInfo)
         }
     },
+    @UiEvent(doc = POSTURE_CHANGED) FACE_AUTH_UPDATED_POSTURE_CHANGED(1265, POSTURE_CHANGED),
     @Deprecated(
         "Not a face auth trigger.",
         ReplaceWith(
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
index deead19..1a06b5f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
@@ -39,6 +39,7 @@
     var keyguardGoingAway: Boolean = false,
     var listeningForFaceAssistant: Boolean = false,
     var occludingAppRequestingFaceAuth: Boolean = false,
+    val postureAllowsListening: Boolean = false,
     var primaryUser: Boolean = false,
     var secureCameraLaunched: Boolean = false,
     var supportsDetect: Boolean = false,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 8de1368..204f09e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -63,11 +63,13 @@
 import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_KEYGUARD_VISIBILITY_CHANGED;
 import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_ON_FACE_AUTHENTICATED;
 import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_ON_KEYGUARD_INIT;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_POSTURE_CHANGED;
 import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN;
 import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_STARTED_WAKING_UP;
 import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_STRONG_AUTH_CHANGED;
 import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_USER_SWITCHING;
 import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
 
 import android.annotation.AnyThread;
 import android.annotation.MainThread;
@@ -155,6 +157,7 @@
 import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.policy.DevicePostureController;
 import com.android.systemui.telephony.TelephonyListenerManager;
 import com.android.systemui.util.Assert;
 import com.android.systemui.util.settings.SecureSettings;
@@ -357,6 +360,7 @@
     private final TrustManager mTrustManager;
     private final UserManager mUserManager;
     private final DevicePolicyManager mDevicePolicyManager;
+    private final DevicePostureController mPostureController;
     private final BroadcastDispatcher mBroadcastDispatcher;
     private final SecureSettings mSecureSettings;
     private final InteractionJankMonitor mInteractionJankMonitor;
@@ -374,6 +378,9 @@
     private final FaceManager mFaceManager;
     private final LockPatternUtils mLockPatternUtils;
     private final boolean mWakeOnFingerprintAcquiredStart;
+    @VisibleForTesting
+    @DevicePostureController.DevicePostureInt
+    protected int mConfigFaceAuthSupportedPosture;
 
     private KeyguardBypassController mKeyguardBypassController;
     private List<SubscriptionInfo> mSubscriptionInfo;
@@ -384,6 +391,7 @@
     private boolean mLogoutEnabled;
     private boolean mIsFaceEnrolled;
     private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    private int mPostureState = DEVICE_POSTURE_UNKNOWN;
     private FingerprintInteractiveToAuthProvider mFingerprintInteractiveToAuthProvider;
 
     /**
@@ -712,8 +720,11 @@
      */
     public void setKeyguardGoingAway(boolean goingAway) {
         mKeyguardGoingAway = goingAway;
-        // This is set specifically to stop face authentication from running.
-        updateBiometricListeningState(BIOMETRIC_ACTION_STOP, FACE_AUTH_STOPPED_KEYGUARD_GOING_AWAY);
+        if (mKeyguardGoingAway) {
+            updateFaceListeningState(BIOMETRIC_ACTION_STOP,
+                    FACE_AUTH_STOPPED_KEYGUARD_GOING_AWAY);
+        }
+        updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
     }
 
     /**
@@ -1792,6 +1803,17 @@
     };
 
     @VisibleForTesting
+    final DevicePostureController.Callback mPostureCallback =
+            new DevicePostureController.Callback() {
+                @Override
+                public void onPostureChanged(int posture) {
+                    mPostureState = posture;
+                    updateFaceListeningState(BIOMETRIC_ACTION_UPDATE,
+                            FACE_AUTH_UPDATED_POSTURE_CHANGED);
+                }
+            };
+
+    @VisibleForTesting
     CancellationSignal mFingerprintCancelSignal;
     @VisibleForTesting
     CancellationSignal mFaceCancelSignal;
@@ -1951,9 +1973,9 @@
                 cb.onFinishedGoingToSleep(arg1);
             }
         }
-        // This is set specifically to stop face authentication from running.
-        updateBiometricListeningState(BIOMETRIC_ACTION_STOP,
+        updateFaceListeningState(BIOMETRIC_ACTION_STOP,
                 FACE_AUTH_STOPPED_FINISHED_GOING_TO_SLEEP);
+        updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
     }
 
     private void handleScreenTurnedOff() {
@@ -2057,6 +2079,7 @@
             @Nullable FingerprintManager fingerprintManager,
             @Nullable BiometricManager biometricManager,
             FaceWakeUpTriggersConfig faceWakeUpTriggersConfig,
+            DevicePostureController devicePostureController,
             Optional<FingerprintInteractiveToAuthProvider> interactiveToAuthProvider) {
         mContext = context;
         mSubscriptionManager = subscriptionManager;
@@ -2086,6 +2109,7 @@
         mDreamManager = dreamManager;
         mTelephonyManager = telephonyManager;
         mDevicePolicyManager = devicePolicyManager;
+        mPostureController = devicePostureController;
         mPackageManager = packageManager;
         mFpm = fingerprintManager;
         mFaceManager = faceManager;
@@ -2097,6 +2121,8 @@
                         R.array.config_face_acquire_device_entry_ignorelist))
                 .boxed()
                 .collect(Collectors.toSet());
+        mConfigFaceAuthSupportedPosture = mContext.getResources().getInteger(
+                R.integer.config_face_auth_supported_posture);
         mFaceWakeUpTriggersConfig = faceWakeUpTriggersConfig;
 
         mHandler = new Handler(mainLooper) {
@@ -2287,6 +2313,9 @@
                         FACE_AUTH_TRIGGERED_ENROLLMENTS_CHANGED));
             }
         });
+        if (mConfigFaceAuthSupportedPosture != DEVICE_POSTURE_UNKNOWN) {
+            mPostureController.addCallback(mPostureCallback);
+        }
         updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, FACE_AUTH_UPDATED_ON_KEYGUARD_INIT);
 
         TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
@@ -2719,7 +2748,7 @@
                     mFingerprintInteractiveToAuthProvider != null &&
                             mFingerprintInteractiveToAuthProvider.isEnabled(getCurrentUser());
             shouldListenSideFpsState =
-                    interactiveToAuthEnabled ? isDeviceInteractive() : true;
+                    interactiveToAuthEnabled ? isDeviceInteractive() && !mGoingToSleep : true;
         }
 
         boolean shouldListen = shouldListenKeyguardState && shouldListenUserState
@@ -2731,7 +2760,7 @@
                     user,
                     shouldListen,
                     biometricEnabledForUser,
-                        mPrimaryBouncerIsOrWillBeShowing,
+                    mPrimaryBouncerIsOrWillBeShowing,
                     userCanSkipBouncer,
                     mCredentialAttempted,
                     mDeviceInteractive,
@@ -2791,6 +2820,9 @@
         final boolean biometricEnabledForUser = mBiometricEnabledForUser.get(user);
         final boolean shouldListenForFaceAssistant = shouldListenForFaceAssistant();
         final boolean isUdfpsFingerDown = mAuthController.isUdfpsFingerDown();
+        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.
@@ -2807,7 +2839,8 @@
                 && faceAuthAllowedOrDetectionIsNeeded && mIsPrimaryUser
                 && (!mSecureCameraLaunched || mOccludingAppRequestingFace)
                 && faceAndFpNotAuthenticated
-                && !mGoingToSleep;
+                && !mGoingToSleep
+                && isPostureAllowedForFaceAuth;
 
         // Aggregate relevant fields for debug logging.
         logListenerModelData(
@@ -2827,6 +2860,7 @@
                     mKeyguardGoingAway,
                     shouldListenForFaceAssistant,
                     mOccludingAppRequestingFace,
+                    isPostureAllowedForFaceAuth,
                     mIsPrimaryUser,
                     mSecureCameraLaunched,
                     supportsDetect,
@@ -2912,7 +2946,7 @@
                 getKeyguardSessionId(),
                 faceAuthUiEvent.getExtraInfo()
         );
-
+        mLogger.logFaceUnlockPossible(unlockPossible);
         if (unlockPossible) {
             mFaceCancelSignal = new CancellationSignal();
 
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index 21d3b24..5b42455 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -132,6 +132,12 @@
         logBuffer.log(TAG, DEBUG, { int1 = faceRunningState }, { "faceRunningState: $int1" })
     }
 
+    fun logFaceUnlockPossible(isFaceUnlockPossible: Boolean) {
+        logBuffer.log(TAG, DEBUG,
+                { bool1 = isFaceUnlockPossible },
+                {"isUnlockWithFacePossible: $bool1"})
+    }
+
     fun logFingerprintAuthForWrongUser(authUserId: Int) {
         logBuffer.log(TAG, DEBUG,
                 { int1 = authUserId },
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
index 0fc9ef9..632fcdc 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
@@ -22,8 +22,6 @@
 import android.os.HandlerThread;
 import android.util.Log;
 
-import androidx.annotation.Nullable;
-
 import com.android.systemui.dagger.GlobalRootComponent;
 import com.android.systemui.dagger.SysUIComponent;
 import com.android.systemui.dagger.WMComponent;
@@ -55,7 +53,6 @@
         mContext = context;
     }
 
-    @Nullable
     protected abstract GlobalRootComponent.Builder getGlobalRootComponentBuilder();
 
     /**
@@ -72,11 +69,6 @@
      * Starts the initialization process. This stands up the Dagger graph.
      */
     public void init(boolean fromTest) throws ExecutionException, InterruptedException {
-        GlobalRootComponent.Builder globalBuilder = getGlobalRootComponentBuilder();
-        if (globalBuilder == null) {
-            return;
-        }
-
         mRootComponent = getGlobalRootComponentBuilder()
                 .context(mContext)
                 .instrumentationTest(fromTest)
@@ -127,7 +119,6 @@
                     .setBackAnimation(Optional.ofNullable(null))
                     .setDesktopMode(Optional.ofNullable(null));
         }
-
         mSysUIComponent = builder.build();
         if (initializeComponents) {
             mSysUIComponent.init();
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt b/packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt
index 55c095b..8aa3040 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui
 
-import android.app.Application
 import android.content.Context
 import com.android.systemui.dagger.DaggerReferenceGlobalRootComponent
 import com.android.systemui.dagger.GlobalRootComponent
@@ -25,17 +24,7 @@
  * {@link SystemUIInitializer} that stands up AOSP SystemUI.
  */
 class SystemUIInitializerImpl(context: Context) : SystemUIInitializer(context) {
-
-    override fun getGlobalRootComponentBuilder(): GlobalRootComponent.Builder? {
-        return when (Application.getProcessName()) {
-            SCREENSHOT_CROSS_PROFILE_PROCESS -> null
-            else -> DaggerReferenceGlobalRootComponent.builder()
-        }
-    }
-
-    companion object {
-        private const val SYSTEMUI_PROCESS = "com.android.systemui"
-        private const val SCREENSHOT_CROSS_PROFILE_PROCESS =
-                "$SYSTEMUI_PROCESS:screenshot_cross_profile"
+    override fun getGlobalRootComponentBuilder(): GlobalRootComponent.Builder {
+        return DaggerReferenceGlobalRootComponent.builder()
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetector.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetector.kt
index 8572242..682d38a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetector.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetector.kt
@@ -18,6 +18,7 @@
 
 import android.graphics.Point
 import android.graphics.Rect
+import androidx.annotation.VisibleForTesting
 import com.android.systemui.dagger.SysUISingleton
 import kotlin.math.cos
 import kotlin.math.pow
@@ -50,7 +51,8 @@
         return result <= 1
     }
 
-    private fun calculateSensorPoints(sensorBounds: Rect): List<Point> {
+    @VisibleForTesting
+    fun calculateSensorPoints(sensorBounds: Rect): List<Point> {
         val sensorX = sensorBounds.centerX()
         val sensorY = sensorBounds.centerY()
         val cornerOffset: Int = sensorBounds.width() / 4
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
index e8e1f2e..e9ac840 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
@@ -176,7 +176,8 @@
     private @Classifier.InteractionType int mPriorInteractionType = Classifier.GENERIC;
 
     @Inject
-    public BrightLineFalsingManager(FalsingDataProvider falsingDataProvider,
+    public BrightLineFalsingManager(
+            FalsingDataProvider falsingDataProvider,
             MetricsLogger metricsLogger,
             @Named(BRIGHT_LINE_GESTURE_CLASSIFERS) Set<FalsingClassifier> classifiers,
             SingleTapClassifier singleTapClassifier, LongTapClassifier longTapClassifier,
@@ -399,7 +400,9 @@
                 || mDataProvider.isJustUnlockedWithFace()
                 || mDataProvider.isDocked()
                 || mAccessibilityManager.isTouchExplorationEnabled()
-                || mDataProvider.isA11yAction();
+                || mDataProvider.isA11yAction()
+                || (mFeatureFlags.isEnabled(Flags.FALSING_OFF_FOR_UNFOLDED)
+                    && !mDataProvider.isFolded());
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
index 09ebeea..5f347c1 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.classifier;
 
+import android.hardware.devicestate.DeviceStateManager.FoldStateListener;
 import android.util.DisplayMetrics;
 import android.view.MotionEvent;
 import android.view.MotionEvent.PointerCoords;
@@ -42,6 +43,7 @@
     private final int mWidthPixels;
     private final int mHeightPixels;
     private BatteryController mBatteryController;
+    private final FoldStateListener mFoldStateListener;
     private final DockManager mDockManager;
     private final float mXdpi;
     private final float mYdpi;
@@ -65,12 +67,14 @@
     public FalsingDataProvider(
             DisplayMetrics displayMetrics,
             BatteryController batteryController,
+            FoldStateListener foldStateListener,
             DockManager dockManager) {
         mXdpi = displayMetrics.xdpi;
         mYdpi = displayMetrics.ydpi;
         mWidthPixels = displayMetrics.widthPixels;
         mHeightPixels = displayMetrics.heightPixels;
         mBatteryController = batteryController;
+        mFoldStateListener = foldStateListener;
         mDockManager = dockManager;
 
         FalsingClassifier.logInfo("xdpi, ydpi: " + getXdpi() + ", " + getYdpi());
@@ -376,6 +380,10 @@
         return mBatteryController.isWirelessCharging() || mDockManager.isDocked();
     }
 
+    public boolean isFolded() {
+        return Boolean.TRUE.equals(mFoldStateListener.getFolded());
+    }
+
     /** Implement to be alerted abotu the beginning and ending of falsing tracking. */
     public interface SessionListener {
         /** Called when the lock screen is shown and falsing-tracking begins. */
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
index f244cb0..96bce4c 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
@@ -26,6 +27,9 @@
 import androidx.constraintlayout.widget.ConstraintLayout;
 
 import com.android.systemui.R;
+import com.android.systemui.shared.shadow.DoubleShadowIconDrawable;
+import com.android.systemui.shared.shadow.DoubleShadowTextHelper.ShadowInfo;
+import com.android.systemui.statusbar.AlphaOptimizedImageView;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -60,8 +64,15 @@
     public static final int STATUS_ICON_PRIORITY_MODE_ON = 6;
 
     private final Map<Integer, View> mStatusIcons = new HashMap<>();
+    private Context mContext;
     private ViewGroup mSystemStatusViewGroup;
     private ViewGroup mExtraSystemStatusViewGroup;
+    private ShadowInfo mKeyShadowInfo;
+    private ShadowInfo mAmbientShadowInfo;
+    private int mDrawableSize;
+    private int mDrawableInsetSize;
+    private static final float KEY_SHADOW_ALPHA = 0.35f;
+    private static final float AMBIENT_SHADOW_ALPHA = 0.4f;
 
     public DreamOverlayStatusBarView(Context context) {
         this(context, null);
@@ -73,6 +84,7 @@
 
     public DreamOverlayStatusBarView(Context context, AttributeSet attrs, int defStyleAttr) {
         this(context, attrs, defStyleAttr, 0);
+        mContext = context;
     }
 
     public DreamOverlayStatusBarView(
@@ -80,14 +92,36 @@
         super(context, attrs, defStyleAttr, defStyleRes);
     }
 
+
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
 
+        mKeyShadowInfo = createShadowInfo(
+            R.dimen.dream_overlay_status_bar_key_text_shadow_radius,
+            R.dimen.dream_overlay_status_bar_key_text_shadow_dx,
+            R.dimen.dream_overlay_status_bar_key_text_shadow_dy,
+            KEY_SHADOW_ALPHA
+        );
+
+        mAmbientShadowInfo = createShadowInfo(
+            R.dimen.dream_overlay_status_bar_ambient_text_shadow_radius,
+            R.dimen.dream_overlay_status_bar_ambient_text_shadow_dx,
+            R.dimen.dream_overlay_status_bar_ambient_text_shadow_dy,
+            AMBIENT_SHADOW_ALPHA
+        );
+
+        mDrawableSize = mContext
+                        .getResources()
+                        .getDimensionPixelSize(R.dimen.dream_overlay_status_bar_icon_size);
+        mDrawableInsetSize = mContext
+                             .getResources()
+                             .getDimensionPixelSize(R.dimen.dream_overlay_icon_inset_dimen);
+
         mStatusIcons.put(STATUS_ICON_WIFI_UNAVAILABLE,
-                fetchStatusIconForResId(R.id.dream_overlay_wifi_status));
+                addDoubleShadow(fetchStatusIconForResId(R.id.dream_overlay_wifi_status)));
         mStatusIcons.put(STATUS_ICON_ALARM_SET,
-                fetchStatusIconForResId(R.id.dream_overlay_alarm_set));
+                addDoubleShadow(fetchStatusIconForResId(R.id.dream_overlay_alarm_set)));
         mStatusIcons.put(STATUS_ICON_CAMERA_DISABLED,
                 fetchStatusIconForResId(R.id.dream_overlay_camera_off));
         mStatusIcons.put(STATUS_ICON_MIC_DISABLED,
@@ -97,7 +131,7 @@
         mStatusIcons.put(STATUS_ICON_NOTIFICATIONS,
                 fetchStatusIconForResId(R.id.dream_overlay_notification_indicator));
         mStatusIcons.put(STATUS_ICON_PRIORITY_MODE_ON,
-                fetchStatusIconForResId(R.id.dream_overlay_priority_mode));
+                addDoubleShadow(fetchStatusIconForResId(R.id.dream_overlay_priority_mode)));
 
         mSystemStatusViewGroup = findViewById(R.id.dream_overlay_system_status);
         mExtraSystemStatusViewGroup = findViewById(R.id.dream_overlay_extra_items);
@@ -137,4 +171,34 @@
         }
         return false;
     }
+
+    private View addDoubleShadow(View icon) {
+        if (icon instanceof AlphaOptimizedImageView) {
+            AlphaOptimizedImageView i = (AlphaOptimizedImageView) icon;
+            Drawable drawableIcon = i.getDrawable();
+            i.setImageDrawable(new DoubleShadowIconDrawable(
+                    mKeyShadowInfo,
+                    mAmbientShadowInfo,
+                    drawableIcon,
+                    mDrawableSize,
+                    mDrawableInsetSize
+            ));
+        }
+        return icon;
+    }
+
+    private ShadowInfo createShadowInfo(int blurId, int offsetXId, int offsetYId, float alpha) {
+        return new ShadowInfo(
+            fetchDimensionForResId(blurId),
+            fetchDimensionForResId(offsetXId),
+            fetchDimensionForResId(offsetYId),
+            alpha
+        );
+    }
+
+    private Float fetchDimensionForResId(int resId) {
+        return mContext
+               .getResources()
+               .getDimension(resId);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index d015b15..c9a2a0b 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -114,8 +114,6 @@
     // ** Flag retired **
     // public static final BooleanFlag KEYGUARD_LAYOUT =
     //         new BooleanFlag(200, true);
-    // TODO(b/254512713): Tracking Bug
-    @JvmField val LOCKSCREEN_ANIMATIONS = releasedFlag(201, "lockscreen_animations")
 
     // TODO(b/254512750): Tracking Bug
     val NEW_UNLOCK_SWIPE_ANIMATION = releasedFlag(202, "new_unlock_swipe_animation")
@@ -209,6 +207,9 @@
     val AUTO_PIN_CONFIRMATION =
         unreleasedFlag(224, "auto_pin_confirmation", "auto_pin_confirmation")
 
+    // TODO(b/262859270): Tracking Bug
+    @JvmField val FALSING_OFF_FOR_UNFOLDED = releasedFlag(225, "falsing_off_for_unfolded")
+
     // 300 - power menu
     // TODO(b/254512600): Tracking Bug
     @JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
@@ -262,10 +263,11 @@
 
     // TODO(b/256614751): Tracking Bug
     val NEW_STATUS_BAR_MOBILE_ICONS_BACKEND =
-        unreleasedFlag(608, "new_status_bar_mobile_icons_backend")
+        unreleasedFlag(608, "new_status_bar_mobile_icons_backend", teamfood = true)
 
     // TODO(b/256613548): Tracking Bug
-    val NEW_STATUS_BAR_WIFI_ICON_BACKEND = unreleasedFlag(609, "new_status_bar_wifi_icon_backend")
+    val NEW_STATUS_BAR_WIFI_ICON_BACKEND =
+        unreleasedFlag(609, "new_status_bar_wifi_icon_backend", teamfood = true)
 
     // TODO(b/256623670): Tracking Bug
     @JvmField
@@ -304,7 +306,7 @@
 
     // 900 - media
     // TODO(b/254512697): Tracking Bug
-    val MEDIA_TAP_TO_TRANSFER = unreleasedFlag(900, "media_tap_to_transfer", teamfood = true)
+    val MEDIA_TAP_TO_TRANSFER = releasedFlag(900, "media_tap_to_transfer")
 
     // TODO(b/254512502): Tracking Bug
     val MEDIA_SESSION_ACTIONS = unreleasedFlag(901, "media_session_actions")
@@ -340,7 +342,8 @@
     // TODO(b/254512758): Tracking Bug
     @JvmField val ROUNDED_BOX_RIPPLE = releasedFlag(1002, "rounded_box_ripple")
 
-    val SHOW_LOWLIGHT_ON_DIRECT_BOOT = unreleasedFlag(1003, "show_lowlight_on_direct_boot")
+    // TODO(b/265045965): Tracking Bug
+    val SHOW_LOWLIGHT_ON_DIRECT_BOOT = releasedFlag(1003, "show_lowlight_on_direct_boot")
 
     // 1100 - windowing
     @Keep
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 8200f25..fe84ac5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1926,13 +1926,23 @@
             return;
         }
 
-        // if the keyguard is already showing, don't bother. check flags in both files
-        // to account for the hiding animation which results in a delay and discrepancy
-        // between flags
+        // If the keyguard is already showing, see if we don't need to bother re-showing it. Check
+        // flags in both files to account for the hiding animation which results in a delay and
+        // discrepancy between flags.
         if (mShowing && mKeyguardStateController.isShowing()) {
-            if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
-            resetStateLocked();
-            return;
+            if (mPM.isInteractive()) {
+                // It's already showing, and we're not trying to show it while the screen is off.
+                // We can simply reset all of the views.
+                if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
+                resetStateLocked();
+                return;
+            } else {
+                // We are trying to show the keyguard while the screen is off - this results from
+                // race conditions involving locking while unlocking. Don't short-circuit here and
+                // ensure the keyguard is fully re-shown.
+                Log.e(TAG,
+                        "doKeyguard: already showing, but re-showing since we're not interactive");
+            }
         }
 
         // In split system user mode, we never unlock system user.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
index 017b65a..ffd8a02 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
@@ -33,6 +33,7 @@
 import com.android.systemui.R;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.util.time.SystemClock;
 
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -63,6 +64,7 @@
 
     private final Context mContext;
     private final DisplayMetrics mDisplayMetrics;
+    private final SystemClock mSystemClock;
 
     @Nullable
     private final IWallpaperManager mWallpaperManagerService;
@@ -71,6 +73,9 @@
 
     private @PowerManager.WakeReason int mLastWakeReason = PowerManager.WAKE_REASON_UNKNOWN;
 
+    public static final long UNKNOWN_LAST_WAKE_TIME = -1;
+    private long mLastWakeTime = UNKNOWN_LAST_WAKE_TIME;
+
     @Nullable
     private Point mLastWakeOriginLocation = null;
 
@@ -84,10 +89,12 @@
     public WakefulnessLifecycle(
             Context context,
             @Nullable IWallpaperManager wallpaperManagerService,
+            SystemClock systemClock,
             DumpManager dumpManager) {
         mContext = context;
         mDisplayMetrics = context.getResources().getDisplayMetrics();
         mWallpaperManagerService = wallpaperManagerService;
+        mSystemClock = systemClock;
 
         dumpManager.registerDumpable(getClass().getSimpleName(), this);
     }
@@ -104,6 +111,14 @@
     }
 
     /**
+     * Returns the most recent time (in device uptimeMillis) the display woke up.
+     * Returns {@link UNKNOWN_LAST_WAKE_TIME} if there hasn't been a wakeup yet.
+     */
+    public long getLastWakeTime() {
+        return mLastWakeTime;
+    }
+
+    /**
      * Returns the most recent reason the device went to sleep up. This is one of
      * PowerManager.GO_TO_SLEEP_REASON_*.
      */
@@ -117,6 +132,7 @@
         }
         setWakefulness(WAKEFULNESS_WAKING);
         mLastWakeReason = pmWakeReason;
+        mLastWakeTime = mSystemClock.uptimeMillis();
         updateLastWakeOriginLocation();
 
         if (mWallpaperManagerService != null) {
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 a4fd087..d99af90 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
@@ -40,6 +40,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.phone.BiometricUnlockController
 import com.android.systemui.statusbar.phone.BiometricUnlockController.WakeAndUnlockMode
+import com.android.systemui.statusbar.phone.DozeParameters
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import javax.inject.Inject
 import kotlinx.coroutines.channels.awaitClose
@@ -88,6 +89,9 @@
     /** Observable for whether the bouncer is showing. */
     val isBouncerShowing: Flow<Boolean>
 
+    /** Is the always-on display available to be used? */
+    val isAodAvailable: Flow<Boolean>
+
     /**
      * Observable for whether we are in doze state.
      *
@@ -182,6 +186,7 @@
     private val keyguardStateController: KeyguardStateController,
     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
     private val dozeTransitionListener: DozeTransitionListener,
+    private val dozeParameters: DozeParameters,
     private val authController: AuthController,
     private val dreamOverlayCallbackController: DreamOverlayCallbackController,
 ) : KeyguardRepository {
@@ -220,6 +225,31 @@
             }
             .distinctUntilChanged()
 
+    override val isAodAvailable: Flow<Boolean> =
+        conflatedCallbackFlow {
+                val callback =
+                    object : DozeParameters.Callback {
+                        override fun onAlwaysOnChange() {
+                            trySendWithFailureLogging(
+                                dozeParameters.getAlwaysOn(),
+                                TAG,
+                                "updated isAodAvailable"
+                            )
+                        }
+                    }
+
+                dozeParameters.addCallback(callback)
+                // Adding the callback does not send an initial update.
+                trySendWithFailureLogging(
+                    dozeParameters.getAlwaysOn(),
+                    TAG,
+                    "initial isAodAvailable"
+                )
+
+                awaitClose { dozeParameters.removeCallback(callback) }
+            }
+            .distinctUntilChanged()
+
     override val isKeyguardOccluded: Flow<Boolean> =
         conflatedCallbackFlow {
                 val callback =
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 fd2d271..ce61f2f 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,9 +21,9 @@
 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.DozeStateModel.Companion.isDozeOff
 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
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlin.time.Duration
@@ -48,12 +48,11 @@
 
     private fun listenForDozingToLockscreen() {
         scope.launch {
-            keyguardInteractor.dozeTransitionModel
+            keyguardInteractor.wakefulnessModel
                 .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
-                .collect { pair ->
-                    val (dozeTransitionModel, lastStartedTransition) = pair
+                .collect { (wakefulnessModel, lastStartedTransition) ->
                     if (
-                        isDozeOff(dozeTransitionModel.to) &&
+                        isWakingOrStartingToWake(wakefulnessModel) &&
                             lastStartedTransition.to == KeyguardState.DOZING
                     ) {
                         keyguardTransitionRepository.startTransition(
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 553fafe..9203a9b 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
@@ -26,7 +26,10 @@
 import com.android.systemui.keyguard.shared.model.WakefulnessState
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.launch
 
 @SysUISingleton
@@ -40,7 +43,7 @@
 ) : TransitionInteractor(FromGoneTransitionInteractor::class.simpleName!!) {
 
     override fun start() {
-        listenForGoneToAod()
+        listenForGoneToAodOrDozing()
         listenForGoneToDreaming()
     }
 
@@ -56,7 +59,7 @@
                                 name,
                                 KeyguardState.GONE,
                                 KeyguardState.DREAMING,
-                                getAnimator(),
+                                getAnimator(TO_DREAMING_DURATION),
                             )
                         )
                     }
@@ -64,12 +67,18 @@
         }
     }
 
-    private fun listenForGoneToAod() {
+    private fun listenForGoneToAodOrDozing() {
         scope.launch {
             keyguardInteractor.wakefulnessModel
-                .sample(keyguardTransitionInteractor.finishedKeyguardState, ::Pair)
-                .collect { pair ->
-                    val (wakefulnessState, keyguardState) = pair
+                .sample(
+                    combine(
+                        keyguardTransitionInteractor.finishedKeyguardState,
+                        keyguardInteractor.isAodAvailable,
+                        ::Pair
+                    ),
+                    ::toTriple
+                )
+                .collect { (wakefulnessState, keyguardState, isAodAvailable) ->
                     if (
                         keyguardState == KeyguardState.GONE &&
                             wakefulnessState.state == WakefulnessState.STARTING_TO_SLEEP
@@ -78,7 +87,11 @@
                             TransitionInfo(
                                 name,
                                 KeyguardState.GONE,
-                                KeyguardState.AOD,
+                                if (isAodAvailable) {
+                                    KeyguardState.AOD
+                                } else {
+                                    KeyguardState.DOZING
+                                },
                                 getAnimator(),
                             )
                         )
@@ -87,14 +100,15 @@
         }
     }
 
-    private fun getAnimator(): ValueAnimator {
+    private fun getAnimator(duration: Duration = DEFAULT_DURATION): ValueAnimator {
         return ValueAnimator().apply {
             setInterpolator(Interpolators.LINEAR)
-            setDuration(TRANSITION_DURATION_MS)
+            setDuration(duration.inWholeMilliseconds)
         }
     }
 
     companion object {
-        private const val TRANSITION_DURATION_MS = 500L
+        private val DEFAULT_DURATION = 500.milliseconds
+        val TO_DREAMING_DURATION = 933.milliseconds
     }
 }
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 20c6531..64028ce 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
@@ -21,11 +21,11 @@
 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.DozeStateModel
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.StatusBarState.KEYGUARD
 import com.android.systemui.keyguard.shared.model.TransitionInfo
 import com.android.systemui.keyguard.shared.model.TransitionState
+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
@@ -54,7 +54,7 @@
         listenForLockscreenToGone()
         listenForLockscreenToOccluded()
         listenForLockscreenToCamera()
-        listenForLockscreenToAod()
+        listenForLockscreenToAodOrDozing()
         listenForLockscreenToBouncer()
         listenForLockscreenToDreaming()
         listenForLockscreenToBouncerDragging()
@@ -230,19 +230,31 @@
         }
     }
 
-    private fun listenForLockscreenToAod() {
+    private fun listenForLockscreenToAodOrDozing() {
         scope.launch {
-            keyguardInteractor
-                .dozeTransitionTo(DozeStateModel.DOZE_AOD)
-                .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
-                .collect { pair ->
-                    val (dozeToAod, lastStartedStep) = pair
-                    if (lastStartedStep.to == KeyguardState.LOCKSCREEN) {
+            keyguardInteractor.wakefulnessModel
+                .sample(
+                    combine(
+                        keyguardTransitionInteractor.startedKeyguardTransitionStep,
+                        keyguardInteractor.isAodAvailable,
+                        ::Pair
+                    ),
+                    ::toTriple
+                )
+                .collect { (wakefulnessState, lastStartedStep, isAodAvailable) ->
+                    if (
+                        lastStartedStep.to == KeyguardState.LOCKSCREEN &&
+                            wakefulnessState.state == WakefulnessState.STARTING_TO_SLEEP
+                    ) {
                         keyguardTransitionRepository.startTransition(
                             TransitionInfo(
                                 name,
                                 KeyguardState.LOCKSCREEN,
-                                KeyguardState.AOD,
+                                if (isAodAvailable) {
+                                    KeyguardState.AOD
+                                } else {
+                                    KeyguardState.DOZING
+                                },
                                 getAnimator(),
                             )
                         )
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
index 8878901..2dc8fee 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
@@ -23,12 +23,14 @@
 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 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
 
 @SysUISingleton
@@ -44,6 +46,7 @@
     override fun start() {
         listenForOccludedToLockscreen()
         listenForOccludedToDreaming()
+        listenForOccludedToAodOrDozing()
     }
 
     private fun listenForOccludedToDreaming() {
@@ -70,8 +73,7 @@
         scope.launch {
             keyguardInteractor.isKeyguardOccluded
                 .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
-                .collect { pair ->
-                    val (isOccluded, lastStartedKeyguardState) = pair
+                .collect { (isOccluded, lastStartedKeyguardState) ->
                     // Occlusion signals come from the framework, and should interrupt any
                     // existing transition
                     if (!isOccluded && lastStartedKeyguardState.to == KeyguardState.OCCLUDED) {
@@ -88,6 +90,39 @@
         }
     }
 
+    private fun listenForOccludedToAodOrDozing() {
+        scope.launch {
+            keyguardInteractor.wakefulnessModel
+                .sample(
+                    combine(
+                        keyguardTransitionInteractor.startedKeyguardTransitionStep,
+                        keyguardInteractor.isAodAvailable,
+                        ::Pair
+                    ),
+                    ::toTriple
+                )
+                .collect { (wakefulnessState, lastStartedStep, isAodAvailable) ->
+                    if (
+                        lastStartedStep.to == KeyguardState.OCCLUDED &&
+                            wakefulnessState.state == WakefulnessState.STARTING_TO_SLEEP
+                    ) {
+                        keyguardTransitionRepository.startTransition(
+                            TransitionInfo(
+                                name,
+                                KeyguardState.OCCLUDED,
+                                if (isAodAvailable) {
+                                    KeyguardState.AOD
+                                } else {
+                                    KeyguardState.DOZING
+                                },
+                                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/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index ac2d230..490d22e 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
@@ -57,6 +57,8 @@
     val dozeAmount: Flow<Float> = repository.linearDozeAmount
     /** Whether the system is in doze mode. */
     val isDozing: Flow<Boolean> = repository.isDozing
+    /** Whether Always-on Display mode is available. */
+    val isAodAvailable: Flow<Boolean> = repository.isAodAvailable
     /** Doze transition information. */
     val dozeTransitionModel: Flow<DozeTransitionModel> = repository.dozeTransitionModel
     /**
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 0e4058b..9d8bf7d 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
@@ -45,7 +45,6 @@
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.statusbar.VibratorHelper
-import com.android.systemui.util.kotlin.pairwise
 import kotlin.math.pow
 import kotlin.math.sqrt
 import kotlin.time.Duration.Companion.milliseconds
@@ -129,18 +128,6 @@
                 }
 
                 launch {
-                    viewModel.startButton
-                        .map { it.isActivated }
-                        .pairwise()
-                        .collect { (prev, next) ->
-                            when {
-                                !prev && next -> vibratorHelper?.vibrate(Vibrations.Activated)
-                                prev && !next -> vibratorHelper?.vibrate(Vibrations.Deactivated)
-                            }
-                        }
-                }
-
-                launch {
                     viewModel.endButton.collect { buttonModel ->
                         updateButton(
                             view = endButton,
@@ -153,18 +140,6 @@
                 }
 
                 launch {
-                    viewModel.endButton
-                        .map { it.isActivated }
-                        .pairwise()
-                        .collect { (prev, next) ->
-                            when {
-                                !prev && next -> vibratorHelper?.vibrate(Vibrations.Activated)
-                                prev && !next -> vibratorHelper?.vibrate(Vibrations.Deactivated)
-                            }
-                        }
-                }
-
-                launch {
                     viewModel.isOverlayContainerVisible.collect { isVisible ->
                         overlayContainer.visibility =
                             if (isVisible) {
@@ -383,6 +358,13 @@
                                 .setDuration(longPressDurationMs)
                                 .withEndAction {
                                     view.setOnClickListener {
+                                        vibratorHelper?.vibrate(
+                                            if (viewModel.isActivated) {
+                                                Vibrations.Activated
+                                            } else {
+                                                Vibrations.Deactivated
+                                            }
+                                        )
                                         viewModel.onClicked(
                                             KeyguardQuickAffordanceViewModel.OnClickedParameters(
                                                 configKey = viewModel.configKey,
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt
index 7a90a74..7ccc43c 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt
@@ -29,6 +29,18 @@
     private val dumpManager: DumpManager,
     private val systemClock: SystemClock,
 ) {
+    private val existingBuffers = mutableMapOf<String, TableLogBuffer>()
+
+    /**
+     * Creates a new [TableLogBuffer]. This method should only be called from static contexts, where
+     * it is guaranteed only to be created one time. See [getOrCreate] for a cache-aware method of
+     * obtaining a buffer.
+     *
+     * @param name a unique table name
+     * @param maxSize the buffer max size. See [adjustMaxSize]
+     *
+     * @return a new [TableLogBuffer] registered with [DumpManager]
+     */
     fun create(
         name: String,
         maxSize: Int,
@@ -37,4 +49,23 @@
         dumpManager.registerNormalDumpable(name, tableBuffer)
         return tableBuffer
     }
+
+    /**
+     * Log buffers are retained indefinitely by [DumpManager], so that they can be represented in
+     * bugreports. Because of this, many of them are created statically in the Dagger graph.
+     *
+     * In the case where you have to create a logbuffer with a name only known at runtime, this
+     * method can be used to lazily create a table log buffer which is then cached for reuse.
+     *
+     * @return a [TableLogBuffer] suitable for reuse
+     */
+    fun getOrCreate(
+        name: String,
+        maxSize: Int,
+    ): TableLogBuffer =
+        existingBuffers.getOrElse(name) {
+            val buffer = create(name, maxSize)
+            existingBuffers[name] = buffer
+            buffer
+        }
 }
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 d5558b2..e7f7647 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
@@ -94,7 +94,7 @@
     private var currentCarouselWidth: Int = 0
 
     /** The current height of the carousel */
-    private var currentCarouselHeight: Int = 0
+    @VisibleForTesting var currentCarouselHeight: Int = 0
 
     /** Are we currently showing only active players */
     private var currentlyShowingOnlyActive: Boolean = false
@@ -128,14 +128,14 @@
     /** The measured height of the carousel */
     private var carouselMeasureHeight: Int = 0
     private var desiredHostState: MediaHostState? = null
-    private val mediaCarousel: MediaScrollView
+    @VisibleForTesting var mediaCarousel: MediaScrollView
     val mediaCarouselScrollHandler: MediaCarouselScrollHandler
     val mediaFrame: ViewGroup
     @VisibleForTesting
     lateinit var settingsButton: View
         private set
     private val mediaContent: ViewGroup
-    @VisibleForTesting val pageIndicator: PageIndicator
+    @VisibleForTesting var pageIndicator: PageIndicator
     private val visualStabilityCallback: OnReorderingAllowedListener
     private var needsReordering: Boolean = false
     private var keysNeedRemoval = mutableSetOf<String>()
@@ -160,25 +160,20 @@
         }
 
     companion object {
-        const val ANIMATION_BASE_DURATION = 2200f
-        const val DURATION = 167f
-        const val DETAILS_DELAY = 1067f
-        const val CONTROLS_DELAY = 1400f
-        const val PAGINATION_DELAY = 1900f
-        const val MEDIATITLES_DELAY = 1000f
-        const val MEDIACONTAINERS_DELAY = 967f
         val TRANSFORM_BEZIER = PathInterpolator(0.68F, 0F, 0F, 1F)
-        val REVERSE_BEZIER = PathInterpolator(0F, 0.68F, 1F, 0F)
 
-        fun calculateAlpha(squishinessFraction: Float, delay: Float, duration: Float): Float {
-            val transformStartFraction = delay / ANIMATION_BASE_DURATION
-            val transformDurationFraction = duration / ANIMATION_BASE_DURATION
-            val squishinessToTime = REVERSE_BEZIER.getInterpolation(squishinessFraction)
-            return MathUtils.constrain(
-                (squishinessToTime - transformStartFraction) / transformDurationFraction,
-                0F,
-                1F
-            )
+        fun calculateAlpha(
+            squishinessFraction: Float,
+            startPosition: Float,
+            endPosition: Float
+        ): Float {
+            val transformFraction =
+                MathUtils.constrain(
+                    (squishinessFraction - startPosition) / (endPosition - startPosition),
+                    0F,
+                    1F
+                )
+            return TRANSFORM_BEZIER.getInterpolation(transformFraction)
         }
     }
 
@@ -813,7 +808,12 @@
         val squishFraction = hostStates[currentEndLocation]?.squishFraction ?: 1.0F
         val endAlpha =
             (if (endIsVisible) 1.0f else 0.0f) *
-                calculateAlpha(squishFraction, PAGINATION_DELAY, DURATION)
+                calculateAlpha(
+                    squishFraction,
+                    (pageIndicator.translationY + pageIndicator.height) /
+                        mediaCarousel.measuredHeight,
+                    1F
+                )
         var alpha = 1.0f
         if (!endIsVisible || !startIsVisible) {
             var progress = currentTransitionProgress
@@ -839,7 +839,8 @@
         pageIndicator.translationX = translationX + mediaCarouselScrollHandler.contentTranslation
         val layoutParams = pageIndicator.layoutParams as ViewGroup.MarginLayoutParams
         pageIndicator.translationY =
-            (currentCarouselHeight - pageIndicator.height - layoutParams.bottomMargin).toFloat()
+            (mediaCarousel.measuredHeight - pageIndicator.height - layoutParams.bottomMargin)
+                .toFloat()
     }
 
     /** Update the dimension of this carousel. */
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 3224213..71cc47a 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
@@ -24,11 +24,6 @@
 import com.android.systemui.media.controls.models.GutsViewHolder
 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.CONTROLS_DELAY
-import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.DETAILS_DELAY
-import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.DURATION
-import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.MEDIACONTAINERS_DELAY
-import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.MEDIATITLES_DELAY
 import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.calculateAlpha
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.animation.MeasurementOutput
@@ -36,6 +31,8 @@
 import com.android.systemui.util.animation.TransitionLayoutController
 import com.android.systemui.util.animation.TransitionViewState
 import com.android.systemui.util.traceSection
+import java.lang.Float.max
+import java.lang.Float.min
 import javax.inject.Inject
 
 /**
@@ -304,42 +301,109 @@
         val squishedViewState = viewState.copy()
         val squishedHeight = (squishedViewState.measureHeight * squishFraction).toInt()
         squishedViewState.height = squishedHeight
-        controlIds.forEach { id ->
-            squishedViewState.widgetStates.get(id)?.let { state ->
-                state.alpha = calculateAlpha(squishFraction, CONTROLS_DELAY, DURATION)
-            }
-        }
-
-        detailIds.forEach { id ->
-            squishedViewState.widgetStates.get(id)?.let { state ->
-                state.alpha = calculateAlpha(squishFraction, DETAILS_DELAY, DURATION)
-            }
-        }
-
         // We are not overriding the squishedViewStates height but only the children to avoid
         // them remeasuring the whole view. Instead it just remains as the original size
         backgroundIds.forEach { id ->
-            squishedViewState.widgetStates.get(id)?.let { state ->
-                state.height = squishedHeight
-            }
+            squishedViewState.widgetStates.get(id)?.let { state -> state.height = squishedHeight }
         }
 
-        RecommendationViewHolder.mediaContainersIds.forEach { id ->
-            squishedViewState.widgetStates.get(id)?.let { state ->
-                state.alpha = calculateAlpha(squishFraction, MEDIACONTAINERS_DELAY, DURATION)
-            }
-        }
-
-        RecommendationViewHolder.mediaTitlesAndSubtitlesIds.forEach { id ->
-            squishedViewState.widgetStates.get(id)?.let { state ->
-                state.alpha = calculateAlpha(squishFraction, MEDIATITLES_DELAY, DURATION)
-            }
-        }
-
+        // media player
+        val controlsTop =
+            calculateWidgetGroupAlphaForSquishiness(
+                controlIds,
+                squishedViewState.measureHeight.toFloat(),
+                squishedViewState,
+                squishFraction
+            )
+        calculateWidgetGroupAlphaForSquishiness(
+            detailIds,
+            controlsTop,
+            squishedViewState,
+            squishFraction
+        )
+        // recommendation card
+        val titlesTop =
+            calculateWidgetGroupAlphaForSquishiness(
+                RecommendationViewHolder.mediaTitlesAndSubtitlesIds,
+                squishedViewState.measureHeight.toFloat(),
+                squishedViewState,
+                squishFraction
+            )
+        calculateWidgetGroupAlphaForSquishiness(
+            RecommendationViewHolder.mediaContainersIds,
+            titlesTop,
+            squishedViewState,
+            squishFraction
+        )
         return squishedViewState
     }
 
     /**
+     * This function is to make each widget in UMO disappear before being clipped by squished UMO
+     *
+     * The general rule is that widgets in UMO has been divided into several groups, and widgets in
+     * one group have the same alpha during squishing It will change from alpha 0.0 when the visible
+     * bottom of UMO reach the bottom of this group It will change to alpha 1.0 when the visible
+     * bottom of UMO reach the top of the group below e.g.Album title, artist title and play-pause
+     * button will change alpha together.
+     * ```
+     *     And their alpha becomes 1.0 when the visible bottom of UMO reach the top of controls,
+     *     including progress bar, next button, previous button
+     * ```
+     * widgetGroupIds: a group of widgets have same state during UMO is squished,
+     * ```
+     *     e.g. Album title, artist title and play-pause button
+     * ```
+     * groupEndPosition: the height of UMO, when the height reaches this value,
+     * ```
+     *     widgets in this group should have 1.0 as alpha
+     *     e.g., the group of album title, artist title and play-pause button will become fully
+     *         visible when the height of UMO reaches the top of controls group
+     *         (progress bar, previous button and next button)
+     * ```
+     * squishedViewState: hold the widgetState of each widget, which will be modified
+     * squishFraction: the squishFraction of UMO
+     */
+    private fun calculateWidgetGroupAlphaForSquishiness(
+        widgetGroupIds: Set<Int>,
+        groupEndPosition: Float,
+        squishedViewState: TransitionViewState,
+        squishFraction: Float
+    ): Float {
+        val nonsquishedHeight = squishedViewState.measureHeight
+        var groupTop = squishedViewState.measureHeight.toFloat()
+        var groupBottom = 0F
+        widgetGroupIds.forEach { id ->
+            squishedViewState.widgetStates.get(id)?.let { state ->
+                groupTop = min(groupTop, state.y)
+                groupBottom = max(groupBottom, state.y + state.height)
+            }
+        }
+        // startPosition means to the height of squished UMO where the widget alpha should start
+        // changing from 0.0
+        // generally, it equals to the bottom of widgets, so that we can meet the requirement that
+        // widget should not go beyond the bounds of background
+        // endPosition means to the height of squished UMO where the widget alpha should finish
+        // changing alpha to 1.0
+        var startPosition = groupBottom
+        val endPosition = groupEndPosition
+        if (startPosition == endPosition) {
+            startPosition = (endPosition - 0.2 * (groupBottom - groupTop)).toFloat()
+        }
+        widgetGroupIds.forEach { id ->
+            squishedViewState.widgetStates.get(id)?.let { state ->
+                state.alpha =
+                    calculateAlpha(
+                        squishFraction,
+                        startPosition / nonsquishedHeight,
+                        endPosition / nonsquishedHeight
+                    )
+            }
+        }
+        return groupTop // used for the widget group above this group
+    }
+
+    /**
      * Obtain a new viewState for a given media state. This usually returns a cached state, but if
      * it's not available, it will recreate one by measuring, which may be expensive.
      */
@@ -544,11 +608,13 @@
         overrideSize?.let {
             // To be safe we're using a maximum here. The override size should always be set
             // properly though.
-            if (result.measureHeight != it.measuredHeight
-                    || result.measureWidth != it.measuredWidth) {
+            if (
+                result.measureHeight != it.measuredHeight || result.measureWidth != it.measuredWidth
+            ) {
                 result.measureHeight = Math.max(it.measuredHeight, result.measureHeight)
                 result.measureWidth = Math.max(it.measuredWidth, result.measureWidth)
-                // The measureHeight and the shown height should both be set to the overridden height
+                // The measureHeight and the shown height should both be set to the overridden
+                // height
                 result.height = result.measureHeight
                 result.width = result.measureWidth
                 // Make sure all background views are also resized such that their size is correct
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
index 7cf63f6..1da30ad 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
@@ -36,7 +36,6 @@
     void removeCallback(Callback callback);
     void removeTile(String tileSpec);
     void removeTiles(Collection<String> specs);
-    void unmarkTileAsAutoAdded(String tileSpec);
 
     int indexOf(String tileSpec);
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index cad296b..100853c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -427,11 +427,6 @@
         mMainExecutor.execute(() -> changeTileSpecs(tileSpecs -> tileSpecs.removeAll(specs)));
     }
 
-    @Override
-    public void unmarkTileAsAutoAdded(String spec) {
-        if (mAutoTiles != null) mAutoTiles.unmarkTileAsAutoAdded(spec);
-    }
-
     /**
      * Add a tile to the end
      *
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
index 30f8124..1921586 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
@@ -219,9 +219,9 @@
             // Small button with the number only.
             foregroundServicesWithTextView.isVisible = false
 
-            foregroundServicesWithNumberView.visibility = View.VISIBLE
+            foregroundServicesWithNumberView.isVisible = true
             foregroundServicesWithNumberView.setOnClickListener {
-                foregroundServices.onClick(Expandable.fromView(foregroundServicesWithTextView))
+                foregroundServices.onClick(Expandable.fromView(foregroundServicesWithNumberView))
             }
             foregroundServicesWithNumberHolder.number.text = foregroundServicesCount.toString()
             foregroundServicesWithNumberHolder.number.contentDescription = foregroundServices.text
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 9f376ae..d32ef32 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
@@ -49,109 +49,135 @@
     }
 
     fun logTileAdded(tileSpec: String) {
-        log(DEBUG, {
-            str1 = tileSpec
-        }, {
-            "[$str1] Tile added"
-        })
+        buffer.log(TAG, DEBUG, { str1 = tileSpec }, { "[$str1] Tile added" })
     }
 
     fun logTileDestroyed(tileSpec: String, reason: String) {
-        log(DEBUG, {
-            str1 = tileSpec
-            str2 = reason
-        }, {
-            "[$str1] Tile destroyed. Reason: $str2"
-        })
+        buffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = tileSpec
+                str2 = reason
+            },
+            { "[$str1] Tile destroyed. Reason: $str2" }
+        )
     }
 
     fun logTileChangeListening(tileSpec: String, listening: Boolean) {
-        log(VERBOSE, {
-            bool1 = listening
-            str1 = tileSpec
-        }, {
-            "[$str1] Tile listening=$bool1"
-        })
+        buffer.log(
+            TAG,
+            VERBOSE,
+            {
+                bool1 = listening
+                str1 = tileSpec
+            },
+            { "[$str1] Tile listening=$bool1" }
+        )
     }
 
     fun logAllTilesChangeListening(listening: Boolean, containerName: String, allSpecs: String) {
-        log(DEBUG, {
-            bool1 = listening
-            str1 = containerName
-            str2 = allSpecs
-        }, {
-            "Tiles listening=$bool1 in $str1. $str2"
-        })
+        buffer.log(
+            TAG,
+            DEBUG,
+            {
+                bool1 = listening
+                str1 = containerName
+                str2 = allSpecs
+            },
+            { "Tiles listening=$bool1 in $str1. $str2" }
+        )
     }
 
     fun logTileClick(tileSpec: String, statusBarState: Int, state: Int, eventId: Int) {
-        log(DEBUG, {
-            str1 = tileSpec
-            int1 = eventId
-            str2 = StatusBarState.toString(statusBarState)
-            str3 = toStateString(state)
-        }, {
-            "[$str1][$int1] Tile clicked. StatusBarState=$str2. TileState=$str3"
-        })
+        buffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = tileSpec
+                int1 = eventId
+                str2 = StatusBarState.toString(statusBarState)
+                str3 = toStateString(state)
+            },
+            { "[$str1][$int1] Tile clicked. StatusBarState=$str2. TileState=$str3" }
+        )
     }
 
     fun logHandleClick(tileSpec: String, eventId: Int) {
-        log(DEBUG, {
-            str1 = tileSpec
-            int1 = eventId
-        }, {
-            "[$str1][$int1] Tile handling click."
-        })
+        buffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = tileSpec
+                int1 = eventId
+            },
+            { "[$str1][$int1] Tile handling click." }
+        )
     }
 
     fun logTileSecondaryClick(tileSpec: String, statusBarState: Int, state: Int, eventId: Int) {
-        log(DEBUG, {
-            str1 = tileSpec
-            int1 = eventId
-            str2 = StatusBarState.toString(statusBarState)
-            str3 = toStateString(state)
-        }, {
-            "[$str1][$int1] Tile secondary clicked. StatusBarState=$str2. TileState=$str3"
-        })
+        buffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = tileSpec
+                int1 = eventId
+                str2 = StatusBarState.toString(statusBarState)
+                str3 = toStateString(state)
+            },
+            { "[$str1][$int1] Tile secondary clicked. StatusBarState=$str2. TileState=$str3" }
+        )
     }
 
     fun logHandleSecondaryClick(tileSpec: String, eventId: Int) {
-        log(DEBUG, {
-            str1 = tileSpec
-            int1 = eventId
-        }, {
-            "[$str1][$int1] Tile handling secondary click."
-        })
+        buffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = tileSpec
+                int1 = eventId
+            },
+            { "[$str1][$int1] Tile handling secondary click." }
+        )
     }
 
     fun logTileLongClick(tileSpec: String, statusBarState: Int, state: Int, eventId: Int) {
-        log(DEBUG, {
-            str1 = tileSpec
-            int1 = eventId
-            str2 = StatusBarState.toString(statusBarState)
-            str3 = toStateString(state)
-        }, {
-            "[$str1][$int1] Tile long clicked. StatusBarState=$str2. TileState=$str3"
-        })
+        buffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = tileSpec
+                int1 = eventId
+                str2 = StatusBarState.toString(statusBarState)
+                str3 = toStateString(state)
+            },
+            { "[$str1][$int1] Tile long clicked. StatusBarState=$str2. TileState=$str3" }
+        )
     }
 
     fun logHandleLongClick(tileSpec: String, eventId: Int) {
-        log(DEBUG, {
-            str1 = tileSpec
-            int1 = eventId
-        }, {
-            "[$str1][$int1] Tile handling long click."
-        })
+        buffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = tileSpec
+                int1 = eventId
+            },
+            { "[$str1][$int1] Tile handling long click." }
+        )
     }
 
     fun logInternetTileUpdate(tileSpec: String, lastType: Int, callback: String) {
-        log(VERBOSE, {
-            str1 = tileSpec
-            int1 = lastType
-            str2 = callback
-        }, {
-            "[$str1] mLastTileState=$int1, Callback=$str2."
-        })
+        buffer.log(
+            TAG,
+            VERBOSE,
+            {
+                str1 = tileSpec
+                int1 = lastType
+                str2 = callback
+            },
+            { "[$str1] mLastTileState=$int1, Callback=$str2." }
+        )
     }
 
     // TODO(b/250618218): Remove this method once we know the root cause of b/250618218.
@@ -167,58 +193,75 @@
         if (tileSpec != "internet") {
             return
         }
-        log(VERBOSE, {
-            str1 = tileSpec
-            int1 = state
-            bool1 = disabledByPolicy
-            int2 = color
-        }, {
-            "[$str1] state=$int1, disabledByPolicy=$bool1, color=$int2."
-        })
+        buffer.log(
+            TAG,
+            VERBOSE,
+            {
+                str1 = tileSpec
+                int1 = state
+                bool1 = disabledByPolicy
+                int2 = color
+            },
+            { "[$str1] state=$int1, disabledByPolicy=$bool1, color=$int2." }
+        )
     }
 
     fun logTileUpdated(tileSpec: String, state: QSTile.State) {
-        log(VERBOSE, {
-            str1 = tileSpec
-            str2 = state.label?.toString()
-            str3 = state.icon?.toString()
-            int1 = state.state
-            if (state is QSTile.SignalState) {
-                bool1 = true
-                bool2 = state.activityIn
-                bool3 = state.activityOut
+        buffer.log(
+            TAG,
+            VERBOSE,
+            {
+                str1 = tileSpec
+                str2 = state.label?.toString()
+                str3 = state.icon?.toString()
+                int1 = state.state
+                if (state is QSTile.SignalState) {
+                    bool1 = true
+                    bool2 = state.activityIn
+                    bool3 = state.activityOut
+                }
+            },
+            {
+                "[$str1] Tile updated. Label=$str2. State=$int1. Icon=$str3." +
+                    if (bool1) " Activity in/out=$bool2/$bool3" else ""
             }
-        }, {
-            "[$str1] Tile updated. Label=$str2. State=$int1. Icon=$str3." +
-                if (bool1) " Activity in/out=$bool2/$bool3" else ""
-        })
+        )
     }
 
     fun logPanelExpanded(expanded: Boolean, containerName: String) {
-        log(DEBUG, {
-            str1 = containerName
-            bool1 = expanded
-        }, {
-            "$str1 expanded=$bool1"
-        })
+        buffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = containerName
+                bool1 = expanded
+            },
+            { "$str1 expanded=$bool1" }
+        )
     }
 
     fun logOnViewAttached(orientation: Int, containerName: String) {
-        log(DEBUG, {
-            str1 = containerName
-            int1 = orientation
-        }, {
-            "onViewAttached: $str1 orientation $int1"
-        })
+        buffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = containerName
+                int1 = orientation
+            },
+            { "onViewAttached: $str1 orientation $int1" }
+        )
     }
 
     fun logOnViewDetached(orientation: Int, containerName: String) {
-        log(DEBUG, {
-            str1 = containerName
-            int1 = orientation
-        }, {
-            "onViewDetached: $str1 orientation $int1"
-        })
+        buffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = containerName
+                int1 = orientation
+            },
+            { "onViewDetached: $str1 orientation $int1" }
+        )
     }
 
     fun logOnConfigurationChanged(
@@ -226,13 +269,16 @@
         newOrientation: Int,
         containerName: String
     ) {
-        log(DEBUG, {
-            str1 = containerName
-            int1 = lastOrientation
-            int2 = newOrientation
-        }, {
-            "configuration change: $str1 orientation was $int1, now $int2"
-        })
+        buffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = containerName
+                int1 = lastOrientation
+                int2 = newOrientation
+            },
+            { "configuration change: $str1 orientation was $int1, now $int2" }
+        )
     }
 
     fun logSwitchTileLayout(
@@ -241,32 +287,41 @@
         force: Boolean,
         containerName: String
     ) {
-        log(DEBUG, {
-            str1 = containerName
-            bool1 = after
-            bool2 = before
-            bool3 = force
-        }, {
-            "change tile layout: $str1 horizontal=$bool1 (was $bool2), force? $bool3"
-        })
+        buffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = containerName
+                bool1 = after
+                bool2 = before
+                bool3 = force
+            },
+            { "change tile layout: $str1 horizontal=$bool1 (was $bool2), force? $bool3" }
+        )
     }
 
     fun logTileDistributionInProgress(tilesPerPageCount: Int, totalTilesCount: Int) {
-        log(DEBUG, {
-            int1 = tilesPerPageCount
-            int2 = totalTilesCount
-        }, {
-            "Distributing tiles: [tilesPerPageCount=$int1] [totalTilesCount=$int2]"
-        })
+        buffer.log(
+            TAG,
+            DEBUG,
+            {
+                int1 = tilesPerPageCount
+                int2 = totalTilesCount
+            },
+            { "Distributing tiles: [tilesPerPageCount=$int1] [totalTilesCount=$int2]" }
+        )
     }
 
     fun logTileDistributed(tileName: String, pageIndex: Int) {
-        log(DEBUG, {
-            str1 = tileName
-            int1 = pageIndex
-        }, {
-            "Adding $str1 to page number $int1"
-        })
+        buffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = tileName
+                int1 = pageIndex
+            },
+            { "Adding $str1 to page number $int1" }
+        )
     }
 
     private fun toStateString(state: Int): String {
@@ -277,12 +332,4 @@
             else -> "wrong state"
         }
     }
-
-    private inline fun log(
-        logLevel: LogLevel,
-        initializer: LogMessage.() -> Unit,
-        noinline printer: LogMessage.() -> String
-    ) {
-        buffer.log(TAG, logLevel, initializer, printer)
-    }
 }
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 a92c7e3..24a4f60b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -33,7 +33,6 @@
 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.CellularTile;
 import com.android.systemui.qs.tiles.ColorCorrectionTile;
 import com.android.systemui.qs.tiles.ColorInversionTile;
 import com.android.systemui.qs.tiles.DataSaverTile;
@@ -54,7 +53,6 @@
 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.WifiTile;
 import com.android.systemui.qs.tiles.WorkModeTile;
 import com.android.systemui.util.leak.GarbageMonitor;
 
@@ -68,10 +66,8 @@
 
     private static final String TAG = "QSFactory";
 
-    private final Provider<WifiTile> mWifiTileProvider;
     private final Provider<InternetTile> mInternetTileProvider;
     private final Provider<BluetoothTile> mBluetoothTileProvider;
-    private final Provider<CellularTile> mCellularTileProvider;
     private final Provider<DndTile> mDndTileProvider;
     private final Provider<ColorCorrectionTile> mColorCorrectionTileProvider;
     private final Provider<ColorInversionTile> mColorInversionTileProvider;
@@ -106,10 +102,8 @@
     public QSFactoryImpl(
             Lazy<QSHost> qsHostLazy,
             Provider<CustomTile.Builder> customTileBuilderProvider,
-            Provider<WifiTile> wifiTileProvider,
             Provider<InternetTile> internetTileProvider,
             Provider<BluetoothTile> bluetoothTileProvider,
-            Provider<CellularTile> cellularTileProvider,
             Provider<DndTile> dndTileProvider,
             Provider<ColorInversionTile> colorInversionTileProvider,
             Provider<AirplaneModeTile> airplaneModeTileProvider,
@@ -139,10 +133,8 @@
         mQsHostLazy = qsHostLazy;
         mCustomTileBuilderProvider = customTileBuilderProvider;
 
-        mWifiTileProvider = wifiTileProvider;
         mInternetTileProvider = internetTileProvider;
         mBluetoothTileProvider = bluetoothTileProvider;
-        mCellularTileProvider = cellularTileProvider;
         mDndTileProvider = dndTileProvider;
         mColorInversionTileProvider = colorInversionTileProvider;
         mAirplaneModeTileProvider = airplaneModeTileProvider;
@@ -186,14 +178,10 @@
     protected QSTileImpl createTileInternal(String tileSpec) {
         // Stock tiles.
         switch (tileSpec) {
-            case "wifi":
-                return mWifiTileProvider.get();
             case "internet":
                 return mInternetTileProvider.get();
             case "bt":
                 return mBluetoothTileProvider.get();
-            case "cell":
-                return mCellularTileProvider.get();
             case "dnd":
                 return mDndTileProvider.get();
             case "inversion":
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
deleted file mode 100644
index 04a25fc..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ /dev/null
@@ -1,301 +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.qs.tiles;
-
-import static com.android.systemui.Prefs.Key.QS_HAS_TURNED_OFF_MOBILE_DATA;
-
-import android.annotation.NonNull;
-import android.app.AlertDialog;
-import android.app.AlertDialog.Builder;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.service.quicksettings.Tile;
-import android.telephony.SubscriptionManager;
-import android.text.Html;
-import android.text.TextUtils;
-import android.view.View;
-import android.view.WindowManager.LayoutParams;
-import android.widget.Switch;
-
-import androidx.annotation.Nullable;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settingslib.net.DataUsageController;
-import com.android.systemui.Prefs;
-import com.android.systemui.R;
-import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.QSIconView;
-import com.android.systemui.plugins.qs.QSTile.SignalState;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.QSHost;
-import com.android.systemui.qs.SignalTileView;
-import com.android.systemui.qs.logging.QSLogger;
-import com.android.systemui.qs.tileimpl.QSTileImpl;
-import com.android.systemui.statusbar.connectivity.IconState;
-import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
-import com.android.systemui.statusbar.connectivity.NetworkController;
-import com.android.systemui.statusbar.connectivity.SignalCallback;
-import com.android.systemui.statusbar.phone.SystemUIDialog;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-
-import javax.inject.Inject;
-
-/** Quick settings tile: Cellular **/
-public class CellularTile extends QSTileImpl<SignalState> {
-    private static final String ENABLE_SETTINGS_DATA_PLAN = "enable.settings.data.plan";
-
-    private final NetworkController mController;
-    private final DataUsageController mDataController;
-    private final KeyguardStateController mKeyguard;
-    private final CellSignalCallback mSignalCallback = new CellSignalCallback();
-
-    @Inject
-    public CellularTile(
-            QSHost host,
-            @Background Looper backgroundLooper,
-            @Main Handler mainHandler,
-            FalsingManager falsingManager,
-            MetricsLogger metricsLogger,
-            StatusBarStateController statusBarStateController,
-            ActivityStarter activityStarter,
-            QSLogger qsLogger,
-            NetworkController networkController,
-            KeyguardStateController keyguardStateController
-
-    ) {
-        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
-                statusBarStateController, activityStarter, qsLogger);
-        mController = networkController;
-        mKeyguard = keyguardStateController;
-        mDataController = mController.getMobileDataController();
-        mController.observe(getLifecycle(), mSignalCallback);
-    }
-
-    @Override
-    public SignalState newTileState() {
-        return new SignalState();
-    }
-
-    @Override
-    public QSIconView createTileView(Context context) {
-        return new SignalTileView(context);
-    }
-
-    @Override
-    public Intent getLongClickIntent() {
-        if (getState().state == Tile.STATE_UNAVAILABLE) {
-            return new Intent(Settings.ACTION_WIRELESS_SETTINGS);
-        }
-        return getCellularSettingIntent();
-    }
-
-    @Override
-    protected void handleClick(@Nullable View view) {
-        if (getState().state == Tile.STATE_UNAVAILABLE) {
-            return;
-        }
-        if (mDataController.isMobileDataEnabled()) {
-            maybeShowDisableDialog();
-        } else {
-            mDataController.setMobileDataEnabled(true);
-        }
-    }
-
-    private void maybeShowDisableDialog() {
-        if (Prefs.getBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA, false)) {
-            // Directly turn off mobile data if the user has seen the dialog before.
-            mDataController.setMobileDataEnabled(false);
-            return;
-        }
-        String carrierName = mController.getMobileDataNetworkName();
-        boolean isInService = mController.isMobileDataNetworkInService();
-        if (TextUtils.isEmpty(carrierName) || !isInService) {
-            carrierName = mContext.getString(R.string.mobile_data_disable_message_default_carrier);
-        }
-        AlertDialog dialog = new Builder(mContext)
-                .setTitle(R.string.mobile_data_disable_title)
-                .setMessage(mContext.getString(R.string.mobile_data_disable_message, carrierName))
-                .setNegativeButton(android.R.string.cancel, null)
-                .setPositiveButton(
-                        com.android.internal.R.string.alert_windows_notification_turn_off_action,
-                        (d, w) -> {
-                            mDataController.setMobileDataEnabled(false);
-                            Prefs.putBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA, true);
-                        })
-                .create();
-        dialog.getWindow().setType(LayoutParams.TYPE_KEYGUARD_DIALOG);
-        SystemUIDialog.setShowForAllUsers(dialog, true);
-        SystemUIDialog.registerDismissListener(dialog);
-        SystemUIDialog.setWindowOnTop(dialog, mKeyguard.isShowing());
-        dialog.show();
-    }
-
-    @Override
-    protected void handleSecondaryClick(@Nullable View view) {
-        handleLongClick(view);
-    }
-
-    @Override
-    public CharSequence getTileLabel() {
-        return mContext.getString(R.string.quick_settings_cellular_detail_title);
-    }
-
-    @Override
-    protected void handleUpdateState(SignalState state, Object arg) {
-        CallbackInfo cb = (CallbackInfo) arg;
-        if (cb == null) {
-            cb = mSignalCallback.mInfo;
-        }
-
-        final Resources r = mContext.getResources();
-        state.label = r.getString(R.string.mobile_data);
-        boolean mobileDataEnabled = mDataController.isMobileDataSupported()
-                && mDataController.isMobileDataEnabled();
-        state.value = mobileDataEnabled;
-        state.activityIn = mobileDataEnabled && cb.activityIn;
-        state.activityOut = mobileDataEnabled && cb.activityOut;
-        state.expandedAccessibilityClassName = Switch.class.getName();
-        if (cb.noSim) {
-            state.icon = ResourceIcon.get(R.drawable.ic_qs_no_sim);
-        } else {
-            state.icon = ResourceIcon.get(R.drawable.ic_swap_vert);
-        }
-
-        if (cb.noSim) {
-            state.state = Tile.STATE_UNAVAILABLE;
-            state.secondaryLabel = r.getString(R.string.keyguard_missing_sim_message_short);
-        } else if (cb.airplaneModeEnabled) {
-            state.state = Tile.STATE_UNAVAILABLE;
-            state.secondaryLabel = r.getString(R.string.status_bar_airplane);
-        } else if (mobileDataEnabled) {
-            state.state = Tile.STATE_ACTIVE;
-            state.secondaryLabel = appendMobileDataType(
-                    // Only show carrier name if there are more than 1 subscription
-                    cb.multipleSubs ? cb.dataSubscriptionName : "",
-                    getMobileDataContentName(cb));
-        } else {
-            state.state = Tile.STATE_INACTIVE;
-            state.secondaryLabel = r.getString(R.string.cell_data_off);
-        }
-
-        state.contentDescription = state.label;
-        if (state.state == Tile.STATE_INACTIVE) {
-            // This information is appended later by converting the Tile.STATE_INACTIVE state.
-            state.stateDescription = "";
-        } else {
-            state.stateDescription = state.secondaryLabel;
-        }
-    }
-
-    private CharSequence appendMobileDataType(CharSequence current, CharSequence dataType) {
-        if (TextUtils.isEmpty(dataType)) {
-            return Html.fromHtml(current.toString(), 0);
-        }
-        if (TextUtils.isEmpty(current)) {
-            return Html.fromHtml(dataType.toString(), 0);
-        }
-        String concat = mContext.getString(R.string.mobile_carrier_text_format, current, dataType);
-        return Html.fromHtml(concat, 0);
-    }
-
-    private CharSequence getMobileDataContentName(CallbackInfo cb) {
-        if (cb.roaming && !TextUtils.isEmpty(cb.dataContentDescription)) {
-            String roaming = mContext.getString(R.string.data_connection_roaming);
-            String dataDescription = cb.dataContentDescription.toString();
-            return mContext.getString(R.string.mobile_data_text_format, roaming, dataDescription);
-        }
-        if (cb.roaming) {
-            return mContext.getString(R.string.data_connection_roaming);
-        }
-        return cb.dataContentDescription;
-    }
-
-    @Override
-    public int getMetricsCategory() {
-        return MetricsEvent.QS_CELLULAR;
-    }
-
-    @Override
-    public boolean isAvailable() {
-        return mController.hasMobileDataFeature()
-            && mHost.getUserContext().getUserId() == UserHandle.USER_SYSTEM;
-    }
-
-    private static final class CallbackInfo {
-        boolean airplaneModeEnabled;
-        @Nullable
-        CharSequence dataSubscriptionName;
-        @Nullable
-        CharSequence dataContentDescription;
-        boolean activityIn;
-        boolean activityOut;
-        boolean noSim;
-        boolean roaming;
-        boolean multipleSubs;
-    }
-
-    private final class CellSignalCallback implements SignalCallback {
-        private final CallbackInfo mInfo = new CallbackInfo();
-
-        @Override
-        public void setMobileDataIndicators(@NonNull MobileDataIndicators indicators) {
-            if (indicators.qsIcon == null) {
-                // Not data sim, don't display.
-                return;
-            }
-            mInfo.dataSubscriptionName = mController.getMobileDataNetworkName();
-            mInfo.dataContentDescription = indicators.qsDescription != null
-                    ? indicators.typeContentDescriptionHtml : null;
-            mInfo.activityIn = indicators.activityIn;
-            mInfo.activityOut = indicators.activityOut;
-            mInfo.roaming = indicators.roaming;
-            mInfo.multipleSubs = mController.getNumberSubscriptions() > 1;
-            refreshState(mInfo);
-        }
-
-        @Override
-        public void setNoSims(boolean show, boolean simDetected) {
-            mInfo.noSim = show;
-            refreshState(mInfo);
-        }
-
-        @Override
-        public void setIsAirplaneMode(@NonNull IconState icon) {
-            mInfo.airplaneModeEnabled = icon.visible;
-            refreshState(mInfo);
-        }
-    }
-
-    static Intent getCellularSettingIntent() {
-        Intent intent = new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS);
-        int dataSub = SubscriptionManager.getDefaultDataSubscriptionId();
-        if (dataSub != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-            intent.putExtra(Settings.EXTRA_SUB_ID,
-                    SubscriptionManager.getDefaultDataSubscriptionId());
-        }
-        return intent;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
deleted file mode 100644
index b2be56cc..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ /dev/null
@@ -1,286 +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.qs.tiles;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.os.Handler;
-import android.os.Looper;
-import android.provider.Settings;
-import android.service.quicksettings.Tile;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.View;
-import android.widget.Switch;
-
-import androidx.annotation.Nullable;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.R;
-import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.QSIconView;
-import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.plugins.qs.QSTile.SignalState;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.AlphaControlledSignalTileView;
-import com.android.systemui.qs.QSHost;
-import com.android.systemui.qs.logging.QSLogger;
-import com.android.systemui.qs.tileimpl.QSIconViewImpl;
-import com.android.systemui.qs.tileimpl.QSTileImpl;
-import com.android.systemui.statusbar.connectivity.AccessPointController;
-import com.android.systemui.statusbar.connectivity.NetworkController;
-import com.android.systemui.statusbar.connectivity.SignalCallback;
-import com.android.systemui.statusbar.connectivity.WifiIcons;
-import com.android.systemui.statusbar.connectivity.WifiIndicators;
-
-import javax.inject.Inject;
-
-/** Quick settings tile: Wifi **/
-public class WifiTile extends QSTileImpl<SignalState> {
-    private static final Intent WIFI_SETTINGS = new Intent(Settings.ACTION_WIFI_SETTINGS);
-
-    protected final NetworkController mController;
-    private final AccessPointController mWifiController;
-    private final QSTile.SignalState mStateBeforeClick = newTileState();
-
-    protected final WifiSignalCallback mSignalCallback = new WifiSignalCallback();
-    private boolean mExpectDisabled;
-
-    @Inject
-    public WifiTile(
-            QSHost host,
-            @Background Looper backgroundLooper,
-            @Main Handler mainHandler,
-            FalsingManager falsingManager,
-            MetricsLogger metricsLogger,
-            StatusBarStateController statusBarStateController,
-            ActivityStarter activityStarter,
-            QSLogger qsLogger,
-            NetworkController networkController,
-            AccessPointController accessPointController
-    ) {
-        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
-                statusBarStateController, activityStarter, qsLogger);
-        mController = networkController;
-        mWifiController = accessPointController;
-        mController.observe(getLifecycle(), mSignalCallback);
-        mStateBeforeClick.spec = "wifi";
-    }
-
-    @Override
-    public SignalState newTileState() {
-        return new SignalState();
-    }
-
-    @Override
-    public QSIconView createTileView(Context context) {
-        return new AlphaControlledSignalTileView(context);
-    }
-
-    @Override
-    public Intent getLongClickIntent() {
-        return WIFI_SETTINGS;
-    }
-
-    @Override
-    protected void handleClick(@Nullable View view) {
-        // Secondary clicks are header clicks, just toggle.
-        mState.copyTo(mStateBeforeClick);
-        boolean wifiEnabled = mState.value;
-        // Immediately enter transient state when turning on wifi.
-        refreshState(wifiEnabled ? null : ARG_SHOW_TRANSIENT_ENABLING);
-        mController.setWifiEnabled(!wifiEnabled);
-        mExpectDisabled = wifiEnabled;
-        if (mExpectDisabled) {
-            mHandler.postDelayed(() -> {
-                if (mExpectDisabled) {
-                    mExpectDisabled = false;
-                    refreshState();
-                }
-            }, QSIconViewImpl.QS_ANIM_LENGTH);
-        }
-    }
-
-    @Override
-    protected void handleSecondaryClick(@Nullable View view) {
-        if (!mWifiController.canConfigWifi()) {
-            mActivityStarter.postStartActivityDismissingKeyguard(
-                    new Intent(Settings.ACTION_WIFI_SETTINGS), 0);
-            return;
-        }
-        if (!mState.value) {
-            mController.setWifiEnabled(true);
-        }
-    }
-
-    @Override
-    public CharSequence getTileLabel() {
-        return mContext.getString(R.string.quick_settings_wifi_label);
-    }
-
-    @Override
-    protected void handleUpdateState(SignalState state, Object arg) {
-        if (DEBUG) Log.d(TAG, "handleUpdateState arg=" + arg);
-        final CallbackInfo cb = mSignalCallback.mInfo;
-        if (mExpectDisabled) {
-            if (cb.enabled) {
-                return; // Ignore updates until disabled event occurs.
-            } else {
-                mExpectDisabled = false;
-            }
-        }
-        boolean transientEnabling = arg == ARG_SHOW_TRANSIENT_ENABLING;
-        boolean wifiConnected = cb.enabled && (cb.wifiSignalIconId > 0)
-                && (cb.ssid != null || cb.wifiSignalIconId != WifiIcons.QS_WIFI_NO_NETWORK);
-        boolean wifiNotConnected = (cb.ssid == null)
-                && (cb.wifiSignalIconId == WifiIcons.QS_WIFI_NO_NETWORK);
-        if (state.slash == null) {
-            state.slash = new SlashState();
-            state.slash.rotation = 6;
-        }
-        state.slash.isSlashed = false;
-        boolean isTransient = transientEnabling || cb.isTransient;
-        state.secondaryLabel = getSecondaryLabel(isTransient, cb.statusLabel);
-        state.state = Tile.STATE_ACTIVE;
-        state.dualTarget = true;
-        state.value = transientEnabling || cb.enabled;
-        state.activityIn = cb.enabled && cb.activityIn;
-        state.activityOut = cb.enabled && cb.activityOut;
-        final StringBuffer minimalContentDescription = new StringBuffer();
-        final StringBuffer minimalStateDescription = new StringBuffer();
-        final Resources r = mContext.getResources();
-        if (isTransient) {
-            state.icon = ResourceIcon.get(
-                    com.android.internal.R.drawable.ic_signal_wifi_transient_animation);
-            state.label = r.getString(R.string.quick_settings_wifi_label);
-        } else if (!state.value) {
-            state.slash.isSlashed = true;
-            state.state = Tile.STATE_INACTIVE;
-            state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_DISABLED);
-            state.label = r.getString(R.string.quick_settings_wifi_label);
-        } else if (wifiConnected) {
-            state.icon = ResourceIcon.get(cb.wifiSignalIconId);
-            state.label = cb.ssid != null ? removeDoubleQuotes(cb.ssid) : getTileLabel();
-        } else if (wifiNotConnected) {
-            state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_NO_NETWORK);
-            state.label = r.getString(R.string.quick_settings_wifi_label);
-        } else {
-            state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_NO_NETWORK);
-            state.label = r.getString(R.string.quick_settings_wifi_label);
-        }
-        minimalContentDescription.append(
-                mContext.getString(R.string.quick_settings_wifi_label)).append(",");
-        if (state.value) {
-            if (wifiConnected) {
-                minimalStateDescription.append(cb.wifiSignalContentDescription);
-                minimalContentDescription.append(removeDoubleQuotes(cb.ssid));
-                if (!TextUtils.isEmpty(state.secondaryLabel)) {
-                    minimalContentDescription.append(",").append(state.secondaryLabel);
-                }
-            }
-        }
-        state.stateDescription = minimalStateDescription.toString();
-        state.contentDescription = minimalContentDescription.toString();
-        state.dualLabelContentDescription = r.getString(
-                R.string.accessibility_quick_settings_open_settings, getTileLabel());
-        state.expandedAccessibilityClassName = Switch.class.getName();
-    }
-
-    private CharSequence getSecondaryLabel(boolean isTransient, String statusLabel) {
-        return isTransient
-                ? mContext.getString(R.string.quick_settings_wifi_secondary_label_transient)
-                : statusLabel;
-    }
-
-    @Override
-    public int getMetricsCategory() {
-        return MetricsEvent.QS_WIFI;
-    }
-
-    @Override
-    public boolean isAvailable() {
-        return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
-    }
-
-    @Nullable
-    private static String removeDoubleQuotes(String string) {
-        if (string == null) return null;
-        final int length = string.length();
-        if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) {
-            return string.substring(1, length - 1);
-        }
-        return string;
-    }
-
-    protected static final class CallbackInfo {
-        boolean enabled;
-        boolean connected;
-        int wifiSignalIconId;
-        @Nullable
-        String ssid;
-        boolean activityIn;
-        boolean activityOut;
-        @Nullable
-        String wifiSignalContentDescription;
-        boolean isTransient;
-        @Nullable
-        public String statusLabel;
-
-        @Override
-        public String toString() {
-            return new StringBuilder("CallbackInfo[")
-                    .append("enabled=").append(enabled)
-                    .append(",connected=").append(connected)
-                    .append(",wifiSignalIconId=").append(wifiSignalIconId)
-                    .append(",ssid=").append(ssid)
-                    .append(",activityIn=").append(activityIn)
-                    .append(",activityOut=").append(activityOut)
-                    .append(",wifiSignalContentDescription=").append(wifiSignalContentDescription)
-                    .append(",isTransient=").append(isTransient)
-                    .append(']').toString();
-        }
-    }
-
-    protected final class WifiSignalCallback implements SignalCallback {
-        final CallbackInfo mInfo = new CallbackInfo();
-
-        @Override
-        public void setWifiIndicators(@NonNull WifiIndicators indicators) {
-            if (DEBUG) Log.d(TAG, "onWifiSignalChanged enabled=" + indicators.enabled);
-            if (indicators.qsIcon == null) {
-                return;
-            }
-            mInfo.enabled = indicators.enabled;
-            mInfo.connected = indicators.qsIcon.visible;
-            mInfo.wifiSignalIconId = indicators.qsIcon.icon;
-            mInfo.ssid = indicators.description;
-            mInfo.activityIn = indicators.activityIn;
-            mInfo.activityOut = indicators.activityOut;
-            mInfo.wifiSignalContentDescription = indicators.qsIcon.contentDescription;
-            mInfo.isTransient = indicators.isTransient;
-            mInfo.statusLabel = indicators.statusLabel;
-            refreshState();
-        }
-    }
-}
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 a6c7781..72c6bfe 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -101,7 +101,6 @@
     @MainThread
     public void onManagedProfileRemoved() {
         mHost.removeTile(getTileSpec());
-        mHost.unmarkTileAsAutoAdded(getTileSpec());
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 91ebf79..b21a485 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -687,8 +687,8 @@
                     }
                 });
         if (mFlags.isEnabled(SCREENSHOT_WORK_PROFILE_POLICY)) {
-            mScreenshotView.badgeScreenshot(
-                    mContext.getPackageManager().getUserBadgeForDensity(owner, 0));
+            mScreenshotView.badgeScreenshot(mContext.getPackageManager().getUserBadgedIcon(
+                    mContext.getDrawable(R.drawable.overlay_badge_background), owner));
         }
         mScreenshotView.setScreenshot(mScreenBitmap, screenInsets);
         if (DEBUG_WINDOW) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 899cdb7..200a7dc 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -1090,7 +1090,7 @@
         mScreenshotBadge.setVisibility(View.GONE);
         mScreenshotBadge.setImageDrawable(null);
         mPendingSharedTransition = false;
-        mActionsContainerBackground.setVisibility(View.GONE);
+        mActionsContainerBackground.setVisibility(View.INVISIBLE);
         mActionsContainer.setVisibility(View.GONE);
         mDismissButton.setVisibility(View.GONE);
         mScrollingScrim.setVisibility(View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index 28da38b..61390c5 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -112,7 +112,7 @@
             // These get called when a managed profile goes in or out of quiet mode.
             addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
             addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
-
+            addAction(Intent.ACTION_MANAGED_PROFILE_ADDED)
             addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED)
             addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED)
         }
@@ -129,6 +129,7 @@
             Intent.ACTION_USER_INFO_CHANGED,
             Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
             Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
+            Intent.ACTION_MANAGED_PROFILE_ADDED,
             Intent.ACTION_MANAGED_PROFILE_REMOVED,
             Intent.ACTION_MANAGED_PROFILE_UNLOCKED -> {
                 handleProfilesChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 80d7bbc..ccffc5f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -2340,7 +2340,7 @@
             // When false, down but not synthesized motion event.
             mLastEventSynthesizedDown = mExpectingSynthesizedDown;
             mLastDownEvents.insert(
-                    mSystemClock.currentTimeMillis(),
+                    event.getEventTime(),
                     mDownX,
                     mDownY,
                     mQsTouchAboveFalsingThreshold,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
index 5fedbeb..11617be 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
@@ -36,16 +36,9 @@
         buffer.log(TAG, LogLevel.DEBUG, msg)
     }
 
-    private inline fun log(
-        logLevel: LogLevel,
-        initializer: LogMessage.() -> Unit,
-        noinline printer: LogMessage.() -> String
-    ) {
-        buffer.log(TAG, logLevel, initializer, printer)
-    }
-
     fun onQsInterceptMoveQsTrackingEnabled(h: Float) {
-        log(
+        buffer.log(
+            TAG,
             LogLevel.VERBOSE,
             { double1 = h.toDouble() },
             { "onQsIntercept: move action, QS tracking enabled. h = $double1" }
@@ -62,7 +55,8 @@
         keyguardShowing: Boolean,
         qsExpansionEnabled: Boolean
     ) {
-        log(
+        buffer.log(
+            TAG,
             LogLevel.VERBOSE,
             {
                 int1 = initialTouchY.toInt()
@@ -82,7 +76,8 @@
     }
 
     fun logMotionEvent(event: MotionEvent, message: String) {
-        log(
+        buffer.log(
+            TAG,
             LogLevel.VERBOSE,
             {
                 str1 = message
@@ -99,7 +94,8 @@
     }
 
     fun logMotionEventStatusBarState(event: MotionEvent, statusBarState: Int, message: String) {
-        log(
+        buffer.log(
+                TAG,
                 LogLevel.VERBOSE,
                 {
                     str1 = message
@@ -128,25 +124,33 @@
             tracking: Boolean,
             dragDownPxAmount: Float,
     ) {
-        log(LogLevel.VERBOSE, {
-            str1 = message
-            double1 = fraction.toDouble()
-            bool1 = expanded
-            bool2 = tracking
-            long1 = dragDownPxAmount.toLong()
-        }, {
-            "$str1 fraction=$double1,expanded=$bool1," +
+        buffer.log(
+            TAG,
+            LogLevel.VERBOSE,
+            {
+                str1 = message
+                double1 = fraction.toDouble()
+                bool1 = expanded
+                bool2 = tracking
+                long1 = dragDownPxAmount.toLong()
+            },
+            {
+                "$str1 fraction=$double1,expanded=$bool1," +
                     "tracking=$bool2," + "dragDownPxAmount=$dragDownPxAmount"
-        })
+            }
+        )
     }
 
     fun logHasVibrated(hasVibratedOnOpen: Boolean, fraction: Float) {
-        log(LogLevel.VERBOSE, {
-            bool1 = hasVibratedOnOpen
-            double1 = fraction.toDouble()
-        }, {
-            "hasVibratedOnOpen=$bool1, expansionFraction=$double1"
-        })
+        buffer.log(
+            TAG,
+            LogLevel.VERBOSE,
+            {
+                bool1 = hasVibratedOnOpen
+                double1 = fraction.toDouble()
+            },
+            { "hasVibratedOnOpen=$bool1, expansionFraction=$double1" }
+        )
     }
 
     fun logQsExpansionChanged(
@@ -159,42 +163,56 @@
             qsAnimatorExpand: Boolean,
             animatingQs: Boolean
     ) {
-        log(LogLevel.VERBOSE, {
-            str1 = message
-            bool1 = qsExpanded
-            int1 = qsMinExpansionHeight
-            int2 = qsMaxExpansionHeight
-            bool2 = stackScrollerOverscrolling
-            bool3 = dozing
-            bool4 = qsAnimatorExpand
-            // 0 = false, 1 = true
-            long1 = animatingQs.compareTo(false).toLong()
-        }, {
-            "$str1 qsExpanded=$bool1,qsMinExpansionHeight=$int1,qsMaxExpansionHeight=$int2," +
+        buffer.log(
+            TAG,
+            LogLevel.VERBOSE,
+            {
+                str1 = message
+                bool1 = qsExpanded
+                int1 = qsMinExpansionHeight
+                int2 = qsMaxExpansionHeight
+                bool2 = stackScrollerOverscrolling
+                bool3 = dozing
+                bool4 = qsAnimatorExpand
+                // 0 = false, 1 = true
+                long1 = animatingQs.compareTo(false).toLong()
+            },
+            {
+                "$str1 qsExpanded=$bool1,qsMinExpansionHeight=$int1,qsMaxExpansionHeight=$int2," +
                     "stackScrollerOverscrolling=$bool2,dozing=$bool3,qsAnimatorExpand=$bool4," +
                     "animatingQs=$long1"
-        })
+            }
+        )
     }
 
     fun logSingleTapUp(isDozing: Boolean, singleTapEnabled: Boolean, isNotDocked: Boolean) {
-        log(LogLevel.DEBUG, {
-            bool1 = isDozing
-            bool2 = singleTapEnabled
-            bool3 = isNotDocked
-        }, {
-            "PulsingGestureListener#onSingleTapUp all of this must true for single " +
-              "tap to be detected: isDozing: $bool1, singleTapEnabled: $bool2, isNotDocked: $bool3"
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            {
+                bool1 = isDozing
+                bool2 = singleTapEnabled
+                bool3 = isNotDocked
+            },
+            {
+                "PulsingGestureListener#onSingleTapUp all of this must true for single " +
+               "tap to be detected: isDozing: $bool1, singleTapEnabled: $bool2, isNotDocked: $bool3"
         })
     }
 
     fun logSingleTapUpFalsingState(proximityIsNotNear: Boolean, isNotFalseTap: Boolean) {
-        log(LogLevel.DEBUG, {
-            bool1 = proximityIsNotNear
-            bool2 = isNotFalseTap
-        }, {
-            "PulsingGestureListener#onSingleTapUp all of this must true for single " +
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            {
+                bool1 = proximityIsNotNear
+                bool2 = isNotFalseTap
+            },
+            {
+                "PulsingGestureListener#onSingleTapUp all of this must true for single " +
                     "tap to be detected: proximityIsNotNear: $bool1, isNotFalseTap: $bool2"
-        })
+            }
+        )
     }
 
     fun logNotInterceptingTouchInstantExpanding(
@@ -202,13 +220,18 @@
             notificationsDragEnabled: Boolean,
             touchDisabled: Boolean
     ) {
-        log(LogLevel.VERBOSE, {
-            bool1 = instantExpanding
-            bool2 = notificationsDragEnabled
-            bool3 = touchDisabled
-        }, {
-            "NPVC not intercepting touch, instantExpanding: $bool1, " +
+        buffer.log(
+            TAG,
+            LogLevel.VERBOSE,
+            {
+                bool1 = instantExpanding
+                bool2 = notificationsDragEnabled
+                bool3 = touchDisabled
+            },
+            {
+                "NPVC not intercepting touch, instantExpanding: $bool1, " +
                     "!notificationsDragEnabled: $bool2, touchDisabled: $bool3"
-        })
+            }
+        )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLogger.kt
index c6a6e87..9851625 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLogger.kt
@@ -32,11 +32,21 @@
     ConstantStringsLogger by ConstantStringsLoggerImpl(buffer, TAG) {
 
     fun logApplyingWindowLayoutParams(lp: WindowManager.LayoutParams) {
-        log(DEBUG, { str1 = lp.toString() }, { "Applying new window layout params: $str1" })
+        buffer.log(
+            TAG,
+            DEBUG,
+            { str1 = lp.toString() },
+            { "Applying new window layout params: $str1" }
+        )
     }
 
     fun logNewState(state: Any) {
-        log(DEBUG, { str1 = state.toString() }, { "Applying new state: $str1" })
+        buffer.log(
+            TAG,
+            DEBUG,
+            { str1 = state.toString() },
+            { "Applying new state: $str1" }
+        )
     }
 
     private inline fun log(
@@ -48,11 +58,16 @@
     }
 
     fun logApplyVisibility(visible: Boolean) {
-        log(DEBUG, { bool1 = visible }, { "Updating visibility, should be visible : $bool1" })
+        buffer.log(
+            TAG,
+            DEBUG,
+            { bool1 = visible },
+            { "Updating visibility, should be visible : $bool1" })
     }
 
     fun logShadeVisibleAndFocusable(visible: Boolean) {
-        log(
+        buffer.log(
+            TAG,
             DEBUG,
             { bool1 = visible },
             { "Updating shade, should be visible and focusable: $bool1" }
@@ -60,6 +75,11 @@
     }
 
     fun logShadeFocusable(focusable: Boolean) {
-        log(DEBUG, { bool1 = focusable }, { "Updating shade, should be focusable : $bool1" })
+        buffer.log(
+            TAG,
+            DEBUG,
+            { bool1 = focusable },
+            { "Updating shade, should be focusable : $bool1" }
+        )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
index c496102..b084a76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
@@ -109,7 +109,7 @@
                 return true;
             }
             if (ev.getAction() == MotionEvent.ACTION_UP) {
-                mView.setLastActionUpTime(SystemClock.uptimeMillis());
+                mView.setLastActionUpTime(ev.getEventTime());
             }
             // With a11y, just do nothing.
             if (mAccessibilityManager.isTouchExplorationEnabled()) {
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 ca1e397..356ddfa 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
@@ -1811,9 +1811,7 @@
     @Override
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
-        mBottomInset = insets.getSystemWindowInsetBottom()
-                + insets.getInsets(WindowInsets.Type.ime()).bottom;
-
+        mBottomInset = insets.getInsets(WindowInsets.Type.ime()).bottom;
         mWaterfallTopInset = 0;
         final DisplayCutout cutout = insets.getDisplayCutout();
         if (cutout != null) {
@@ -2262,7 +2260,11 @@
 
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private int getImeInset() {
-        return Math.max(0, mBottomInset - (getRootView().getHeight() - getHeight()));
+        // The NotificationStackScrollLayout does not extend all the way to the bottom of the
+        // display. Therefore, subtract that space from the mBottomInset, in order to only include
+        // the portion of the bottom inset that actually overlaps the NotificationStackScrollLayout.
+        return Math.max(0, mBottomInset
+                - (getRootView().getHeight() - getHeight() - getLocationOnScreen()[1]));
     }
 
     /**
@@ -2970,12 +2972,19 @@
             childInGroup = (ExpandableNotificationRow) requestedView;
             requestedView = requestedRow = childInGroup.getNotificationParent();
         }
-        int position = 0;
+        final float scrimTopPadding = mAmbientState.isOnKeyguard() ? 0 : mMinimumPaddings;
+        int position = (int) scrimTopPadding;
+        int visibleIndex = -1;
+        ExpandableView lastVisibleChild = null;
         for (int i = 0; i < getChildCount(); i++) {
             ExpandableView child = getChildAtIndex(i);
             boolean notGone = child.getVisibility() != View.GONE;
+            if (notGone) visibleIndex++;
             if (notGone && !child.hasNoContentHeight()) {
-                if (position != 0) {
+                if (position != scrimTopPadding) {
+                    if (lastVisibleChild != null) {
+                        position += calculateGapHeight(lastVisibleChild, child, visibleIndex);
+                    }
                     position += mPaddingBetweenElements;
                 }
             }
@@ -2987,6 +2996,7 @@
             }
             if (notGone) {
                 position += getIntrinsicHeight(child);
+                lastVisibleChild = child;
             }
         }
         return 0;
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 9070ead..149ec54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -154,9 +154,7 @@
         if (!mAutoTracker.isAdded(SAVER)) {
             mDataSaverController.addCallback(mDataSaverListener);
         }
-        if (!mAutoTracker.isAdded(WORK)) {
-            mManagedProfileController.addCallback(mProfileCallback);
-        }
+        mManagedProfileController.addCallback(mProfileCallback);
         if (!mAutoTracker.isAdded(NIGHT)
                 && ColorDisplayManager.isNightDisplayAvailable(mContext)) {
             mNightDisplayListener.setCallback(mNightDisplayCallback);
@@ -275,18 +273,18 @@
         return mCurrentUser.getIdentifier();
     }
 
-    public void unmarkTileAsAutoAdded(String tabSpec) {
-        mAutoTracker.setTileRemoved(tabSpec);
-    }
-
     private final ManagedProfileController.Callback mProfileCallback =
             new ManagedProfileController.Callback() {
                 @Override
                 public void onManagedProfileChanged() {
-                    if (mAutoTracker.isAdded(WORK)) return;
                     if (mManagedProfileController.hasActiveProfile()) {
+                        if (mAutoTracker.isAdded(WORK)) return;
                         mHost.addTile(WORK);
                         mAutoTracker.setTileAdded(WORK);
+                    } else {
+                        if (!mAutoTracker.isAdded(WORK)) return;
+                        mHost.removeTile(WORK);
+                        mAutoTracker.setTileRemoved(WORK);
                     }
                 }
 
@@ -429,7 +427,7 @@
                 initSafetyTile();
             } else if (!isSafetyCenterEnabled && mAutoTracker.isAdded(mSafetySpec)) {
                 mHost.removeTile(mSafetySpec);
-                mHost.unmarkTileAsAutoAdded(mSafetySpec);
+                mAutoTracker.setTileRemoved(mSafetySpec);
             }
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 895a293..db2c0a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -18,6 +18,8 @@
 
 import static android.app.StatusBarManager.SESSION_KEYGUARD;
 
+import static com.android.systemui.keyguard.WakefulnessLifecycle.UNKNOWN_LAST_WAKE_TIME;
+
 import android.annotation.IntDef;
 import android.content.res.Resources;
 import android.hardware.biometrics.BiometricFaceConstants;
@@ -27,7 +29,6 @@
 import android.metrics.LogMaker;
 import android.os.Handler;
 import android.os.PowerManager;
-import android.os.SystemClock;
 import android.os.Trace;
 
 import androidx.annotation.Nullable;
@@ -62,6 +63,7 @@
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.time.SystemClock;
 
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -78,6 +80,7 @@
  */
 @SysUISingleton
 public class BiometricUnlockController extends KeyguardUpdateMonitorCallback implements Dumpable {
+    private static final long RECENT_POWER_BUTTON_PRESS_THRESHOLD_MS = 400L;
     private static final long BIOMETRIC_WAKELOCK_TIMEOUT_MS = 15 * 1000;
     private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock:wakelock";
     private static final UiEventLogger UI_EVENT_LOGGER = new UiEventLoggerImpl();
@@ -169,9 +172,11 @@
     private final MetricsLogger mMetricsLogger;
     private final AuthController mAuthController;
     private final StatusBarStateController mStatusBarStateController;
+    private final WakefulnessLifecycle mWakefulnessLifecycle;
     private final LatencyTracker mLatencyTracker;
     private final VibratorHelper mVibratorHelper;
     private final BiometricUnlockLogger mLogger;
+    private final SystemClock mSystemClock;
 
     private long mLastFpFailureUptimeMillis;
     private int mNumConsecutiveFpFailures;
@@ -279,14 +284,17 @@
             SessionTracker sessionTracker,
             LatencyTracker latencyTracker,
             ScreenOffAnimationController screenOffAnimationController,
-            VibratorHelper vibrator) {
+            VibratorHelper vibrator,
+            SystemClock systemClock
+    ) {
         mPowerManager = powerManager;
         mShadeController = shadeController;
         mUpdateMonitor = keyguardUpdateMonitor;
         mUpdateMonitor.registerCallback(this);
         mMediaManager = notificationMediaManager;
         mLatencyTracker = latencyTracker;
-        wakefulnessLifecycle.addObserver(mWakefulnessObserver);
+        mWakefulnessLifecycle = wakefulnessLifecycle;
+        mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
         screenLifecycle.addObserver(mScreenObserver);
 
         mNotificationShadeWindowController = notificationShadeWindowController;
@@ -306,6 +314,7 @@
         mScreenOffAnimationController = screenOffAnimationController;
         mVibratorHelper = vibrator;
         mLogger = biometricUnlockLogger;
+        mSystemClock = systemClock;
 
         dumpManager.registerDumpable(getClass().getName(), this);
     }
@@ -429,8 +438,11 @@
         Runnable wakeUp = ()-> {
             if (!wasDeviceInteractive || mUpdateMonitor.isDreaming()) {
                 mLogger.i("bio wakelock: Authenticated, waking up...");
-                mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_BIOMETRIC,
-                        "android.policy:BIOMETRIC");
+                mPowerManager.wakeUp(
+                        mSystemClock.uptimeMillis(),
+                        PowerManager.WAKE_REASON_BIOMETRIC,
+                        "android.policy:BIOMETRIC"
+                );
             }
             Trace.beginSection("release wake-and-unlock");
             releaseBiometricWakeLock();
@@ -670,7 +682,7 @@
             startWakeAndUnlock(MODE_ONLY_WAKE);
         } else if (biometricSourceType == BiometricSourceType.FINGERPRINT
                 && mUpdateMonitor.isUdfpsSupported()) {
-            long currUptimeMillis = SystemClock.uptimeMillis();
+            long currUptimeMillis = mSystemClock.uptimeMillis();
             if (currUptimeMillis - mLastFpFailureUptimeMillis < mConsecutiveFpFailureThreshold) {
                 mNumConsecutiveFpFailures += 1;
             } else {
@@ -718,12 +730,26 @@
         cleanup();
     }
 
-    //these haptics are for device-entry only
+    // these haptics are for device-entry only
     private void vibrateSuccess(BiometricSourceType type) {
+        if (mAuthController.isSfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser())
+                && lastWakeupFromPowerButtonWithinHapticThreshold()) {
+            mLogger.d("Skip auth success haptic. Power button was recently pressed.");
+            return;
+        }
         mVibratorHelper.vibrateAuthSuccess(
                 getClass().getSimpleName() + ", type =" + type + "device-entry::success");
     }
 
+    private boolean lastWakeupFromPowerButtonWithinHapticThreshold() {
+        final boolean lastWakeupFromPowerButton = mWakefulnessLifecycle.getLastWakeReason()
+                == PowerManager.WAKE_REASON_POWER_BUTTON;
+        return lastWakeupFromPowerButton
+                && mWakefulnessLifecycle.getLastWakeTime() != UNKNOWN_LAST_WAKE_TIME
+                && mSystemClock.uptimeMillis() - mWakefulnessLifecycle.getLastWakeTime()
+                < RECENT_POWER_BUTTON_PRESS_THRESHOLD_MS;
+    }
+
     private void vibrateError(BiometricSourceType type) {
         mVibratorHelper.vibrateAuthError(
                 getClass().getSimpleName() + ", type =" + type + "device-entry::error");
@@ -816,7 +842,7 @@
         if (mUpdateMonitor.isUdfpsSupported()) {
             pw.print("   mNumConsecutiveFpFailures="); pw.println(mNumConsecutiveFpFailures);
             pw.print("   time since last failure=");
-            pw.println(SystemClock.uptimeMillis() - mLastFpFailureUptimeMillis);
+            pw.println(mSystemClock.uptimeMillis() - mLastFpFailureUptimeMillis);
         }
     }
 
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 8f27045..f1e1f42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -4266,8 +4266,7 @@
 
                 @Override
                 public void onDozeAmountChanged(float linear, float eased) {
-                    if (mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ANIMATIONS)
-                            && !mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)
+                    if (!mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)
                             && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
                         mLightRevealScrim.setRevealAmount(1f - linear);
                     }
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 de7b152..0446cef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -44,10 +44,9 @@
 import com.android.systemui.doze.AlwaysOnDisplayPolicy;
 import com.android.systemui.doze.DozeScreenState;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 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.statusbar.policy.DevicePostureController;
 import com.android.systemui.tuner.TunerService;
@@ -82,7 +81,6 @@
     private final AlwaysOnDisplayPolicy mAlwaysOnPolicy;
     private final Resources mResources;
     private final BatteryController mBatteryController;
-    private final FeatureFlags mFeatureFlags;
     private final ScreenOffAnimationController mScreenOffAnimationController;
     private final FoldAodAnimationController mFoldAodAnimationController;
     private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
@@ -125,7 +123,6 @@
             BatteryController batteryController,
             TunerService tunerService,
             DumpManager dumpManager,
-            FeatureFlags featureFlags,
             ScreenOffAnimationController screenOffAnimationController,
             Optional<SysUIUnfoldComponent> sysUiUnfoldComponent,
             UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
@@ -141,7 +138,6 @@
         mControlScreenOffAnimation = !getDisplayNeedsBlanking();
         mPowerManager = powerManager;
         mPowerManager.setDozeAfterScreenOff(!mControlScreenOffAnimation);
-        mFeatureFlags = featureFlags;
         mScreenOffAnimationController = screenOffAnimationController;
         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
 
@@ -162,6 +158,13 @@
 
         SettingsObserver quickPickupSettingsObserver = new SettingsObserver(context, handler);
         quickPickupSettingsObserver.observe();
+
+        batteryController.addCallback(new BatteryStateChangeCallback() {
+                @Override
+                public void onPowerSaveChanged(boolean isPowerSave) {
+                    dispatchAlwaysOnEvent();
+                }
+            });
     }
 
     private void updateQuickPickupEnabled() {
@@ -300,13 +303,10 @@
 
     /**
      * Whether we're capable of controlling the screen off animation if we want to. This isn't
-     * possible if AOD isn't even enabled or if the flag is disabled, or if the display needs
-     * blanking.
+     * possible if AOD isn't even enabled or if the display needs blanking.
      */
     public boolean canControlUnlockedScreenOff() {
-        return getAlwaysOn()
-                && mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ANIMATIONS)
-                && !getDisplayNeedsBlanking();
+        return getAlwaysOn() && !getDisplayNeedsBlanking();
     }
 
     /**
@@ -424,9 +424,7 @@
             updateControlScreenOff();
         }
 
-        for (Callback callback : mCallbacks) {
-            callback.onAlwaysOnChange();
-        }
+        dispatchAlwaysOnEvent();
         mScreenOffAnimationController.onAlwaysOnChanged(getAlwaysOn());
     }
 
@@ -463,6 +461,12 @@
         pw.print("isQuickPickupEnabled(): "); pw.println(isQuickPickupEnabled());
     }
 
+    private void dispatchAlwaysOnEvent() {
+        for (Callback callback : mCallbacks) {
+            callback.onAlwaysOnChange();
+        }
+    }
+
     private boolean getPostureSpecificBool(
             int[] postureMapping,
             boolean defaultSensorBool,
@@ -477,7 +481,8 @@
         return bool;
     }
 
-    interface Callback {
+    /** Callbacks for doze parameter related information */
+    public interface Callback {
         /**
          * Invoked when the value of getAlwaysOn may have changed.
          */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt
index 6cd8c78..9e6bb20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt
@@ -16,7 +16,9 @@
 
 package com.android.systemui.statusbar.phone
 
+import android.view.InsetsFlags
 import android.view.InsetsVisibilities
+import android.view.ViewDebug
 import android.view.WindowInsetsController.Appearance
 import android.view.WindowInsetsController.Behavior
 import com.android.internal.statusbar.LetterboxDetails
@@ -148,4 +150,20 @@
 ) {
     val letterboxesArray = letterboxes.toTypedArray()
     val appearanceRegionsArray = appearanceRegions.toTypedArray()
+    override fun toString(): String {
+        val appearanceToString =
+                ViewDebug.flagsToString(InsetsFlags::class.java, "appearance", appearance)
+        return """SystemBarAttributesParams(
+            displayId=$displayId,
+            appearance=$appearanceToString,
+            appearanceRegions=$appearanceRegions,
+            navbarColorManagedByIme=$navbarColorManagedByIme,
+            behavior=$behavior,
+            requestedVisibilities=$requestedVisibilities,
+            packageName='$packageName',
+            letterboxes=$letterboxes,
+            letterboxesArray=${letterboxesArray.contentToString()},
+            appearanceRegionsArray=${appearanceRegionsArray.contentToString()}
+            )""".trimMargin()
+    }
 }
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 0e164e7..8ac1237 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
@@ -149,7 +149,7 @@
     }
 
     private fun createDemoMobileConnectionRepo(subId: Int): DemoMobileConnectionRepository {
-        val tableLogBuffer = logFactory.create("DemoMobileConnectionLog [$subId]", 100)
+        val tableLogBuffer = logFactory.getOrCreate("DemoMobileConnectionLog [$subId]", 100)
 
         return DemoMobileConnectionRepository(
             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 0fa0fea..4e42f9b 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
@@ -308,7 +308,7 @@
             networkNameSeparator: String,
             globalMobileDataSettingChangedEvent: Flow<Unit>,
         ): MobileConnectionRepository {
-            val mobileLogger = logFactory.create(tableBufferLogName(subId), 100)
+            val mobileLogger = logFactory.getOrCreate(tableBufferLogName(subId), 100)
 
             return MobileConnectionRepositoryImpl(
                 context,
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
index 59ad24a..2709da3 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
@@ -17,6 +17,9 @@
 package com.android.systemui.unfold
 
 import android.content.Context
+import android.hardware.devicestate.DeviceStateManager
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.LifecycleScreenStatusProvider
 import com.android.systemui.unfold.config.UnfoldTransitionConfig
 import com.android.systemui.unfold.system.SystemUnfoldSharedModule
@@ -32,6 +35,7 @@
 import dagger.Module
 import dagger.Provides
 import java.util.Optional
+import java.util.concurrent.Executor
 import javax.inject.Named
 import javax.inject.Singleton
 
@@ -40,6 +44,20 @@
 
     @Provides @UnfoldTransitionATracePrefix fun tracingTagPrefix() = "systemui"
 
+    /** A globally available FoldStateListener that allows one to query the fold state. */
+    @Provides
+    @Singleton
+    fun providesFoldStateListener(
+        deviceStateManager: DeviceStateManager,
+        @Application context: Context,
+        @Main executor: Executor
+    ): DeviceStateManager.FoldStateListener {
+        val listener = DeviceStateManager.FoldStateListener(context)
+        deviceStateManager.registerCallback(executor, listener)
+
+        return listener
+    }
+
     @Provides
     @Singleton
     fun providesFoldStateLoggingProvider(
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 9a9387b..df6752a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -32,6 +32,9 @@
 import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT;
 import static com.android.keyguard.KeyguardUpdateMonitor.HAL_POWER_PRESS_TIMEOUT;
 import static com.android.keyguard.KeyguardUpdateMonitor.getCurrentUser;
+import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_CLOSED;
+import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED;
+import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -92,6 +95,7 @@
 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;
@@ -124,6 +128,7 @@
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.policy.DevicePostureController;
 import com.android.systemui.telephony.TelephonyListenerManager;
 import com.android.systemui.util.settings.GlobalSettings;
 import com.android.systemui.util.settings.SecureSettings;
@@ -193,6 +198,8 @@
     @Mock
     private DevicePolicyManager mDevicePolicyManager;
     @Mock
+    private DevicePostureController mDevicePostureController;
+    @Mock
     private IDreamManager mDreamManager;
     @Mock
     private KeyguardBypassController mKeyguardBypassController;
@@ -300,6 +307,7 @@
                 .thenReturn(new ServiceState());
         when(mLockPatternUtils.getLockSettings()).thenReturn(mLockSettings);
         when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(false);
+        when(mDevicePostureController.getDevicePosture()).thenReturn(DEVICE_POSTURE_UNKNOWN);
 
         mMockitoSession = ExtendedMockito.mockitoSession()
                 .spyStatic(SubscriptionManager.class)
@@ -311,6 +319,9 @@
         when(mUserTracker.getUserId()).thenReturn(mCurrentUserId);
         ExtendedMockito.doReturn(mActivityService).when(ActivityManager::getService);
 
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.systemui.R.integer.config_face_auth_supported_posture,
+                DEVICE_POSTURE_UNKNOWN);
         mFaceWakeUpTriggersConfig = new FaceWakeUpTriggersConfig(
                 mContext.getResources(),
                 mGlobalSettings,
@@ -1254,7 +1265,7 @@
     }
 
     @Test
-    public void testStartsListeningForSfps_whenKeyguardIsVisible_ifRequireScreenOnToAuthEnabled()
+    public void startsListeningForSfps_whenKeyguardIsVisible_ifRequireInteractiveToAuthEnabled()
             throws RemoteException {
         // SFPS supported and enrolled
         final ArrayList<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
@@ -1262,12 +1273,9 @@
         when(mAuthController.getSfpsProps()).thenReturn(props);
         when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
 
-        // WHEN require screen on to auth is disabled, and keyguard is not awake
+        // WHEN require interactive to auth is disabled, and keyguard is not awake
         when(mInteractiveToAuthProvider.isEnabled(anyInt())).thenReturn(false);
 
-        mContext.getOrCreateTestableResources().addOverride(
-                com.android.internal.R.bool.config_requireScreenOnToAuthEnabled, true);
-
         // Preconditions for sfps auth to run
         keyguardNotGoingAway();
         currentUserIsPrimary();
@@ -1282,7 +1290,7 @@
         // THEN we should listen for sfps when screen off, because require screen on is disabled
         assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isTrue();
 
-        // WHEN require screen on to auth is enabled, and keyguard is not awake
+        // WHEN require interactive to auth is enabled, and keyguard is not awake
         when(mInteractiveToAuthProvider.isEnabled(anyInt())).thenReturn(true);
 
         // THEN we shouldn't listen for sfps when screen off, because require screen on is enabled
@@ -1297,6 +1305,61 @@
         assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isTrue();
     }
 
+    @Test
+    public void notListeningForSfps_whenGoingToSleep_ifRequireInteractiveToAuthEnabled()
+            throws RemoteException {
+        // GIVEN SFPS supported and enrolled
+        final ArrayList<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
+        props.add(newFingerprintSensorPropertiesInternal(TYPE_POWER_BUTTON));
+        when(mAuthController.getSfpsProps()).thenReturn(props);
+        when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
+
+        // GIVEN Preconditions for sfps auth to run
+        keyguardNotGoingAway();
+        currentUserIsPrimary();
+        currentUserDoesNotHaveTrust();
+        biometricsNotDisabledThroughDevicePolicyManager();
+        biometricsEnabledForCurrentUser();
+        userNotCurrentlySwitching();
+        statusBarShadeIsLocked();
+
+        // WHEN require interactive to auth is enabled & keyguard is going to sleep
+        when(mInteractiveToAuthProvider.isEnabled(anyInt())).thenReturn(true);
+        deviceGoingToSleep();
+
+        mTestableLooper.processAllMessages();
+
+        // THEN we should NOT listen for sfps because device is going to sleep
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isFalse();
+    }
+
+    @Test
+    public void listeningForSfps_whenGoingToSleep_ifRequireInteractiveToAuthDisabled()
+            throws RemoteException {
+        // GIVEN SFPS supported and enrolled
+        final ArrayList<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
+        props.add(newFingerprintSensorPropertiesInternal(TYPE_POWER_BUTTON));
+        when(mAuthController.getSfpsProps()).thenReturn(props);
+        when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
+
+        // GIVEN Preconditions for sfps auth to run
+        keyguardNotGoingAway();
+        currentUserIsPrimary();
+        currentUserDoesNotHaveTrust();
+        biometricsNotDisabledThroughDevicePolicyManager();
+        biometricsEnabledForCurrentUser();
+        userNotCurrentlySwitching();
+        statusBarShadeIsLocked();
+
+        // WHEN require interactive to auth is disabled & keyguard is going to sleep
+        when(mInteractiveToAuthProvider.isEnabled(anyInt())).thenReturn(false);
+        deviceGoingToSleep();
+
+        mTestableLooper.processAllMessages();
+
+        // THEN we should listen for sfps because screen on to auth is  disabled
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isTrue();
+    }
 
     private FingerprintSensorPropertiesInternal newFingerprintSensorPropertiesInternal(
             @FingerprintSensorProperties.SensorType int sensorType) {
@@ -2189,6 +2252,54 @@
                 eq(true));
     }
 
+    @Test
+    public void testShouldListenForFace_withAuthSupportPostureConfig_returnsTrue()
+            throws RemoteException {
+        mKeyguardUpdateMonitor.mConfigFaceAuthSupportedPosture = DEVICE_POSTURE_CLOSED;
+        keyguardNotGoingAway();
+        bouncerFullyVisibleAndNotGoingToSleep();
+        currentUserIsPrimary();
+        currentUserDoesNotHaveTrust();
+        biometricsNotDisabledThroughDevicePolicyManager();
+        biometricsEnabledForCurrentUser();
+        userNotCurrentlySwitching();
+        supportsFaceDetection();
+
+        deviceInPostureStateOpened();
+        mTestableLooper.processAllMessages();
+        // Should not listen for face when posture state in DEVICE_POSTURE_OPENED
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+
+        deviceInPostureStateClosed();
+        mTestableLooper.processAllMessages();
+        // Should listen for face when posture state in DEVICE_POSTURE_CLOSED
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+    }
+
+    @Test
+    public void testShouldListenForFace_withoutAuthSupportPostureConfig_returnsTrue()
+            throws RemoteException {
+        mKeyguardUpdateMonitor.mConfigFaceAuthSupportedPosture = DEVICE_POSTURE_UNKNOWN;
+        keyguardNotGoingAway();
+        bouncerFullyVisibleAndNotGoingToSleep();
+        currentUserIsPrimary();
+        currentUserDoesNotHaveTrust();
+        biometricsNotDisabledThroughDevicePolicyManager();
+        biometricsEnabledForCurrentUser();
+        userNotCurrentlySwitching();
+        supportsFaceDetection();
+
+        deviceInPostureStateClosed();
+        mTestableLooper.processAllMessages();
+        // Whether device in any posture state, always listen for face
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+
+        deviceInPostureStateOpened();
+        mTestableLooper.processAllMessages();
+        // Whether device in any posture state, always listen for face
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+    }
+
     private void userDeviceLockDown() {
         when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
         when(mStrongAuthTracker.getStrongAuthForUser(mCurrentUserId))
@@ -2268,6 +2379,14 @@
                 .onAuthenticationAcquired(FINGERPRINT_ACQUIRED_START);
     }
 
+    private void deviceInPostureStateOpened() {
+        mKeyguardUpdateMonitor.mPostureCallback.onPostureChanged(DEVICE_POSTURE_OPENED);
+    }
+
+    private void deviceInPostureStateClosed() {
+        mKeyguardUpdateMonitor.mPostureCallback.onPostureChanged(DEVICE_POSTURE_CLOSED);
+    }
+
     private void successfulFingerprintAuth() {
         mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
                 .onAuthenticationSucceeded(
@@ -2409,7 +2528,8 @@
                     mPowerManager, mTrustManager, mSubscriptionManager, mUserManager,
                     mDreamManager, mDevicePolicyManager, mSensorPrivacyManager, mTelephonyManager,
                     mPackageManager, mFaceManager, mFingerprintManager, mBiometricManager,
-                    mFaceWakeUpTriggersConfig, Optional.of(mInteractiveToAuthProvider));
+                    mFaceWakeUpTriggersConfig, mDevicePostureController,
+                    Optional.of(mInteractiveToAuthProvider));
             setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetectorTest.kt
new file mode 100644
index 0000000..af46d9b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetectorTest.kt
@@ -0,0 +1,131 @@
+/*
+ * 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.biometrics.udfps
+
+import android.graphics.Point
+import android.graphics.Rect
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+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.Mockito.spy
+import org.mockito.Mockito.`when` as whenEver
+
+@SmallTest
+@RunWith(Parameterized::class)
+class EllipseOverlapDetectorTest(val testCase: TestCase) : SysuiTestCase() {
+    val underTest = spy(EllipseOverlapDetector(neededPoints = 1))
+
+    @Before
+    fun setUp() {
+        // Use one single center point for testing, required or total number of points may change
+        whenEver(underTest.calculateSensorPoints(SENSOR))
+            .thenReturn(listOf(Point(SENSOR.centerX(), SENSOR.centerY())))
+    }
+
+    @Test
+    fun isGoodOverlap() {
+        val touchData =
+            TOUCH_DATA.copy(
+                x = testCase.x.toFloat(),
+                y = testCase.y.toFloat(),
+                minor = testCase.minor,
+                major = testCase.major
+            )
+        val actual = underTest.isGoodOverlap(touchData, SENSOR)
+
+        assertThat(actual).isEqualTo(testCase.expected)
+    }
+
+    data class TestCase(
+        val x: Int,
+        val y: Int,
+        val minor: Float,
+        val major: Float,
+        val expected: Boolean
+    )
+
+    companion object {
+        @Parameters(name = "{0}")
+        @JvmStatic
+        fun data(): List<TestCase> =
+            listOf(
+                    genTestCases(
+                        innerXs = listOf(SENSOR.left, SENSOR.right, SENSOR.centerX()),
+                        innerYs = listOf(SENSOR.top, SENSOR.bottom, SENSOR.centerY()),
+                        outerXs = listOf(SENSOR.left - 1, SENSOR.right + 1),
+                        outerYs = listOf(SENSOR.top - 1, SENSOR.bottom + 1),
+                        minor = 300f,
+                        major = 300f,
+                        expected = true
+                    ),
+                    genTestCases(
+                        innerXs = listOf(SENSOR.left, SENSOR.right),
+                        innerYs = listOf(SENSOR.top, SENSOR.bottom),
+                        outerXs = listOf(SENSOR.left - 1, SENSOR.right + 1),
+                        outerYs = listOf(SENSOR.top - 1, SENSOR.bottom + 1),
+                        minor = 100f,
+                        major = 100f,
+                        expected = false
+                    )
+                )
+                .flatten()
+    }
+}
+
+/* Placeholder touch parameters. */
+private const val POINTER_ID = 42
+private const val NATIVE_MINOR = 2.71828f
+private const val NATIVE_MAJOR = 3.14f
+private const val ORIENTATION = 0f // used for perfect circles
+private const val TIME = 12345699L
+private const val GESTURE_START = 12345600L
+
+/* Template [NormalizedTouchData]. */
+private val TOUCH_DATA =
+    NormalizedTouchData(
+        POINTER_ID,
+        x = 0f,
+        y = 0f,
+        NATIVE_MINOR,
+        NATIVE_MAJOR,
+        ORIENTATION,
+        TIME,
+        GESTURE_START
+    )
+
+private val SENSOR = Rect(100 /* left */, 200 /* top */, 300 /* right */, 400 /* bottom */)
+
+private fun genTestCases(
+    innerXs: List<Int>,
+    innerYs: List<Int>,
+    outerXs: List<Int>,
+    outerYs: List<Int>,
+    minor: Float,
+    major: Float,
+    expected: Boolean
+): List<EllipseOverlapDetectorTest.TestCase> {
+    return (innerXs + outerXs).flatMap { x ->
+        (innerYs + outerYs).map { y ->
+            EllipseOverlapDetectorTest.TestCase(x, y, minor, major, expected)
+        }
+    }
+}
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 0fadc13..e4df754 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
@@ -106,6 +106,7 @@
         mClassifiers.add(mClassifierB);
         when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(mMotionEventList);
         when(mKeyguardStateController.isShowing()).thenReturn(true);
+        when(mFalsingDataProvider.isFolded()).thenReturn(true);
         mBrightLineFalsingManager = new BrightLineFalsingManager(mFalsingDataProvider,
                 mMetricsLogger, mClassifiers, mSingleTapClassfier, mLongTapClassifier,
                 mDoubleTapClassifier, mHistoryTracker, mKeyguardStateController,
@@ -121,6 +122,7 @@
         mGestureFinalizedListener = gestureCompleteListenerCaptor.getValue();
         mFakeFeatureFlags.set(Flags.FALSING_FOR_LONG_TAPS, true);
         mFakeFeatureFlags.set(Flags.MEDIA_FALSING_PENALTY, true);
+        mFakeFeatureFlags.set(Flags.FALSING_OFF_FOR_UNFOLDED, true);
     }
 
     @Test
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 4281ee0..ae38eb6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
@@ -89,25 +89,27 @@
         mClassifiers.add(mClassifierA);
         when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(mMotionEventList);
         when(mKeyguardStateController.isShowing()).thenReturn(true);
+        when(mFalsingDataProvider.isFolded()).thenReturn(true);
         mBrightLineFalsingManager = new BrightLineFalsingManager(mFalsingDataProvider,
                 mMetricsLogger, mClassifiers, mSingleTapClassifier, mLongTapClassifier,
                 mDoubleTapClassifier, mHistoryTracker, mKeyguardStateController,
                 mAccessibilityManager, false, mFakeFeatureFlags);
         mFakeFeatureFlags.set(Flags.FALSING_FOR_LONG_TAPS, true);
+        mFakeFeatureFlags.set(Flags.FALSING_OFF_FOR_UNFOLDED, true);
     }
 
     @Test
     public void testA11yDisablesGesture() {
-        assertThat(mBrightLineFalsingManager.isFalseTap(1)).isTrue();
+        assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isTrue();
         when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true);
-        assertThat(mBrightLineFalsingManager.isFalseTap(1)).isFalse();
+        assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isFalse();
     }
 
     @Test
     public void testA11yDisablesTap() {
-        assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isTrue();
+        assertThat(mBrightLineFalsingManager.isFalseTap(1)).isTrue();
         when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true);
-        assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isFalse();
+        assertThat(mBrightLineFalsingManager.isFalseTap(1)).isFalse();
     }
 
 
@@ -179,4 +181,11 @@
         when(mFalsingDataProvider.isA11yAction()).thenReturn(true);
         assertThat(mBrightLineFalsingManager.isFalseTap(1)).isFalse();
     }
+
+    @Test
+    public void testSkipUnfolded() {
+        assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isTrue();
+        when(mFalsingDataProvider.isFolded()).thenReturn(false);
+        assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isFalse();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java
index 5fa7214..94cf384 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.classifier;
 
+import android.hardware.devicestate.DeviceStateManager.FoldStateListener;
 import android.util.DisplayMetrics;
 import android.view.MotionEvent;
 
@@ -38,6 +39,7 @@
     private float mOffsetY = 0;
     @Mock
     private BatteryController mBatteryController;
+    private FoldStateListener mFoldStateListener = new FoldStateListener(mContext);
     private final DockManagerFake mDockManager = new DockManagerFake();
 
     public void setup() {
@@ -47,7 +49,8 @@
         displayMetrics.ydpi = 100;
         displayMetrics.widthPixels = 1000;
         displayMetrics.heightPixels = 1000;
-        mDataProvider = new FalsingDataProvider(displayMetrics, mBatteryController, mDockManager);
+        mDataProvider = new FalsingDataProvider(
+                displayMetrics, mBatteryController, mFoldStateListener, mDockManager);
     }
 
     @After
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 d315c2d..c451a1e7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
@@ -24,6 +24,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.hardware.devicestate.DeviceStateManager.FoldStateListener;
 import android.testing.AndroidTestingRunner;
 import android.util.DisplayMetrics;
 import android.view.MotionEvent;
@@ -50,6 +51,8 @@
     private FalsingDataProvider mDataProvider;
     @Mock
     private BatteryController mBatteryController;
+    @Mock
+    private FoldStateListener mFoldStateListener;
     private final DockManagerFake mDockManager = new DockManagerFake();
 
     @Before
@@ -61,7 +64,8 @@
         displayMetrics.ydpi = 100;
         displayMetrics.widthPixels = 1000;
         displayMetrics.heightPixels = 1000;
-        mDataProvider = new FalsingDataProvider(displayMetrics, mBatteryController, mDockManager);
+        mDataProvider = new FalsingDataProvider(
+                displayMetrics, mBatteryController, mFoldStateListener, mDockManager);
     }
 
     @After
@@ -316,4 +320,16 @@
         mDataProvider.onA11yAction();
         assertThat(mDataProvider.isA11yAction()).isTrue();
     }
+
+    @Test
+    public void test_FoldedState_Folded() {
+        when(mFoldStateListener.getFolded()).thenReturn(true);
+        assertThat(mDataProvider.isFolded()).isTrue();
+    }
+
+    @Test
+    public void test_FoldedState_Unfolded() {
+        when(mFoldStateListener.getFolded()).thenReturn(false);
+        assertThat(mDataProvider.isFolded()).isFalse();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 122d7fd..f55b866 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -29,6 +29,7 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -485,6 +486,38 @@
         assertTrue(mViewMediator.isShowingAndNotOccluded());
     }
 
+    @Test
+    @TestableLooper.RunWithLooper(setAsMainLooper = true)
+    public void testDoKeyguardWhileInteractive_resets() {
+        mViewMediator.setShowingLocked(true);
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+        TestableLooper.get(this).processAllMessages();
+
+        when(mPowerManager.isInteractive()).thenReturn(true);
+
+        mViewMediator.onSystemReady();
+        TestableLooper.get(this).processAllMessages();
+
+        assertTrue(mViewMediator.isShowingAndNotOccluded());
+        verify(mStatusBarKeyguardViewManager).reset(anyBoolean());
+    }
+
+    @Test
+    @TestableLooper.RunWithLooper(setAsMainLooper = true)
+    public void testDoKeyguardWhileNotInteractive_showsInsteadOfResetting() {
+        mViewMediator.setShowingLocked(true);
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+        TestableLooper.get(this).processAllMessages();
+
+        when(mPowerManager.isInteractive()).thenReturn(false);
+
+        mViewMediator.onSystemReady();
+        TestableLooper.get(this).processAllMessages();
+
+        assertTrue(mViewMediator.isShowingAndNotOccluded());
+        verify(mStatusBarKeyguardViewManager, never()).reset(anyBoolean());
+    }
+
     private void createAndStartViewMediator() {
         mViewMediator = new KeyguardViewMediator(
                 mContext,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
index f32d76b..39a453d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
@@ -30,6 +30,7 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -51,7 +52,12 @@
     public void setUp() throws Exception {
         mWallpaperManager = mock(IWallpaperManager.class);
         mWakefulness =
-                new WakefulnessLifecycle(mContext, mWallpaperManager, mock(DumpManager.class));
+                new WakefulnessLifecycle(
+                        mContext,
+                        mWallpaperManager,
+                        new FakeSystemClock(),
+                        mock(DumpManager.class)
+                );
         mWakefulnessObserver = mock(WakefulnessLifecycle.Observer.class);
         mWakefulness.addObserver(mWakefulnessObserver);
     }
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 be712f6..f997d18 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
@@ -24,6 +24,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.AuthController
 import com.android.systemui.common.shared.model.Position
+import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.doze.DozeHost
 import com.android.systemui.doze.DozeMachine
 import com.android.systemui.doze.DozeTransitionCallback
@@ -38,14 +39,17 @@
 import com.android.systemui.keyguard.shared.model.WakefulnessState
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.phone.BiometricUnlockController
+import com.android.systemui.statusbar.phone.DozeParameters
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.mockito.withArgCaptor
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onCompletion
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
@@ -68,6 +72,7 @@
     @Mock private lateinit var authController: AuthController
     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
     @Mock private lateinit var dreamOverlayCallbackController: DreamOverlayCallbackController
+    @Mock private lateinit var dozeParameters: DozeParameters
 
     private lateinit var underTest: KeyguardRepositoryImpl
 
@@ -84,6 +89,7 @@
                 keyguardStateController,
                 keyguardUpdateMonitor,
                 dozeTransitionListener,
+                dozeParameters,
                 authController,
                 dreamOverlayCallbackController,
             )
@@ -170,6 +176,26 @@
         }
 
     @Test
+    fun isAodAvailable() = runTest {
+        val flow = underTest.isAodAvailable
+        var isAodAvailable = collectLastValue(flow)
+        runCurrent()
+
+        val callback =
+            withArgCaptor<DozeParameters.Callback> { verify(dozeParameters).addCallback(capture()) }
+
+        whenever(dozeParameters.getAlwaysOn()).thenReturn(false)
+        callback.onAlwaysOnChange()
+        assertThat(isAodAvailable()).isEqualTo(false)
+
+        whenever(dozeParameters.getAlwaysOn()).thenReturn(true)
+        callback.onAlwaysOnChange()
+        assertThat(isAodAvailable()).isEqualTo(true)
+
+        flow.onCompletion { verify(dozeParameters).removeCallback(callback) }
+    }
+
+    @Test
     fun isKeyguardOccluded() =
         runTest(UnconfinedTestDispatcher()) {
             whenever(keyguardStateController.isOccluded).thenReturn(false)
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 754adfd..b3cee22 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
@@ -71,6 +71,10 @@
 
     private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor
     private lateinit var fromDreamingTransitionInteractor: FromDreamingTransitionInteractor
+    private lateinit var fromDozingTransitionInteractor: FromDozingTransitionInteractor
+    private lateinit var fromOccludedTransitionInteractor: FromOccludedTransitionInteractor
+    private lateinit var fromGoneTransitionInteractor: FromGoneTransitionInteractor
+    private lateinit var fromAodTransitionInteractor: FromAodTransitionInteractor
 
     @Before
     fun setUp() {
@@ -102,6 +106,42 @@
                 keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
             )
         fromDreamingTransitionInteractor.start()
+
+        fromAodTransitionInteractor =
+            FromAodTransitionInteractor(
+                scope = testScope,
+                keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
+                keyguardTransitionRepository = mockTransitionRepository,
+                keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
+            )
+        fromAodTransitionInteractor.start()
+
+        fromGoneTransitionInteractor =
+            FromGoneTransitionInteractor(
+                scope = testScope,
+                keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
+                keyguardTransitionRepository = mockTransitionRepository,
+                keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
+            )
+        fromGoneTransitionInteractor.start()
+
+        fromDozingTransitionInteractor =
+            FromDozingTransitionInteractor(
+                scope = testScope,
+                keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
+                keyguardTransitionRepository = mockTransitionRepository,
+                keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
+            )
+        fromDozingTransitionInteractor.start()
+
+        fromOccludedTransitionInteractor =
+            FromOccludedTransitionInteractor(
+                scope = testScope,
+                keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
+                keyguardTransitionRepository = mockTransitionRepository,
+                keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
+            )
+        fromOccludedTransitionInteractor.start()
     }
 
     @Test
@@ -192,6 +232,289 @@
             coroutineContext.cancelChildren()
         }
 
+    @Test
+    fun `OCCLUDED to DOZING`() =
+        testScope.runTest {
+            // GIVEN a device with AOD not available
+            keyguardRepository.setAodAvailable(false)
+            runCurrent()
+
+            // GIVEN a prior transition has run to OCCLUDED
+            runner.startTransition(
+                testScope,
+                TransitionInfo(
+                    ownerName = "",
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.OCCLUDED,
+                    animator =
+                        ValueAnimator().apply {
+                            duration = 10
+                            interpolator = Interpolators.LINEAR
+                        },
+                )
+            )
+            runCurrent()
+            reset(mockTransitionRepository)
+
+            // WHEN the device begins to sleep
+            keyguardRepository.setWakefulnessModel(startingToSleep())
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(mockTransitionRepository).startTransition(capture())
+                }
+            // THEN a transition to DOZING should occur
+            assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.OCCLUDED)
+            assertThat(info.to).isEqualTo(KeyguardState.DOZING)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun `OCCLUDED to AOD`() =
+        testScope.runTest {
+            // GIVEN a device with AOD available
+            keyguardRepository.setAodAvailable(true)
+            runCurrent()
+
+            // GIVEN a prior transition has run to OCCLUDED
+            runner.startTransition(
+                testScope,
+                TransitionInfo(
+                    ownerName = "",
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.OCCLUDED,
+                    animator =
+                        ValueAnimator().apply {
+                            duration = 10
+                            interpolator = Interpolators.LINEAR
+                        },
+                )
+            )
+            runCurrent()
+            reset(mockTransitionRepository)
+
+            // WHEN the device begins to sleep
+            keyguardRepository.setWakefulnessModel(startingToSleep())
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(mockTransitionRepository).startTransition(capture())
+                }
+            // THEN a transition to DOZING should occur
+            assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.OCCLUDED)
+            assertThat(info.to).isEqualTo(KeyguardState.AOD)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun `LOCKSCREEN to DOZING`() =
+        testScope.runTest {
+            // GIVEN a device with AOD not available
+            keyguardRepository.setAodAvailable(false)
+            runCurrent()
+
+            // GIVEN a prior transition has run to LOCKSCREEN
+            runner.startTransition(
+                testScope,
+                TransitionInfo(
+                    ownerName = "",
+                    from = KeyguardState.GONE,
+                    to = KeyguardState.LOCKSCREEN,
+                    animator =
+                        ValueAnimator().apply {
+                            duration = 10
+                            interpolator = Interpolators.LINEAR
+                        },
+                )
+            )
+            runCurrent()
+            reset(mockTransitionRepository)
+
+            // WHEN the device begins to sleep
+            keyguardRepository.setWakefulnessModel(startingToSleep())
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(mockTransitionRepository).startTransition(capture())
+                }
+            // THEN a transition to DOZING should occur
+            assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
+            assertThat(info.to).isEqualTo(KeyguardState.DOZING)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun `LOCKSCREEN to AOD`() =
+        testScope.runTest {
+            // GIVEN a device with AOD available
+            keyguardRepository.setAodAvailable(true)
+            runCurrent()
+
+            // GIVEN a prior transition has run to LOCKSCREEN
+            runner.startTransition(
+                testScope,
+                TransitionInfo(
+                    ownerName = "",
+                    from = KeyguardState.GONE,
+                    to = KeyguardState.LOCKSCREEN,
+                    animator =
+                        ValueAnimator().apply {
+                            duration = 10
+                            interpolator = Interpolators.LINEAR
+                        },
+                )
+            )
+            runCurrent()
+            reset(mockTransitionRepository)
+
+            // WHEN the device begins to sleep
+            keyguardRepository.setWakefulnessModel(startingToSleep())
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(mockTransitionRepository).startTransition(capture())
+                }
+            // THEN a transition to DOZING should occur
+            assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
+            assertThat(info.to).isEqualTo(KeyguardState.AOD)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun `DOZING to LOCKSCREEN`() =
+        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 the device begins to wake
+            keyguardRepository.setWakefulnessModel(startingToWake())
+            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.LOCKSCREEN)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun `GONE to DOZING`() =
+        testScope.runTest {
+            // GIVEN a device with AOD not available
+            keyguardRepository.setAodAvailable(false)
+            runCurrent()
+
+            // 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 device begins to sleep
+            keyguardRepository.setWakefulnessModel(startingToSleep())
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(mockTransitionRepository).startTransition(capture())
+                }
+            // THEN a transition to DOZING should occur
+            assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.GONE)
+            assertThat(info.to).isEqualTo(KeyguardState.DOZING)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun `GONE to AOD`() =
+        testScope.runTest {
+            // GIVEN a device with AOD available
+            keyguardRepository.setAodAvailable(true)
+            runCurrent()
+
+            // 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 device begins to sleep
+            keyguardRepository.setWakefulnessModel(startingToSleep())
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(mockTransitionRepository).startTransition(capture())
+                }
+            // THEN a transition to DOZING should occur
+            assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.GONE)
+            assertThat(info.to).isEqualTo(KeyguardState.AOD)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
     private fun startingToWake() =
         WakefulnessModel(
             WakefulnessState.STARTING_TO_WAKE,
@@ -199,4 +522,12 @@
             WakeSleepReason.OTHER,
             WakeSleepReason.OTHER
         )
+
+    private fun startingToSleep() =
+        WakefulnessModel(
+            WakefulnessState.STARTING_TO_SLEEP,
+            true,
+            WakeSleepReason.OTHER,
+            WakeSleepReason.OTHER
+        )
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.kt
new file mode 100644
index 0000000..411b1bd
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.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.log.table
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+@SmallTest
+class TableLogBufferFactoryTest : SysuiTestCase() {
+    private val dumpManager: DumpManager = mock()
+    private val systemClock = FakeSystemClock()
+    private val underTest = TableLogBufferFactory(dumpManager, systemClock)
+
+    @Test
+    fun `create - always creates new instance`() {
+        val b1 = underTest.create(NAME_1, SIZE)
+        val b1_copy = underTest.create(NAME_1, SIZE)
+        val b2 = underTest.create(NAME_2, SIZE)
+        val b2_copy = underTest.create(NAME_2, SIZE)
+
+        assertThat(b1).isNotSameInstanceAs(b1_copy)
+        assertThat(b1).isNotSameInstanceAs(b2)
+        assertThat(b2).isNotSameInstanceAs(b2_copy)
+    }
+
+    @Test
+    fun `getOrCreate - reuses instance`() {
+        val b1 = underTest.getOrCreate(NAME_1, SIZE)
+        val b1_copy = underTest.getOrCreate(NAME_1, SIZE)
+        val b2 = underTest.getOrCreate(NAME_2, SIZE)
+        val b2_copy = underTest.getOrCreate(NAME_2, SIZE)
+
+        assertThat(b1).isSameInstanceAs(b1_copy)
+        assertThat(b2).isSameInstanceAs(b2_copy)
+        assertThat(b1).isNotSameInstanceAs(b2)
+        assertThat(b1_copy).isNotSameInstanceAs(b2_copy)
+    }
+
+    companion object {
+        const val NAME_1 = "name 1"
+        const val NAME_2 = "name 2"
+
+        const val SIZE = 8
+    }
+}
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 039dd4d..e4e95e5 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
@@ -20,6 +20,7 @@
 import android.content.res.Configuration
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import android.util.MathUtils.abs
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.InstanceId
 import com.android.systemui.SysuiTestCase
@@ -31,14 +32,11 @@
 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.MediaCarouselController.Companion.ANIMATION_BASE_DURATION
-import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.DURATION
-import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.PAGINATION_DELAY
-import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.TRANSFORM_BEZIER
 import com.android.systemui.media.controls.ui.MediaHierarchyManager.Companion.LOCATION_QS
 import com.android.systemui.media.controls.util.MediaUiEventLogger
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.qs.PageIndicator
 import com.android.systemui.statusbar.notification.collection.provider.OnReorderingAllowedListener
 import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider
 import com.android.systemui.statusbar.policy.ConfigurationController
@@ -56,6 +54,7 @@
 import org.mockito.ArgumentCaptor
 import org.mockito.Captor
 import org.mockito.Mock
+import org.mockito.Mockito.floatThat
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
@@ -86,6 +85,8 @@
     @Mock lateinit var debugLogger: MediaCarouselControllerLogger
     @Mock lateinit var mediaViewController: MediaViewController
     @Mock lateinit var smartspaceMediaData: SmartspaceMediaData
+    @Mock lateinit var mediaCarousel: MediaScrollView
+    @Mock lateinit var pageIndicator: PageIndicator
     @Captor lateinit var listener: ArgumentCaptor<MediaDataManager.Listener>
     @Captor
     lateinit var configListener: ArgumentCaptor<ConfigurationController.ConfigurationListener>
@@ -647,25 +648,22 @@
     @Test
     fun testSetCurrentState_UpdatePageIndicatorAlphaWhenSquish() {
         val delta = 0.0001F
-        val paginationSquishMiddle =
-            TRANSFORM_BEZIER.getInterpolation(
-                (PAGINATION_DELAY + DURATION / 2) / ANIMATION_BASE_DURATION
-            )
-        val paginationSquishEnd =
-            TRANSFORM_BEZIER.getInterpolation(
-                (PAGINATION_DELAY + DURATION) / ANIMATION_BASE_DURATION
-            )
+        mediaCarouselController.mediaCarousel = mediaCarousel
+        mediaCarouselController.pageIndicator = pageIndicator
+        whenever(mediaCarousel.measuredHeight).thenReturn(100)
+        whenever(pageIndicator.translationY).thenReturn(80F)
+        whenever(pageIndicator.height).thenReturn(10)
         whenever(mediaHostStatesManager.mediaHostStates)
             .thenReturn(mutableMapOf(LOCATION_QS to mediaHostState))
         whenever(mediaHostState.visible).thenReturn(true)
         mediaCarouselController.currentEndLocation = LOCATION_QS
-        whenever(mediaHostState.squishFraction).thenReturn(paginationSquishMiddle)
+        whenever(mediaHostState.squishFraction).thenReturn(0.938F)
         mediaCarouselController.updatePageIndicatorAlpha()
-        assertEquals(mediaCarouselController.pageIndicator.alpha, 0.5F, delta)
+        verify(pageIndicator).alpha = floatThat { abs(it - 0.5F) < delta }
 
-        whenever(mediaHostState.squishFraction).thenReturn(paginationSquishEnd)
+        whenever(mediaHostState.squishFraction).thenReturn(1.0F)
         mediaCarouselController.updatePageIndicatorAlpha()
-        assertEquals(mediaCarouselController.pageIndicator.alpha, 1.0F, delta)
+        verify(pageIndicator).alpha = floatThat { abs(it - 1.0F) < delta }
     }
 
     @Ignore("b/253229241")
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 35b0eb6..4ed6d7c 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,13 +22,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.ANIMATION_BASE_DURATION
-import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.CONTROLS_DELAY
-import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.DETAILS_DELAY
-import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.DURATION
-import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.MEDIACONTAINERS_DELAY
-import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.MEDIATITLES_DELAY
-import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.TRANSFORM_BEZIER
 import com.android.systemui.util.animation.MeasurementInput
 import com.android.systemui.util.animation.TransitionLayout
 import com.android.systemui.util.animation.TransitionViewState
@@ -60,9 +53,10 @@
     @Mock private lateinit var controlWidgetState: WidgetState
     @Mock private lateinit var bgWidgetState: WidgetState
     @Mock private lateinit var mediaTitleWidgetState: WidgetState
+    @Mock private lateinit var mediaSubTitleWidgetState: WidgetState
     @Mock private lateinit var mediaContainerWidgetState: WidgetState
 
-    val delta = 0.0001F
+    val delta = 0.1F
 
     private lateinit var mediaViewController: MediaViewController
 
@@ -76,10 +70,11 @@
     @Test
     fun testObtainViewState_applySquishFraction_toPlayerTransitionViewState_height() {
         mediaViewController.attach(player, MediaViewController.TYPE.PLAYER)
-        player.measureState = TransitionViewState().apply {
-            this.height = 100
-            this.measureHeight = 100
-        }
+        player.measureState =
+            TransitionViewState().apply {
+                this.height = 100
+                this.measureHeight = 100
+            }
         mediaHostStateHolder.expansion = 1f
         val widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY)
         val heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY)
@@ -128,29 +123,21 @@
                     R.id.header_artist to detailWidgetState
                 )
             )
-
-        val detailSquishMiddle =
-            TRANSFORM_BEZIER.getInterpolation(
-                (DETAILS_DELAY + DURATION / 2) / ANIMATION_BASE_DURATION
-            )
-        mediaViewController.squishViewState(mockViewState, detailSquishMiddle)
+        whenever(mockCopiedState.measureHeight).thenReturn(200)
+        // detail widgets occupy [90, 100]
+        whenever(detailWidgetState.y).thenReturn(90F)
+        whenever(detailWidgetState.height).thenReturn(10)
+        // control widgets occupy [150, 170]
+        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 }
-
-        val detailSquishEnd =
-            TRANSFORM_BEZIER.getInterpolation((DETAILS_DELAY + DURATION) / ANIMATION_BASE_DURATION)
-        mediaViewController.squishViewState(mockViewState, detailSquishEnd)
+        mediaViewController.squishViewState(mockViewState, 150F / 200F)
         verify(detailWidgetState).alpha = floatThat { kotlin.math.abs(it - 1.0F) < delta }
-
-        val controlSquishMiddle =
-            TRANSFORM_BEZIER.getInterpolation(
-                (CONTROLS_DELAY + DURATION / 2) / ANIMATION_BASE_DURATION
-            )
-        mediaViewController.squishViewState(mockViewState, controlSquishMiddle)
+        mediaViewController.squishViewState(mockViewState, 181.4F / 200F)
         verify(controlWidgetState).alpha = floatThat { kotlin.math.abs(it - 0.5F) < delta }
-
-        val controlSquishEnd =
-            TRANSFORM_BEZIER.getInterpolation((CONTROLS_DELAY + DURATION) / ANIMATION_BASE_DURATION)
-        mediaViewController.squishViewState(mockViewState, controlSquishEnd)
+        mediaViewController.squishViewState(mockViewState, 200F / 200F)
         verify(controlWidgetState).alpha = floatThat { kotlin.math.abs(it - 1.0F) < delta }
     }
 
@@ -161,36 +148,33 @@
             .thenReturn(
                 mutableMapOf(
                     R.id.media_title1 to mediaTitleWidgetState,
+                    R.id.media_subtitle1 to mediaSubTitleWidgetState,
                     R.id.media_cover1_container to mediaContainerWidgetState
                 )
             )
+        whenever(mockCopiedState.measureHeight).thenReturn(360)
+        // media container widgets occupy [20, 300]
+        whenever(mediaContainerWidgetState.y).thenReturn(20F)
+        whenever(mediaContainerWidgetState.height).thenReturn(280)
+        // media title widgets occupy [320, 330]
+        whenever(mediaTitleWidgetState.y).thenReturn(320F)
+        whenever(mediaTitleWidgetState.height).thenReturn(10)
+        // media subtitle widgets occupy [340, 350]
+        whenever(mediaSubTitleWidgetState.y).thenReturn(340F)
+        whenever(mediaSubTitleWidgetState.height).thenReturn(10)
 
-        val containerSquishMiddle =
-            TRANSFORM_BEZIER.getInterpolation(
-                (MEDIACONTAINERS_DELAY + DURATION / 2) / ANIMATION_BASE_DURATION
-            )
-        mediaViewController.squishViewState(mockViewState, containerSquishMiddle)
+        // in current beizer, when the progress reach 0.38, the result will be 0.5
+        mediaViewController.squishViewState(mockViewState, 307.6F / 360F)
         verify(mediaContainerWidgetState).alpha = floatThat { kotlin.math.abs(it - 0.5F) < delta }
-
-        val containerSquishEnd =
-            TRANSFORM_BEZIER.getInterpolation(
-                (MEDIACONTAINERS_DELAY + DURATION) / ANIMATION_BASE_DURATION
-            )
-        mediaViewController.squishViewState(mockViewState, containerSquishEnd)
+        mediaViewController.squishViewState(mockViewState, 320F / 360F)
         verify(mediaContainerWidgetState).alpha = floatThat { kotlin.math.abs(it - 1.0F) < delta }
-
-        val titleSquishMiddle =
-            TRANSFORM_BEZIER.getInterpolation(
-                (MEDIATITLES_DELAY + DURATION / 2) / ANIMATION_BASE_DURATION
-            )
-        mediaViewController.squishViewState(mockViewState, titleSquishMiddle)
+        // media title and media subtitle are in same widget group, should be calculate together and
+        // have same alpha
+        mediaViewController.squishViewState(mockViewState, 353.8F / 360F)
         verify(mediaTitleWidgetState).alpha = floatThat { kotlin.math.abs(it - 0.5F) < delta }
-
-        val titleSquishEnd =
-            TRANSFORM_BEZIER.getInterpolation(
-                (MEDIATITLES_DELAY + DURATION) / ANIMATION_BASE_DURATION
-            )
-        mediaViewController.squishViewState(mockViewState, titleSquishEnd)
+        verify(mediaSubTitleWidgetState).alpha = floatThat { kotlin.math.abs(it - 0.5F) < delta }
+        mediaViewController.squishViewState(mockViewState, 360F / 360F)
         verify(mediaTitleWidgetState).alpha = floatThat { kotlin.math.abs(it - 1.0F) < delta }
+        verify(mediaSubTitleWidgetState).alpha = floatThat { kotlin.math.abs(it - 1.0F) < delta }
     }
 }
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 ca3182a..3281fa9 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
@@ -28,7 +28,6 @@
 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.CellularTile
 import com.android.systemui.qs.tiles.ColorCorrectionTile
 import com.android.systemui.qs.tiles.ColorInversionTile
 import com.android.systemui.qs.tiles.DataSaverTile
@@ -49,7 +48,6 @@
 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.WifiTile
 import com.android.systemui.qs.tiles.WorkModeTile
 import com.android.systemui.util.leak.GarbageMonitor
 import com.google.common.truth.Truth.assertThat
@@ -63,10 +61,8 @@
 import org.mockito.Mockito.`when` as whenever
 
 private val specMap = mapOf(
-        "wifi" to WifiTile::class.java,
         "internet" to InternetTile::class.java,
         "bt" to BluetoothTile::class.java,
-        "cell" to CellularTile::class.java,
         "dnd" to DndTile::class.java,
         "inversion" to ColorInversionTile::class.java,
         "airplane" to AirplaneModeTile::class.java,
@@ -102,10 +98,8 @@
     @Mock(answer = Answers.RETURNS_SELF) private lateinit var customTileBuilder: CustomTile.Builder
     @Mock private lateinit var customTile: CustomTile
 
-    @Mock private lateinit var wifiTile: WifiTile
     @Mock private lateinit var internetTile: InternetTile
     @Mock private lateinit var bluetoothTile: BluetoothTile
-    @Mock private lateinit var cellularTile: CellularTile
     @Mock private lateinit var dndTile: DndTile
     @Mock private lateinit var colorInversionTile: ColorInversionTile
     @Mock private lateinit var airplaneTile: AirplaneModeTile
@@ -146,10 +140,8 @@
         factory = QSFactoryImpl(
                 { qsHost },
                 { customTileBuilder },
-                { wifiTile },
                 { internetTile },
                 { bluetoothTile },
-                { cellularTile },
                 { dndTile },
                 { colorInversionTile },
                 { airplaneTile },
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
new file mode 100644
index 0000000..3710281
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
@@ -0,0 +1,100 @@
+package com.android.systemui.settings
+
+import android.content.Context
+import android.content.Intent
+import android.content.pm.UserInfo
+import android.os.Handler
+import android.os.UserHandle
+import android.os.UserManager
+import androidx.concurrent.futures.DirectExecutor
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.mockito.any
+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.junit.runners.Parameterized
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(Parameterized::class)
+class UserTrackerImplReceiveTest : SysuiTestCase() {
+
+    companion object {
+
+        @JvmStatic
+        @Parameterized.Parameters
+        fun data(): Iterable<String> =
+            listOf(
+                Intent.ACTION_USER_INFO_CHANGED,
+                Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
+                Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
+                Intent.ACTION_MANAGED_PROFILE_ADDED,
+                Intent.ACTION_MANAGED_PROFILE_REMOVED,
+                Intent.ACTION_MANAGED_PROFILE_UNLOCKED
+            )
+    }
+
+    private val executor: Executor = DirectExecutor.INSTANCE
+
+    @Mock private lateinit var context: Context
+    @Mock private lateinit var userManager: UserManager
+    @Mock(stubOnly = true) private lateinit var dumpManager: DumpManager
+    @Mock(stubOnly = true) private lateinit var handler: Handler
+
+    @Parameterized.Parameter lateinit var intentAction: String
+    @Mock private lateinit var callback: UserTracker.Callback
+    @Captor private lateinit var captor: ArgumentCaptor<List<UserInfo>>
+
+    private lateinit var tracker: UserTrackerImpl
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        `when`(context.user).thenReturn(UserHandle.SYSTEM)
+        `when`(context.createContextAsUser(ArgumentMatchers.any(), anyInt())).thenReturn(context)
+
+        tracker = UserTrackerImpl(context, userManager, dumpManager, handler)
+    }
+
+    @Test
+    fun `calls callback and updates profiles when an intent received`() {
+        tracker.initialize(0)
+        tracker.addCallback(callback, executor)
+        val profileID = tracker.userId + 10
+
+        `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
+            val id = invocation.getArgument<Int>(0)
+            val info = UserInfo(id, "", UserInfo.FLAG_FULL)
+            val infoProfile =
+                UserInfo(
+                    id + 10,
+                    "",
+                    "",
+                    UserInfo.FLAG_MANAGED_PROFILE,
+                    UserManager.USER_TYPE_PROFILE_MANAGED
+                )
+            infoProfile.profileGroupId = id
+            listOf(info, infoProfile)
+        }
+
+        tracker.onReceive(context, Intent(intentAction))
+
+        verify(callback, times(0)).onUserChanged(anyInt(), any())
+        verify(callback, times(1)).onProfilesChanged(capture(captor))
+        assertThat(captor.value.map { it.id }).containsExactly(0, profileID)
+    }
+}
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 52462c7..e65bbb1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -124,6 +124,16 @@
 
         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(hasAction(Intent.ACTION_USER_INFO_CHANGED)).isTrue()
+            assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)).isTrue()
+            assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)).isTrue()
+            assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_ADDED)).isTrue()
+            assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_REMOVED)).isTrue()
+            assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED)).isTrue()
+        }
     }
 
     @Test
@@ -280,37 +290,6 @@
     }
 
     @Test
-    fun testCallbackCalledOnProfileChanged() {
-        tracker.initialize(0)
-        val callback = TestCallback()
-        tracker.addCallback(callback, executor)
-        val profileID = tracker.userId + 10
-
-        `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
-            val id = invocation.getArgument<Int>(0)
-            val info = UserInfo(id, "", UserInfo.FLAG_FULL)
-            val infoProfile = UserInfo(
-                    id + 10,
-                    "",
-                    "",
-                    UserInfo.FLAG_MANAGED_PROFILE,
-                    UserManager.USER_TYPE_PROFILE_MANAGED
-            )
-            infoProfile.profileGroupId = id
-            listOf(info, infoProfile)
-        }
-
-        val intent = Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
-                .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
-
-        tracker.onReceive(context, intent)
-
-        assertThat(callback.calledOnUserChanged).isEqualTo(0)
-        assertThat(callback.calledOnProfilesChanged).isEqualTo(1)
-        assertThat(callback.lastUserProfiles.map { it.id }).containsExactly(0, profileID)
-    }
-
-    @Test
     fun testCallbackCalledOnUserInfoChanged() {
         tracker.initialize(0)
         val callback = TestCallback()
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 88651c1..f802a5e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.shade
 
 import android.testing.AndroidTestingRunner
+import android.view.ViewGroup
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
 import androidx.constraintlayout.widget.ConstraintSet.START
@@ -92,12 +93,12 @@
             assertThat(getConstraint(R.id.clock).layout.horizontalBias).isEqualTo(0f)
 
             assertThat(getConstraint(R.id.date).layout.startToStart).isEqualTo(PARENT_ID)
-            assertThat(getConstraint(R.id.date).layout.horizontalBias).isEqualTo(0f)
+            assertThat(getConstraint(R.id.date).layout.horizontalBias).isEqualTo(0.5f)
 
             assertThat(getConstraint(R.id.batteryRemainingIcon).layout.endToEnd)
                 .isEqualTo(PARENT_ID)
             assertThat(getConstraint(R.id.batteryRemainingIcon).layout.horizontalBias)
-                .isEqualTo(1f)
+                .isEqualTo(0.5f)
 
             assertThat(getConstraint(R.id.privacy_container).layout.endToEnd)
                 .isEqualTo(R.id.end_guide)
@@ -331,10 +332,8 @@
         val views = mapOf(
                 R.id.clock to "clock",
                 R.id.date to "date",
-                R.id.statusIcons to "icons",
                 R.id.privacy_container to "privacy",
                 R.id.carrier_group to "carriers",
-                R.id.batteryRemainingIcon to "battery",
         )
         views.forEach { (id, name) ->
             assertWithMessage("$name has 0 height in qqs")
@@ -352,11 +351,8 @@
     fun testCheckViewsDontChangeSizeBetweenAnimationConstraints() {
         val views = mapOf(
                 R.id.clock to "clock",
-                R.id.date to "date",
-                R.id.statusIcons to "icons",
                 R.id.privacy_container to "privacy",
                 R.id.carrier_group to "carriers",
-                R.id.batteryRemainingIcon to "battery",
         )
         views.forEach { (id, name) ->
             expect.withMessage("$name changes height")
@@ -369,8 +365,8 @@
     }
 
     private fun Int.fromConstraint() = when (this) {
-        -1 -> "MATCH_PARENT"
-        -2 -> "WRAP_CONTENT"
+        ViewGroup.LayoutParams.MATCH_PARENT -> "MATCH_PARENT"
+        ViewGroup.LayoutParams.WRAP_CONTENT -> "WRAP_CONTENT"
         else -> toString()
     }
 
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 4ccbc6d..091bb54 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
@@ -24,6 +24,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNotNull;
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.doReturn;
@@ -74,6 +75,7 @@
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.mockito.Spy;
+import org.mockito.stubbing.Answer;
 
 import java.util.Collections;
 import java.util.List;
@@ -115,8 +117,10 @@
     @Spy private PackageManager mPackageManager;
     private final boolean mIsReduceBrightColorsAvailable = true;
 
-    private AutoTileManager mAutoTileManager;
+    private AutoTileManager mAutoTileManager; // under test
+
     private SecureSettings mSecureSettings;
+    private ManagedProfileController.Callback mManagedProfileCallback;
 
     @Before
     public void setUp() throws Exception {
@@ -303,7 +307,7 @@
 
         InOrder inOrderManagedProfile = inOrder(mManagedProfileController);
         inOrderManagedProfile.verify(mManagedProfileController).removeCallback(any());
-        inOrderManagedProfile.verify(mManagedProfileController, never()).addCallback(any());
+        inOrderManagedProfile.verify(mManagedProfileController).addCallback(any());
 
         if (ColorDisplayManager.isNightDisplayAvailable(mContext)) {
             InOrder inOrderNightDisplay = inOrder(mNightDisplayListener);
@@ -504,6 +508,40 @@
     }
 
     @Test
+    public void managedProfileAdded_tileAdded() {
+        when(mAutoAddTracker.isAdded(eq("work"))).thenReturn(false);
+        mAutoTileManager = createAutoTileManager(mContext);
+        Mockito.doAnswer((Answer<Object>) invocation -> {
+            mManagedProfileCallback = invocation.getArgument(0);
+            return null;
+        }).when(mManagedProfileController).addCallback(any());
+        mAutoTileManager.init();
+        when(mManagedProfileController.hasActiveProfile()).thenReturn(true);
+
+        mManagedProfileCallback.onManagedProfileChanged();
+
+        verify(mQsTileHost, times(1)).addTile(eq("work"));
+        verify(mAutoAddTracker, times(1)).setTileAdded(eq("work"));
+    }
+
+    @Test
+    public void managedProfileRemoved_tileRemoved() {
+        when(mAutoAddTracker.isAdded(eq("work"))).thenReturn(true);
+        mAutoTileManager = createAutoTileManager(mContext);
+        Mockito.doAnswer((Answer<Object>) invocation -> {
+            mManagedProfileCallback = invocation.getArgument(0);
+            return null;
+        }).when(mManagedProfileController).addCallback(any());
+        mAutoTileManager.init();
+        when(mManagedProfileController.hasActiveProfile()).thenReturn(false);
+
+        mManagedProfileCallback.onManagedProfileChanged();
+
+        verify(mQsTileHost, times(1)).removeTile(eq("work"));
+        verify(mAutoAddTracker, times(1)).setTileRemoved(eq("work"));
+    }
+
+    @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/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 74f8c61..daf7dd0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -57,6 +59,7 @@
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -122,6 +125,7 @@
     private VibratorHelper mVibratorHelper;
     @Mock
     private BiometricUnlockLogger mLogger;
+    private final FakeSystemClock mSystemClock = new FakeSystemClock();
     private BiometricUnlockController mBiometricUnlockController;
 
     @Before
@@ -144,7 +148,9 @@
                 mMetricsLogger, mDumpManager, mPowerManager, mLogger,
                 mNotificationMediaManager, mWakefulnessLifecycle, mScreenLifecycle,
                 mAuthController, mStatusBarStateController, mKeyguardUnlockAnimationController,
-                mSessionTracker, mLatencyTracker, mScreenOffAnimationController, mVibratorHelper);
+                mSessionTracker, mLatencyTracker, mScreenOffAnimationController, mVibratorHelper,
+                mSystemClock
+        );
         mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
         mBiometricUnlockController.addBiometricModeListener(mBiometricModeListener);
         when(mUpdateMonitor.getStrongAuthTracker()).thenReturn(mStrongAuthTracker);
@@ -207,7 +213,7 @@
 
         verify(mKeyguardViewMediator).onWakeAndUnlocking();
         assertThat(mBiometricUnlockController.getMode())
-                .isEqualTo(BiometricUnlockController.MODE_WAKE_AND_UNLOCK);
+                .isEqualTo(MODE_WAKE_AND_UNLOCK);
     }
 
     @Test
@@ -457,4 +463,83 @@
         // THEN wakeup the device
         verify(mPowerManager).wakeUp(anyLong(), anyInt(), anyString());
     }
+
+    @Test
+    public void onSideFingerprintSuccess_recentPowerButtonPress_noHaptic() {
+        // GIVEN side fingerprint enrolled, last wake reason was power button
+        when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
+        when(mWakefulnessLifecycle.getLastWakeReason())
+                .thenReturn(PowerManager.WAKE_REASON_POWER_BUTTON);
+
+        // GIVEN last wake time just occurred
+        when(mWakefulnessLifecycle.getLastWakeTime()).thenReturn(mSystemClock.uptimeMillis());
+
+        // WHEN biometric fingerprint succeeds
+        givenFingerprintModeUnlockCollapsing();
+        mBiometricUnlockController.startWakeAndUnlock(BiometricSourceType.FINGERPRINT,
+                true);
+
+        // THEN DO NOT vibrate the device
+        verify(mVibratorHelper, never()).vibrateAuthSuccess(anyString());
+    }
+
+    @Test
+    public void onSideFingerprintSuccess_oldPowerButtonPress_playHaptic() {
+        // GIVEN side fingerprint enrolled, last wake reason was power button
+        when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
+        when(mWakefulnessLifecycle.getLastWakeReason())
+                .thenReturn(PowerManager.WAKE_REASON_POWER_BUTTON);
+
+        // GIVEN last wake time was 500ms ago
+        when(mWakefulnessLifecycle.getLastWakeTime()).thenReturn(mSystemClock.uptimeMillis());
+        mSystemClock.advanceTime(500);
+
+        // WHEN biometric fingerprint succeeds
+        givenFingerprintModeUnlockCollapsing();
+        mBiometricUnlockController.startWakeAndUnlock(BiometricSourceType.FINGERPRINT,
+                true);
+
+        // THEN vibrate the device
+        verify(mVibratorHelper).vibrateAuthSuccess(anyString());
+    }
+
+    @Test
+    public void onSideFingerprintSuccess_recentGestureWakeUp_playHaptic() {
+        // GIVEN side fingerprint enrolled, wakeup just happened
+        when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
+        when(mWakefulnessLifecycle.getLastWakeTime()).thenReturn(mSystemClock.uptimeMillis());
+
+        // GIVEN last wake reason was from a gesture
+        when(mWakefulnessLifecycle.getLastWakeReason())
+                .thenReturn(PowerManager.WAKE_REASON_GESTURE);
+
+        // WHEN biometric fingerprint succeeds
+        givenFingerprintModeUnlockCollapsing();
+        mBiometricUnlockController.startWakeAndUnlock(BiometricSourceType.FINGERPRINT,
+                true);
+
+        // THEN vibrate the device
+        verify(mVibratorHelper).vibrateAuthSuccess(anyString());
+    }
+
+    @Test
+    public void onSideFingerprintFail_alwaysPlaysHaptic() {
+        // GIVEN side fingerprint enrolled, last wake reason was recent power button
+        when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
+        when(mWakefulnessLifecycle.getLastWakeReason())
+                .thenReturn(PowerManager.WAKE_REASON_POWER_BUTTON);
+        when(mWakefulnessLifecycle.getLastWakeTime()).thenReturn(mSystemClock.uptimeMillis());
+
+        // WHEN biometric fingerprint fails
+        mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
+
+        // THEN always vibrate the device
+        verify(mVibratorHelper).vibrateAuthError(anyString());
+    }
+
+    private void givenFingerprintModeUnlockCollapsing() {
+        when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
+        when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true);
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+    }
 }
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 5a21945..c8157cc 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
@@ -380,7 +380,8 @@
         }).when(mStatusBarKeyguardViewManager).addAfterKeyguardGoneRunnable(any());
 
         mWakefulnessLifecycle =
-                new WakefulnessLifecycle(mContext, mIWallpaperManager, mDumpManager);
+                new WakefulnessLifecycle(mContext, mIWallpaperManager, mFakeSystemClock,
+                        mDumpManager);
         mWakefulnessLifecycle.dispatchStartedWakingUp(PowerManager.WAKE_REASON_UNKNOWN);
         mWakefulnessLifecycle.dispatchFinishedWakingUp();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
index 077b41a..c843850 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
@@ -23,6 +23,10 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+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.content.res.Resources;
@@ -39,10 +43,9 @@
 import com.android.systemui.doze.AlwaysOnDisplayPolicy;
 import com.android.systemui.doze.DozeScreenState;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 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.tuner.TunerService;
 import com.android.systemui.unfold.FoldAodAnimationController;
@@ -52,6 +55,8 @@
 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;
 
@@ -69,7 +74,6 @@
     @Mock private PowerManager mPowerManager;
     @Mock private TunerService mTunerService;
     @Mock private BatteryController mBatteryController;
-    @Mock private FeatureFlags mFeatureFlags;
     @Mock private DumpManager mDumpManager;
     @Mock private ScreenOffAnimationController mScreenOffAnimationController;
     @Mock private FoldAodAnimationController mFoldAodAnimationController;
@@ -78,6 +82,7 @@
     @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Mock private StatusBarStateController mStatusBarStateController;
     @Mock private ConfigurationController mConfigurationController;
+    @Captor private ArgumentCaptor<BatteryStateChangeCallback> mBatteryStateChangeCallback;
 
     /**
      * The current value of PowerManager's dozeAfterScreenOff property.
@@ -113,7 +118,6 @@
             mBatteryController,
             mTunerService,
             mDumpManager,
-            mFeatureFlags,
             mScreenOffAnimationController,
             Optional.of(mSysUIUnfoldComponent),
             mUnlockedScreenOffAnimationController,
@@ -122,7 +126,8 @@
             mStatusBarStateController
         );
 
-        when(mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ANIMATIONS)).thenReturn(true);
+        verify(mBatteryController).addCallback(mBatteryStateChangeCallback.capture());
+
         setAodEnabledForTest(true);
         setShouldControlUnlockedScreenOffForTest(true);
         setDisplayNeedsBlankingForTest(false);
@@ -173,6 +178,29 @@
         assertThat(mDozeParameters.getAlwaysOn()).isFalse();
     }
 
+    @Test
+    public void testGetAlwaysOn_whenBatterySaverCallback() {
+        DozeParameters.Callback callback = mock(DozeParameters.Callback.class);
+        mDozeParameters.addCallback(callback);
+
+        when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
+        when(mBatteryController.isAodPowerSave()).thenReturn(true);
+
+        // Both lines should trigger an event
+        mDozeParameters.onTuningChanged(Settings.Secure.DOZE_ALWAYS_ON, "1");
+        mBatteryStateChangeCallback.getValue().onPowerSaveChanged(true);
+
+        verify(callback, times(2)).onAlwaysOnChange();
+        assertThat(mDozeParameters.getAlwaysOn()).isFalse();
+
+        reset(callback);
+        when(mBatteryController.isAodPowerSave()).thenReturn(false);
+        mBatteryStateChangeCallback.getValue().onPowerSaveChanged(true);
+
+        verify(callback).onAlwaysOnChange();
+        assertThat(mDozeParameters.getAlwaysOn()).isTrue();
+    }
+
     /**
      * PowerManager.setDozeAfterScreenOff(true) means we are not controlling screen off, and calling
      * it with false means we are. Confusing, but sure - make sure that we call PowerManager with
@@ -196,17 +224,6 @@
     }
 
     @Test
-    public void testControlUnlockedScreenOffAnimationDisabled_dozeAfterScreenOff() {
-        when(mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ANIMATIONS)).thenReturn(false);
-
-        assertFalse(mDozeParameters.shouldControlUnlockedScreenOff());
-
-        // Trigger the setter for the current value.
-        mDozeParameters.setControlScreenOffAnimation(mDozeParameters.shouldControlScreenOff());
-        assertFalse(mDozeParameters.shouldControlScreenOff());
-    }
-
-    @Test
     public void propagatesAnimateScreenOff_noAlwaysOn() {
         setAodEnabledForTest(false);
         setDisplayNeedsBlankingForTest(false);
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 0da15e2..0958970 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
@@ -37,6 +37,7 @@
 import com.android.systemui.log.table.TableLogBuffer
 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.NetworkNameModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
@@ -46,6 +47,7 @@
 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
 import kotlinx.coroutines.Dispatchers
@@ -94,7 +96,7 @@
             }
         }
 
-        whenever(logBufferFactory.create(anyString(), anyInt())).thenAnswer { _ ->
+        whenever(logBufferFactory.getOrCreate(anyString(), anyInt())).thenAnswer { _ ->
             mock<TableLogBuffer>()
         }
 
@@ -292,13 +294,13 @@
             // Get repos to trigger creation
             underTest.getRepoForSubId(SUB_1_ID)
             verify(logBufferFactory)
-                .create(
+                .getOrCreate(
                     eq(MobileConnectionRepositoryImpl.tableBufferLogName(SUB_1_ID)),
                     anyInt(),
                 )
             underTest.getRepoForSubId(SUB_2_ID)
             verify(logBufferFactory)
-                .create(
+                .getOrCreate(
                     eq(MobileConnectionRepositoryImpl.tableBufferLogName(SUB_2_ID)),
                     anyInt(),
                 )
@@ -307,6 +309,46 @@
         }
 
     @Test
+    fun `connection repository factory - reuses log buffers for same connection`() =
+        runBlocking(IMMEDIATE) {
+            val realLoggerFactory = TableLogBufferFactory(mock(), FakeSystemClock())
+
+            connectionFactory =
+                MobileConnectionRepositoryImpl.Factory(
+                    fakeBroadcastDispatcher,
+                    context = context,
+                    telephonyManager = telephonyManager,
+                    bgDispatcher = IMMEDIATE,
+                    globalSettings = globalSettings,
+                    logger = logger,
+                    mobileMappingsProxy = mobileMappings,
+                    scope = scope,
+                    logFactory = realLoggerFactory,
+                )
+
+            // Create two connections for the same subId. Similar to if the connection appeared
+            // and disappeared from the connectionFactory's perspective
+            val connection1 =
+                connectionFactory.build(
+                    1,
+                    NetworkNameModel.Default("default_name"),
+                    "-",
+                    underTest.globalMobileDataSettingChangedEvent,
+                )
+
+            val connection1_repeat =
+                connectionFactory.build(
+                    1,
+                    NetworkNameModel.Default("default_name"),
+                    "-",
+                    underTest.globalMobileDataSettingChangedEvent,
+                )
+
+            assertThat(connection1.tableLogBuffer)
+                .isSameInstanceAs(connection1_repeat.tableLogBuffer)
+        }
+
+    @Test
     fun mobileConnectivity_default() {
         assertThat(underTest.defaultMobileNetworkConnectivity.value)
             .isEqualTo(MobileConnectivityModel(isConnected = false, isValidated = false))
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/MemoryTrackingTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/MemoryTrackingTestCase.java
index 3767fbe..3428553 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/MemoryTrackingTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/MemoryTrackingTestCase.java
@@ -40,24 +40,49 @@
 public class MemoryTrackingTestCase extends SysuiTestCase {
     private static File sFilesDir = null;
     private static String sLatestTestClassName = null;
+    private static int sHeapCount = 0;
+    private static File sLatestBaselineHeapFile = null;
 
-    @Before public void grabFilesDir() {
+    // Ideally, we would do this in @BeforeClass just once, but we need mContext to get the files
+    // dir, and that does not exist until @Before on each test method.
+    @Before
+    public void grabFilesDir() throws IOException {
+        // This should happen only once per suite
         if (sFilesDir == null) {
             sFilesDir = mContext.getFilesDir();
         }
-        sLatestTestClassName = getClass().getName();
+
+        // This will happen before the first test method in each class
+        if (sLatestTestClassName == null) {
+            sLatestTestClassName = getClass().getName();
+            sLatestBaselineHeapFile = dump("baseline" + (++sHeapCount), "before-test");
+        }
     }
 
     @AfterClass
     public static void dumpHeap() throws IOException {
+        File afterTestHeap = dump(sLatestTestClassName, "after-test");
+        if (sLatestBaselineHeapFile != null && afterTestHeap != null) {
+            Log.w("MEMORY", "To compare heap to baseline (use go/ahat):");
+            Log.w("MEMORY", "  adb pull " + sLatestBaselineHeapFile);
+            Log.w("MEMORY", "  adb pull " + afterTestHeap);
+            Log.w("MEMORY",
+                    "  java -jar ahat.jar --baseline " + sLatestBaselineHeapFile.getName() + " "
+                            + afterTestHeap.getName());
+        }
+        sLatestTestClassName = null;
+    }
+
+    private static File dump(String basename, String heapKind) throws IOException {
         if (sFilesDir == null) {
             Log.e("MEMORY", "Somehow no test cases??");
-            return;
+            return null;
         }
         mockitoTearDown();
-        Log.w("MEMORY", "about to dump heap");
-        File path = new File(sFilesDir, sLatestTestClassName + ".ahprof");
+        Log.w("MEMORY", "about to dump " + heapKind + " heap");
+        File path = new File(sFilesDir, basename + ".ahprof");
         Debug.dumpHprofData(path.getPath());
-        Log.w("MEMORY", "did it!  Location: " + path);
+        Log.w("MEMORY", "Success!  Location: " + path);
+        return path;
     }
 }
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 39d2eca..15b4736 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
@@ -52,6 +52,9 @@
     private val _isDozing = MutableStateFlow(false)
     override val isDozing: Flow<Boolean> = _isDozing
 
+    private val _isAodAvailable = MutableStateFlow(false)
+    override val isAodAvailable: Flow<Boolean> = _isAodAvailable
+
     private val _isDreaming = MutableStateFlow(false)
     override val isDreaming: Flow<Boolean> = _isDreaming
 
@@ -126,6 +129,10 @@
         _isDozing.value = isDozing
     }
 
+    fun setAodAvailable(isAodAvailable: Boolean) {
+        _isAodAvailable.value = isAodAvailable
+    }
+
     fun setDreamingWithOverlay(isDreaming: Boolean) {
         _isDreamingWithOverlay.value = isDreaming
     }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 148797f..1bd8f1e 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3603,9 +3603,11 @@
             setRingerMode(getNewRingerMode(stream, index, flags),
                     TAG + ".onSetStreamVolume", false /*external*/);
         }
-        // setting non-zero volume for a muted stream unmutes the stream and vice versa,
+        // 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) {
+        if ((streamType != AudioSystem.STREAM_BLUETOOTH_SCO)
+                && (getDeviceForStream(stream) == device)) {
             mStreamStates[stream].mute(index == 0);
         }
     }
diff --git a/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java b/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
index 9dd2f84..b9ca57e 100644
--- a/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
+++ b/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
@@ -76,6 +76,10 @@
         return layout;
     }
 
+    int size() {
+        return mLayoutMap.size();
+    }
+
     private Layout createLayout(int state) {
         if (mLayoutMap.contains(state)) {
             Slog.e(TAG, "Attempted to create a second layout for state " + state);
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 9278743..2b7fbfb 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -402,6 +402,8 @@
     private static final String STABLE_ID_SUFFIX_FORMAT = "id_%d";
     private static final String NO_SUFFIX_FORMAT = "%d";
     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_LOW_REFRESH_RATE = 60;
     private static final int DEFAULT_HIGH_REFRESH_RATE = 0;
     private static final int[] DEFAULT_BRIGHTNESS_THRESHOLDS = new int[]{};
@@ -570,17 +572,29 @@
      * using higher refresh rates, even if display modes with higher refresh rates are available
      * from hardware composer. Only has an effect if the value is non-zero.
      */
-    private int mDefaultHighRefreshRate = DEFAULT_HIGH_REFRESH_RATE;
+    private int mDefaultPeakRefreshRate = DEFAULT_PEAK_REFRESH_RATE;
 
     /**
      * The default refresh rate for a given device. This value sets the higher default
      * refresh rate. If the hardware composer on the device supports display modes with
      * a higher refresh rate than the default value specified here, the framework may use those
      * higher refresh rate modes if an app chooses one by setting preferredDisplayModeId or calling
-     * setFrameRate(). We have historically allowed fallback to mDefaultHighRefreshRate if
-     * mDefaultLowRefreshRate is set to 0, but this is not supported anymore.
+     * setFrameRate(). We have historically allowed fallback to mDefaultPeakRefreshRate if
+     * mDefaultRefreshRate is set to 0, but this is not supported anymore.
      */
-    private int mDefaultLowRefreshRate = DEFAULT_LOW_REFRESH_RATE;
+    private int mDefaultRefreshRate = DEFAULT_REFRESH_RATE;
+
+    /**
+     * 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.
+     */
+    private int mDefaultHighBlockingZoneRefreshRate = DEFAULT_HIGH_REFRESH_RATE;
+
+    /**
+     * Default refresh rate in the zone defined by brightness and ambient thresholds.
+     * If non-positive, then the refresh rate is unchanged even if thresholds are configured.
+     */
+    private int mDefaultLowBlockingZoneRefreshRate = DEFAULT_LOW_REFRESH_RATE;
 
     /**
      * The display uses different gamma curves for different refresh rates. It's hard for panel
@@ -1296,15 +1310,29 @@
     /**
      * @return Default peak refresh rate of the associated display
      */
-    public int getDefaultHighRefreshRate() {
-        return mDefaultHighRefreshRate;
+    public int getDefaultPeakRefreshRate() {
+        return mDefaultPeakRefreshRate;
     }
 
     /**
      * @return Default refresh rate of the associated display
      */
-    public int getDefaultLowRefreshRate() {
-        return mDefaultLowRefreshRate;
+    public int getDefaultRefreshRate() {
+        return mDefaultRefreshRate;
+    }
+
+    /**
+     * @return Default refresh rate in the higher blocking zone of the associated display
+     */
+    public int getDefaultHighBlockingZoneRefreshRate() {
+        return mDefaultHighBlockingZoneRefreshRate;
+    }
+
+    /**
+     * @return Default refresh rate in the lower blocking zone of the associated display
+     */
+    public int getDefaultLowBlockingZoneRefreshRate() {
+        return mDefaultLowBlockingZoneRefreshRate;
     }
 
     /**
@@ -1442,8 +1470,10 @@
                 + ", mDdcAutoBrightnessAvailable= " + mDdcAutoBrightnessAvailable
                 + ", mAutoBrightnessAvailable= " + mAutoBrightnessAvailable
                 + "\n"
-                + ", mDefaultRefreshRate= " + mDefaultLowRefreshRate
-                + ", mDefaultPeakRefreshRate= " + mDefaultHighRefreshRate
+                + ", mDefaultLowBlockingZoneRefreshRate= " + mDefaultLowBlockingZoneRefreshRate
+                + ", mDefaultHighBlockingZoneRefreshRate= " + mDefaultHighBlockingZoneRefreshRate
+                + ", mDefaultPeakRefreshRate= " + mDefaultPeakRefreshRate
+                + ", mDefaultRefreshRate= " + mDefaultRefreshRate
                 + ", mLowDisplayBrightnessThresholds= "
                 + Arrays.toString(mLowDisplayBrightnessThresholds)
                 + ", mLowAmbientBrightnessThresholds= "
@@ -1757,10 +1787,31 @@
         BlockingZoneConfig higherBlockingZoneConfig =
                 (refreshRateConfigs == null) ? null
                         : refreshRateConfigs.getHigherBlockingZoneConfigs();
+        loadPeakDefaultRefreshRate(refreshRateConfigs);
+        loadDefaultRefreshRate(refreshRateConfigs);
         loadLowerRefreshRateBlockingZones(lowerBlockingZoneConfig);
         loadHigherRefreshRateBlockingZones(higherBlockingZoneConfig);
     }
 
+    private void loadPeakDefaultRefreshRate(RefreshRateConfigs refreshRateConfigs) {
+        if (refreshRateConfigs == null || refreshRateConfigs.getDefaultPeakRefreshRate() == null) {
+            mDefaultPeakRefreshRate = mContext.getResources().getInteger(
+                R.integer.config_defaultPeakRefreshRate);
+        } else {
+            mDefaultPeakRefreshRate =
+                refreshRateConfigs.getDefaultPeakRefreshRate().intValue();
+        }
+    }
+
+    private void loadDefaultRefreshRate(RefreshRateConfigs refreshRateConfigs) {
+        if (refreshRateConfigs == null || refreshRateConfigs.getDefaultRefreshRate() == null) {
+            mDefaultRefreshRate = mContext.getResources().getInteger(
+                R.integer.config_defaultRefreshRate);
+        } else {
+            mDefaultRefreshRate =
+                refreshRateConfigs.getDefaultRefreshRate().intValue();
+        }
+    }
 
     /**
      * Loads the refresh rate configurations pertaining to the upper blocking zones.
@@ -1785,10 +1836,10 @@
     private void loadHigherBlockingZoneDefaultRefreshRate(
                 BlockingZoneConfig upperBlockingZoneConfig) {
         if (upperBlockingZoneConfig == null) {
-            mDefaultHighRefreshRate = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_defaultPeakRefreshRate);
+            mDefaultHighBlockingZoneRefreshRate = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_fixedRefreshRateInHighZone);
         } else {
-            mDefaultHighRefreshRate =
+            mDefaultHighBlockingZoneRefreshRate =
                 upperBlockingZoneConfig.getDefaultRefreshRate().intValue();
         }
     }
@@ -1800,10 +1851,10 @@
     private void loadLowerBlockingZoneDefaultRefreshRate(
                 BlockingZoneConfig lowerBlockingZoneConfig) {
         if (lowerBlockingZoneConfig == null) {
-            mDefaultLowRefreshRate = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_defaultRefreshRate);
+            mDefaultLowBlockingZoneRefreshRate = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_defaultRefreshRateInZone);
         } else {
-            mDefaultLowRefreshRate =
+            mDefaultLowBlockingZoneRefreshRate =
                 lowerBlockingZoneConfig.getDefaultRefreshRate().intValue();
         }
     }
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index aafba5a..fdfc20a 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -1169,7 +1169,7 @@
             mDefaultRefreshRate =
                     (displayDeviceConfig == null) ? (float) mContext.getResources().getInteger(
                         R.integer.config_defaultRefreshRate)
-                        : (float) displayDeviceConfig.getDefaultLowRefreshRate();
+                        : (float) displayDeviceConfig.getDefaultRefreshRate();
         }
 
         public void observe() {
@@ -1256,7 +1256,7 @@
                 defaultPeakRefreshRate =
                         (displayDeviceConfig == null) ? (float) mContext.getResources().getInteger(
                                 R.integer.config_defaultPeakRefreshRate)
-                                : (float) displayDeviceConfig.getDefaultHighRefreshRate();
+                                : (float) displayDeviceConfig.getDefaultPeakRefreshRate();
             }
             mDefaultPeakRefreshRate = defaultPeakRefreshRate;
         }
@@ -1612,8 +1612,26 @@
             return mHighAmbientBrightnessThresholds;
         }
 
+        /**
+         * @return the refresh rate to lock to when in a high brightness zone
+         */
+        @VisibleForTesting
+        int getRefreshRateInHighZone() {
+            return mRefreshRateInHighZone;
+        }
+
+        /**
+         * @return the refresh rate to lock to when in a low brightness zone
+         */
+        @VisibleForTesting
+        int getRefreshRateInLowZone() {
+            return mRefreshRateInLowZone;
+        }
+
         private void loadLowBrightnessThresholds(DisplayDeviceConfig displayDeviceConfig,
                 boolean attemptLoadingFromDeviceConfig) {
+            loadRefreshRateInHighZone(displayDeviceConfig, attemptLoadingFromDeviceConfig);
+            loadRefreshRateInLowZone(displayDeviceConfig, attemptLoadingFromDeviceConfig);
             mLowDisplayBrightnessThresholds = loadBrightnessThresholds(
                     () -> mDeviceConfigDisplaySettings.getLowDisplayBrightnessThresholds(),
                     () -> displayDeviceConfig.getLowDisplayBrightnessThresholds(),
@@ -1634,6 +1652,44 @@
             }
         }
 
+        private void loadRefreshRateInLowZone(DisplayDeviceConfig displayDeviceConfig,
+                boolean attemptLoadingFromDeviceConfig) {
+            int refreshRateInLowZone =
+                    (displayDeviceConfig == null) ? mContext.getResources().getInteger(
+                        R.integer.config_defaultRefreshRateInZone)
+                        : displayDeviceConfig.getDefaultLowBlockingZoneRefreshRate();
+            if (attemptLoadingFromDeviceConfig) {
+                try {
+                    refreshRateInLowZone = mDeviceConfig.getInt(
+                        DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+                        DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE,
+                        refreshRateInLowZone);
+                } catch (Exception exception) {
+                    // Do nothing
+                }
+            }
+            mRefreshRateInLowZone = refreshRateInLowZone;
+        }
+
+        private void loadRefreshRateInHighZone(DisplayDeviceConfig displayDeviceConfig,
+                boolean attemptLoadingFromDeviceConfig) {
+            int refreshRateInHighZone =
+                    (displayDeviceConfig == null) ? mContext.getResources().getInteger(
+                        R.integer.config_fixedRefreshRateInHighZone) : displayDeviceConfig
+                        .getDefaultHighBlockingZoneRefreshRate();
+            if (attemptLoadingFromDeviceConfig) {
+                try {
+                    refreshRateInHighZone = mDeviceConfig.getInt(
+                        DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+                        DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE,
+                        refreshRateInHighZone);
+                } catch (Exception exception) {
+                    // Do nothing
+                }
+            }
+            mRefreshRateInHighZone = refreshRateInHighZone;
+        }
+
         private void loadHighBrightnessThresholds(DisplayDeviceConfig displayDeviceConfig,
                 boolean attemptLoadingFromDeviceConfig) {
             mHighDisplayBrightnessThresholds = loadBrightnessThresholds(
@@ -1687,14 +1743,6 @@
         }
 
         /**
-         * @return the refresh to lock to when in a low brightness zone
-         */
-        @VisibleForTesting
-        int getRefreshRateInLowZone() {
-            return mRefreshRateInLowZone;
-        }
-
-        /**
          * @return the display brightness thresholds for the low brightness zones
          */
         @VisibleForTesting
@@ -1739,8 +1787,17 @@
                 mHighAmbientBrightnessThresholds = highAmbientBrightnessThresholds;
             }
 
-            mRefreshRateInLowZone = mDeviceConfigDisplaySettings.getRefreshRateInLowZone();
-            mRefreshRateInHighZone = mDeviceConfigDisplaySettings.getRefreshRateInHighZone();
+            final int refreshRateInLowZone = mDeviceConfigDisplaySettings
+                    .getRefreshRateInLowZone();
+            if (refreshRateInLowZone != -1) {
+                mRefreshRateInLowZone = refreshRateInLowZone;
+            }
+
+            final int refreshRateInHighZone = mDeviceConfigDisplaySettings
+                    .getRefreshRateInHighZone();
+            if (refreshRateInHighZone != -1) {
+                mRefreshRateInHighZone = refreshRateInHighZone;
+            }
 
             restartObserver();
             mDeviceConfigDisplaySettings.startListening();
@@ -1794,6 +1851,10 @@
             restartObserver();
         }
 
+        /**
+         * Used to reload the lower blocking zone refresh rate in case of changes in the
+         * DeviceConfig properties.
+         */
         public void onDeviceConfigRefreshRateInLowZoneChanged(int refreshRate) {
             if (refreshRate != mRefreshRateInLowZone) {
                 mRefreshRateInLowZone = refreshRate;
@@ -1817,6 +1878,10 @@
             restartObserver();
         }
 
+        /**
+         * Used to reload the higher blocking zone refresh rate in case of changes in the
+         * DeviceConfig properties.
+         */
         public void onDeviceConfigRefreshRateInHighZoneChanged(int refreshRate) {
             if (refreshRate != mRefreshRateInHighZone) {
                 mRefreshRateInHighZone = refreshRate;
@@ -2664,15 +2729,10 @@
         }
 
         public int getRefreshRateInLowZone() {
-            int defaultRefreshRateInZone = mContext.getResources().getInteger(
-                    R.integer.config_defaultRefreshRateInZone);
-
-            int refreshRate = mDeviceConfig.getInt(
+            return mDeviceConfig.getInt(
                     DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
-                    DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE,
-                    defaultRefreshRateInZone);
+                    DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE, -1);
 
-            return refreshRate;
         }
 
         /*
@@ -2694,15 +2754,10 @@
         }
 
         public int getRefreshRateInHighZone() {
-            int defaultRefreshRateInZone = mContext.getResources().getInteger(
-                    R.integer.config_fixedRefreshRateInHighZone);
-
-            int refreshRate = mDeviceConfig.getInt(
+            return mDeviceConfig.getInt(
                     DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
                     DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE,
-                    defaultRefreshRateInZone);
-
-            return refreshRate;
+                    -1);
         }
 
         public int getRefreshRateInHbmSunlight() {
@@ -2750,23 +2805,29 @@
 
             int[] lowDisplayBrightnessThresholds = getLowDisplayBrightnessThresholds();
             int[] lowAmbientBrightnessThresholds = getLowAmbientBrightnessThresholds();
-            int refreshRateInLowZone = getRefreshRateInLowZone();
+            final int refreshRateInLowZone = getRefreshRateInLowZone();
 
             mHandler.obtainMessage(MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED,
                     new Pair<>(lowDisplayBrightnessThresholds, lowAmbientBrightnessThresholds))
                     .sendToTarget();
-            mHandler.obtainMessage(MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED, refreshRateInLowZone, 0)
+
+            if (refreshRateInLowZone != -1) {
+                mHandler.obtainMessage(MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED, refreshRateInLowZone)
                     .sendToTarget();
+            }
 
             int[] highDisplayBrightnessThresholds = getHighDisplayBrightnessThresholds();
             int[] highAmbientBrightnessThresholds = getHighAmbientBrightnessThresholds();
-            int refreshRateInHighZone = getRefreshRateInHighZone();
+            final int refreshRateInHighZone = getRefreshRateInHighZone();
 
             mHandler.obtainMessage(MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED,
                     new Pair<>(highDisplayBrightnessThresholds, highAmbientBrightnessThresholds))
                     .sendToTarget();
-            mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED, refreshRateInHighZone, 0)
+
+            if (refreshRateInHighZone != -1) {
+                mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED, refreshRateInHighZone)
                     .sendToTarget();
+            }
 
             final int refreshRateInHbmSunlight = getRefreshRateInHbmSunlight();
             mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HBM_SUNLIGHT_CHANGED,
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index bf576b8..375e51c 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -372,12 +372,23 @@
     void setDeviceStateLocked(int state, boolean isOverrideActive) {
         Slog.i(TAG, "Requesting Transition to state: " + state + ", from state=" + mDeviceState
                 + ", interactive=" + mInteractive + ", mBootCompleted=" + mBootCompleted);
+        mPendingDeviceState = state;
+
+        if (!mBootCompleted) {
+            // The boot animation might still be in progress, we do not want to switch states now
+            // as the boot animation would end up with an incorrect size.
+            if (DEBUG) {
+                Slog.d(TAG, "Postponing transition to state: " + mPendingDeviceState
+                        + " until boot is completed");
+            }
+            return;
+        }
+
         // As part of a state transition, we may need to turn off some displays temporarily so that
         // the transition is smooth. Plus, on some devices, only one internal displays can be
         // on at a time. We use LogicalDisplay.setIsInTransition to mark a display that needs to be
         // temporarily turned off.
         resetLayoutLocked(mDeviceState, state, /* isStateChangeStarting= */ true);
-        mPendingDeviceState = state;
         final boolean wakeDevice = shouldDeviceBeWoken(mPendingDeviceState, mDeviceState,
                 mInteractive, mBootCompleted);
         final boolean sleepDevice = shouldDeviceBePutToSleep(mPendingDeviceState, mDeviceState,
@@ -424,6 +435,9 @@
     void onBootCompleted() {
         synchronized (mSyncRoot) {
             mBootCompleted = true;
+            if (mPendingDeviceState != DeviceStateManager.INVALID_DEVICE_STATE) {
+                setDeviceStateLocked(mPendingDeviceState, /* isOverrideActive= */ false);
+            }
         }
     }
 
@@ -926,6 +940,15 @@
         final int layerStack = assignLayerStackLocked(displayId);
         final LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
         display.updateLocked(mDisplayDeviceRepo);
+
+        final DisplayInfo info = display.getDisplayInfoLocked();
+        if (info.type == Display.TYPE_INTERNAL && mDeviceStateToLayoutMap.size() > 1) {
+            // If this is an internal display and the device uses a display layout configuration,
+            // the display should be disabled as later we will receive a device state update, which
+            // will tell us which internal displays should be enabled and which should be disabled.
+            display.setEnabledLocked(false);
+        }
+
         mLogicalDisplays.put(displayId, display);
         return display;
     }
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 554e269..20c9a21 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -4215,7 +4215,6 @@
         }
         boolean changed = false;
 
-        Set<Permission> needsUpdate = null;
         synchronized (mLock) {
             final Iterator<Permission> it = mRegistry.getPermissionTrees().iterator();
             while (it.hasNext()) {
@@ -4234,26 +4233,6 @@
                             + " that used to be declared by " + bp.getPackageName());
                     it.remove();
                 }
-                if (needsUpdate == null) {
-                    needsUpdate = new ArraySet<>();
-                }
-                needsUpdate.add(bp);
-            }
-        }
-        if (needsUpdate != null) {
-            for (final Permission bp : needsUpdate) {
-                final AndroidPackage sourcePkg =
-                        mPackageManagerInt.getPackage(bp.getPackageName());
-                final PackageStateInternal sourcePs =
-                        mPackageManagerInt.getPackageStateInternal(bp.getPackageName());
-                synchronized (mLock) {
-                    if (sourcePkg != null && sourcePs != null) {
-                        continue;
-                    }
-                    Slog.w(TAG, "Removing dangling permission tree: " + bp.getName()
-                            + " from package " + bp.getPackageName());
-                    mRegistry.removePermission(bp.getName());
-                }
             }
         }
         return changed;
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 8ad76a3..79be946 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1074,10 +1074,14 @@
         // Use launch-adjacent-flag-root if launching with launch-adjacent flag.
         if ((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0
                 && mLaunchAdjacentFlagRootTask != null) {
-            // If the adjacent launch is coming from the same root, launch to adjacent root instead.
-            if (sourceTask != null && mLaunchAdjacentFlagRootTask.getAdjacentTaskFragment() != null
+            if (sourceTask != null && sourceTask == candidateTask) {
+                // Do nothing when task that is getting opened is same as the source.
+            } else if (sourceTask != null
+                    && mLaunchAdjacentFlagRootTask.getAdjacentTaskFragment() != null
                     && (sourceTask == mLaunchAdjacentFlagRootTask
                     || sourceTask.isDescendantOf(mLaunchAdjacentFlagRootTask))) {
+                // If the adjacent launch is coming from the same root, launch to
+                // adjacent root instead.
                 return mLaunchAdjacentFlagRootTask.getAdjacentTaskFragment().asTask();
             } else {
                 return mLaunchAdjacentFlagRootTask;
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 f628fba..abe48f8 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -464,6 +464,14 @@
     </xs:complexType>
 
     <xs:complexType name="refreshRateConfigs">
+        <xs:element name="defaultRefreshRate" type="xs:nonNegativeInteger"
+                    minOccurs="0" maxOccurs="1">
+            <xs:annotation name="final"/>
+        </xs:element>
+        <xs:element name="defaultPeakRefreshRate" 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 cb08179..2c97af5 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -186,8 +186,12 @@
 
   public class RefreshRateConfigs {
     ctor public RefreshRateConfigs();
+    method public final java.math.BigInteger getDefaultPeakRefreshRate();
+    method public final java.math.BigInteger getDefaultRefreshRate();
     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 setHigherBlockingZoneConfigs(com.android.server.display.config.BlockingZoneConfig);
     method public final void setLowerBlockingZoneConfigs(com.android.server.display.config.BlockingZoneConfig);
   }
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 86c5937..77e5d1d 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -51,6 +51,8 @@
 public final class DisplayDeviceConfigTest {
     private static final int DEFAULT_PEAK_REFRESH_RATE = 75;
     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[] 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};
@@ -150,8 +152,10 @@
 
         assertEquals("ProximitySensor123", mDisplayDeviceConfig.getProximitySensor().name);
         assertEquals("prox_type_1", mDisplayDeviceConfig.getProximitySensor().type);
-        assertEquals(75, mDisplayDeviceConfig.getDefaultLowRefreshRate());
-        assertEquals(90, mDisplayDeviceConfig.getDefaultHighRefreshRate());
+        assertEquals(75, mDisplayDeviceConfig.getDefaultLowBlockingZoneRefreshRate());
+        assertEquals(90, mDisplayDeviceConfig.getDefaultHighBlockingZoneRefreshRate());
+        assertEquals(85, mDisplayDeviceConfig.getDefaultPeakRefreshRate());
+        assertEquals(45, mDisplayDeviceConfig.getDefaultRefreshRate());
         assertArrayEquals(new int[]{45, 55},
                 mDisplayDeviceConfig.getLowDisplayBrightnessThresholds());
         assertArrayEquals(new int[]{50, 60},
@@ -230,8 +234,12 @@
                 mDisplayDeviceConfig.getAmbientDarkeningLevelsIdle(), ZERO_DELTA);
         assertArrayEquals(new float[]{29, 30, 31},
                 mDisplayDeviceConfig.getAmbientDarkeningPercentagesIdle(), ZERO_DELTA);
-        assertEquals(mDisplayDeviceConfig.getDefaultLowRefreshRate(), DEFAULT_REFRESH_RATE);
-        assertEquals(mDisplayDeviceConfig.getDefaultHighRefreshRate(), DEFAULT_PEAK_REFRESH_RATE);
+        assertEquals(mDisplayDeviceConfig.getDefaultLowBlockingZoneRefreshRate(),
+                DEFAULT_LOW_BLOCKING_ZONE_REFRESH_RATE);
+        assertEquals(mDisplayDeviceConfig.getDefaultHighBlockingZoneRefreshRate(),
+                DEFAULT_HIGH_BLOCKING_ZONE_REFRESH_RATE);
+        assertEquals(mDisplayDeviceConfig.getDefaultPeakRefreshRate(), DEFAULT_PEAK_REFRESH_RATE);
+        assertEquals(mDisplayDeviceConfig.getDefaultRefreshRate(), DEFAULT_REFRESH_RATE);
         assertArrayEquals(mDisplayDeviceConfig.getLowDisplayBrightnessThresholds(),
                 LOW_BRIGHTNESS_THRESHOLD_OF_PEAK_REFRESH_RATE);
         assertArrayEquals(mDisplayDeviceConfig.getLowAmbientBrightnessThresholds(),
@@ -449,6 +457,8 @@
                 +       "<type>prox_type_1</type>\n"
                 +   "</proxSensor>\n"
                 +   "<refreshRate>\n"
+                +       "<defaultRefreshRate>45</defaultRefreshRate>\n"
+                +       "<defaultPeakRefreshRate>85</defaultPeakRefreshRate>\n"
                 +       "<lowerBlockingZoneConfigs>\n"
                 +           "<defaultRefreshRate>75</defaultRefreshRate>\n"
                 +           "<blockingZoneThreshold>\n"
@@ -550,10 +560,14 @@
                 .thenReturn(new int[]{370, 380, 390});
 
         // Configs related to refresh rates and blocking zones
-        when(mResources.getInteger(com.android.internal.R.integer.config_defaultPeakRefreshRate))
+        when(mResources.getInteger(R.integer.config_defaultPeakRefreshRate))
                 .thenReturn(DEFAULT_PEAK_REFRESH_RATE);
-        when(mResources.getInteger(com.android.internal.R.integer.config_defaultRefreshRate))
+        when(mResources.getInteger(R.integer.config_defaultRefreshRate))
                 .thenReturn(DEFAULT_REFRESH_RATE);
+        when(mResources.getInteger(R.integer.config_fixedRefreshRateInHighZone))
+            .thenReturn(DEFAULT_HIGH_BLOCKING_ZONE_REFRESH_RATE);
+        when(mResources.getInteger(R.integer.config_defaultRefreshRateInZone))
+            .thenReturn(DEFAULT_LOW_BLOCKING_ZONE_REFRESH_RATE);
         when(mResources.getIntArray(R.array.config_brightnessThresholdsOfPeakRefreshRate))
                 .thenReturn(LOW_BRIGHTNESS_THRESHOLD_OF_PEAK_REFRESH_RATE);
         when(mResources.getIntArray(R.array.config_ambientThresholdsOfPeakRefreshRate))
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 b133a2a..af39dd4 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -1869,6 +1869,10 @@
             .thenReturn(75);
         when(resources.getInteger(R.integer.config_defaultRefreshRate))
             .thenReturn(45);
+        when(resources.getInteger(R.integer.config_fixedRefreshRateInHighZone))
+            .thenReturn(65);
+        when(resources.getInteger(R.integer.config_defaultRefreshRateInZone))
+            .thenReturn(85);
         when(resources.getIntArray(R.array.config_brightnessThresholdsOfPeakRefreshRate))
             .thenReturn(new int[]{5});
         when(resources.getIntArray(R.array.config_ambientThresholdsOfPeakRefreshRate))
@@ -1888,6 +1892,8 @@
         assertEquals(director.getSettingsObserver().getDefaultRefreshRate(), 45, 0.0);
         assertEquals(director.getSettingsObserver().getDefaultPeakRefreshRate(), 75,
                 0.0);
+        assertEquals(director.getBrightnessObserver().getRefreshRateInHighZone(), 65);
+        assertEquals(director.getBrightnessObserver().getRefreshRateInLowZone(), 85);
         assertArrayEquals(director.getBrightnessObserver().getHighDisplayBrightnessThreshold(),
                 new int[]{250});
         assertArrayEquals(director.getBrightnessObserver().getHighAmbientBrightnessThreshold(),
@@ -1899,17 +1905,21 @@
 
         // Notify that the default display is updated, such that DisplayDeviceConfig has new values
         DisplayDeviceConfig displayDeviceConfig = mock(DisplayDeviceConfig.class);
-        when(displayDeviceConfig.getDefaultLowRefreshRate()).thenReturn(50);
-        when(displayDeviceConfig.getDefaultHighRefreshRate()).thenReturn(55);
+        when(displayDeviceConfig.getDefaultLowBlockingZoneRefreshRate()).thenReturn(50);
+        when(displayDeviceConfig.getDefaultHighBlockingZoneRefreshRate()).thenReturn(55);
+        when(displayDeviceConfig.getDefaultRefreshRate()).thenReturn(60);
+        when(displayDeviceConfig.getDefaultPeakRefreshRate()).thenReturn(65);
         when(displayDeviceConfig.getLowDisplayBrightnessThresholds()).thenReturn(new int[]{25});
         when(displayDeviceConfig.getLowAmbientBrightnessThresholds()).thenReturn(new int[]{30});
         when(displayDeviceConfig.getHighDisplayBrightnessThresholds()).thenReturn(new int[]{210});
         when(displayDeviceConfig.getHighAmbientBrightnessThresholds()).thenReturn(new int[]{2100});
         director.defaultDisplayDeviceUpdated(displayDeviceConfig);
 
-        assertEquals(director.getSettingsObserver().getDefaultRefreshRate(), 50, 0.0);
-        assertEquals(director.getSettingsObserver().getDefaultPeakRefreshRate(), 55,
+        assertEquals(director.getSettingsObserver().getDefaultRefreshRate(), 60, 0.0);
+        assertEquals(director.getSettingsObserver().getDefaultPeakRefreshRate(), 65,
                 0.0);
+        assertEquals(director.getBrightnessObserver().getRefreshRateInHighZone(), 55);
+        assertEquals(director.getBrightnessObserver().getRefreshRateInLowZone(), 50);
         assertArrayEquals(director.getBrightnessObserver().getHighDisplayBrightnessThreshold(),
                 new int[]{210});
         assertArrayEquals(director.getBrightnessObserver().getHighAmbientBrightnessThreshold(),
@@ -1922,6 +1932,8 @@
         // Notify that the default display is updated, such that DeviceConfig has new values
         FakeDeviceConfig config = mInjector.getDeviceConfig();
         config.setDefaultPeakRefreshRate(60);
+        config.setRefreshRateInHighZone(65);
+        config.setRefreshRateInLowZone(70);
         config.setLowAmbientBrightnessThresholds(new int[]{20});
         config.setLowDisplayBrightnessThresholds(new int[]{10});
         config.setHighDisplayBrightnessThresholds(new int[]{255});
@@ -1929,9 +1941,11 @@
 
         director.defaultDisplayDeviceUpdated(displayDeviceConfig);
 
-        assertEquals(director.getSettingsObserver().getDefaultRefreshRate(), 50, 0.0);
+        assertEquals(director.getSettingsObserver().getDefaultRefreshRate(), 60, 0.0);
         assertEquals(director.getSettingsObserver().getDefaultPeakRefreshRate(), 60,
                 0.0);
+        assertEquals(director.getBrightnessObserver().getRefreshRateInHighZone(), 65);
+        assertEquals(director.getBrightnessObserver().getRefreshRateInLowZone(), 70);
         assertArrayEquals(director.getBrightnessObserver().getHighDisplayBrightnessThreshold(),
                 new int[]{255});
         assertArrayEquals(director.getBrightnessObserver().getHighAmbientBrightnessThreshold(),
@@ -1971,8 +1985,8 @@
                         any(Handler.class));
 
         DisplayDeviceConfig ddcMock = mock(DisplayDeviceConfig.class);
-        when(ddcMock.getDefaultLowRefreshRate()).thenReturn(50);
-        when(ddcMock.getDefaultHighRefreshRate()).thenReturn(55);
+        when(ddcMock.getDefaultLowBlockingZoneRefreshRate()).thenReturn(50);
+        when(ddcMock.getDefaultHighBlockingZoneRefreshRate()).thenReturn(55);
         when(ddcMock.getLowDisplayBrightnessThresholds()).thenReturn(new int[]{25});
         when(ddcMock.getLowAmbientBrightnessThresholds()).thenReturn(new int[]{30});
         when(ddcMock.getHighDisplayBrightnessThresholds()).thenReturn(new int[]{210});
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 638637d..6790ad9 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -18,6 +18,7 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.DEFAULT_DISPLAY_GROUP;
+import static android.view.Display.TYPE_EXTERNAL;
 import static android.view.Display.TYPE_INTERNAL;
 import static android.view.Display.TYPE_VIRTUAL;
 
@@ -173,7 +174,7 @@
 
     @Test
     public void testDisplayDeviceAddAndRemove_NonInternalTypes() {
-        testDisplayDeviceAddAndRemove_NonInternal(Display.TYPE_EXTERNAL);
+        testDisplayDeviceAddAndRemove_NonInternal(TYPE_EXTERNAL);
         testDisplayDeviceAddAndRemove_NonInternal(Display.TYPE_WIFI);
         testDisplayDeviceAddAndRemove_NonInternal(Display.TYPE_OVERLAY);
         testDisplayDeviceAddAndRemove_NonInternal(TYPE_VIRTUAL);
@@ -218,7 +219,7 @@
 
     @Test
     public void testDisplayDeviceAddAndRemove_OneExternalDefault() {
-        DisplayDevice device = createDisplayDevice(Display.TYPE_EXTERNAL, 600, 800,
+        DisplayDevice device = createDisplayDevice(TYPE_EXTERNAL, 600, 800,
                 FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY);
 
         // add
@@ -268,7 +269,7 @@
     public void testGetDisplayIdsLocked() {
         add(createDisplayDevice(TYPE_INTERNAL, 600, 800,
                 FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY));
-        add(createDisplayDevice(Display.TYPE_EXTERNAL, 600, 800, 0));
+        add(createDisplayDevice(TYPE_EXTERNAL, 600, 800, 0));
         add(createDisplayDevice(TYPE_VIRTUAL, 600, 800, 0));
 
         int [] ids = mLogicalDisplayMapper.getDisplayIdsLocked(Process.SYSTEM_UID,
@@ -460,7 +461,7 @@
 
         Layout layout = new Layout();
         layout.createDisplayLocked(device1.getDisplayDeviceInfoLocked().address, true, true);
-        layout.createDisplayLocked(device2.getDisplayDeviceInfoLocked().address, false, false);
+        layout.createDisplayLocked(device2.getDisplayDeviceInfoLocked().address, false, true);
         when(mDeviceStateToLayoutMapSpy.get(0)).thenReturn(layout);
 
         layout = new Layout();
@@ -469,6 +470,8 @@
         when(mDeviceStateToLayoutMapSpy.get(1)).thenReturn(layout);
         when(mDeviceStateToLayoutMapSpy.get(2)).thenReturn(layout);
 
+        when(mDeviceStateToLayoutMapSpy.size()).thenReturn(4);
+
         LogicalDisplay display1 = add(device1);
         assertEquals(info(display1).address, info(device1).address);
         assertEquals(DEFAULT_DISPLAY, id(display1));
@@ -481,8 +484,15 @@
         mLogicalDisplayMapper.setDeviceStateLocked(0, false);
         mLooper.moveTimeForward(1000);
         mLooper.dispatchAll();
+        // The new state is not applied until the boot is completed
         assertTrue(mLogicalDisplayMapper.getDisplayLocked(device1).isEnabledLocked());
         assertFalse(mLogicalDisplayMapper.getDisplayLocked(device2).isEnabledLocked());
+
+        mLogicalDisplayMapper.onBootCompleted();
+        mLooper.moveTimeForward(1000);
+        mLooper.dispatchAll();
+        assertTrue(mLogicalDisplayMapper.getDisplayLocked(device1).isEnabledLocked());
+        assertTrue(mLogicalDisplayMapper.getDisplayLocked(device2).isEnabledLocked());
         assertFalse(mLogicalDisplayMapper.getDisplayLocked(device1).isInTransitionLocked());
         assertFalse(mLogicalDisplayMapper.getDisplayLocked(device2).isInTransitionLocked());
 
@@ -623,6 +633,23 @@
         assertEquals(3, threeDisplaysEnabled.length);
     }
 
+    @Test
+    public void testCreateNewLogicalDisplay() {
+        DisplayDevice device1 = createDisplayDevice(TYPE_EXTERNAL, 600, 800,
+                FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY);
+        when(mDeviceStateToLayoutMapSpy.size()).thenReturn(1);
+        LogicalDisplay display1 = add(device1);
+
+        assertTrue(display1.isEnabledLocked());
+
+        DisplayDevice device2 = createDisplayDevice(TYPE_INTERNAL, 600, 800,
+                FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY);
+        when(mDeviceStateToLayoutMapSpy.size()).thenReturn(2);
+        LogicalDisplay display2 = add(device2);
+
+        assertFalse(display2.isEnabledLocked());
+    }
+
     /////////////////
     // Helper Methods
     /////////////////