Merge "Include the history tag string in checkin file verbatim" into udc-qpr-dev
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index f53c087..8a4b6af 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2151,6 +2151,7 @@
 
   public class SharedConnectivityManager {
     method @Nullable public static android.net.wifi.sharedconnectivity.app.SharedConnectivityManager create(@NonNull android.content.Context, @NonNull String, @NonNull String);
+    method @NonNull public android.content.BroadcastReceiver getBroadcastReceiver();
     method @Nullable public android.content.ServiceConnection getServiceConnection();
     method public void setService(@Nullable android.os.IInterface);
   }
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 4dea4a7..b17dfa6 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -36,6 +36,7 @@
 import android.app.PendingIntent;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
+import android.companion.datatransfer.PermissionSyncRequest;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -177,7 +178,7 @@
     public @interface DataSyncTypes {}
 
     /**
-     * Used by {@link #enableSystemDataSync(int, int)}}.
+     * Used by {@link #enableSystemDataSyncForTypes(int, int)}}.
      * Sync call metadata like muting, ending and silencing a call.
      *
      */
@@ -529,6 +530,39 @@
     }
 
     /**
+     * @hide
+     */
+    public void enablePermissionsSync(int associationId) {
+        try {
+            mService.enablePermissionsSync(associationId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public void disablePermissionsSync(int associationId) {
+        try {
+            mService.disablePermissionsSync(associationId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public PermissionSyncRequest getPermissionSyncRequest(int associationId) {
+        try {
+            return mService.getPermissionSyncRequest(associationId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * <p>Calling this API requires a uses-feature
      * {@link PackageManager#FEATURE_COMPANION_DEVICE_SETUP} declaration in the manifest</p>
      *
diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl
index b5e2670..4d05a28 100644
--- a/core/java/android/companion/ICompanionDeviceManager.aidl
+++ b/core/java/android/companion/ICompanionDeviceManager.aidl
@@ -24,6 +24,7 @@
 import android.companion.ISystemDataTransferCallback;
 import android.companion.AssociationInfo;
 import android.companion.AssociationRequest;
+import android.companion.datatransfer.PermissionSyncRequest;
 import android.content.ComponentName;
 
 /**
@@ -101,5 +102,11 @@
 
     void disableSystemDataSync(int associationId, int flags);
 
+    void enablePermissionsSync(int associationId);
+
+    void disablePermissionsSync(int associationId);
+
+    PermissionSyncRequest getPermissionSyncRequest(int associationId);
+
     void enableSecureTransport(boolean enabled);
 }
diff --git a/core/java/android/widget/ToastPresenter.java b/core/java/android/widget/ToastPresenter.java
index 7cb61fe..0a0071a 100644
--- a/core/java/android/widget/ToastPresenter.java
+++ b/core/java/android/widget/ToastPresenter.java
@@ -89,9 +89,10 @@
         return view;
     }
 
+    private final WeakReference<Context> mContext;
     private final Resources mResources;
     private final WeakReference<WindowManager> mWindowManager;
-    private final WeakReference<AccessibilityManager> mAccessibilityManager;
+    private final IAccessibilityManager mAccessibilityManagerService;
     private final INotificationManager mNotificationManager;
     private final String mPackageName;
     private final String mContextPackageName;
@@ -101,21 +102,14 @@
 
     public ToastPresenter(Context context, IAccessibilityManager accessibilityManager,
             INotificationManager notificationManager, String packageName) {
+        mContext = new WeakReference<>(context);
         mResources = context.getResources();
         mWindowManager = new WeakReference<>(context.getSystemService(WindowManager.class));
         mNotificationManager = notificationManager;
         mPackageName = packageName;
         mContextPackageName = context.getPackageName();
         mParams = createLayoutParams();
-
-        // We obtain AccessibilityManager manually via its constructor instead of using method
-        // AccessibilityManager.getInstance() for 2 reasons:
-        //   1. We want to be able to inject IAccessibilityManager in tests to verify behavior.
-        //   2. getInstance() caches the instance for the process even if we pass a different
-        //      context to it. This is problematic for multi-user because callers can pass a context
-        //      created via Context.createContextAsUser().
-        mAccessibilityManager = new WeakReference<>(
-                new AccessibilityManager(context, accessibilityManager, context.getUserId()));
+        mAccessibilityManagerService = accessibilityManager;
     }
 
     public String getPackageName() {
@@ -306,11 +300,20 @@
      * enabled.
      */
     public void trySendAccessibilityEvent(View view, String packageName) {
-        final AccessibilityManager accessibilityManager = mAccessibilityManager.get();
-        if (accessibilityManager == null) {
+        final Context context = mContext.get();
+        if (context == null) {
             return;
         }
 
+        // We obtain AccessibilityManager manually via its constructor instead of using method
+        // AccessibilityManager.getInstance() for 2 reasons:
+        //   1. We want to be able to inject IAccessibilityManager in tests to verify behavior.
+        //   2. getInstance() caches the instance for the process even if we pass a different
+        //      context to it. This is problematic for multi-user because callers can pass a context
+        //      created via Context.createContextAsUser().
+        final AccessibilityManager accessibilityManager = new AccessibilityManager(context,
+                    mAccessibilityManagerService, context.getUserId());
+
         if (!accessibilityManager.isEnabled()) {
             accessibilityManager.removeClient();
             return;
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
index 2ef923d..84a6c45 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
@@ -251,6 +251,20 @@
     }
 
     @Test
+    public void identifierToHalProgramIdentifier_withDeprecateDabId() {
+        long value = 0x98765ABCDL;
+        ProgramSelector.Identifier dabId = new ProgramSelector.Identifier(
+                        ProgramSelector.IDENTIFIER_TYPE_DAB_SID_EXT, value);
+        ProgramIdentifier halDabIdExpected = AidlTestUtils.makeHalIdentifier(
+                IdentifierType.DAB_SID_EXT, 0x987650000ABCDL);
+
+        ProgramIdentifier halDabId = ConversionUtils.identifierToHalProgramIdentifier(dabId);
+
+        expect.withMessage("Converted 28-bit DAB identifier for HAL").that(halDabId)
+                .isEqualTo(halDabIdExpected);
+    }
+
+    @Test
     public void identifierFromHalProgramIdentifier_withDabId() {
         ProgramSelector.Identifier dabId =
                 ConversionUtils.identifierFromHalProgramIdentifier(TEST_HAL_DAB_SID_EXT_ID);
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
index 0112e32..15d14e8 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
@@ -213,9 +213,6 @@
             if (mRearDisplayStateRequest != null || isRearDisplayActive()) {
                 mRearDisplayStateRequest = null;
                 mDeviceStateManager.cancelStateRequest();
-            } else {
-                throw new IllegalStateException(
-                        "Unable to cancel a rear display session as there is no active session");
             }
         }
     }
@@ -432,10 +429,6 @@
         synchronized (mLock) {
             if (mRearDisplayPresentationController != null) {
                 mDeviceStateManager.cancelStateRequest();
-            } else {
-                throw new IllegalStateException(
-                        "Unable to cancel a rear display presentation session as there is no "
-                                + "active session");
             }
         }
     }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
index 2eacaaf..6db2ae4 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -17,7 +17,6 @@
 package androidx.window.extensions.layout;
 
 import static android.view.Display.DEFAULT_DISPLAY;
-
 import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_FLAT;
 import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_HALF_OPENED;
 import static androidx.window.util.ExtensionHelper.isZero;
@@ -327,13 +326,17 @@
             return features;
         }
 
+        // We will transform the feature bounds to the Activity window, so using the rotation
+        // from the same source (WindowConfiguration) to make sure they are synchronized.
+        final int rotation = windowConfiguration.getDisplayRotation();
+
         for (CommonFoldingFeature baseFeature : storedFeatures) {
             Integer state = convertToExtensionState(baseFeature.getState());
             if (state == null) {
                 continue;
             }
             Rect featureRect = baseFeature.getRect();
-            rotateRectToDisplayRotation(displayId, featureRect);
+            rotateRectToDisplayRotation(displayId, rotation, featureRect);
             transformToWindowSpaceRect(windowConfiguration, featureRect);
 
             if (isZero(featureRect)) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
index 5bfb0ebd..a836e05 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
@@ -17,7 +17,6 @@
 package androidx.window.sidecar;
 
 import static android.view.Display.DEFAULT_DISPLAY;
-
 import static androidx.window.util.ExtensionHelper.rotateRectToDisplayRotation;
 import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect;
 
@@ -120,10 +119,12 @@
         }
 
         List<SidecarDisplayFeature> features = new ArrayList<>();
+        final int rotation = activity.getResources().getConfiguration().windowConfiguration
+                .getDisplayRotation();
         for (CommonFoldingFeature baseFeature : mStoredFeatures) {
             SidecarDisplayFeature feature = new SidecarDisplayFeature();
             Rect featureRect = baseFeature.getRect();
-            rotateRectToDisplayRotation(displayId, featureRect);
+            rotateRectToDisplayRotation(displayId, rotation, featureRect);
             transformToWindowSpaceRect(activity, featureRect);
             feature.setRect(featureRect);
             feature.setType(baseFeature.getType());
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java
index 9e2611f..a08db79 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java
@@ -16,21 +16,22 @@
 
 package androidx.window.util;
 
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_180;
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
 
+import android.annotation.SuppressLint;
 import android.app.WindowConfiguration;
 import android.content.Context;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManagerGlobal;
+import android.util.RotationUtils;
 import android.view.DisplayInfo;
 import android.view.Surface;
 import android.view.WindowManager;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.UiContext;
+import androidx.annotation.VisibleForTesting;
 
 /**
  * Util class for both Sidecar and Extensions.
@@ -44,47 +45,39 @@
     /**
      * Rotates the input rectangle specified in default display orientation to the current display
      * rotation.
+     *
+     * @param displayId the display id.
+     * @param rotation the target rotation relative to the default display orientation.
+     * @param inOutRect the input/output Rect as specified in the default display orientation.
      */
-    public static void rotateRectToDisplayRotation(int displayId, Rect inOutRect) {
-        DisplayManagerGlobal dmGlobal = DisplayManagerGlobal.getInstance();
-        DisplayInfo displayInfo = dmGlobal.getDisplayInfo(displayId);
-        int rotation = displayInfo.rotation;
+    public static void rotateRectToDisplayRotation(
+            int displayId, @Surface.Rotation int rotation, @NonNull Rect inOutRect) {
+        final DisplayManagerGlobal dmGlobal = DisplayManagerGlobal.getInstance();
+        final DisplayInfo displayInfo = dmGlobal.getDisplayInfo(displayId);
 
-        boolean isSideRotation = rotation == ROTATION_90 || rotation == ROTATION_270;
-        int displayWidth = isSideRotation ? displayInfo.logicalHeight : displayInfo.logicalWidth;
-        int displayHeight = isSideRotation ? displayInfo.logicalWidth : displayInfo.logicalHeight;
-
-        inOutRect.intersect(0, 0, displayWidth, displayHeight);
-
-        rotateBounds(inOutRect, displayWidth, displayHeight, rotation);
+        rotateRectToDisplayRotation(displayInfo, rotation, inOutRect);
     }
 
-    /**
-     * Rotates the input rectangle within parent bounds for a given delta.
-     */
-    private static void rotateBounds(Rect inOutRect, int parentWidth, int parentHeight,
-            @Surface.Rotation int delta) {
-        int origLeft = inOutRect.left;
-        switch (delta) {
-            case ROTATION_0:
-                return;
-            case ROTATION_90:
-                inOutRect.left = inOutRect.top;
-                inOutRect.top = parentWidth - inOutRect.right;
-                inOutRect.right = inOutRect.bottom;
-                inOutRect.bottom = parentWidth - origLeft;
-                return;
-            case ROTATION_180:
-                inOutRect.left = parentWidth - inOutRect.right;
-                inOutRect.right = parentWidth - origLeft;
-                return;
-            case ROTATION_270:
-                inOutRect.left = parentHeight - inOutRect.bottom;
-                inOutRect.bottom = inOutRect.right;
-                inOutRect.right = parentHeight - inOutRect.top;
-                inOutRect.top = origLeft;
-                return;
-        }
+    // We suppress the Lint error CheckResult for Rect#intersect because in case the displayInfo and
+    // folding features are out of sync, e.g. when a foldable devices is unfolding, it is acceptable
+    // to provide the original folding feature Rect even if they don't intersect.
+    @SuppressLint("RectIntersectReturnValueIgnored")
+    @VisibleForTesting
+    static void rotateRectToDisplayRotation(@NonNull DisplayInfo displayInfo,
+            @Surface.Rotation int rotation, @NonNull Rect inOutRect) {
+        // The inOutRect is specified in the default display orientation, so here we need to get
+        // the display width and height in the default orientation to perform the intersection and
+        // rotation.
+        final boolean isSideRotation =
+                displayInfo.rotation == ROTATION_90 || displayInfo.rotation == ROTATION_270;
+        final int baseDisplayWidth =
+                isSideRotation ? displayInfo.logicalHeight : displayInfo.logicalWidth;
+        final int baseDisplayHeight =
+                isSideRotation ? displayInfo.logicalWidth : displayInfo.logicalHeight;
+
+        inOutRect.intersect(0, 0, baseDisplayWidth, baseDisplayHeight);
+
+        RotationUtils.rotateBounds(inOutRect, baseDisplayWidth, baseDisplayHeight, rotation);
     }
 
     /** Transforms rectangle from absolute coordinate space to the window coordinate space. */
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java
index d189ae2..a0590dc 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java
@@ -29,7 +29,7 @@
 import org.junit.runner.RunWith;
 
 /**
- * Test class for {@link WindowExtensionsTest}.
+ * Test class for {@link WindowExtensions}.
  *
  * Build/Install/Run:
  *  atest WMJetpackUnitTests:WindowExtensionsTest
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/util/ExtensionHelperTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/util/ExtensionHelperTest.java
new file mode 100644
index 0000000..3278cdf
--- /dev/null
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/util/ExtensionHelperTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.util;
+
+import static org.junit.Assert.assertEquals;
+
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.view.DisplayInfo;
+import android.view.Surface;
+
+import androidx.annotation.NonNull;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test class for {@link ExtensionHelper}.
+ *
+ * Build/Install/Run:
+ *  atest WMJetpackUnitTests:ExtensionHelperTest
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ExtensionHelperTest {
+
+    private static final int MOCK_DISPLAY_HEIGHT = 1000;
+    private static final int MOCK_DISPLAY_WIDTH = 2000;
+    private static final int MOCK_FEATURE_LEFT = 100;
+    private static final int MOCK_FEATURE_RIGHT = 200;
+
+    private static final int[] ROTATIONS = {
+            Surface.ROTATION_0,
+            Surface.ROTATION_90,
+            Surface.ROTATION_180,
+            Surface.ROTATION_270
+    };
+
+    private static final DisplayInfo[] MOCK_DISPLAY_INFOS = {
+            getMockDisplayInfo(Surface.ROTATION_0),
+            getMockDisplayInfo(Surface.ROTATION_90),
+            getMockDisplayInfo(Surface.ROTATION_180),
+            getMockDisplayInfo(Surface.ROTATION_270),
+    };
+
+    @Test
+    public void testRotateRectToDisplayRotation() {
+        for (int rotation : ROTATIONS) {
+            final Rect expectedResult = getExpectedFeatureRectAfterRotation(rotation);
+            // The method should return correctly rotated Rect even if the requested rotation value
+            // differs from the rotation in DisplayInfo. This is because the WindowConfiguration is
+            // not always synced with DisplayInfo.
+            for (DisplayInfo displayInfo : MOCK_DISPLAY_INFOS) {
+                final Rect rect = getMockFeatureRect();
+                ExtensionHelper.rotateRectToDisplayRotation(displayInfo, rotation, rect);
+                assertEquals(
+                        "Result Rect should equal to expected for rotation: " + rotation
+                                + "; displayInfo: " + displayInfo,
+                        expectedResult, rect);
+            }
+        }
+    }
+
+    @NonNull
+    private static DisplayInfo getMockDisplayInfo(@Surface.Rotation int rotation) {
+        final DisplayInfo displayInfo = new DisplayInfo();
+        displayInfo.rotation = rotation;
+        if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
+            displayInfo.logicalWidth = MOCK_DISPLAY_WIDTH;
+            displayInfo.logicalHeight = MOCK_DISPLAY_HEIGHT;
+        } else {
+            displayInfo.logicalWidth = MOCK_DISPLAY_HEIGHT;
+            displayInfo.logicalHeight = MOCK_DISPLAY_WIDTH;
+        }
+        return displayInfo;
+    }
+
+    @NonNull
+    private static Rect getMockFeatureRect() {
+        return new Rect(MOCK_FEATURE_LEFT, 0, MOCK_FEATURE_RIGHT, MOCK_DISPLAY_HEIGHT);
+    }
+
+    @NonNull
+    private static Rect getExpectedFeatureRectAfterRotation(@Surface.Rotation int rotation) {
+        switch (rotation) {
+            case Surface.ROTATION_0:
+                return new Rect(
+                        MOCK_FEATURE_LEFT, 0, MOCK_FEATURE_RIGHT, MOCK_DISPLAY_HEIGHT);
+            case Surface.ROTATION_90:
+                return new Rect(0, MOCK_DISPLAY_WIDTH - MOCK_FEATURE_RIGHT,
+                        MOCK_DISPLAY_HEIGHT, MOCK_DISPLAY_WIDTH - MOCK_FEATURE_LEFT);
+            case Surface.ROTATION_180:
+                return new Rect(MOCK_DISPLAY_WIDTH - MOCK_FEATURE_RIGHT, 0,
+                        MOCK_DISPLAY_WIDTH - MOCK_FEATURE_LEFT, MOCK_DISPLAY_HEIGHT);
+            case Surface.ROTATION_270:
+                return new Rect(0, MOCK_FEATURE_LEFT, MOCK_DISPLAY_HEIGHT,
+                        MOCK_FEATURE_RIGHT);
+            default:
+                throw new IllegalArgumentException("Unknown rotation value: " + rotation);
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index a11d952..bb65da3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -219,6 +219,13 @@
         private ArrayList<TaskState> mPausingTasks = null;
 
         /**
+         * List of tasks were pausing but closed in a subsequent merged transition. If a
+         * closing task is reopened, the leash is not initially hidden since it is already
+         * visible.
+         */
+        private ArrayList<TaskState> mClosingTasks = null;
+
+        /**
          * List of tasks that we are switching to. Upon finish, these will remain visible and
          * on top.
          */
@@ -369,6 +376,7 @@
             }
             mFinishTransaction = null;
             mPausingTasks = null;
+            mClosingTasks = null;
             mOpeningTasks = null;
             mInfo = null;
             mTransition = null;
@@ -413,6 +421,7 @@
             mFinishCB = finishCB;
             mFinishTransaction = finishT;
             mPausingTasks = new ArrayList<>();
+            mClosingTasks = new ArrayList<>();
             mOpeningTasks = new ArrayList<>();
             mLeashMap = new ArrayMap<>();
             mKeyguardLocked = (info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0;
@@ -659,7 +668,10 @@
                     final TransitionInfo.Change change = closingTasks.get(i);
                     final int pausingIdx = TaskState.indexOf(mPausingTasks, change);
                     if (pausingIdx >= 0) {
-                        mPausingTasks.remove(pausingIdx);
+                        // We are closing the pausing task, but it is still visible and can be
+                        // restart by another transition prior to this transition finishing
+                        final TaskState closingTask = mPausingTasks.remove(pausingIdx);
+                        mClosingTasks.add(closingTask);
                         didMergeThings = true;
                         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
                                 "  closing pausing taskId=%d", change.getTaskInfo().taskId);
@@ -695,7 +707,12 @@
                 for (int i = 0; i < openingTasks.size(); ++i) {
                     final TransitionInfo.Change change = openingTasks.get(i);
                     final boolean isLeaf = openingTaskIsLeafs.get(i) == 1;
-                    int pausingIdx = TaskState.indexOf(mPausingTasks, change);
+                    final int closingIdx = TaskState.indexOf(mClosingTasks, change);
+                    if (closingIdx >= 0) {
+                        // Remove opening tasks from closing set
+                        mClosingTasks.remove(closingIdx);
+                    }
+                    final int pausingIdx = TaskState.indexOf(mPausingTasks, change);
                     if (pausingIdx >= 0) {
                         // Something is showing/opening a previously-pausing app.
                         if (isLeaf) {
@@ -718,12 +735,14 @@
                         appearedTargets[nextTargetIdx++] = target;
                         // reparent into the original `mInfo` since that's where we are animating.
                         final int rootIdx = TransitionUtil.rootIndexFor(change, mInfo);
+                        final boolean wasClosing = closingIdx >= 0;
                         t.reparent(target.leash, mInfo.getRoot(rootIdx).getLeash());
                         t.setLayer(target.leash, layer);
-                        // Hide the animation leash, let listener show it.
-                        t.hide(target.leash);
+                        // Hide the animation leash if not already visible, let listener show it
+                        t.setVisibility(target.leash, !wasClosing);
                         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
-                                "  opening new leaf taskId=%d", target.taskId);
+                                "  opening new leaf taskId=%d wasClosing=%b",
+                                target.taskId, wasClosing);
                         mOpeningTasks.add(new TaskState(change, target.leash));
                     } else {
                         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
index 097ed7a..93d7636 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
@@ -54,12 +54,13 @@
     private static final String TAG = TaskViewTaskController.class.getSimpleName();
 
     private final CloseGuard mGuard = new CloseGuard();
-
+    private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
+    /** Used to inset the activity content to allow space for a caption bar. */
+    private final Binder mCaptionInsetsOwner = new Binder();
     private final ShellTaskOrganizer mTaskOrganizer;
     private final Executor mShellExecutor;
     private final SyncTransactionQueue mSyncQueue;
     private final TaskViewTransitions mTaskViewTransitions;
-    private TaskViewBase mTaskViewBase;
     private final Context mContext;
 
     /**
@@ -70,21 +71,19 @@
      * in this situation to allow us to notify listeners correctly if the task failed to open.
      */
     private ActivityManager.RunningTaskInfo mPendingInfo;
-    /* Indicates that the task we attempted to launch in the task view failed to launch. */
-    private boolean mTaskNotFound;
+    private TaskViewBase mTaskViewBase;
     protected ActivityManager.RunningTaskInfo mTaskInfo;
     private WindowContainerToken mTaskToken;
     private SurfaceControl mTaskLeash;
-    private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
+    /* Indicates that the task we attempted to launch in the task view failed to launch. */
+    private boolean mTaskNotFound;
     private boolean mSurfaceCreated;
     private SurfaceControl mSurfaceControl;
     private boolean mIsInitialized;
     private boolean mNotifiedForInitialized;
+    private boolean mHideTaskWithSurface = true;
     private TaskView.Listener mListener;
     private Executor mListenerExecutor;
-
-    /** Used to inset the activity content to allow space for a caption bar. */
-    private final Binder mCaptionInsetsOwner = new Binder();
     private Rect mCaptionInsets;
 
     public TaskViewTaskController(Context context, ShellTaskOrganizer organizer,
@@ -102,6 +101,19 @@
         mGuard.open("release");
     }
 
+    /**
+     * Specifies if the task should be hidden when the surface is destroyed.
+     * <p>This is {@code true} by default.
+     *
+     * @param hideTaskWithSurface {@code false} if task needs to remain visible even when the
+     *                            surface is destroyed, {@code true} otherwise.
+     */
+    public void setHideTaskWithSurface(boolean hideTaskWithSurface) {
+        // TODO(b/299535374): Remove mHideTaskWithSurface once the taskviews with launch root tasks
+        // are moved to a window in SystemUI in auto.
+        mHideTaskWithSurface = hideTaskWithSurface;
+    }
+
     SurfaceControl getSurfaceControl() {
         return mSurfaceControl;
     }
@@ -257,10 +269,15 @@
         mTaskNotFound = false;
     }
 
+    /** This method shouldn't be called when shell transitions are enabled. */
     private void updateTaskVisibility() {
+        boolean visible = mSurfaceCreated;
+        if (!visible && !mHideTaskWithSurface) {
+            return;
+        }
         WindowContainerTransaction wct = new WindowContainerTransaction();
-        wct.setHidden(mTaskToken, !mSurfaceCreated /* hidden */);
-        if (!mSurfaceCreated) {
+        wct.setHidden(mTaskToken, !visible /* hidden */);
+        if (!visible) {
             wct.reorder(mTaskToken, false /* onTop */);
         }
         mSyncQueue.queue(wct);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
index d098d33..0088051 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
@@ -221,6 +221,20 @@
     }
 
     @Test
+    public void testSurfaceDestroyed_withTask_shouldNotHideTask_legacyTransitions() {
+        assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
+        mTaskViewTaskController.setHideTaskWithSurface(false);
+
+        SurfaceHolder sh = mock(SurfaceHolder.class);
+        mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash);
+        mTaskView.surfaceCreated(sh);
+        reset(mViewListener);
+        mTaskView.surfaceDestroyed(sh);
+
+        verify(mViewListener, never()).onTaskVisibilityChanged(anyInt(), anyBoolean());
+    }
+
+    @Test
     public void testSurfaceDestroyed_withTask_legacyTransitions() {
         assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
         SurfaceHolder sh = mock(SurfaceHolder.class);
diff --git a/packages/SystemUI/res/layout/battery_percentage_view.xml b/packages/SystemUI/res/layout/battery_percentage_view.xml
index 82facd0..8b5aeaa 100644
--- a/packages/SystemUI/res/layout/battery_percentage_view.xml
+++ b/packages/SystemUI/res/layout/battery_percentage_view.xml
@@ -27,4 +27,5 @@
         android:gravity="center_vertical|start"
         android:paddingStart="@dimen/battery_level_padding_start"
         android:importantForAccessibility="no"
+        android:includeFontPadding="false"
         />
diff --git a/packages/SystemUI/res/layout/battery_status_chip.xml b/packages/SystemUI/res/layout/battery_status_chip.xml
index ff68ac0..7437183 100644
--- a/packages/SystemUI/res/layout/battery_status_chip.xml
+++ b/packages/SystemUI/res/layout/battery_status_chip.xml
@@ -25,7 +25,8 @@
     <LinearLayout
         android:id="@+id/rounded_container"
         android:layout_width="wrap_content"
-        android:layout_height="@dimen/ongoing_appops_chip_height"
+        android:layout_height="wrap_content"
+        android:minHeight="@dimen/ongoing_appops_chip_height"
         android:layout_gravity="center"
         android:background="@drawable/statusbar_chip_bg"
         android:clipToOutline="true"
@@ -36,7 +37,7 @@
         <com.android.systemui.battery.BatteryMeterView
             android:id="@+id/battery_meter_view"
             android:layout_width="wrap_content"
-            android:layout_height="match_parent"
+            android:layout_height="wrap_content"
             android:layout_marginHorizontal="10dp" />
 
     </LinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 5132e57..c5351b1 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -79,6 +79,7 @@
                     android:id="@+id/status_bar_start_side_except_heads_up"
                     android:layout_height="wrap_content"
                     android:layout_width="match_parent"
+                    android:layout_gravity="center_vertical|start"
                     android:clipChildren="false">
                     <ViewStub
                         android:id="@+id/operator_name"
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
index 8200e5c..9059230 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
@@ -17,6 +17,7 @@
 package com.android.systemui.shared.rotation;
 
 import static android.content.pm.PackageManager.FEATURE_PC;
+import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.internal.view.RotationPolicy.NATURAL_ROTATION;
@@ -37,6 +38,8 @@
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.SystemProperties;
@@ -67,6 +70,7 @@
 
 import java.io.PrintWriter;
 import java.util.Optional;
+import java.util.concurrent.Executor;
 import java.util.function.Supplier;
 
 /**
@@ -119,6 +123,8 @@
     private final int mIconCwStart0ResId;
     @DrawableRes
     private final int mIconCwStart90ResId;
+    /** Defaults to mainExecutor if not set via {@link #setBgExecutor(Executor)}. */
+    private Executor mBgExecutor;
 
     @DrawableRes
     private int mIconResId;
@@ -178,6 +184,8 @@
         mAccessibilityManager = AccessibilityManager.getInstance(context);
         mTaskStackListener = new TaskStackListenerImpl();
         mWindowRotationProvider = windowRotationProvider;
+
+        mBgExecutor = context.getMainExecutor();
     }
 
     public void setRotationButton(RotationButton rotationButton,
@@ -193,6 +201,10 @@
         return mContext;
     }
 
+    public void setBgExecutor(Executor bgExecutor) {
+        mBgExecutor = bgExecutor;
+    }
+
     /**
      * Called during Taskbar initialization.
      */
@@ -219,8 +231,11 @@
 
         mListenersRegistered = true;
 
-        updateDockedState(mContext.registerReceiver(mDockedReceiver,
-                new IntentFilter(Intent.ACTION_DOCK_EVENT)));
+        mBgExecutor.execute(() -> {
+            final Intent intent = mContext.registerReceiver(mDockedReceiver,
+                    new IntentFilter(Intent.ACTION_DOCK_EVENT));
+            mContext.getMainExecutor().execute(() -> updateDockedState(intent));
+        });
 
         if (registerRotationWatcher) {
             try {
@@ -246,11 +261,13 @@
 
         mListenersRegistered = false;
 
-        try {
-            mContext.unregisterReceiver(mDockedReceiver);
-        } catch (IllegalArgumentException e) {
-            Log.e(TAG, "Docked receiver already unregistered", e);
-        }
+        mBgExecutor.execute(() -> {
+            try {
+                mContext.unregisterReceiver(mDockedReceiver);
+            } catch (IllegalArgumentException e) {
+                Log.e(TAG, "Docked receiver already unregistered", e);
+            }
+        });
 
         if (mRotationWatcherRegistered) {
             try {
diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
index ca43705..a79be6e 100644
--- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
@@ -354,11 +354,13 @@
                 if (mPercentageStyleId != 0) { // Only set if specified as attribute
                     mBatteryPercentView.setTextAppearance(mPercentageStyleId);
                 }
+                float fontHeight = mBatteryPercentView.getPaint().getFontMetricsInt(null);
+                mBatteryPercentView.setLineHeight(TypedValue.COMPLEX_UNIT_PX, fontHeight);
                 if (mTextColor != 0) mBatteryPercentView.setTextColor(mTextColor);
                 updatePercentText();
                 addView(mBatteryPercentView, new LayoutParams(
                         LayoutParams.WRAP_CONTENT,
-                        LayoutParams.WRAP_CONTENT));
+                        (int) Math.ceil(fontHeight)));
             }
         } else {
             if (showing) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
index 53dc0e3..62e2369 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
@@ -25,8 +25,8 @@
 import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepositoryImpl
 import com.android.systemui.biometrics.data.repository.PromptRepository
 import com.android.systemui.biometrics.data.repository.PromptRepositoryImpl
-import com.android.systemui.biometrics.data.repository.RearDisplayStateRepository
-import com.android.systemui.biometrics.data.repository.RearDisplayStateRepositoryImpl
+import com.android.systemui.biometrics.data.repository.DisplayStateRepository
+import com.android.systemui.biometrics.data.repository.DisplayStateRepositoryImpl
 import com.android.systemui.biometrics.domain.interactor.CredentialInteractor
 import com.android.systemui.biometrics.domain.interactor.CredentialInteractorImpl
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
@@ -65,9 +65,10 @@
     @SysUISingleton
     fun fingerprintRepository(impl: FingerprintPropertyRepositoryImpl):
             FingerprintPropertyRepository
+
     @Binds
     @SysUISingleton
-    fun rearDisplayStateRepository(impl: RearDisplayStateRepositoryImpl): RearDisplayStateRepository
+    fun displayStateRepository(impl: DisplayStateRepositoryImpl): DisplayStateRepository
 
     @Binds
     @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
new file mode 100644
index 0000000..7a9efcf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.data.repository
+
+import android.content.Context
+import android.hardware.devicestate.DeviceStateManager
+import android.hardware.display.DisplayManager
+import android.hardware.display.DisplayManager.DisplayListener
+import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
+import android.os.Handler
+import android.view.DisplayInfo
+import com.android.internal.util.ArrayUtils
+import com.android.systemui.biometrics.shared.model.DisplayRotation
+import com.android.systemui.biometrics.shared.model.toDisplayRotation
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.stateIn
+
+/** Repository for the current state of the display */
+interface DisplayStateRepository {
+    /** Provides the current rear display state. */
+    val isInRearDisplayMode: StateFlow<Boolean>
+
+    /** Provides the current display rotation */
+    val currentRotation: StateFlow<DisplayRotation>
+}
+
+@SysUISingleton
+class DisplayStateRepositoryImpl
+@Inject
+constructor(
+    @Application applicationScope: CoroutineScope,
+    @Application val context: Context,
+    deviceStateManager: DeviceStateManager,
+    displayManager: DisplayManager,
+    @Main handler: Handler,
+    @Main mainExecutor: Executor
+) : DisplayStateRepository {
+    override val isInRearDisplayMode: StateFlow<Boolean> =
+        conflatedCallbackFlow {
+                val sendRearDisplayStateUpdate = { state: Boolean ->
+                    trySendWithFailureLogging(
+                        state,
+                        TAG,
+                        "Error sending rear display state update to $state"
+                    )
+                }
+
+                val callback =
+                    DeviceStateManager.DeviceStateCallback { state ->
+                        val isInRearDisplayMode =
+                            ArrayUtils.contains(
+                                context.resources.getIntArray(
+                                    com.android.internal.R.array.config_rearDisplayDeviceStates
+                                ),
+                                state
+                            )
+                        sendRearDisplayStateUpdate(isInRearDisplayMode)
+                    }
+
+                sendRearDisplayStateUpdate(false)
+                deviceStateManager.registerCallback(mainExecutor, callback)
+                awaitClose { deviceStateManager.unregisterCallback(callback) }
+            }
+            .stateIn(
+                applicationScope,
+                started = SharingStarted.Eagerly,
+                initialValue = false,
+            )
+
+    private fun getDisplayRotation(): DisplayRotation {
+        val cachedDisplayInfo = DisplayInfo()
+        context.display?.getDisplayInfo(cachedDisplayInfo)
+        return cachedDisplayInfo.rotation.toDisplayRotation()
+    }
+
+    override val currentRotation: StateFlow<DisplayRotation> =
+        conflatedCallbackFlow {
+                val callback =
+                    object : DisplayListener {
+                        override fun onDisplayRemoved(displayId: Int) {}
+
+                        override fun onDisplayAdded(displayId: Int) {}
+
+                        override fun onDisplayChanged(displayId: Int) {
+                            val rotation = getDisplayRotation()
+                            trySendWithFailureLogging(
+                                rotation,
+                                TAG,
+                                "Error sending display rotation to $rotation"
+                            )
+                        }
+                    }
+                displayManager.registerDisplayListener(
+                    callback,
+                    handler,
+                    EVENT_FLAG_DISPLAY_CHANGED
+                )
+                awaitClose { displayManager.unregisterDisplayListener(callback) }
+            }
+            .stateIn(
+                applicationScope,
+                started = SharingStarted.Eagerly,
+                initialValue = getDisplayRotation(),
+            )
+
+    companion object {
+        const val TAG = "DisplayStateRepositoryImpl"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepository.kt
deleted file mode 100644
index d17d961..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepository.kt
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics.data.repository
-
-import android.content.Context
-import android.hardware.devicestate.DeviceStateManager
-import com.android.internal.util.ArrayUtils
-import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Main
-import java.util.concurrent.Executor
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.stateIn
-
-/** Provide current rear display state. */
-interface RearDisplayStateRepository {
-    /** Provides the current rear display state. */
-    val isInRearDisplayMode: StateFlow<Boolean>
-}
-
-@SysUISingleton
-class RearDisplayStateRepositoryImpl
-@Inject
-constructor(
-    @Application applicationScope: CoroutineScope,
-    @Application context: Context,
-    deviceStateManager: DeviceStateManager,
-    @Main mainExecutor: Executor
-) : RearDisplayStateRepository {
-    override val isInRearDisplayMode: StateFlow<Boolean> =
-        conflatedCallbackFlow {
-                val sendRearDisplayStateUpdate = { state: Boolean ->
-                    trySendWithFailureLogging(
-                        state,
-                        TAG,
-                        "Error sending rear display state update to $state"
-                    )
-                }
-
-                val callback =
-                    DeviceStateManager.DeviceStateCallback { state ->
-                        val isInRearDisplayMode =
-                            ArrayUtils.contains(
-                                context.resources.getIntArray(
-                                    com.android.internal.R.array.config_rearDisplayDeviceStates
-                                ),
-                                state
-                            )
-                        sendRearDisplayStateUpdate(isInRearDisplayMode)
-                    }
-
-                sendRearDisplayStateUpdate(false)
-                deviceStateManager.registerCallback(mainExecutor, callback)
-                awaitClose { deviceStateManager.unregisterCallback(callback) }
-            }
-            .stateIn(
-                applicationScope,
-                started = SharingStarted.Eagerly,
-                initialValue = false,
-            )
-
-    companion object {
-        const val TAG = "RearDisplayStateRepositoryImpl"
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt
index 26b6f2a..6fe79c6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt
@@ -18,7 +18,8 @@
 
 import android.content.Context
 import android.content.res.Configuration
-import com.android.systemui.biometrics.data.repository.RearDisplayStateRepository
+import com.android.systemui.biometrics.data.repository.DisplayStateRepository
+import com.android.systemui.biometrics.shared.model.DisplayRotation
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.dagger.qualifiers.Application
@@ -43,6 +44,9 @@
     /** Whether the device is currently folded. */
     val isFolded: Flow<Boolean>
 
+    /** Current rotation of the display */
+    val currentRotation: StateFlow<DisplayRotation>
+
     /** Called on configuration changes, used to keep the display state in sync */
     fun onConfigurationChanged(newConfig: Configuration)
 }
@@ -54,7 +58,7 @@
     @Application applicationScope: CoroutineScope,
     @Application context: Context,
     @Main mainExecutor: Executor,
-    rearDisplayStateRepository: RearDisplayStateRepository,
+    displayStateRepository: DisplayStateRepository,
 ) : DisplayStateInteractor {
     private var screenSizeFoldProvider: ScreenSizeFoldProvider = ScreenSizeFoldProvider(context)
 
@@ -90,7 +94,10 @@
             )
 
     override val isInRearDisplayMode: StateFlow<Boolean> =
-        rearDisplayStateRepository.isInRearDisplayMode
+        displayStateRepository.isInRearDisplayMode
+
+    override val currentRotation: StateFlow<DisplayRotation> =
+        displayStateRepository.currentRotation
 
     override fun onConfigurationChanged(newConfig: Configuration) {
         screenSizeFoldProvider.onConfigurationChange(newConfig)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/DisplayRotation.kt b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/DisplayRotation.kt
new file mode 100644
index 0000000..10a3e91
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/DisplayRotation.kt
@@ -0,0 +1,21 @@
+package com.android.systemui.biometrics.shared.model
+
+import android.view.Surface
+
+/** Shadows [Surface.Rotation] for kotlin use within SysUI. */
+enum class DisplayRotation {
+    ROTATION_0,
+    ROTATION_90,
+    ROTATION_180,
+    ROTATION_270,
+}
+
+/** Converts [Surface.Rotation] to corresponding [DisplayRotation] */
+fun Int.toDisplayRotation(): DisplayRotation =
+    when (this) {
+        Surface.ROTATION_0 -> DisplayRotation.ROTATION_0
+        Surface.ROTATION_90 -> DisplayRotation.ROTATION_90
+        Surface.ROTATION_180 -> DisplayRotation.ROTATION_180
+        Surface.ROTATION_270 -> DisplayRotation.ROTATION_270
+        else -> throw IllegalArgumentException("Invalid DisplayRotation value: $this")
+    }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index b1439fd..ce31b5a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -261,6 +261,18 @@
                     }
                 }
 
+                // set padding
+                launch {
+                    viewModel.promptPadding.collect { promptPadding ->
+                        view.setPadding(
+                            promptPadding.left,
+                            promptPadding.top,
+                            promptPadding.right,
+                            promptPadding.bottom
+                        )
+                    }
+                }
+
                 // configure & hide/disable buttons
                 launch {
                     viewModel.credentialKind
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index be08932..25634aa 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -15,16 +15,21 @@
  */
 package com.android.systemui.biometrics.ui.viewmodel
 
+import android.content.Context
+import android.graphics.Rect
 import android.hardware.biometrics.BiometricPrompt
 import android.util.Log
 import android.view.HapticFeedbackConstants
 import android.view.MotionEvent
 import com.android.systemui.biometrics.AuthBiometricView
+import com.android.systemui.biometrics.Utils
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
 import com.android.systemui.biometrics.domain.model.BiometricModalities
 import com.android.systemui.biometrics.shared.model.BiometricModality
+import com.android.systemui.biometrics.shared.model.DisplayRotation
 import com.android.systemui.biometrics.shared.model.PromptKind
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
 import com.android.systemui.statusbar.VibratorHelper
@@ -49,6 +54,7 @@
     private val displayStateInteractor: DisplayStateInteractor,
     private val promptSelectorInteractor: PromptSelectorInteractor,
     private val vibrator: VibratorHelper,
+    @Application context: Context,
     private val featureFlags: FeatureFlags,
 ) {
     /** Models UI of [BiometricPromptLayout.iconView] */
@@ -135,6 +141,23 @@
             !isOverlayTouched && size.isNotSmall
         }
 
+    /** Padding for prompt UI elements */
+    val promptPadding: Flow<Rect> =
+        combine(size, displayStateInteractor.currentRotation) { size, rotation ->
+            if (size != PromptSize.LARGE) {
+                val navBarInsets = Utils.getNavbarInsets(context)
+                if (rotation == DisplayRotation.ROTATION_90) {
+                    Rect(0, 0, navBarInsets.right, 0)
+                } else if (rotation == DisplayRotation.ROTATION_270) {
+                    Rect(navBarInsets.left, 0, 0, 0)
+                } else {
+                    Rect(0, 0, 0, navBarInsets.bottom)
+                }
+            } else {
+                Rect(0, 0, 0, 0)
+            }
+        }
+
     /** Title for the prompt. */
     val title: Flow<String> =
         promptSelectorInteractor.prompt.map { it?.title ?: "" }.distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
index 5d732fb..cbb7e1d 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.mediaprojection.appselector.view
 
 import android.app.ActivityOptions
+import android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
 import android.app.IActivityTaskManager
 import android.graphics.Rect
 import android.os.Binder
@@ -129,6 +130,9 @@
                 view.width,
                 view.height
             )
+        activityOptions.setPendingIntentBackgroundActivityStartMode(
+            MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+        )
         activityOptions.launchCookie = launchCookie
 
         activityTaskManager.startActivityFromRecents(task.taskId, activityOptions.toBundle())
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 146b5f5..79e7b71 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -359,6 +359,7 @@
 
     public void setBackgroundExecutor(Executor bgExecutor) {
         mBgExecutor = bgExecutor;
+        mRotationButtonController.setBgExecutor(bgExecutor);
     }
 
     public void setDisplayTracker(DisplayTracker displayTracker) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 83a040c..1966033 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -40,6 +40,7 @@
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
+import com.android.systemui.statusbar.policy.Clock;
 import com.android.systemui.user.ui.binder.StatusBarUserChipViewBinder;
 import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel;
 import com.android.systemui.util.leak.RotationUtils;
@@ -51,7 +52,7 @@
     private final StatusBarContentInsetsProvider mContentInsetsProvider;
 
     private DarkReceiver mBattery;
-    private DarkReceiver mClock;
+    private Clock mClock;
     private int mRotationOrientation = -1;
     @Nullable
     private View mCutoutSpace;
@@ -123,6 +124,10 @@
         }
     }
 
+    void onDensityOrFontScaleChanged() {
+        mClock.onDensityOrFontScaleChanged();
+    }
+
     @Override
     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
         if (updateDisplayParameters()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index 4de669c..85a75ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -71,12 +71,20 @@
         override fun onConfigChanged(newConfig: Configuration?) {
             mView.updateResources()
         }
+
+        override fun onDensityOrFontScaleChanged() {
+            mView.onDensityOrFontScaleChanged()
+        }
     }
 
     override fun onViewAttached() {
         statusContainer = mView.requireViewById(R.id.system_icons)
         statusContainer.setOnHoverListener(
             statusOverlayHoverListenerFactory.createDarkAwareListener(statusContainer))
+
+        progressProvider?.setReadyToHandleTransition(true)
+        configurationController.addCallback(configurationListener)
+
         if (moveFromCenterAnimationController == null) return
 
         val statusBarLeftSide: View =
@@ -103,9 +111,6 @@
                 moveFromCenterAnimationController.onStatusBarWidthChanged()
             }
         }
-
-        progressProvider?.setReadyToHandleTransition(true)
-        configurationController.addCallback(configurationListener)
     }
 
     override fun onViewDetached() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
index 4b2fb43..6483b7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
@@ -19,7 +19,6 @@
 import android.content.res.ColorStateList
 import android.view.View
 import android.view.View.GONE
-import android.view.View.INVISIBLE
 import android.view.View.VISIBLE
 import android.view.ViewGroup
 import android.widget.ImageView
@@ -33,12 +32,11 @@
 import com.android.systemui.common.ui.binder.IconViewBinder
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.statusbar.StatusBarIconView
-import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT
 import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN
-import com.android.systemui.statusbar.StatusBarIconView.STATE_ICON
 import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.LocationBasedMobileViewModel
 import com.android.systemui.statusbar.pipeline.shared.ui.binder.ModernStatusBarViewBinding
+import com.android.systemui.statusbar.pipeline.shared.ui.binder.ModernStatusBarViewVisibilityHelper
 import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.distinctUntilChanged
@@ -83,20 +81,11 @@
 
                 launch {
                     visibilityState.collect { state ->
-                        when (state) {
-                            STATE_ICON -> {
-                                mobileGroupView.visibility = VISIBLE
-                                dotView.visibility = GONE
-                            }
-                            STATE_DOT -> {
-                                mobileGroupView.visibility = INVISIBLE
-                                dotView.visibility = VISIBLE
-                            }
-                            STATE_HIDDEN -> {
-                                mobileGroupView.visibility = INVISIBLE
-                                dotView.visibility = INVISIBLE
-                            }
-                        }
+                        ModernStatusBarViewVisibilityHelper.setVisibilityState(
+                            state,
+                            mobileGroupView,
+                            dotView,
+                        )
                     }
                 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/ModernStatusBarViewVisibilityHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/ModernStatusBarViewVisibilityHelper.kt
new file mode 100644
index 0000000..6668cbc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/ModernStatusBarViewVisibilityHelper.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.shared.ui.binder
+
+import android.view.View
+import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconBinder
+import com.android.systemui.statusbar.pipeline.wifi.ui.binder.WifiViewBinder
+
+/**
+ * The helper to update the groupView and dotView visibility based on given visibility state, only
+ * used for [MobileIconBinder] and [WifiViewBinder] now.
+ */
+class ModernStatusBarViewVisibilityHelper {
+    companion object {
+
+        fun setVisibilityState(
+            @StatusBarIconView.VisibleState state: Int,
+            groupView: View,
+            dotView: View,
+        ) {
+            when (state) {
+                StatusBarIconView.STATE_ICON -> {
+                    groupView.visibility = View.VISIBLE
+                    dotView.visibility = View.GONE
+                }
+                StatusBarIconView.STATE_DOT -> {
+                    groupView.visibility = View.INVISIBLE
+                    dotView.visibility = View.VISIBLE
+                }
+                StatusBarIconView.STATE_HIDDEN -> {
+                    groupView.visibility = View.INVISIBLE
+                    dotView.visibility = View.INVISIBLE
+                }
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
index 3082a66..e593575 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
@@ -27,10 +27,9 @@
 import com.android.systemui.common.ui.binder.IconViewBinder
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.statusbar.StatusBarIconView
-import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT
 import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN
-import com.android.systemui.statusbar.StatusBarIconView.STATE_ICON
 import com.android.systemui.statusbar.pipeline.shared.ui.binder.ModernStatusBarViewBinding
+import com.android.systemui.statusbar.pipeline.shared.ui.binder.ModernStatusBarViewVisibilityHelper
 import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
 import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel
 import kotlinx.coroutines.InternalCoroutinesApi
@@ -83,8 +82,18 @@
 
                 launch {
                     visibilityState.collect { visibilityState ->
-                        groupView.isVisible = visibilityState == STATE_ICON
-                        dotView.isVisible = visibilityState == STATE_DOT
+                        // for b/296864006, we can not hide all the child views if visibilityState
+                        // is STATE_HIDDEN. Because hiding all child views would cause the
+                        // getWidth() of this view return 0, and that would cause the translation
+                        // calculation fails in StatusIconContainer. Therefore, like class
+                        // MobileIconBinder, instead of set the child views visibility to View.GONE,
+                        // we set their visibility to View.INVISIBLE to make them invisible but
+                        // keep the width.
+                        ModernStatusBarViewVisibilityHelper.setVisibilityState(
+                            visibilityState,
+                            groupView,
+                            dotView,
+                        )
                     }
                 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index f994372..b7ae233 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -37,9 +37,11 @@
 import android.text.style.CharacterStyle;
 import android.text.style.RelativeSizeSpan;
 import android.util.AttributeSet;
+import android.util.TypedValue;
 import android.view.ContextThemeWrapper;
 import android.view.Display;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.TextView;
 
 import com.android.settingslib.Utils;
@@ -143,6 +145,8 @@
         }
         mBroadcastDispatcher = Dependency.get(BroadcastDispatcher.class);
         mUserTracker = Dependency.get(UserTracker.class);
+
+        setIncludeFontPadding(false);
     }
 
     @Override
@@ -389,6 +393,15 @@
                 mContext.getResources().getDimensionPixelSize(
                         R.dimen.status_bar_clock_end_padding),
                 0);
+
+        float fontHeight = getPaint().getFontMetricsInt(null);
+        setLineHeight(TypedValue.COMPLEX_UNIT_PX, fontHeight);
+
+        ViewGroup.LayoutParams lp = getLayoutParams();
+        if (lp != null) {
+            lp.height = (int) Math.ceil(fontHeight);
+            setLayoutParams(lp);
+        }
     }
 
     private void updateShowSeconds() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 9584d88..bddc4c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -41,7 +41,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
 import com.android.systemui.biometrics.data.repository.FakePromptRepository
-import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository
+import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
 import com.android.systemui.biometrics.domain.interactor.FakeCredentialInteractor
 import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor
@@ -110,7 +110,7 @@
     private val fakeExecutor = FakeExecutor(FakeSystemClock())
     private val biometricPromptRepository = FakePromptRepository()
     private val fingerprintRepository = FakeFingerprintPropertyRepository()
-    private val rearDisplayStateRepository = FakeRearDisplayStateRepository()
+    private val displayStateRepository = FakeDisplayStateRepository()
     private val credentialInteractor = FakeCredentialInteractor()
     private val bpCredentialInteractor = PromptCredentialInteractor(
         Dispatchers.Main.immediate,
@@ -129,7 +129,7 @@
         testScope.backgroundScope,
         mContext,
         fakeExecutor,
-        rearDisplayStateRepository
+        displayStateRepository
     )
 
 
@@ -531,6 +531,7 @@
             displayStateInteractor,
             promptSelectorInteractor,
             vibrator,
+            context,
             featureFlags
         ),
         { credentialViewModel },
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
index 994db46..c71ef47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
@@ -56,7 +56,7 @@
 import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.SysuiTestableContext
-import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository
+import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
 import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
@@ -120,7 +120,7 @@
     private lateinit var displayStateInteractor: DisplayStateInteractor
 
     private val executor = FakeExecutor(FakeSystemClock())
-    private val rearDisplayStateRepository = FakeRearDisplayStateRepository()
+    private val displayStateRepository = FakeDisplayStateRepository()
     private val testScope = TestScope(StandardTestDispatcher())
 
     private lateinit var overlayController: ISidefpsController
@@ -157,7 +157,7 @@
                 testScope.backgroundScope,
                 context,
                 executor,
-                rearDisplayStateRepository
+                displayStateRepository
             )
 
         context.addMockSystemService(DisplayManager::class.java, displayManager)
@@ -268,7 +268,7 @@
                 TestCoroutineScope(),
                 dumpManager
             )
-        rearDisplayStateRepository.setIsInRearDisplayMode(inRearDisplayMode)
+        displayStateRepository.setIsInRearDisplayMode(inRearDisplayMode)
 
         overlayController =
             ArgumentCaptor.forClass(ISidefpsController::class.java)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt
new file mode 100644
index 0000000..c9c46cb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import android.hardware.devicestate.DeviceStateManager
+import android.hardware.display.DisplayManager
+import android.os.Handler
+import android.view.Display
+import android.view.DisplayInfo
+import android.view.Surface
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.DisplayStateRepository
+import com.android.systemui.biometrics.data.repository.DisplayStateRepositoryImpl
+import com.android.systemui.biometrics.shared.model.DisplayRotation
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.mockito.withArgCaptor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.same
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+private const val NORMAL_DISPLAY_MODE_DEVICE_STATE = 2
+private const val REAR_DISPLAY_MODE_DEVICE_STATE = 3
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class DisplayStateRepositoryTest : SysuiTestCase() {
+    @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
+    @Mock private lateinit var deviceStateManager: DeviceStateManager
+    @Mock private lateinit var displayManager: DisplayManager
+    @Mock private lateinit var handler: Handler
+    @Mock private lateinit var display: Display
+    private lateinit var underTest: DisplayStateRepository
+
+    private val testScope = TestScope(StandardTestDispatcher())
+    private val fakeExecutor = FakeExecutor(FakeSystemClock())
+
+    @Captor
+    private lateinit var displayListenerCaptor: ArgumentCaptor<DisplayManager.DisplayListener>
+
+    @Before
+    fun setUp() {
+        val rearDisplayDeviceStates = intArrayOf(REAR_DISPLAY_MODE_DEVICE_STATE)
+        mContext.orCreateTestableResources.addOverride(
+            com.android.internal.R.array.config_rearDisplayDeviceStates,
+            rearDisplayDeviceStates
+        )
+
+        mContext = spy(mContext)
+        whenever(mContext.display).thenReturn(display)
+
+        underTest =
+            DisplayStateRepositoryImpl(
+                testScope.backgroundScope,
+                mContext,
+                deviceStateManager,
+                displayManager,
+                handler,
+                fakeExecutor
+            )
+    }
+
+    @Test
+    fun updatesIsInRearDisplayMode_whenRearDisplayStateChanges() =
+        testScope.runTest {
+            val isInRearDisplayMode by collectLastValue(underTest.isInRearDisplayMode)
+            runCurrent()
+
+            val callback = deviceStateManager.captureCallback()
+
+            callback.onStateChanged(NORMAL_DISPLAY_MODE_DEVICE_STATE)
+            assertThat(isInRearDisplayMode).isFalse()
+
+            callback.onStateChanged(REAR_DISPLAY_MODE_DEVICE_STATE)
+            assertThat(isInRearDisplayMode).isTrue()
+        }
+
+    @Test
+    fun updatesCurrentRotation_whenDisplayStateChanges() =
+        testScope.runTest {
+            val currentRotation by collectLastValue(underTest.currentRotation)
+            runCurrent()
+
+            verify(displayManager)
+                .registerDisplayListener(
+                    displayListenerCaptor.capture(),
+                    same(handler),
+                    eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED)
+                )
+
+            whenever(display.getDisplayInfo(any())).then {
+                val info = it.getArgument<DisplayInfo>(0)
+                info.rotation = Surface.ROTATION_90
+                return@then true
+            }
+            displayListenerCaptor.value.onDisplayChanged(Surface.ROTATION_90)
+            assertThat(currentRotation).isEqualTo(DisplayRotation.ROTATION_90)
+
+            whenever(display.getDisplayInfo(any())).then {
+                val info = it.getArgument<DisplayInfo>(0)
+                info.rotation = Surface.ROTATION_180
+                return@then true
+            }
+            displayListenerCaptor.value.onDisplayChanged(Surface.ROTATION_180)
+            assertThat(currentRotation).isEqualTo(DisplayRotation.ROTATION_180)
+        }
+}
+
+private fun DeviceStateManager.captureCallback() =
+    withArgCaptor<DeviceStateManager.DeviceStateCallback> {
+        verify(this@captureCallback).registerCallback(any(), capture())
+    }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepositoryTest.kt
deleted file mode 100644
index dfe8d36..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepositoryTest.kt
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard.data.repository
-
-import android.hardware.devicestate.DeviceStateManager
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.biometrics.data.repository.RearDisplayStateRepository
-import com.android.systemui.biometrics.data.repository.RearDisplayStateRepositoryImpl
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.withArgCaptor
-import com.android.systemui.util.time.FakeSystemClock
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import org.mockito.ArgumentCaptor
-import org.mockito.Captor
-import org.mockito.Mock
-import org.mockito.Mockito.verify
-import org.mockito.junit.MockitoJUnit
-import org.mockito.junit.MockitoRule
-
-private const val NORMAL_DISPLAY_MODE_DEVICE_STATE = 2
-private const val REAR_DISPLAY_MODE_DEVICE_STATE = 3
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(JUnit4::class)
-class RearDisplayStateRepositoryTest : SysuiTestCase() {
-    @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
-    @Mock private lateinit var deviceStateManager: DeviceStateManager
-    private lateinit var underTest: RearDisplayStateRepository
-
-    private val testScope = TestScope(StandardTestDispatcher())
-    private val fakeExecutor = FakeExecutor(FakeSystemClock())
-
-    @Captor
-    private lateinit var callbackCaptor: ArgumentCaptor<DeviceStateManager.DeviceStateCallback>
-
-    @Before
-    fun setUp() {
-        val rearDisplayDeviceStates = intArrayOf(REAR_DISPLAY_MODE_DEVICE_STATE)
-        mContext.orCreateTestableResources.addOverride(
-            com.android.internal.R.array.config_rearDisplayDeviceStates,
-            rearDisplayDeviceStates
-        )
-
-        underTest =
-            RearDisplayStateRepositoryImpl(
-                testScope.backgroundScope,
-                mContext,
-                deviceStateManager,
-                fakeExecutor
-            )
-    }
-
-    @Test
-    fun updatesIsInRearDisplayMode_whenRearDisplayStateChanges() =
-        testScope.runTest {
-            val isInRearDisplayMode = collectLastValue(underTest.isInRearDisplayMode)
-            runCurrent()
-
-            val callback = deviceStateManager.captureCallback()
-
-            callback.onStateChanged(NORMAL_DISPLAY_MODE_DEVICE_STATE)
-            assertThat(isInRearDisplayMode()).isFalse()
-
-            callback.onStateChanged(REAR_DISPLAY_MODE_DEVICE_STATE)
-            assertThat(isInRearDisplayMode()).isTrue()
-        }
-}
-
-private fun DeviceStateManager.captureCallback() =
-    withArgCaptor<DeviceStateManager.DeviceStateCallback> {
-        verify(this@captureCallback).registerCallback(any(), capture())
-    }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt
index 2217c5c..0f84d9d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt
@@ -2,7 +2,8 @@
 
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository
+import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
+import com.android.systemui.biometrics.shared.model.DisplayRotation
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.unfold.compat.ScreenSizeFoldProvider
 import com.android.systemui.unfold.updates.FoldProvider
@@ -34,7 +35,7 @@
 
     private val fakeExecutor = FakeExecutor(FakeSystemClock())
     private val testScope = TestScope(StandardTestDispatcher())
-    private val rearDisplayStateRepository = FakeRearDisplayStateRepository()
+    private val displayStateRepository = FakeDisplayStateRepository()
 
     @Mock private lateinit var screenSizeFoldProvider: ScreenSizeFoldProvider
     private lateinit var interactor: DisplayStateInteractorImpl
@@ -46,7 +47,7 @@
                 testScope.backgroundScope,
                 mContext,
                 fakeExecutor,
-                rearDisplayStateRepository
+                displayStateRepository
             )
         interactor.setScreenSizeFoldProvider(screenSizeFoldProvider)
     }
@@ -56,14 +57,26 @@
         testScope.runTest {
             val isInRearDisplayMode = collectLastValue(interactor.isInRearDisplayMode)
 
-            rearDisplayStateRepository.setIsInRearDisplayMode(false)
+            displayStateRepository.setIsInRearDisplayMode(false)
             assertThat(isInRearDisplayMode()).isFalse()
 
-            rearDisplayStateRepository.setIsInRearDisplayMode(true)
+            displayStateRepository.setIsInRearDisplayMode(true)
             assertThat(isInRearDisplayMode()).isTrue()
         }
 
     @Test
+    fun currentRotationChanges() =
+        testScope.runTest {
+            val currentRotation = collectLastValue(interactor.currentRotation)
+
+            displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
+            assertThat(currentRotation()).isEqualTo(DisplayRotation.ROTATION_180)
+
+            displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+            assertThat(currentRotation()).isEqualTo(DisplayRotation.ROTATION_90)
+        }
+
+    @Test
     fun isFoldedChanges() =
         testScope.runTest {
             val isFolded = collectLastValue(interactor.isFolded)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModelTest.kt
index 7697c09..0d61e30 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModelTest.kt
@@ -4,9 +4,9 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
 import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
 import com.android.systemui.biometrics.data.repository.FakePromptRepository
-import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
@@ -41,7 +41,7 @@
 
     private val fingerprintRepository = FakeFingerprintPropertyRepository()
     private val promptRepository = FakePromptRepository()
-    private val rearDisplayStateRepository = FakeRearDisplayStateRepository()
+    private val displayStateRepository = FakeDisplayStateRepository()
 
     private val testScope = TestScope(StandardTestDispatcher())
     private val fakeExecutor = FakeExecutor(FakeSystemClock())
@@ -59,7 +59,7 @@
                 testScope.backgroundScope,
                 mContext,
                 fakeExecutor,
-                rearDisplayStateRepository
+                displayStateRepository
             )
         viewModel = PromptFingerprintIconViewModel(displayStateInteractor, promptSelectorInteractor)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index 0ed46da..c53172d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -25,9 +25,9 @@
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.AuthBiometricView
+import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
 import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
 import com.android.systemui.biometrics.data.repository.FakePromptRepository
-import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl
@@ -79,14 +79,14 @@
     private val testScope = TestScope()
     private val fingerprintRepository = FakeFingerprintPropertyRepository()
     private val promptRepository = FakePromptRepository()
-    private val rearDisplayStateRepository = FakeRearDisplayStateRepository()
+    private val displayStateRepository = FakeDisplayStateRepository()
 
     private val displayStateInteractor =
         DisplayStateInteractorImpl(
             testScope.backgroundScope,
             mContext,
             fakeExecutor,
-            rearDisplayStateRepository
+            displayStateRepository
         )
 
     private lateinit var selector: PromptSelectorInteractor
@@ -99,7 +99,8 @@
             PromptSelectorInteractorImpl(fingerprintRepository, promptRepository, lockPatternUtils)
         selector.resetPrompt()
 
-        viewModel = PromptViewModel(displayStateInteractor, selector, vibrator, featureFlags)
+        viewModel =
+            PromptViewModel(displayStateInteractor, selector, vibrator, mContext, featureFlags)
         featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false)
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index 0b31523..af0d6b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -103,6 +103,21 @@
     }
 
     @Test
+    fun onViewAttachedAndDrawn_startListeningConfigurationControllerCallback() {
+        val view = createViewMock()
+        val argumentCaptor = ArgumentCaptor.forClass(
+                ConfigurationController.ConfigurationListener::class.java)
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            controller = createAndInitController(view)
+        }
+
+        verify(configurationController).addCallback(argumentCaptor.capture())
+        argumentCaptor.value.onDensityOrFontScaleChanged()
+
+        verify(view).onDensityOrFontScaleChanged()
+    }
+
+    @Test
     fun onViewAttachedAndDrawn_moveFromCenterAnimationEnabled_moveFromCenterAnimationInitialized() {
         whenever(featureFlags.isEnabled(Flags.ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS))
                 .thenReturn(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
index a0d4d13..bbf048d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
@@ -22,6 +22,7 @@
 import android.testing.TestableLooper.RunWithLooper
 import android.testing.ViewUtils
 import android.view.View
+import android.view.ViewGroup
 import android.widget.ImageView
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
@@ -138,7 +139,7 @@
         ViewUtils.attachView(view)
         testableLooper.processAllMessages()
 
-        assertThat(view.getIconGroupView().visibility).isEqualTo(View.GONE)
+        assertThat(view.getIconGroupView().visibility).isEqualTo(View.INVISIBLE)
         assertThat(view.getDotView().visibility).isEqualTo(View.VISIBLE)
 
         ViewUtils.detachView(view)
@@ -153,8 +154,36 @@
         ViewUtils.attachView(view)
         testableLooper.processAllMessages()
 
-        assertThat(view.getIconGroupView().visibility).isEqualTo(View.GONE)
-        assertThat(view.getDotView().visibility).isEqualTo(View.GONE)
+        assertThat(view.getIconGroupView().visibility).isEqualTo(View.INVISIBLE)
+        assertThat(view.getDotView().visibility).isEqualTo(View.INVISIBLE)
+
+        ViewUtils.detachView(view)
+    }
+
+    /* Regression test for b/296864006. When STATE_HIDDEN we need to ensure the wifi view width
+     * would not break the StatusIconContainer translation calculation. */
+    @Test
+    fun setVisibleState_hidden_keepWidth() {
+        val view = ModernStatusBarWifiView.constructAndBind(context, SLOT_NAME, viewModel)
+
+        view.setVisibleState(STATE_ICON, /* animate= */ false)
+
+        // get the view width when it's in visible state
+        ViewUtils.attachView(view)
+        val lp = view.layoutParams
+        lp.width = ViewGroup.LayoutParams.WRAP_CONTENT
+        lp.height = ViewGroup.LayoutParams.WRAP_CONTENT
+        view.layoutParams = lp
+        testableLooper.processAllMessages()
+        val currentWidth = view.width
+
+        view.setVisibleState(STATE_HIDDEN, /* animate= */ false)
+        testableLooper.processAllMessages()
+
+        // the view width when STATE_HIDDEN should be at least the width when STATE_ICON. Because
+        // when STATE_HIDDEN the invisible dot view width might be larger than group view width,
+        // then the wifi view width would be enlarged.
+        assertThat(view.width).isAtLeast(currentWidth)
 
         ViewUtils.detachView(view)
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeRearDisplayStateRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt
similarity index 71%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeRearDisplayStateRepository.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt
index fd91391..60291ee 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeRearDisplayStateRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt
@@ -17,15 +17,23 @@
 
 package com.android.systemui.biometrics.data.repository
 
+import com.android.systemui.biometrics.shared.model.DisplayRotation
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
 
-class FakeRearDisplayStateRepository : RearDisplayStateRepository {
+class FakeDisplayStateRepository : DisplayStateRepository {
     private val _isInRearDisplayMode = MutableStateFlow<Boolean>(false)
     override val isInRearDisplayMode: StateFlow<Boolean> = _isInRearDisplayMode.asStateFlow()
 
+    private val _currentRotation = MutableStateFlow<DisplayRotation>(DisplayRotation.ROTATION_0)
+    override val currentRotation: StateFlow<DisplayRotation> = _currentRotation.asStateFlow()
+
     fun setIsInRearDisplayMode(isInRearDisplayMode: Boolean) {
         _isInRearDisplayMode.value = isInRearDisplayMode
     }
+
+    fun setCurrentRotation(currentRotation: DisplayRotation) {
+        _currentRotation.value = currentRotation
+    }
 }
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 9d6283b8..2511b50 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -64,6 +64,7 @@
 import android.companion.IOnMessageReceivedListener;
 import android.companion.IOnTransportsChangedListener;
 import android.companion.ISystemDataTransferCallback;
+import android.companion.datatransfer.PermissionSyncRequest;
 import android.companion.utils.FeatureUtils;
 import android.content.ComponentName;
 import android.content.Context;
@@ -794,6 +795,24 @@
         }
 
         @Override
+        public void enablePermissionsSync(int associationId) {
+            getAssociationWithCallerChecks(associationId);
+            mSystemDataTransferProcessor.enablePermissionsSync(associationId);
+        }
+
+        @Override
+        public void disablePermissionsSync(int associationId) {
+            getAssociationWithCallerChecks(associationId);
+            mSystemDataTransferProcessor.disablePermissionsSync(associationId);
+        }
+
+        @Override
+        public PermissionSyncRequest getPermissionSyncRequest(int associationId) {
+            getAssociationWithCallerChecks(associationId);
+            return mSystemDataTransferProcessor.getPermissionSyncRequest(associationId);
+        }
+
+        @Override
         public void enableSecureTransport(boolean enabled) {
             mTransportManager.enableSecureTransport(enabled);
         }
@@ -934,7 +953,7 @@
                 String[] args, ShellCallback callback, ResultReceiver resultReceiver)
                 throws RemoteException {
             new CompanionDeviceShellCommand(CompanionDeviceManagerService.this, mAssociationStore,
-                    mDevicePresenceMonitor, mTransportManager, mSystemDataTransferRequestStore,
+                    mDevicePresenceMonitor, mTransportManager, mSystemDataTransferProcessor,
                     mAssociationRequestsProcessor)
                     .exec(this, in, out, err, args, callback, resultReceiver);
         }
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index 04fbab4..d56436b1 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -25,7 +25,7 @@
 import android.os.ShellCommand;
 import android.util.proto.ProtoOutputStream;
 
-import com.android.server.companion.datatransfer.SystemDataTransferRequestStore;
+import com.android.server.companion.datatransfer.SystemDataTransferProcessor;
 import com.android.server.companion.datatransfer.contextsync.BitmapUtils;
 import com.android.server.companion.datatransfer.contextsync.CrossDeviceSyncController;
 import com.android.server.companion.presence.CompanionDevicePresenceMonitor;
@@ -43,20 +43,20 @@
     private final CompanionDevicePresenceMonitor mDevicePresenceMonitor;
     private final CompanionTransportManager mTransportManager;
 
-    private final SystemDataTransferRequestStore mSystemDataTransferRequestStore;
+    private final SystemDataTransferProcessor mSystemDataTransferProcessor;
     private final AssociationRequestsProcessor mAssociationRequestsProcessor;
 
     CompanionDeviceShellCommand(CompanionDeviceManagerService service,
             AssociationStoreImpl associationStore,
             CompanionDevicePresenceMonitor devicePresenceMonitor,
             CompanionTransportManager transportManager,
-            SystemDataTransferRequestStore systemDataTransferRequestStore,
+            SystemDataTransferProcessor systemDataTransferProcessor,
             AssociationRequestsProcessor associationRequestsProcessor) {
         mService = service;
         mAssociationStore = associationStore;
         mDevicePresenceMonitor = devicePresenceMonitor;
         mTransportManager = transportManager;
-        mSystemDataTransferRequestStore = systemDataTransferRequestStore;
+        mSystemDataTransferProcessor = systemDataTransferProcessor;
         mAssociationRequestsProcessor = associationRequestsProcessor;
     }
 
@@ -265,16 +265,47 @@
                     break;
                 }
 
-                case "allow-permission-sync": {
-                    int userId = getNextIntArgRequired();
+                case "get-perm-sync-state": {
                     associationId = getNextIntArgRequired();
-                    boolean enabled = getNextBooleanArgRequired();
-                    PermissionSyncRequest request = new PermissionSyncRequest(associationId);
-                    request.setUserId(userId);
-                    request.setUserConsented(enabled);
-                    mSystemDataTransferRequestStore.writeRequest(userId, request);
+                    PermissionSyncRequest request =
+                            mSystemDataTransferProcessor.getPermissionSyncRequest(associationId);
+                    out.println((request == null ? "null" : request.isUserConsented()));
+                    break;
                 }
-                break;
+
+                case "remove-perm-sync-state": {
+                    associationId = getNextIntArgRequired();
+                    PermissionSyncRequest request =
+                            mSystemDataTransferProcessor.getPermissionSyncRequest(associationId);
+                    out.print((request == null ? "null" : request.isUserConsented()));
+                    mSystemDataTransferProcessor.removePermissionSyncRequest(associationId);
+                    request = mSystemDataTransferProcessor.getPermissionSyncRequest(associationId);
+                    // should print " -> null"
+                    out.println(" -> " + (request == null ? "null" : request.isUserConsented()));
+                    break;
+                }
+
+                case "enable-perm-sync": {
+                    associationId = getNextIntArgRequired();
+                    PermissionSyncRequest request =
+                            mSystemDataTransferProcessor.getPermissionSyncRequest(associationId);
+                    out.print((request == null ? "null" : request.isUserConsented()));
+                    mSystemDataTransferProcessor.enablePermissionsSync(associationId);
+                    request = mSystemDataTransferProcessor.getPermissionSyncRequest(associationId);
+                    out.println(" -> " + request.isUserConsented()); // should print " -> true"
+                    break;
+                }
+
+                case "disable-perm-sync": {
+                    associationId = getNextIntArgRequired();
+                    PermissionSyncRequest request =
+                            mSystemDataTransferProcessor.getPermissionSyncRequest(associationId);
+                    out.print((request == null ? "null" : request.isUserConsented()));
+                    mSystemDataTransferProcessor.disablePermissionsSync(associationId);
+                    request = mSystemDataTransferProcessor.getPermissionSyncRequest(associationId);
+                    out.println(" -> " + request.isUserConsented()); // should print " -> false"
+                    break;
+                }
 
                 default:
                     return handleDefaultCommands(cmd);
@@ -346,5 +377,14 @@
 
         pw.println("  create-emulated-transport <ASSOCIATION_ID>");
         pw.println("      Create an EmulatedTransport for testing purposes only");
+
+        pw.println("  enable-perm-sync <ASSOCIATION_ID>");
+        pw.println("      Enable perm sync for the association.");
+        pw.println("  disable-perm-sync <ASSOCIATION_ID>");
+        pw.println("      Disable perm sync for the association.");
+        pw.println("  get-perm-sync-state <ASSOCIATION_ID>");
+        pw.println("      Get perm sync state for the association.");
+        pw.println("  remove-perm-sync-state <ASSOCIATION_ID>");
+        pw.println("      Remove perm sync state for the association.");
     }
 }
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
index 800a3d9..aeb273b 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
@@ -138,6 +138,13 @@
             @UserIdInt int userId, int associationId) {
         if (PackageUtils.isPackageAllowlisted(mContext, mPackageManager, packageName)) {
             Slog.i(LOG_TAG, "User consent Intent should be skipped. Returning null.");
+            // Auto enable perm sync for the allowlisted packages, but don't override user decision
+            PermissionSyncRequest request = getPermissionSyncRequest(associationId);
+            if (request == null) {
+                PermissionSyncRequest newRequest = new PermissionSyncRequest(associationId);
+                newRequest.setUserConsented(true);
+                mSystemDataTransferRequestStore.writeRequest(userId, newRequest);
+            }
             return null;
         }
 
@@ -184,29 +191,17 @@
         final AssociationInfo association = resolveAssociation(packageName, userId, associationId);
 
         // Check if the request has been consented by the user.
-        if (PackageUtils.isPackageAllowlisted(mContext, mPackageManager, packageName)) {
-            Slog.i(LOG_TAG, "Skip user consent check due to the same OEM package.");
-        } else {
-            List<SystemDataTransferRequest> storedRequests =
-                    mSystemDataTransferRequestStore.readRequestsByAssociationId(userId,
-                            associationId);
-            boolean hasConsented = false;
-            for (SystemDataTransferRequest storedRequest : storedRequests) {
-                if (storedRequest instanceof PermissionSyncRequest
-                        && storedRequest.isUserConsented()) {
-                    hasConsented = true;
-                    break;
-                }
+        PermissionSyncRequest request = getPermissionSyncRequest(associationId);
+        if (request == null || !request.isUserConsented()) {
+            String message =
+                    "User " + userId + " hasn't consented permission sync for associationId ["
+                            + associationId + ".";
+            Slog.e(LOG_TAG, message);
+            try {
+                callback.onError(message);
+            } catch (RemoteException ignored) {
             }
-            if (!hasConsented) {
-                String message = "User " + userId + " hasn't consented permission sync.";
-                Slog.e(LOG_TAG, message);
-                try {
-                    callback.onError(message);
-                } catch (RemoteException ignored) {
-                }
-                return;
-            }
+            return;
         }
 
         // Start permission sync
@@ -224,6 +219,66 @@
         }
     }
 
+    /**
+     * Enable perm sync for the association
+     */
+    public void enablePermissionsSync(int associationId) {
+        final long callingIdentityToken = Binder.clearCallingIdentity();
+        try {
+            int userId = mAssociationStore.getAssociationById(associationId).getUserId();
+            PermissionSyncRequest request = new PermissionSyncRequest(associationId);
+            request.setUserConsented(true);
+            mSystemDataTransferRequestStore.writeRequest(userId, request);
+        } finally {
+            Binder.restoreCallingIdentity(callingIdentityToken);
+        }
+    }
+
+    /**
+     * Disable perm sync for the association
+     */
+    public void disablePermissionsSync(int associationId) {
+        final long callingIdentityToken = Binder.clearCallingIdentity();
+        try {
+            int userId = mAssociationStore.getAssociationById(associationId).getUserId();
+            PermissionSyncRequest request = new PermissionSyncRequest(associationId);
+            request.setUserConsented(false);
+            mSystemDataTransferRequestStore.writeRequest(userId, request);
+        } finally {
+            Binder.restoreCallingIdentity(callingIdentityToken);
+        }
+    }
+
+    /**
+     * Get perm sync request for the association.
+     */
+    @Nullable
+    public PermissionSyncRequest getPermissionSyncRequest(int associationId) {
+        final long callingIdentityToken = Binder.clearCallingIdentity();
+        try {
+            int userId = mAssociationStore.getAssociationById(associationId).getUserId();
+            List<SystemDataTransferRequest> requests =
+                    mSystemDataTransferRequestStore.readRequestsByAssociationId(userId,
+                            associationId);
+            for (SystemDataTransferRequest request : requests) {
+                if (request instanceof PermissionSyncRequest) {
+                    return (PermissionSyncRequest) request;
+                }
+            }
+            return null;
+        } finally {
+            Binder.restoreCallingIdentity(callingIdentityToken);
+        }
+    }
+
+    /**
+     * Remove perm sync request for the association.
+     */
+    public void removePermissionSyncRequest(int associationId) {
+        int userId = mAssociationStore.getAssociationById(associationId).getUserId();
+        mSystemDataTransferRequestStore.removeRequestsByAssociationId(userId, associationId);
+    }
+
     private void onReceivePermissionRestore(byte[] message) {
         Slog.i(LOG_TAG, "Applying permissions.");
         // Start applying permissions
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
index 720cefa..9f489e8 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
@@ -104,7 +104,7 @@
     }
 
     @NonNull
-    List<SystemDataTransferRequest> readRequestsByAssociationId(@UserIdInt int userId,
+    public List<SystemDataTransferRequest> readRequestsByAssociationId(@UserIdInt int userId,
             int associationId) {
         List<SystemDataTransferRequest> cachedRequests;
         synchronized (mLock) {
diff --git a/services/core/java/com/android/server/SmartStorageMaintIdler.java b/services/core/java/com/android/server/SmartStorageMaintIdler.java
index 8996926..44f1e76 100644
--- a/services/core/java/com/android/server/SmartStorageMaintIdler.java
+++ b/services/core/java/com/android/server/SmartStorageMaintIdler.java
@@ -25,6 +25,7 @@
 import android.util.Slog;
 
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 public class SmartStorageMaintIdler extends JobService {
     private static final String TAG = "SmartStorageMaintIdler";
@@ -34,15 +35,15 @@
 
     private static final int SMART_MAINT_JOB_ID = 2808;
 
-    private boolean mStarted;
+    private final AtomicBoolean mStarted = new AtomicBoolean(false);
     private JobParameters mJobParams;
     private final Runnable mFinishCallback = new Runnable() {
         @Override
         public void run() {
             Slog.i(TAG, "Got smart storage maintenance service completion callback");
-            if (mStarted) {
+            if (mStarted.get()) {
                 jobFinished(mJobParams, false);
-                mStarted = false;
+                mStarted.set(false);
             }
             // ... and try again in a next period
             scheduleSmartIdlePass(SmartStorageMaintIdler.this,
@@ -52,18 +53,26 @@
 
     @Override
     public boolean onStartJob(JobParameters params) {
-        mJobParams = params;
-        StorageManagerService ms = StorageManagerService.sSelf;
-        if (ms != null) {
-            mStarted = true;
-            ms.runSmartIdleMaint(mFinishCallback);
+        final StorageManagerService ms = StorageManagerService.sSelf;
+        if (mStarted.compareAndSet(false, true)) {
+            new Thread() {
+                public void run() {
+                    mJobParams = params;
+                    if (ms != null) {
+                        ms.runSmartIdleMaint(mFinishCallback);
+                    } else {
+                        mStarted.set(false);
+                    }
+                }
+            }.start();
+            return ms != null;
         }
-        return ms != null;
+        return false;
     }
 
     @Override
     public boolean onStopJob(JobParameters params) {
-        mStarted = false;
+        mStarted.set(false);
         return false;
     }
 
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index d47573d..2606757 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -2771,7 +2771,7 @@
         return true;
     }
 
-    void runSmartIdleMaint(Runnable callback) {
+    synchronized void runSmartIdleMaint(Runnable callback) {
         enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
 
         try {
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
index adea13f..24c7b87 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
@@ -323,10 +323,15 @@
     static ProgramIdentifier identifierToHalProgramIdentifier(ProgramSelector.Identifier id) {
         ProgramIdentifier hwId = new ProgramIdentifier();
         hwId.type = id.getType();
-        if (hwId.type == ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT) {
+        if (id.getType() == ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT) {
             hwId.type = IdentifierType.DAB_SID_EXT;
         }
-        hwId.value = id.getValue();
+        long value = id.getValue();
+        if (id.getType() == ProgramSelector.IDENTIFIER_TYPE_DAB_SID_EXT) {
+            hwId.value = (value & 0xFFFF) | ((value >>> 16) << 32);
+        } else {
+            hwId.value = value;
+        }
         return hwId;
     }
 
@@ -609,6 +614,9 @@
                 || isNewIdentifierInU(info.getPhysicallyTunedTo())) {
             return false;
         }
+        if (info.getRelatedContent() == null) {
+            return true;
+        }
         Iterator<ProgramSelector.Identifier> relatedContentIt = info.getRelatedContent().iterator();
         while (relatedContentIt.hasNext()) {
             if (isNewIdentifierInU(relatedContentIt.next())) {
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 9954a90..802a7f2 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -975,6 +975,9 @@
                     throw new SecurityException("Media projections require a foreground service"
                             + " of type ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION");
                 }
+
+                mCallback = callback;
+                registerCallback(mCallback);
                 try {
                     mToken = callback.asBinder();
                     mDeathEater = () -> {
@@ -1019,11 +1022,6 @@
                     }
                 }
                 startProjectionLocked(this);
-
-                // Register new callbacks after stop has been dispatched to previous session.
-                mCallback = callback;
-                registerCallback(mCallback);
-
                 // Mark this token as used when the app gets the MediaProjection instance.
                 mCountStarts++;
             }
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 5947b75..6601467 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -999,12 +999,11 @@
                     return;
                 }
 
-                if (!mWallpaper.wallpaperUpdating
-                        && mWallpaper.userId == mCurrentUserId) {
+                if (!mWallpaper.wallpaperUpdating && mWallpaper.userId == mCurrentUserId) {
                     Slog.w(TAG, "Wallpaper reconnect timed out for " + mWallpaper.wallpaperComponent
                             + ", reverting to built-in wallpaper!");
-                    clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId,
-                            null);
+                    int which = mIsLockscreenLiveWallpaperEnabled ? mWallpaper.mWhich : FLAG_SYSTEM;
+                    clearWallpaperLocked(true, which, mWallpaper.userId, null);
                 }
             }
         };
@@ -1184,7 +1183,7 @@
                 } else {
                     // Timeout
                     Slog.w(TAG, "Reverting to built-in wallpaper!");
-                    clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null);
+                    clearWallpaperLocked(true, mWallpaper.mWhich, mWallpaper.userId, null);
                     final String flattened = wpService.flattenToString();
                     EventLog.writeEvent(EventLogTags.WP_WALLPAPER_CRASHED,
                             flattened.substring(0, Math.min(flattened.length(),
@@ -1213,7 +1212,7 @@
                                 && mWallpaper.lastDiedTime + MIN_WALLPAPER_CRASH_TIME
                                 > SystemClock.uptimeMillis()) {
                             Slog.w(TAG, "Reverting to built-in wallpaper!");
-                            clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null);
+                            clearWallpaperLocked(true, mWallpaper.mWhich, mWallpaper.userId, null);
                         } else {
                             mWallpaper.lastDiedTime = SystemClock.uptimeMillis();
                             tryToRebind();
@@ -1427,8 +1426,7 @@
                 if (mCurrentUserId != getChangingUserId()) {
                     return;
                 }
-                WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
-                if (wallpaper != null) {
+                for (WallpaperData wallpaper: getWallpapers()) {
                     final ComponentName wpService = wallpaper.wallpaperComponent;
                     if (wpService != null && wpService.getPackageName().equals(packageName)) {
                         if (DEBUG_LIVE) {
@@ -1440,7 +1438,9 @@
                                 wallpaper, null)) {
                             Slog.w(TAG, "Wallpaper " + wpService
                                     + " no longer available; reverting to default");
-                            clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
+                            int which = mIsLockscreenLiveWallpaperEnabled
+                                    ? wallpaper.mWhich : FLAG_SYSTEM;
+                            clearWallpaperLocked(false, which, wallpaper.userId, null);
                         }
                     }
                 }
@@ -1453,13 +1453,11 @@
                 if (mCurrentUserId != getChangingUserId()) {
                     return;
                 }
-                WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
-                if (wallpaper != null) {
-                    if (wallpaper.wallpaperComponent == null
-                            || !wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
-                        return;
+                for (WallpaperData wallpaper: getWallpapers()) {
+                    if (wallpaper.wallpaperComponent != null
+                            && wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
+                        doPackagesChangedLocked(true, wallpaper);
                     }
-                    doPackagesChangedLocked(true, wallpaper);
                 }
             }
         }
@@ -1470,8 +1468,7 @@
                 if (mCurrentUserId != getChangingUserId()) {
                     return;
                 }
-                WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
-                if (wallpaper != null) {
+                for (WallpaperData wallpaper: getWallpapers()) {
                     if (wallpaper.wallpaperComponent != null
                             && wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
                         if (DEBUG_LIVE) {
@@ -1495,8 +1492,7 @@
                 if (mCurrentUserId != getChangingUserId()) {
                     return false;
                 }
-                WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
-                if (wallpaper != null) {
+                for (WallpaperData wallpaper: getWallpapers()) {
                     boolean res = doPackagesChangedLocked(doit, wallpaper);
                     changed |= res;
                 }
@@ -1510,8 +1506,7 @@
                 if (mCurrentUserId != getChangingUserId()) {
                     return;
                 }
-                WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
-                if (wallpaper != null) {
+                for (WallpaperData wallpaper: getWallpapers()) {
                     doPackagesChangedLocked(true, wallpaper);
                 }
             }
@@ -1519,6 +1514,7 @@
 
         boolean doPackagesChangedLocked(boolean doit, WallpaperData wallpaper) {
             boolean changed = false;
+            int which = mIsLockscreenLiveWallpaperEnabled ? wallpaper.mWhich : FLAG_SYSTEM;
             if (wallpaper.wallpaperComponent != null) {
                 int change = isPackageDisappearing(wallpaper.wallpaperComponent
                         .getPackageName());
@@ -1528,7 +1524,7 @@
                     if (doit) {
                         Slog.w(TAG, "Wallpaper uninstalled, removing: "
                                 + wallpaper.wallpaperComponent);
-                        clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
+                        clearWallpaperLocked(false, which, wallpaper.userId, null);
                     }
                 }
             }
@@ -1549,7 +1545,7 @@
                 } catch (NameNotFoundException e) {
                     Slog.w(TAG, "Wallpaper component gone, removing: "
                             + wallpaper.wallpaperComponent);
-                    clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
+                    clearWallpaperLocked(false, which, wallpaper.userId, null);
                 }
             }
             if (wallpaper.nextWallpaperComponent != null
@@ -1664,7 +1660,8 @@
                 if (DEBUG) {
                     Slog.i(TAG, "Unable to regenerate crop; resetting");
                 }
-                clearWallpaperLocked(false, FLAG_SYSTEM, UserHandle.USER_SYSTEM, null);
+                int which = isLockscreenLiveWallpaperEnabled() ? wallpaper.mWhich : FLAG_SYSTEM;
+                clearWallpaperLocked(false, which, UserHandle.USER_SYSTEM, null);
             }
         } else {
             if (DEBUG) {
@@ -2786,6 +2783,18 @@
                 : new WallpaperData[0];
     }
 
+    // TODO(b/266818039) remove
+    private WallpaperData[] getWallpapers() {
+        WallpaperData systemWallpaper = mWallpaperMap.get(mCurrentUserId);
+        WallpaperData lockWallpaper = mLockWallpaperMap.get(mCurrentUserId);
+        boolean systemValid = systemWallpaper != null;
+        boolean lockValid = lockWallpaper != null && !isLockscreenLiveWallpaperEnabled();
+        return systemValid && lockValid ? new WallpaperData[]{systemWallpaper, lockWallpaper}
+                : systemValid ? new WallpaperData[]{systemWallpaper}
+                : lockValid ? new WallpaperData[]{lockWallpaper}
+                : new WallpaperData[0];
+    }
+
     private IWallpaperEngine getEngine(int which, int userId, int displayId) {
         WallpaperData wallpaperData = findWallpaperAtDisplay(userId, displayId);
         if (wallpaperData == null) return null;
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index d8c684f..4979274 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -96,6 +96,9 @@
     private static final String DEVICE_CONFIG_ENABLE_CREDENTIAL_MANAGER =
             "enable_credential_manager";
 
+    private static final String DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API =
+            "enable_credential_description_api";
+
     private final Context mContext;
 
     /** Cache of system service list per user id. */
@@ -321,7 +324,14 @@
     }
 
     public static boolean isCredentialDescriptionApiEnabled() {
-        return true;
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            return DeviceConfig.getBoolean(
+                    DeviceConfig.NAMESPACE_CREDENTIAL, DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API,
+                    false);
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
     }
 
     @SuppressWarnings("GuardedBy") // ErrorProne requires initiateProviderSessionForRequestLocked
@@ -954,7 +964,7 @@
             Slog.i(TAG, "registerCredentialDescription with callingPackage: " + callingPackage);
 
             if (!isCredentialDescriptionApiEnabled()) {
-                throw new UnsupportedOperationException();
+                throw new UnsupportedOperationException("Feature not supported");
             }
 
             enforceCallingPackage(callingPackage, Binder.getCallingUid());
@@ -974,7 +984,7 @@
 
 
             if (!isCredentialDescriptionApiEnabled()) {
-                throw new UnsupportedOperationException();
+                throw new UnsupportedOperationException("Feature not supported");
             }
 
             enforceCallingPackage(callingPackage, Binder.getCallingUid());
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index b0fd606..ddd1221 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -202,24 +202,6 @@
     }
 
     @Test
-    public void testCreateProjection_priorProjectionGrant() throws NameNotFoundException {
-        // Create a first projection.
-        MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
-        FakeIMediaProjectionCallback callback1 = new FakeIMediaProjectionCallback();
-        projection.start(callback1);
-
-        // Create a second projection.
-        MediaProjectionManagerService.MediaProjection secondProjection =
-                startProjectionPreconditions();
-        FakeIMediaProjectionCallback callback2 = new FakeIMediaProjectionCallback();
-        secondProjection.start(callback2);
-
-        // Check that the second projection's callback hasn't been stopped.
-        assertThat(callback1.mStopped).isTrue();
-        assertThat(callback2.mStopped).isFalse();
-    }
-
-    @Test
     public void testCreateProjection_attemptReuse_noPriorProjectionGrant()
             throws NameNotFoundException {
         // Create a first projection.
@@ -803,11 +785,8 @@
     }
 
     private static class FakeIMediaProjectionCallback extends IMediaProjectionCallback.Stub {
-        boolean mStopped = false;
-
         @Override
         public void onStop() throws RemoteException {
-            mStopped = true;
         }
 
         @Override
diff --git a/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerService.java b/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerService.java
index 9015563..2411498 100644
--- a/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerService.java
+++ b/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerService.java
@@ -63,6 +63,12 @@
         public void createSession(String engine,
                 ITextToSpeechSessionCallback sessionCallback) {
             synchronized (mLock) {
+                if (engine == null) {
+                    runSessionCallbackMethod(
+                            () -> sessionCallback.onError("Engine cannot be null"));
+                    return;
+                }
+
                 TextToSpeechManagerPerUserService perUserService = getServiceForUserLocked(
                         UserHandle.getCallingUserId());
                 if (perUserService != null) {
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
index d41c019..bc41829 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
@@ -23,9 +23,11 @@
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.res.Resources;
 import android.net.wifi.sharedconnectivity.service.ISharedConnectivityCallback;
@@ -35,6 +37,7 @@
 import android.os.IBinder;
 import android.os.IInterface;
 import android.os.RemoteException;
+import android.os.UserManager;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -67,7 +70,7 @@
 @SystemApi
 public class SharedConnectivityManager {
     private static final String TAG = SharedConnectivityManager.class.getSimpleName();
-    private static final boolean DEBUG = true;
+    private static final boolean DEBUG = false;
 
     private static final class SharedConnectivityCallbackProxy extends
             ISharedConnectivityCallback.Stub {
@@ -172,6 +175,7 @@
     private final String mServicePackageName;
     private final String mIntentAction;
     private ServiceConnection mServiceConnection;
+    private UserManager mUserManager;
 
     /**
      * Creates a new instance of {@link SharedConnectivityManager}.
@@ -217,12 +221,14 @@
         mContext = context;
         mServicePackageName = servicePackageName;
         mIntentAction = serviceIntentAction;
+        mUserManager = context.getSystemService(UserManager.class);
     }
 
     private void bind() {
         mServiceConnection = new ServiceConnection() {
             @Override
             public void onServiceConnected(ComponentName name, IBinder service) {
+                if (DEBUG) Log.i(TAG, "onServiceConnected");
                 mService = ISharedConnectivityService.Stub.asInterface(service);
                 synchronized (mProxyDataLock) {
                     if (!mCallbackProxyCache.isEmpty()) {
@@ -253,9 +259,45 @@
             }
         };
 
-        mContext.bindService(
+        boolean result = mContext.bindService(
                 new Intent().setPackage(mServicePackageName).setAction(mIntentAction),
                 mServiceConnection, Context.BIND_AUTO_CREATE);
+        if (!result) {
+            if (DEBUG) Log.i(TAG, "bindService failed");
+            mServiceConnection = null;
+            if (mUserManager != null && !mUserManager.isUserUnlocked()) {  // In direct boot mode
+                IntentFilter intentFilter = new IntentFilter();
+                intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
+                mContext.registerReceiver(mBroadcastReceiver, intentFilter);
+            } else {
+                synchronized (mProxyDataLock) {
+                    if (!mCallbackProxyCache.isEmpty()) {
+                        mCallbackProxyCache.keySet().forEach(
+                                callback -> callback.onRegisterCallbackFailed(
+                                        new IllegalStateException(
+                                                "Failed to bind after user unlock")));
+                        mCallbackProxyCache.clear();
+                    }
+                }
+            }
+        }
+    }
+
+    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            context.unregisterReceiver(mBroadcastReceiver);
+            bind();
+        }
+    };
+
+    /**
+     * @hide
+     */
+    @TestApi
+    @NonNull
+    public BroadcastReceiver getBroadcastReceiver() {
+        return mBroadcastReceiver;
     }
 
     private void registerCallbackInternal(SharedConnectivityClientCallback callback,
@@ -357,6 +399,13 @@
             return false;
         }
 
+        // Try to unregister the broadcast receiver to guard against memory leaks.
+        try {
+            mContext.unregisterReceiver(mBroadcastReceiver);
+        } catch (IllegalArgumentException e) {
+            // This is fine, it means the receiver was never registered or was already unregistered.
+        }
+
         if (mService == null) {
             boolean shouldUnbind;
             synchronized (mProxyDataLock) {