Merge "Freezer: check freeze binder ioctl"
diff --git a/core/java/android/hardware/biometrics/SensorLocationInternal.aidl b/core/java/android/hardware/biometrics/SensorLocationInternal.aidl
new file mode 100644
index 0000000..0981904
--- /dev/null
+++ b/core/java/android/hardware/biometrics/SensorLocationInternal.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.biometrics;
+
+// @hide
+parcelable SensorLocationInternal;
diff --git a/core/java/android/hardware/biometrics/SensorLocationInternal.java b/core/java/android/hardware/biometrics/SensorLocationInternal.java
new file mode 100644
index 0000000..fb25a2f
--- /dev/null
+++ b/core/java/android/hardware/biometrics/SensorLocationInternal.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * The location of a sensor relative to a physical display.
+ *
+ * Note that the location may change depending on other attributes of the device, such as
+ * fold status, which are not yet included in this class.
+ * @hide
+ */
+public class SensorLocationInternal implements Parcelable {
+
+    /** Default value to use when the sensor's location is unknown or undefined. */
+    public static final SensorLocationInternal DEFAULT = new SensorLocationInternal("", 0, 0, 0);
+
+    /**
+     * The stable display id.
+     */
+    @NonNull
+    public final String displayId;
+
+    /**
+     * The location of the center of the sensor if applicable. For example, sensors of type
+     * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the
+     * distance in pixels, measured from the left edge of the screen.
+     */
+    public final int sensorLocationX;
+
+    /**
+     * The location of the center of the sensor if applicable. For example, sensors of type
+     * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the
+     * distance in pixels, measured from the top edge of the screen.
+     *
+     */
+    public final int sensorLocationY;
+
+    /**
+     * The radius of the sensor if applicable. For example, sensors of type
+     * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the radius
+     * of the sensor, in pixels.
+     */
+    public final int sensorRadius;
+
+    public SensorLocationInternal(@Nullable String displayId,
+            int sensorLocationX, int sensorLocationY, int sensorRadius) {
+        this.displayId = displayId != null ? displayId : "";
+        this.sensorLocationX = sensorLocationX;
+        this.sensorLocationY = sensorLocationY;
+        this.sensorRadius = sensorRadius;
+    }
+
+    protected SensorLocationInternal(Parcel in) {
+        displayId = in.readString16NoHelper();
+        sensorLocationX = in.readInt();
+        sensorLocationY = in.readInt();
+        sensorRadius = in.readInt();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(displayId);
+        dest.writeInt(sensorLocationX);
+        dest.writeInt(sensorLocationY);
+        dest.writeInt(sensorRadius);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Creator<SensorLocationInternal> CREATOR =
+            new Creator<SensorLocationInternal>() {
+        @Override
+        public SensorLocationInternal createFromParcel(Parcel in) {
+            return new SensorLocationInternal(in);
+        }
+
+        @Override
+        public SensorLocationInternal[] newArray(int size) {
+            return new SensorLocationInternal[size];
+        }
+    };
+
+    @Override
+    public String toString() {
+        return "[id: " + displayId
+                + ", x: " + sensorLocationX
+                + ", y: " + sensorLocationY
+                + ", r: " + sensorRadius + "]";
+    }
+}
diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
index 45c6b29..4bf9a74 100644
--- a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
@@ -21,7 +21,9 @@
 import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.hardware.biometrics.ComponentInfoInternal;
+import android.hardware.biometrics.SensorLocationInternal;
 import android.hardware.biometrics.SensorProperties;
 import android.hardware.biometrics.SensorPropertiesInternal;
 import android.os.Parcel;
@@ -38,34 +40,14 @@
      */
     public final @FingerprintSensorProperties.SensorType int sensorType;
 
-    /**
-     * The location of the center of the sensor if applicable. For example, sensors of type
-     * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the
-     * distance in pixels, measured from the left edge of the screen.
-     */
-    public final int sensorLocationX;
-
-    /**
-     * The location of the center of the sensor if applicable. For example, sensors of type
-     * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the
-     * distance in pixels, measured from the top edge of the screen.
-     *
-     */
-    public final int sensorLocationY;
-
-    /**
-     * The radius of the sensor if applicable. For example, sensors of type
-     * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the radius
-     * of the sensor, in pixels.
-     */
-    public final int sensorRadius;
+    private final List<SensorLocationInternal> mSensorLocations;
 
     public FingerprintSensorPropertiesInternal(int sensorId,
             @SensorProperties.Strength int strength, int maxEnrollmentsPerUser,
             @NonNull List<ComponentInfoInternal> componentInfo,
             @FingerprintSensorProperties.SensorType int sensorType,
-            boolean resetLockoutRequiresHardwareAuthToken, int sensorLocationX, int sensorLocationY,
-            int sensorRadius) {
+            boolean resetLockoutRequiresHardwareAuthToken,
+            @NonNull List<SensorLocationInternal> sensorLocations) {
         // IBiometricsFingerprint@2.1 handles lockout in the framework, so the challenge is not
         // required as it can only be generated/attested/verified by TEE components.
         // IFingerprint@1.0 handles lockout below the HAL, but does not require a challenge. See
@@ -73,9 +55,7 @@
         super(sensorId, strength, maxEnrollmentsPerUser, componentInfo,
             resetLockoutRequiresHardwareAuthToken, false /* resetLockoutRequiresChallenge */);
         this.sensorType = sensorType;
-        this.sensorLocationX = sensorLocationX;
-        this.sensorLocationY = sensorLocationY;
-        this.sensorRadius = sensorRadius;
+        this.mSensorLocations = List.copyOf(sensorLocations);
     }
 
     /**
@@ -88,16 +68,15 @@
             boolean resetLockoutRequiresHardwareAuthToken) {
         // TODO(b/179175438): Value should be provided from the HAL
         this(sensorId, strength, maxEnrollmentsPerUser, componentInfo, sensorType,
-                resetLockoutRequiresHardwareAuthToken, 540 /* sensorLocationX */,
-                1636 /* sensorLocationY */, 130 /* sensorRadius */);
+                resetLockoutRequiresHardwareAuthToken, List.of(new SensorLocationInternal(
+                        "" /* displayId */,  540 /* sensorLocationX */, 1636 /* sensorLocationY */,
+                        130 /* sensorRadius */)));
     }
 
     protected FingerprintSensorPropertiesInternal(Parcel in) {
         super(in);
         sensorType = in.readInt();
-        sensorLocationX = in.readInt();
-        sensorLocationY = in.readInt();
-        sensorRadius = in.readInt();
+        mSensorLocations = in.createTypedArrayList(SensorLocationInternal.CREATOR);
     }
 
     public static final Creator<FingerprintSensorPropertiesInternal> CREATOR =
@@ -122,9 +101,7 @@
     public void writeToParcel(Parcel dest, int flags) {
         super.writeToParcel(dest, flags);
         dest.writeInt(sensorType);
-        dest.writeInt(sensorLocationX);
-        dest.writeInt(sensorLocationY);
-        dest.writeInt(sensorRadius);
+        dest.writeTypedList(mSensorLocations);
     }
 
     public boolean isAnyUdfpsType() {
@@ -150,6 +127,44 @@
         }
     }
 
+    /**
+     * Get the default location.
+     *
+     * Use this method when the sensor's relationship to the displays on the device do not
+     * matter.
+     * @return
+     */
+    @NonNull
+    public SensorLocationInternal getLocation() {
+        final SensorLocationInternal location = getLocation("" /* displayId */);
+        return location != null ? location : SensorLocationInternal.DEFAULT;
+    }
+
+    /**
+     * Get the location of a sensor relative to a physical display layout.
+     *
+     * @param displayId stable display id
+     * @return location or null if none is specified
+     */
+    @Nullable
+    public SensorLocationInternal getLocation(String displayId) {
+        for (SensorLocationInternal location : mSensorLocations) {
+            if (location.displayId.equals(displayId)) {
+                return location;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Gets all locations relative to all supported display layouts.
+     * @return supported locations
+     */
+    @NonNull
+    public List<SensorLocationInternal> getAllLocations() {
+        return mSensorLocations;
+    }
+
     @Override
     public String toString() {
         return "ID: " + sensorId + ", Strength: " + sensorStrength + ", Type: " + sensorType;
diff --git a/core/java/android/inputmethodservice/RemoteInputConnection.java b/core/java/android/inputmethodservice/RemoteInputConnection.java
index 657f842..ae97fe7 100644
--- a/core/java/android/inputmethodservice/RemoteInputConnection.java
+++ b/core/java/android/inputmethodservice/RemoteInputConnection.java
@@ -108,7 +108,11 @@
     @Nullable
     @AnyThread
     public CharSequence getTextAfterCursor(@IntRange(from = 0) int length, int flags) {
-        if (length < 0 || mCancellationGroup.isCanceled()) {
+        if (length < 0) {
+            // TODO: Should we throw an InvalidParameterException() based on targetSdkVersion?
+            Log.e(TAG, "length=" + length + " is invalid and always results in null result.");
+        }
+        if (mCancellationGroup.isCanceled()) {
             return null;
         }
 
@@ -132,7 +136,11 @@
     @Nullable
     @AnyThread
     public CharSequence getTextBeforeCursor(@IntRange(from = 0) int length, int flags) {
-        if (length < 0 || mCancellationGroup.isCanceled()) {
+        if (length < 0) {
+            // TODO: Should we throw an InvalidParameterException() based on targetSdkVersion?
+            Log.e(TAG, "length=" + length + " is invalid and always results in null result.");
+        }
+        if (mCancellationGroup.isCanceled()) {
             return null;
         }
 
@@ -185,7 +193,17 @@
     @AnyThread
     public SurroundingText getSurroundingText(
             @IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength, int flags) {
-        if (beforeLength < 0 || afterLength < 0 || mCancellationGroup.isCanceled()) {
+        if (beforeLength < 0) {
+            // TODO: Should we throw an InvalidParameterException() based on targetSdkVersion?
+            Log.e(TAG, "beforeLength=" + beforeLength
+                    + " is invalid and always results in null result.");
+        }
+        if (afterLength < 0) {
+            // TODO: Should we throw an InvalidParameterException() based on targetSdkVersion?
+            Log.e(TAG, "afterLength=" + afterLength
+                    + " is invalid and always results in null result.");
+        }
+        if (mCancellationGroup.isCanceled()) {
             return null;
         }
 
diff --git a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
index 29c1b1b..7a668fb 100644
--- a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
@@ -233,6 +233,11 @@
                 Log.w(TAG, "getTextAfterCursor on inactive InputConnection");
                 return null;
             }
+            if (length < 0) {
+                Log.i(TAG, "Returning null to getTextAfterCursor due to an invalid length="
+                        + length);
+                return null;
+            }
             return ic.getTextAfterCursor(length, flags);
         }, useImeTracing() ? result -> buildGetTextAfterCursorProto(length, flags, result) : null);
     }
@@ -246,6 +251,11 @@
                 Log.w(TAG, "getTextBeforeCursor on inactive InputConnection");
                 return null;
             }
+            if (length < 0) {
+                Log.i(TAG, "Returning null to getTextBeforeCursor due to an invalid length="
+                        + length);
+                return null;
+            }
             return ic.getTextBeforeCursor(length, flags);
         }, useImeTracing() ? result -> buildGetTextBeforeCursorProto(length, flags, result) : null);
     }
@@ -276,6 +286,16 @@
                 Log.w(TAG, "getSurroundingText on inactive InputConnection");
                 return null;
             }
+            if (beforeLength < 0) {
+                Log.i(TAG, "Returning null to getSurroundingText due to an invalid"
+                        + " beforeLength=" + beforeLength);
+                return null;
+            }
+            if (afterLength < 0) {
+                Log.i(TAG, "Returning null to getSurroundingText due to an invalid"
+                        + " afterLength=" + afterLength);
+                return null;
+            }
             return ic.getSurroundingText(beforeLength, afterLength, flags);
         }, useImeTracing() ? result -> buildGetSurroundingTextProto(
                 beforeLength, afterLength, flags, result) : null);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 719dc53..7c4de82 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -8578,7 +8578,7 @@
          * inactive so can be dropped.
          */
         @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-        public boolean reset(long uptimeUs, long realtimeUs) {
+        public boolean reset(long uptimeUs, long realtimeUs, int resetReason) {
             boolean active = false;
 
             mOnBatteryBackgroundTimeBase.init(uptimeUs, realtimeUs);
@@ -8648,7 +8648,11 @@
             resetIfNotNull(mBluetoothControllerActivity, false, realtimeUs);
             resetIfNotNull(mModemControllerActivity, false, realtimeUs);
 
-            MeasuredEnergyStats.resetIfNotNull(mUidMeasuredEnergyStats);
+            if (resetReason == RESET_REASON_MEASURED_ENERGY_BUCKETS_CHANGE) {
+                mUidMeasuredEnergyStats = null;
+            } else {
+                MeasuredEnergyStats.resetIfNotNull(mUidMeasuredEnergyStats);
+            }
 
             resetIfNotNull(mUserCpuTime, false, realtimeUs);
             resetIfNotNull(mSystemCpuTime, false, realtimeUs);
@@ -11332,7 +11336,7 @@
         mNumConnectivityChange = 0;
 
         for (int i=0; i<mUidStats.size(); i++) {
-            if (mUidStats.valueAt(i).reset(uptimeUs, elapsedRealtimeUs)) {
+            if (mUidStats.valueAt(i).reset(uptimeUs, elapsedRealtimeUs, resetReason)) {
                 mUidStats.valueAt(i).detachFromTimeBase();
                 mUidStats.remove(mUidStats.keyAt(i));
                 i--;
diff --git a/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java b/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java
index 9b51a8e..bb307a0 100644
--- a/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java
+++ b/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java
@@ -21,13 +21,18 @@
 import android.os.BatteryUsageStats;
 import android.os.BatteryUsageStatsQuery;
 import android.os.UidBatteryConsumer;
+import android.util.Slog;
 import android.util.SparseArray;
 
+import java.util.Arrays;
+
 /**
  * Calculates the amount of power consumed by custom energy consumers (i.e. consumers of type
  * {@link android.hardware.power.stats.EnergyConsumerType#OTHER}).
  */
 public class CustomMeasuredPowerCalculator extends PowerCalculator {
+    private static final String TAG = "CustomMeasuredPowerCalc";
+
     public CustomMeasuredPowerCalculator(PowerProfile powerProfile) {
     }
 
@@ -76,9 +81,9 @@
             if (totalPowerMah == null) {
                 newTotalPowerMah = new double[customMeasuredPowerMah.length];
             } else if (totalPowerMah.length != customMeasuredPowerMah.length) {
-                newTotalPowerMah = new double[customMeasuredPowerMah.length];
-                System.arraycopy(totalPowerMah, 0, newTotalPowerMah, 0,
-                        customMeasuredPowerMah.length);
+                Slog.wtf(TAG, "Number of custom energy components is not the same for all apps: "
+                        + totalPowerMah.length + ", " + customMeasuredPowerMah.length);
+                newTotalPowerMah = Arrays.copyOf(totalPowerMah, customMeasuredPowerMah.length);
             } else {
                 newTotalPowerMah = totalPowerMah;
             }
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 265426c2..790a2a3 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4570,6 +4570,20 @@
       -->
     </integer-array>
 
+    <!-- An array of arrays of side fingerprint sensor properties relative to each display.
+         Note: this value is temporary and is expected to be queried directly
+         from the HAL in the future. -->
+    <array name="config_sfps_sensor_props" translatable="false">
+        <!--
+            <array>
+                <item>displayId</item>
+                <item>sensorLocationX</item>
+                <item>sensorLocationY</item>
+                <item>sensorRadius</item>
+            <array>
+        -->
+    </array>
+
     <!-- How long it takes for the HW to start illuminating after the illumination is requested. -->
     <integer name="config_udfps_illumination_transition_ms">50</integer>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 58e9cda..b0c0a15 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2611,6 +2611,7 @@
   <java-symbol type="array" name="config_biometric_sensors" />
   <java-symbol type="bool" name="allow_test_udfps" />
   <java-symbol type="array" name="config_udfps_sensor_props" />
+  <java-symbol type="array" name="config_sfps_sensor_props" />
   <java-symbol type="integer" name="config_udfps_illumination_transition_ms" />
   <java-symbol type="bool" name="config_is_powerbutton_fps" />
 
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
index f833981..0e394c1 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
@@ -403,7 +403,7 @@
         assertNotNull(sensor.getSensorBackgroundTime());
 
         // Reset the stats. Since the sensor is still running, we should still see the timer
-        bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000);
+        bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000, 0);
 
         sensor = uid.getSensorStats().get(SENSOR_ID);
         assertNotNull(sensor);
@@ -413,7 +413,7 @@
         bi.noteStopSensorLocked(UID, SENSOR_ID);
 
         // Now the sensor timer has stopped so this reset should also take out the sensor.
-        bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000);
+        bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000, 0);
 
         sensor = uid.getSensorStats().get(SENSOR_ID);
         assertNull(sensor);
@@ -465,7 +465,7 @@
 
         // Reset the stats. Since the sensor is still running, we should still see the timer
         // but still with 0 times.
-        bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000);
+        bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000, 0);
         assertEquals(0, timer.getTotalTimeLocked(1000*clocks.realtime, which));
         assertEquals(0, timer.getTotalDurationMsLocked(clocks.realtime));
         assertEquals(0, bgTimer.getTotalTimeLocked(1000*clocks.realtime, which));
@@ -504,7 +504,7 @@
 
         // Reset the stats. Since the sensor is still running, we should still see the timer
         // but with 0 times.
-        bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000);
+        bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000, 0);
         assertEquals(0, timer.getTotalTimeLocked(1000*clocks.realtime, which));
         assertEquals(0, timer.getTotalDurationMsLocked(clocks.realtime));
         assertEquals(0, bgTimer.getTotalTimeLocked(1000*clocks.realtime, which));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
index 3800b8d..c2cb72a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
@@ -43,6 +43,7 @@
 import com.android.wm.shell.common.SurfaceUtils;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.split.SplitLayout;
+import com.android.wm.shell.common.split.SplitWindowManager;
 
 import java.io.PrintWriter;
 
@@ -70,6 +71,19 @@
     private final DisplayImeController mDisplayImeController;
     private SplitLayout mSplitLayout;
 
+    private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks =
+            new SplitWindowManager.ParentContainerCallbacks() {
+        @Override
+        public void attachToParentSurface(SurfaceControl.Builder b) {
+            b.setParent(mRootTaskLeash);
+        }
+
+        @Override
+        public void onLeashReady(SurfaceControl leash) {
+            mSyncQueue.runInSync(t -> t.show(leash));
+        }
+    };
+
     AppPair(AppPairsController controller) {
         mController = controller;
         mSyncQueue = controller.getSyncTransactionQueue();
@@ -110,8 +124,7 @@
         mSplitLayout = new SplitLayout(TAG + "SplitDivider",
                 mDisplayController.getDisplayContext(mRootTaskInfo.displayId),
                 mRootTaskInfo.configuration, this /* layoutChangeListener */,
-                b -> b.setParent(mRootTaskLeash), mDisplayImeController,
-                mController.getTaskOrganizer());
+                mParentContainerCallbacks, mDisplayImeController, mController.getTaskOrganizer());
 
         final WindowContainerToken token1 = task1.token;
         final WindowContainerToken token2 = task2.token;
@@ -218,8 +231,6 @@
                 if (mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)) {
                     onLayoutChanged(mSplitLayout);
                 }
-                // updateConfiguration re-inits the dividerbar, so show it now
-                mSyncQueue.runInSync(t -> t.show(mSplitLayout.getDividerLeash()));
             }
         } else if (taskInfo.taskId == getTaskId1()) {
             mTaskInfo1 = taskInfo;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 754b8da..27c8d7a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -125,8 +125,8 @@
         mRotation = configuration.windowConfiguration.getRotation();
         mSplitLayoutHandler = splitLayoutHandler;
         mDisplayImeController = displayImeController;
-        mSplitWindowManager = new SplitWindowManager(
-                windowName, mContext, configuration, parentContainerCallbacks);
+        mSplitWindowManager = new SplitWindowManager(windowName, mContext, configuration,
+                parentContainerCallbacks);
         mTaskOrganizer = taskOrganizer;
         mImePositionProcessor = new ImePositionProcessor(mContext.getDisplayId());
         mDismissingParallaxPolicy = new DismissingParallaxPolicy();
@@ -181,22 +181,19 @@
     public boolean updateConfiguration(Configuration configuration) {
         boolean affectsLayout = false;
 
-        // Make sure to render the divider bar with proper resources that matching the screen
-        // orientation.
-        final int orientation = configuration.orientation;
-        if (orientation != mOrientation) {
-            mOrientation = orientation;
-            mContext = mContext.createConfigurationContext(configuration);
-            mSplitWindowManager.setConfiguration(configuration);
-            affectsLayout = true;
-        }
-
         // Update the split bounds when necessary. Besides root bounds changed, split bounds need to
         // be updated when the rotation changed to cover the case that users rotated the screen 180
         // degrees.
+        // Make sure to render the divider bar with proper resources that matching the screen
+        // orientation.
         final int rotation = configuration.windowConfiguration.getRotation();
         final Rect rootBounds = configuration.windowConfiguration.getBounds();
-        if (rotation != mRotation || !mRootBounds.equals(rootBounds)) {
+        final int orientation = configuration.orientation;
+        if (rotation != mRotation || !mRootBounds.equals(rootBounds)
+                || orientation != mOrientation) {
+            mContext = mContext.createConfigurationContext(configuration);
+            mSplitWindowManager.setConfiguration(configuration);
+            mOrientation = orientation;
             mTempRect.set(mRootBounds);
             mRootBounds.set(rootBounds);
             mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
index fc7edfc..47dceb3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
@@ -64,6 +64,7 @@
 
     public interface ParentContainerCallbacks {
         void attachToParentSurface(SurfaceControl.Builder b);
+        void onLeashReady(SurfaceControl leash);
     }
 
     public SplitWindowManager(String windowName, Context context, Configuration config,
@@ -100,6 +101,7 @@
                 .setCallsite("SplitWindowManager#attachToParentSurface");
         mParentContainerCallbacks.attachToParentSurface(builder);
         mLeash = builder.build();
+        mParentContainerCallbacks.onLeashReady(mLeash);
         b.setParent(mLeash);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index d9708f0..493870d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -18,8 +18,6 @@
 
 import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
@@ -90,6 +88,7 @@
 import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.common.split.SplitLayout;
 import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
+import com.android.wm.shell.common.split.SplitWindowManager;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.transition.Transitions;
 
@@ -163,6 +162,19 @@
         mDismissTop = NO_DISMISS;
     };
 
+    private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks =
+            new SplitWindowManager.ParentContainerCallbacks() {
+        @Override
+        public void attachToParentSurface(SurfaceControl.Builder b) {
+            mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b);
+        }
+
+        @Override
+        public void onLeashReady(SurfaceControl leash) {
+            mSyncQueue.runInSync(t -> applyDividerVisibility(t));
+        }
+    };
+
     StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
             RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer,
             DisplayImeController displayImeController,
@@ -501,7 +513,8 @@
         mSideStage.removeAllTasks(wct, childrenToTop == mSideStage);
         mMainStage.deactivate(wct, childrenToTop == mMainStage);
         mTaskOrganizer.applyTransaction(wct);
-        // Reset divider position.
+        // Hide divider and reset its position.
+        setDividerVisibility(false);
         mSplitLayout.resetDividerPosition();
         mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
         if (childrenToTop != null) {
@@ -635,10 +648,16 @@
     private void onStageVisibilityChanged(StageListenerImpl stageListener) {
         final boolean sideStageVisible = mSideStageListener.mVisible;
         final boolean mainStageVisible = mMainStageListener.mVisible;
-        // Divider is only visible if both the main stage and side stages are visible
-        setDividerVisibility(isSplitScreenVisible());
+        final boolean bothStageVisible = sideStageVisible && mainStageVisible;
+        final boolean bothStageInvisible = !sideStageVisible && !mainStageVisible;
+        final boolean sameVisibility = sideStageVisible == mainStageVisible;
+        // Only add or remove divider when both visible or both invisible to avoid sometimes we only
+        // got one stage visibility changed for a moment and it will cause flicker.
+        if (sameVisibility) {
+            setDividerVisibility(bothStageVisible);
+        }
 
-        if (!mainStageVisible && !sideStageVisible) {
+        if (bothStageInvisible) {
             if (mExitSplitScreenOnHide
             // Don't dismiss staged split when both stages are not visible due to sleeping display,
             // like the cases keyguard showing or screen off.
@@ -655,59 +674,32 @@
             exitSplitScreen(toTop, SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED_SHOW_ON_TOP);
         }
 
-        // When both stage's visibility changed to visible, main stage might receives visibility
-        // changed before side stage if it has higher z-order than side stage. Make sure we only
-        // update main stage's windowing mode with the visibility changed of side stage to prevent
-        // stacking multiple windowing mode transactions which result to flicker issue.
-        if (mainStageVisible && stageListener == mSideStageListener) {
-            final WindowContainerTransaction wct = new WindowContainerTransaction();
-            if (sideStageVisible) {
-                // The main stage configuration should to follow split layout when side stage is
-                // visible.
-                mMainStage.updateConfiguration(
-                        WINDOWING_MODE_MULTI_WINDOW, getMainStageBounds(), wct);
-            } else if (!mSideStage.mRootTaskInfo.isSleeping) {
-                // We want the main stage configuration to be fullscreen when the side stage isn't
-                // visible.
-                // We should not do it when side stage are not visible due to sleeping display too.
-                mMainStage.updateConfiguration(WINDOWING_MODE_FULLSCREEN, null, wct);
-            }
-            // TODO: Change to `mSyncQueue.queue(wct)` once BLAST is stable.
-            mTaskOrganizer.applyTransaction(wct);
-        }
-
         mSyncQueue.runInSync(t -> {
             final SurfaceControl sideStageLeash = mSideStage.mRootLeash;
             final SurfaceControl mainStageLeash = mMainStage.mRootLeash;
 
             if (sideStageVisible) {
                 final Rect sideStageBounds = getSideStageBounds();
-                t.show(sideStageLeash)
-                        .setPosition(sideStageLeash,
-                                sideStageBounds.left, sideStageBounds.top)
+                t.setPosition(sideStageLeash,
+                        sideStageBounds.left, sideStageBounds.top)
                         .setWindowCrop(sideStageLeash,
                                 sideStageBounds.width(), sideStageBounds.height());
-            } else {
-                t.hide(sideStageLeash);
             }
 
             if (mainStageVisible) {
                 final Rect mainStageBounds = getMainStageBounds();
-                t.show(mainStageLeash);
-                if (sideStageVisible) {
-                    t.setPosition(mainStageLeash, mainStageBounds.left, mainStageBounds.top)
-                            .setWindowCrop(mainStageLeash,
-                                    mainStageBounds.width(), mainStageBounds.height());
-                } else {
-                    // Clear window crop and position if side stage isn't visible.
-                    t.setPosition(mainStageLeash, 0, 0)
-                            .setWindowCrop(mainStageLeash, null);
-                }
-            } else {
-                t.hide(mainStageLeash);
+                t.setPosition(mainStageLeash, mainStageBounds.left, mainStageBounds.top)
+                        .setWindowCrop(mainStageLeash,
+                                mainStageBounds.width(), mainStageBounds.height());
             }
 
-            applyDividerVisibility(t);
+            // Same above, we only set root tasks and divider leash visibility when both stage
+            // change to visible or invisible to avoid flicker.
+            if (sameVisibility) {
+                t.setVisibility(sideStageLeash, bothStageVisible)
+                        .setVisibility(mainStageLeash, bothStageVisible);
+                applyDividerVisibility(t);
+            }
         });
     }
 
@@ -726,7 +718,6 @@
         } else {
             t.hide(dividerLeash);
         }
-
     }
 
     private void onStageHasChildrenChanged(StageListenerImpl stageListener) {
@@ -852,8 +843,7 @@
         mDisplayAreaInfo = displayAreaInfo;
         if (mSplitLayout == null) {
             mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
-                    mDisplayAreaInfo.configuration, this,
-                    b -> mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b),
+                    mDisplayAreaInfo.configuration, this, mParentContainerCallbacks,
                     mDisplayImeController, mTaskOrganizer);
             mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
         }
@@ -871,7 +861,6 @@
                 && mSplitLayout.updateConfiguration(mDisplayAreaInfo.configuration)
                 && mMainStage.isActive()) {
             onLayoutChanged(mSplitLayout);
-            mSyncQueue.runInSync(t -> applyDividerVisibility(t));
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
index 9e20bbb..c1ec324 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
@@ -20,11 +20,13 @@
 import android.content.Context
 import android.platform.test.annotations.Presubmit
 import android.system.helpers.ActivityHelper
+import android.view.Surface
 import androidx.test.filters.FlakyTest
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.isRotated
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
@@ -177,9 +179,15 @@
 
     @Presubmit
     @Test
-    open fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
+    open fun navBarLayerRotatesAndScales() {
+        testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0,
+            testSpec.config.endRotation)
+    }
 
     @Presubmit
     @Test
-    open fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    open fun statusBarLayerRotatesScales() {
+        testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0,
+            testSpec.config.endRotation)
+    }
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
index 2ccd03b..b6680d9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
@@ -107,11 +107,13 @@
 
     @Presubmit
     @Test
-    fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
+    fun navBarLayerRotatesAndScales() =
+        testSpec.navBarLayerRotatesAndScales(testSpec.config.endRotation)
 
     @Presubmit
     @Test
-    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    fun statusBarLayerRotatesScales() =
+        testSpec.statusBarLayerRotatesScales(testSpec.config.endRotation)
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
index 58e1def..14b006e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
@@ -27,6 +27,7 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.helpers.ImeAppHelper
 import com.android.server.wm.flicker.helpers.WindowUtils
@@ -132,10 +133,12 @@
     fun entireScreenCovered() = testSpec.entireScreenCovered()
 
     @Test
-    fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
+    fun navBarLayerRotatesAndScales() =
+        testSpec.navBarLayerRotatesAndScales(testSpec.config.endRotation)
 
     @Test
-    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    fun statusBarLayerRotatesScales() =
+        testSpec.statusBarLayerRotatesScales(testSpec.config.endRotation)
 
     @Test
     fun topAppLayerIsAlwaysVisible() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
index 8a50bc0..8a2b55b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
@@ -25,6 +25,7 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
@@ -74,11 +75,15 @@
 
     @Presubmit
     @Test
-    fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
+    fun navBarLayerRotatesAndScales() =
+        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation,
+            testSpec.config.endRotation)
 
     @Presubmit
     @Test
-    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    fun statusBarLayerRotatesScales() =
+        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation,
+            testSpec.config.endRotation)
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
index 84676a9..b325157 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
@@ -25,6 +25,7 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
@@ -73,11 +74,13 @@
 
     @Presubmit
     @Test
-    fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
+    fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(
+        testSpec.config.startRotation, testSpec.config.endRotation)
 
     @Presubmit
     @Test
-    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(
+        testSpec.config.startRotation, testSpec.config.endRotation)
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
index cb2e8039..2be6936 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
@@ -24,6 +24,7 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.reopenAppFromOverview
 import com.android.server.wm.flicker.helpers.setRotation
@@ -82,11 +83,14 @@
 
     @Presubmit
     @Test
-    fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
+    fun navBarLayerRotatesAndScales() =
+        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation,
+            testSpec.config.endRotation)
 
     @Presubmit
     @Test
-    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(
+        testSpec.config.startRotation, testSpec.config.endRotation)
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
index fe9b9f5..5782f14 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
@@ -25,6 +25,7 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.reopenAppFromOverview
 import com.android.server.wm.flicker.helpers.setRotation
@@ -88,11 +89,15 @@
 
     @Presubmit
     @Test
-    fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
+    fun navBarLayerRotatesAndScales() =
+        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation,
+            testSpec.config.endRotation)
 
     @Presubmit
     @Test
-    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    fun statusBarLayerRotatesScales() =
+        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation,
+            testSpec.config.endRotation)
 
     @FlakyTest
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index 7ef0eef..d17a2bc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -111,7 +111,8 @@
      */
     @FlakyTest
     @Test
-    override fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
+    override fun navBarLayerRotatesAndScales() =
+        testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_90, Surface.ROTATION_0)
 
     /**
      * Checks that the [FlickerComponentName.STATUS_BAR] has the correct position at
@@ -119,7 +120,8 @@
      */
     @Presubmit
     @Test
-    override fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    override fun statusBarLayerRotatesScales() =
+        testSpec.statusBarLayerRotatesScales(Surface.ROTATION_90, Surface.ROTATION_0)
 
     /**
      * Checks that all parts of the screen are covered at the start and end of the transition
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
index 9e43dee..2cdfc2b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
@@ -25,6 +25,7 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -96,7 +97,8 @@
 
     @Presubmit
     @Test
-    override fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    override fun statusBarLayerRotatesScales() =
+        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index 669f37a..08d5209 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -95,14 +95,18 @@
      */
     @FlakyTest
     @Test
-    override fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
+    override fun navBarLayerRotatesAndScales() =
+        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation,
+            testSpec.config.endRotation)
 
     /**
      * Checks the position of the status bar at the start and end of the transition
      */
     @Presubmit
     @Test
-    override fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    override fun statusBarLayerRotatesScales() =
+        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation,
+            testSpec.config.endRotation)
 
     /**
      * Checks that [fixedApp] layer is within [screenBoundsStart] at the start of the transition
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
index e8a61e8..ce89fb6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
@@ -177,11 +177,13 @@
 
     @Presubmit
     @Test
-    open fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
+    open fun navBarLayerRotatesAndScales() =
+        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
 
     @Presubmit
     @Test
-    open fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    open fun statusBarLayerRotatesScales() =
+        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index 3557906..defa58d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -29,7 +29,6 @@
 
 import android.content.res.Configuration;
 import android.graphics.Rect;
-import android.view.SurfaceControl;
 
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -53,7 +52,7 @@
 @RunWith(AndroidJUnit4.class)
 public class SplitLayoutTests extends ShellTestCase {
     @Mock SplitLayout.SplitLayoutHandler mSplitLayoutHandler;
-    @Mock SurfaceControl mRootLeash;
+    @Mock SplitWindowManager.ParentContainerCallbacks mCallbacks;
     @Mock DisplayImeController mDisplayImeController;
     @Mock ShellTaskOrganizer mTaskOrganizer;
     @Captor ArgumentCaptor<Runnable> mRunnableCaptor;
@@ -67,7 +66,7 @@
                 mContext,
                 getConfiguration(),
                 mSplitLayoutHandler,
-                b -> b.setParent(mRootLeash),
+                mCallbacks,
                 mDisplayImeController,
                 mTaskOrganizer));
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java
index c456c7d..9bb54a1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java
@@ -23,7 +23,6 @@
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.view.InsetsState;
-import android.view.SurfaceControl;
 
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -41,8 +40,8 @@
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class SplitWindowManagerTests extends ShellTestCase {
-    @Mock SurfaceControl mSurfaceControl;
     @Mock SplitLayout mSplitLayout;
+    @Mock SplitWindowManager.ParentContainerCallbacks mCallbacks;
     private SplitWindowManager mSplitWindowManager;
 
     @Before
@@ -51,7 +50,7 @@
         final Configuration configuration = new Configuration();
         configuration.setToDefaults();
         mSplitWindowManager = new SplitWindowManager("TestSplitDivider", mContext, configuration,
-                b -> b.setParent(mSurfaceControl));
+                mCallbacks);
         when(mSplitLayout.getDividerBounds()).thenReturn(
                 new Rect(0, 0, configuration.windowConfiguration.getBounds().width(),
                         configuration.windowConfiguration.getBounds().height()));
diff --git a/media/Android.bp b/media/Android.bp
index 7592c15..b049cb6 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -56,6 +56,10 @@
         "aidl/android/media/audio/common/AudioConfig.aidl",
         "aidl/android/media/audio/common/AudioConfigBase.aidl",
         "aidl/android/media/audio/common/AudioContentType.aidl",
+        "aidl/android/media/audio/common/AudioDevice.aidl",
+        "aidl/android/media/audio/common/AudioDeviceAddress.aidl",
+        "aidl/android/media/audio/common/AudioDeviceDescription.aidl",
+        "aidl/android/media/audio/common/AudioDeviceType.aidl",
         "aidl/android/media/audio/common/AudioEncapsulationMetadataType.aidl",
         "aidl/android/media/audio/common/AudioEncapsulationMode.aidl",
         "aidl/android/media/audio/common/AudioEncapsulationType.aidl",
diff --git a/media/aidl/android/media/audio/common/AudioDevice.aidl b/media/aidl/android/media/audio/common/AudioDevice.aidl
new file mode 100644
index 0000000..f897eb2
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioDevice.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.audio.common;
+
+import android.media.audio.common.AudioDeviceAddress;
+import android.media.audio.common.AudioDeviceDescription;
+
+/**
+ * Represents a concrete audio device by bundling together the device type and
+ * the device address.
+ *
+ * {@hide}
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+parcelable AudioDevice {
+    AudioDeviceDescription type;
+    AudioDeviceAddress address;
+}
diff --git a/media/aidl/android/media/audio/common/AudioDeviceAddress.aidl b/media/aidl/android/media/audio/common/AudioDeviceAddress.aidl
new file mode 100644
index 0000000..ca48f7e
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioDeviceAddress.aidl
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.audio.common;
+
+/**
+ * This structure defines various representations for the audio device
+ * address.
+ *
+ * {@hide}
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+union AudioDeviceAddress {
+    /**
+     * String uniquely identifying the device among other devices
+     * of the same type. Can be empty in case there is only one device
+     * of this type.
+     *
+     * Depending on the device type, its id may be assigned by the framework
+     * (one case used at the time of writing is REMOTE_SUBMIX), or assigned by
+     * the HAL service (the canonical examples are BUS and MIC devices). In any
+     * case, both framework and HAL must never attempt to parse the value of the
+     * 'id' field, regardless of whom has generated it. If the address must be
+     * parsed, one of the members below must be used instead of 'id'.
+     */
+    @utf8InCpp String id;
+    /**
+     * IEEE 802 MAC address. Set for Bluetooth devices. The array must have
+     * exactly 6 elements.
+     */
+    byte[] mac;
+    /**
+     * IPv4 Address. Set for IPv4 devices. The array must have exactly 4
+     * elements.
+     */
+    byte[] ipv4;
+    /**
+     * IPv6 Address. Set for IPv6 devices. The array must have exactly 8
+     * elements.
+     */
+    int[] ipv6;
+    /**
+     * PCI bus Address. Set for USB devices. The array must have exactly 2
+     * elements, in the following order: the card id, and the device id.
+     */
+    int[] alsa;
+}
diff --git a/media/aidl/android/media/audio/common/AudioDeviceDescription.aidl b/media/aidl/android/media/audio/common/AudioDeviceDescription.aidl
new file mode 100644
index 0000000..4e8a735
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioDeviceDescription.aidl
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.audio.common;
+
+import android.media.audio.common.AudioDeviceType;
+
+/**
+ * Describes the kind of an audio device.
+ *
+ * {@hide}
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+parcelable AudioDeviceDescription {
+    /**
+     * Type and directionality of the device. For bidirectional audio devices
+     * two descriptions need to be created, having the same value for
+     * the 'connection' field.
+     *
+     * See 'AudioDeviceType' for the list of supported values.
+     */
+    AudioDeviceType type = AudioDeviceType.NONE;
+    /**
+     * Specifies the type of the connection of the device to the audio system.
+     * Usually it's some kind of a communication protocol, e.g. Bluetooth SCO or
+     * USB. There is a list of connection types recognized by the framework,
+     * defined using 'CONNECTION_' constants. Vendors can add their own
+     * connection types with "vx.<vendor>." prefix.
+     *
+     * When the 'connection' field is left empty and 'type != NONE | DEFAULT',
+     * it is assumed that the device is permanently attached to the audio
+     * system, e.g. a built-in speaker or microphone.
+     *
+     * The 'connection' field must be left empty if 'type' is 'NONE' or
+     * '{IN|OUT}_DEFAULT'.
+     */
+    @utf8InCpp String connection;
+    /**
+     * Analog connection, for example, via 3.5 mm analog jack.
+     */
+    const @utf8InCpp String CONNECTION_ANALOG = "analog";
+    /**
+     * Low-End (Analog) Desk Dock.
+     */
+    const @utf8InCpp String CONNECTION_ANALOG_DOCK = "analog-dock";
+    /**
+     * Bluetooth A2DP connection.
+     */
+    const @utf8InCpp String CONNECTION_BT_A2DP = "bt-a2dp";
+    /**
+     * Bluetooth Low Energy (LE) connection.
+     */
+    const @utf8InCpp String CONNECTION_BT_LE = "bt-le";
+    /**
+     * Bluetooth SCO connection.
+     */
+    const @utf8InCpp String CONNECTION_BT_SCO = "bt-sco";
+    /**
+     * Bus connection. Mostly used in automotive scenarios.
+     */
+    const @utf8InCpp String CONNECTION_BUS = "bus";
+    /**
+     * High-End (Digital) Desk Dock.
+     */
+    const @utf8InCpp String CONNECTION_DIGITAL_DOCK = "digital-dock";
+    /**
+     * HDMI connection.
+     */
+    const @utf8InCpp String CONNECTION_HDMI = "hdmi";
+    /**
+     * HDMI ARC connection.
+     */
+    const @utf8InCpp String CONNECTION_HDMI_ARC = "hdmi-arc";
+    /**
+     * HDMI eARC connection.
+     */
+    const @utf8InCpp String CONNECTION_HDMI_EARC = "hdmi-earc";
+    /**
+     * IP v4 connection.
+     */
+    const @utf8InCpp String CONNECTION_IP_V4 = "ip-v4";
+    /**
+     * SPDIF connection.
+     */
+    const @utf8InCpp String CONNECTION_SPDIF = "spdif";
+    /**
+     * A wireless connection when the actual protocol is unspecified.
+     */
+    const @utf8InCpp String CONNECTION_WIRELESS = "wireless";
+    /**
+     * USB connection.
+     */
+    const @utf8InCpp String CONNECTION_USB = "usb";
+}
diff --git a/media/aidl/android/media/audio/common/AudioDeviceType.aidl b/media/aidl/android/media/audio/common/AudioDeviceType.aidl
new file mode 100644
index 0000000..95dbe2a
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioDeviceType.aidl
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.audio.common;
+
+/**
+ * The type of the audio device. Only used as part of 'AudioDeviceDescription'
+ * structure.
+ *
+ * Types are divided into "input" and "output" categories. Audio devices that
+ * have both audio input and output, for example, headsets, are represented by a
+ * pair of input and output device types.
+ *
+ * The 'AudioDeviceType' intentionally binds together directionality and 'kind'
+ * of the device to avoid making them fully orthogonal. This is because not all
+ * types of devices are bidirectional, for example, speakers can only be used
+ * for output and microphones can only be used for input (at least, in the
+ * context of the audio framework).
+ *
+ * {@hide}
+ */
+@VintfStability
+@Backing(type="int")
+enum AudioDeviceType {
+    /**
+     * "None" type is a "null" value. All fields of 'AudioDeviceDescription'
+     * must have default / empty / null values.
+     */
+    NONE = 0,
+    /**
+     * The "default" device is used when the client does not have any
+     * preference for a particular device.
+     */
+    IN_DEFAULT = 1,
+    /**
+     * A device implementing Android Open Accessory protocol.
+     */
+    IN_ACCESSORY = 2,
+    /**
+     * Input from a DSP front-end proxy device.
+     */
+    IN_AFE_PROXY = 3,
+    /**
+     * Used when only the connection protocol is known, e.g. a "HDMI Device."
+     */
+    IN_DEVICE = 4,
+    /**
+     * A device providing reference input for echo canceller.
+     */
+    IN_ECHO_REFERENCE = 5,
+    /**
+     * FM Tuner input.
+     */
+    IN_FM_TUNER = 6,
+    /**
+     * A microphone of a headset.
+     */
+    IN_HEADSET = 7,
+    /**
+     * Loopback input.
+     */
+    IN_LOOPBACK = 8,
+    /**
+     * The main microphone (the frontal mic on mobile devices).
+     */
+    IN_MICROPHONE = 9,
+    /**
+     * The secondary microphone (the back mic on mobile devices).
+     */
+    IN_MICROPHONE_BACK = 10,
+    /**
+     * Input from a submix of other streams.
+     */
+    IN_SUBMIX = 11,
+    /**
+     * Audio received via the telephone line.
+     */
+    IN_TELEPHONY_RX = 12,
+    /**
+     * TV Tuner audio input.
+     */
+    IN_TV_TUNER = 13,
+    /**
+     * The "default" device is used when the client does not have any
+     * preference for a particular device.
+     */
+    OUT_DEFAULT = 129,
+    /**
+     * A device implementing Android Open Accessory protocol.
+     */
+    OUT_ACCESSORY = 130,
+    /**
+     * Output from a DSP front-end proxy device.
+     */
+    OUT_AFE_PROXY = 131,
+    /**
+     * Car audio system.
+     */
+    OUT_CARKIT = 132,
+    /**
+     * Used when only the connection protocol is known, e.g. a "HDMI Device."
+     */
+    OUT_DEVICE = 133,
+    /**
+     * The echo canceller device.
+     */
+    OUT_ECHO_CANCELLER = 134,
+    /**
+     * The FM Tuner device.
+     */
+    OUT_FM = 135,
+    /**
+     * Headphones.
+     */
+    OUT_HEADPHONE = 136,
+    /**
+     * Headphones of a headset.
+     */
+    OUT_HEADSET = 137,
+    /**
+     * Hearing aid.
+     */
+    OUT_HEARING_AID = 138,
+    /**
+     * Secondary line level output.
+     */
+    OUT_LINE_AUX = 139,
+    /**
+     * The main speaker.
+     */
+    OUT_SPEAKER = 140,
+    /**
+     * The speaker of a mobile device in the case when it is close to the ear.
+     */
+    OUT_SPEAKER_EARPIECE = 141,
+    /**
+     * The main speaker with overload / overheating protection.
+     */
+    OUT_SPEAKER_SAFE = 142,
+    /**
+     * Output into a submix.
+     */
+    OUT_SUBMIX = 143,
+    /**
+     * Output into a telephone line.
+     */
+    OUT_TELEPHONY_TX = 144,
+}
diff --git a/media/aidl/android/media/audio/common/AudioFormatType.aidl b/media/aidl/android/media/audio/common/AudioFormatType.aidl
index 1759401..ea78c7a 100644
--- a/media/aidl/android/media/audio/common/AudioFormatType.aidl
+++ b/media/aidl/android/media/audio/common/AudioFormatType.aidl
@@ -19,6 +19,8 @@
 /**
  * The type of the audio format. Only used as part of 'AudioFormatDescription'
  * structure.
+ *
+ * {@hide}
  */
 @VintfStability
 @Backing(type="byte")
diff --git a/media/aidl/android/media/audio/common/PcmType.aidl b/media/aidl/android/media/audio/common/PcmType.aidl
index db77769..6e07d9b 100644
--- a/media/aidl/android/media/audio/common/PcmType.aidl
+++ b/media/aidl/android/media/audio/common/PcmType.aidl
@@ -19,6 +19,8 @@
 /**
  * The type of the encoding used for representing PCM samples. Only used as
  * part of 'AudioFormatDescription' structure.
+ *
+ * {@hide}
  */
 @VintfStability
 @Backing(type="byte")
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDevice.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDevice.aidl
new file mode 100644
index 0000000..fb5cb62
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDevice.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable AudioDevice {
+  android.media.audio.common.AudioDeviceDescription type;
+  android.media.audio.common.AudioDeviceAddress address;
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDeviceAddress.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDeviceAddress.aidl
new file mode 100644
index 0000000..905d3aa
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDeviceAddress.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+union AudioDeviceAddress {
+  @utf8InCpp String id;
+  byte[] mac;
+  byte[] ipv4;
+  int[] ipv6;
+  int[] alsa;
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDeviceDescription.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDeviceDescription.aidl
new file mode 100644
index 0000000..b4d71d7
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDeviceDescription.aidl
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable AudioDeviceDescription {
+  android.media.audio.common.AudioDeviceType type = android.media.audio.common.AudioDeviceType.NONE;
+  @utf8InCpp String connection;
+  const @utf8InCpp String CONNECTION_ANALOG = "analog";
+  const @utf8InCpp String CONNECTION_ANALOG_DOCK = "analog-dock";
+  const @utf8InCpp String CONNECTION_BT_A2DP = "bt-a2dp";
+  const @utf8InCpp String CONNECTION_BT_LE = "bt-le";
+  const @utf8InCpp String CONNECTION_BT_SCO = "bt-sco";
+  const @utf8InCpp String CONNECTION_BUS = "bus";
+  const @utf8InCpp String CONNECTION_DIGITAL_DOCK = "digital-dock";
+  const @utf8InCpp String CONNECTION_HDMI = "hdmi";
+  const @utf8InCpp String CONNECTION_HDMI_ARC = "hdmi-arc";
+  const @utf8InCpp String CONNECTION_HDMI_EARC = "hdmi-earc";
+  const @utf8InCpp String CONNECTION_IP_V4 = "ip-v4";
+  const @utf8InCpp String CONNECTION_SPDIF = "spdif";
+  const @utf8InCpp String CONNECTION_WIRELESS = "wireless";
+  const @utf8InCpp String CONNECTION_USB = "usb";
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDeviceType.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDeviceType.aidl
new file mode 100644
index 0000000..ffdb778
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDeviceType.aidl
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioDeviceType {
+  NONE = 0,
+  IN_DEFAULT = 1,
+  IN_ACCESSORY = 2,
+  IN_AFE_PROXY = 3,
+  IN_DEVICE = 4,
+  IN_ECHO_REFERENCE = 5,
+  IN_FM_TUNER = 6,
+  IN_HEADSET = 7,
+  IN_LOOPBACK = 8,
+  IN_MICROPHONE = 9,
+  IN_MICROPHONE_BACK = 10,
+  IN_SUBMIX = 11,
+  IN_TELEPHONY_RX = 12,
+  IN_TV_TUNER = 13,
+  OUT_DEFAULT = 129,
+  OUT_ACCESSORY = 130,
+  OUT_AFE_PROXY = 131,
+  OUT_CARKIT = 132,
+  OUT_DEVICE = 133,
+  OUT_ECHO_CANCELLER = 134,
+  OUT_FM = 135,
+  OUT_HEADPHONE = 136,
+  OUT_HEADSET = 137,
+  OUT_HEARING_AID = 138,
+  OUT_LINE_AUX = 139,
+  OUT_SPEAKER = 140,
+  OUT_SPEAKER_EARPIECE = 141,
+  OUT_SPEAKER_SAFE = 142,
+  OUT_SUBMIX = 143,
+  OUT_TELEPHONY_TX = 144,
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioFormatType.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioFormatType.aidl
index b94c850..7f55abe 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioFormatType.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioFormatType.aidl
@@ -32,6 +32,7 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package android.media.audio.common;
+/* @hide */
 @Backing(type="byte") @VintfStability
 enum AudioFormatType {
   DEFAULT = 0,
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/PcmType.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/PcmType.aidl
index 42c4679..79bfa62 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/PcmType.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/PcmType.aidl
@@ -32,6 +32,7 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package android.media.audio.common;
+/* @hide */
 @Backing(type="byte") @VintfStability
 enum PcmType {
   DEFAULT = 0,
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index fe92e26..d919af5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -106,6 +106,7 @@
     private boolean mIsA2dpProfileConnectedFail = false;
     private boolean mIsHeadsetProfileConnectedFail = false;
     private boolean mIsHearingAidProfileConnectedFail = false;
+    private boolean mUnpairing;
     // Group second device for Hearing Aid
     private CachedBluetoothDevice mSubDevice;
     @VisibleForTesting
@@ -142,6 +143,7 @@
         fillData();
         mHiSyncId = BluetoothHearingAid.HI_SYNC_ID_INVALID;
         initDrawableCache();
+        mUnpairing = false;
     }
 
     private void initDrawableCache() {
@@ -402,6 +404,7 @@
         if (state != BluetoothDevice.BOND_NONE) {
             final BluetoothDevice dev = mDevice;
             if (dev != null) {
+                mUnpairing = true;
                 final boolean successful = dev.removeBond();
                 if (successful) {
                     releaseLruCache();
@@ -1243,4 +1246,8 @@
     void releaseLruCache() {
         mDrawableCache.evictAll();
     }
+
+    boolean getUnpairing() {
+        return mUnpairing;
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
index 20ece69..818f5ca 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
@@ -180,6 +180,9 @@
                 break;
             case BluetoothProfile.STATE_DISCONNECTED:
                 mainDevice = findMainDevice(cachedDevice);
+                if (cachedDevice.getUnpairing()) {
+                    return true;
+                }
                 if (mainDevice != null) {
                     // When main device exists, receiving sub device disconnection
                     // To update main device UI
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
index c501b3a..2e8f368 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
@@ -110,6 +110,18 @@
     }
 
     /**
+     * Check if target package is in allow list except idle app
+     */
+    public boolean isAllowlistedExceptIdle(String pkg) {
+        try {
+            return mDeviceIdleService.isPowerSaveWhitelistExceptIdleApp(pkg);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Unable to reach IDeviceIdleController", e);
+            return true;
+        }
+    }
+
+    /**
      *
      * @param pkgs a list of packageName
      * @return true when one of package is in allow list
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java
index 4f11fb1..6caf762 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java
@@ -151,4 +151,14 @@
         assertThat(mPowerAllowlistBackend.isSysAllowlisted(PACKAGE_TWO)).isFalse();
         assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isFalse();
     }
+
+    @Test
+    public void testIsPowerSaveWhitelistExceptIdleApp() throws Exception {
+        doReturn(true).when(mDeviceIdleService)
+                .isPowerSaveWhitelistExceptIdleApp(PACKAGE_ONE);
+
+        mPowerAllowlistBackend.refreshList();
+
+        assertThat(mPowerAllowlistBackend.isAllowlistedExceptIdle(PACKAGE_ONE)).isTrue();
+    }
 }
diff --git a/packages/SystemUI/res/layout/sidefps_view.xml b/packages/SystemUI/res/layout/sidefps_view.xml
index 3977635..921f788 100644
--- a/packages/SystemUI/res/layout/sidefps_view.xml
+++ b/packages/SystemUI/res/layout/sidefps_view.xml
@@ -14,11 +14,13 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.systemui.biometrics.SidefpsView
+<com.airbnb.lottie.LottieAnimationView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:systemui="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/sidefps_view"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:contentDescription="@string/accessibility_fingerprint_label">
-</com.android.systemui.biometrics.SidefpsView>
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/sidefps_animation"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    app:lottie_autoPlay="true"
+    app:lottie_loop="true"
+    app:lottie_rawRes="@raw/sfps_pulse"
+    android:contentDescription="@string/accessibility_fingerprint_label"/>
diff --git a/packages/SystemUI/res/raw/sfps_pulse.json b/packages/SystemUI/res/raw/sfps_pulse.json
new file mode 100644
index 0000000..c4903a2
--- /dev/null
+++ b/packages/SystemUI/res/raw/sfps_pulse.json
@@ -0,0 +1 @@
+{"v":"5.7.6","fr":60,"ip":0,"op":300,"w":42,"h":80,"nm":"Fingerprint Pulse Motion","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".black","cl":"black","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[28,40,0],"to":[0.751,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":30,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":60,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":90,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":120,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":150,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":180,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":210,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":240,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":270,"s":[32.503,40,0],"to":[0,0,0],"ti":[0.751,0,0]},{"t":300,"s":[28,40,0]}],"ix":2,"l":2},"a":{"a":0,"k":[28.253,40,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[1.566,0],[-3.929,-5.503],[-2.751,-6.68],[3.929,0],[-2.751,6.68],[-3.929,5.503]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[30.218,40],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".blue600","cl":"blue600","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[41.878,40,0],"ix":2,"l":2},"a":{"a":0,"k":[28.253,40,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[55,55],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 2","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.101960785687,0.450980395079,0.909803926945,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[28.253,40],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[20]},{"t":30,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":-30,"s":[55,55]},{"t":30,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-30,"op":30,"st":-30,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":40,"s":[20]},{"t":60,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":0,"s":[55,55]},{"t":60,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":70,"s":[20]},{"t":90,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":30,"s":[55,55]},{"t":90,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":30,"op":90,"st":30,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":100,"s":[20]},{"t":120,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":60,"s":[55,55]},{"t":120,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":60,"op":120,"st":60,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":130,"s":[20]},{"t":150,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":90,"s":[55,55]},{"t":150,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":90,"op":150,"st":90,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":160,"s":[20]},{"t":180,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":120,"s":[55,55]},{"t":180,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":120,"op":180,"st":120,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":190,"s":[20]},{"t":210,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":150,"s":[55,55]},{"t":210,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":150,"op":210,"st":150,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":220,"s":[20]},{"t":240,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":180,"s":[55,55]},{"t":240,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":180,"op":240,"st":180,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[20]},{"t":270,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":210,"s":[55,55]},{"t":270,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":210,"op":270,"st":210,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":280,"s":[20]},{"t":300,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":240,"s":[55,55]},{"t":300,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":240,"op":300,"st":240,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":310,"s":[20]},{"t":330,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":270,"s":[55,55]},{"t":330,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":270,"op":330,"st":270,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/sfps_pulse_landscape.json b/packages/SystemUI/res/raw/sfps_pulse_landscape.json
new file mode 100644
index 0000000..8c91762
--- /dev/null
+++ b/packages/SystemUI/res/raw/sfps_pulse_landscape.json
@@ -0,0 +1 @@
+{"v":"5.7.6","fr":60,"ip":0,"op":300,"w":80,"h":42,"nm":"Fingerprint Pulse Motion Portrait","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".black","cl":"black","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[40,14,0],"to":[0,-0.751,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":30,"s":[40,9.497,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":60,"s":[40,14,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":90,"s":[40,9.497,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":120,"s":[40,14,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":150,"s":[40,9.497,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":180,"s":[40,14,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":210,"s":[40,9.497,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":240,"s":[40,14,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":270,"s":[40,9.497,0],"to":[0,0,0],"ti":[0,-0.751,0]},{"t":300,"s":[40,14,0]}],"ix":2,"l":2},"a":{"a":0,"k":[28.253,40,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[1.566,0],[-3.929,-5.503],[-2.751,-6.68],[3.929,0],[-2.751,6.68],[-3.929,5.503]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[30.218,40],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".blue600","cl":"blue600","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,0.122,0],"ix":2,"l":2},"a":{"a":0,"k":[28.253,40,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[55,55],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 2","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.101960785687,0.450980395079,0.909803926945,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[28.253,40],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[20]},{"t":30,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":-30,"s":[55,55]},{"t":30,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-30,"op":30,"st":-30,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":40,"s":[20]},{"t":60,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":0,"s":[55,55]},{"t":60,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":70,"s":[20]},{"t":90,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":30,"s":[55,55]},{"t":90,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":30,"op":90,"st":30,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":100,"s":[20]},{"t":120,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":60,"s":[55,55]},{"t":120,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":60,"op":120,"st":60,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":130,"s":[20]},{"t":150,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":90,"s":[55,55]},{"t":150,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":90,"op":150,"st":90,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":160,"s":[20]},{"t":180,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":120,"s":[55,55]},{"t":180,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":120,"op":180,"st":120,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":190,"s":[20]},{"t":210,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":150,"s":[55,55]},{"t":210,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":150,"op":210,"st":150,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":220,"s":[20]},{"t":240,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":180,"s":[55,55]},{"t":240,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":180,"op":240,"st":180,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[20]},{"t":270,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":210,"s":[55,55]},{"t":270,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":210,"op":270,"st":210,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":280,"s":[20]},{"t":300,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":240,"s":[55,55]},{"t":300,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":240,"op":300,"st":240,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":310,"s":[20]},{"t":330,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":270,"s":[55,55]},{"t":330,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":270,"op":330,"st":270,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 2a4022c..a115195 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -27,6 +27,7 @@
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.Drawable;
 import android.hardware.biometrics.BiometricSourceType;
+import android.hardware.biometrics.SensorLocationInternal;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.media.AudioAttributes;
 import android.os.Process;
@@ -330,8 +331,9 @@
     private void updateLockIconLocation() {
         if (mUdfpsSupported) {
             FingerprintSensorPropertiesInternal props = mAuthController.getUdfpsProps().get(0);
-            mView.setCenterLocation(new PointF(props.sensorLocationX, props.sensorLocationY),
-                    props.sensorRadius);
+            final SensorLocationInternal location = props.getLocation();
+            mView.setCenterLocation(new PointF(location.sensorLocationX, location.sensorLocationY),
+                    location.sensorRadius);
         } else {
             mView.setCenterLocation(
                     new PointF(mWidthPixels / 2,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 0790af9..71445a7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -114,7 +114,7 @@
     @VisibleForTesting
     IBiometricSysuiReceiver mReceiver;
     @VisibleForTesting
-    @NonNull final BiometricOrientationEventListener mOrientationListener;
+    @NonNull final BiometricDisplayListener mOrientationListener;
     @Nullable private final List<FaceSensorPropertiesInternal> mFaceProps;
     @Nullable private List<FingerprintSensorPropertiesInternal> mFpProps;
     @Nullable private List<FingerprintSensorPropertiesInternal> mUdfpsProps;
@@ -459,13 +459,15 @@
         mSidefpsControllerFactory = sidefpsControllerFactory;
         mWindowManager = windowManager;
         mUdfpsEnrolledForUser = new SparseBooleanArray();
-        mOrientationListener = new BiometricOrientationEventListener(context,
+        mOrientationListener = new BiometricDisplayListener(
+                context,
+                displayManager,
+                handler,
+                BiometricDisplayListener.SensorType.Generic.INSTANCE,
                 () -> {
                     onOrientationChanged();
                     return Unit.INSTANCE;
-                },
-                displayManager,
-                handler);
+                });
 
         mFaceProps = mFaceManager != null ? mFaceManager.getSensorPropertiesInternal() : null;
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index e232316..f057bb1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -241,7 +241,7 @@
     private fun updateUdfpsDependentParams() {
         authController.udfpsProps?.let {
             if (it.size > 0) {
-                udfpsRadius = it[0].sensorRadius.toFloat()
+                udfpsRadius = it[0].location.sensorRadius.toFloat()
                 udfpsController = udfpsControllerProvider.get()
 
                 if (mView.isAttachedToWindow) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt
new file mode 100644
index 0000000..b7404df
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics
+
+import android.content.Context
+import android.hardware.display.DisplayManager
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
+import android.os.Handler
+import android.view.Surface
+import com.android.systemui.biometrics.BiometricDisplayListener.SensorType.Generic
+
+/**
+ * A listener for keeping overlays for biometric sensors aligned with the physical device
+ * device's screen. The [onChanged] will be dispatched on the [handler]
+ * whenever a relevant change to the device's configuration (orientation, fold, display change,
+ * etc.) may require the UI to change for the given [sensorType].
+ */
+class BiometricDisplayListener(
+    private val context: Context,
+    private val displayManager: DisplayManager,
+    private val handler: Handler,
+    private val sensorType: SensorType = SensorType.Generic,
+    private val onChanged: () -> Unit
+) : DisplayManager.DisplayListener {
+
+    private var lastRotation = context.display?.rotation ?: Surface.ROTATION_0
+
+    override fun onDisplayAdded(displayId: Int) {}
+    override fun onDisplayRemoved(displayId: Int) {}
+    override fun onDisplayChanged(displayId: Int) {
+        val rotationChanged = didRotationChange()
+
+        when (sensorType) {
+            is SensorType.SideFingerprint -> onChanged()
+            else -> {
+                if (rotationChanged) {
+                    onChanged()
+                }
+            }
+        }
+    }
+
+    private fun didRotationChange(): Boolean {
+        val rotation = context.display?.rotation ?: return false
+        val last = lastRotation
+        lastRotation = rotation
+        return last != rotation
+    }
+
+    /** Listen for changes. */
+    fun enable() {
+        displayManager.registerDisplayListener(this, handler)
+    }
+
+    /** Stop listening for changes. */
+    fun disable() {
+        displayManager.unregisterDisplayListener(this)
+    }
+
+    /**
+     * Type of sensor to determine what kind of display changes require layouts.
+     *
+     * The [Generic] type should be used in cases where the modality can vary, such as
+     * biometric prompt (and this object will likely change as multi-mode auth is added).
+     */
+    sealed class SensorType {
+        object Generic : SensorType()
+        data class UnderDisplayFingerprint(
+            val properties: FingerprintSensorPropertiesInternal
+        ) : SensorType()
+        data class SideFingerprint(
+            val properties: FingerprintSensorPropertiesInternal
+        ) : SensorType()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricOrientationEventListener.kt b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricOrientationEventListener.kt
deleted file mode 100644
index 98a03a1..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricOrientationEventListener.kt
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics
-
-import android.content.Context
-import android.hardware.display.DisplayManager
-import android.os.Handler
-import android.view.OrientationEventListener
-import android.view.Surface
-
-/**
- * An [OrientationEventListener] that invokes the [onOrientationChanged] callback whenever
- * the orientation of the device has changed in order to keep overlays for biometric sensors
- * aligned with the device's screen.
- */
-class BiometricOrientationEventListener(
-    private val context: Context,
-    private val onOrientationChanged: () -> Unit,
-    private val displayManager: DisplayManager,
-    private val handler: Handler
-) : DisplayManager.DisplayListener {
-
-    private var lastRotation = context.display?.rotation ?: Surface.ROTATION_0
-
-    override fun onDisplayAdded(displayId: Int) {}
-    override fun onDisplayRemoved(displayId: Int) {}
-    override fun onDisplayChanged(displayId: Int) {
-        val rotation = context.display?.rotation ?: return
-        if (lastRotation != rotation) {
-            lastRotation = rotation
-
-            onOrientationChanged()
-        }
-    }
-
-    fun enable() {
-        displayManager.registerDisplayListener(this, handler)
-    }
-
-    fun disable() {
-        displayManager.unregisterDisplayListener(this)
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.java
deleted file mode 100644
index 8f6e249..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.java
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import static com.android.internal.util.Preconditions.checkArgument;
-import static com.android.internal.util.Preconditions.checkNotNull;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.PixelFormat;
-import android.hardware.display.DisplayManager;
-import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
-import android.hardware.fingerprint.ISidefpsController;
-import android.os.Handler;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.Surface;
-import android.view.WindowManager;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.R;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.util.concurrency.DelayableExecutor;
-
-import javax.inject.Inject;
-
-import kotlin.Unit;
-
-/**
- * Shows and hides the side fingerprint sensor (side-fps) overlay and handles side fps touch events.
- */
-@SysUISingleton
-public class SidefpsController {
-    private static final String TAG = "SidefpsController";
-    @NonNull private final Context mContext;
-    @NonNull private final LayoutInflater mInflater;
-    private final FingerprintManager mFingerprintManager;
-    private final WindowManager mWindowManager;
-    private final DelayableExecutor mFgExecutor;
-    @VisibleForTesting @NonNull final BiometricOrientationEventListener mOrientationListener;
-
-    // TODO: update mDisplayHeight and mDisplayWidth for multi-display devices
-    private final int mDisplayHeight;
-    private final int mDisplayWidth;
-
-    private boolean mIsVisible = false;
-    @Nullable private SidefpsView mView;
-
-    static final int SFPS_AFFORDANCE_WIDTH = 50; // in default portrait mode
-
-    @NonNull
-    private final ISidefpsController mSidefpsControllerImpl = new ISidefpsController.Stub() {
-        @Override
-        public void show() {
-            mFgExecutor.execute(() -> {
-                SidefpsController.this.show();
-                mIsVisible = true;
-            });
-        }
-
-        @Override
-        public void hide() {
-            mFgExecutor.execute(() -> {
-                SidefpsController.this.hide();
-                mIsVisible = false;
-            });
-        }
-    };
-
-    @VisibleForTesting
-    final FingerprintSensorPropertiesInternal mSensorProps;
-    private final WindowManager.LayoutParams mCoreLayoutParams;
-
-    @Inject
-    public SidefpsController(@NonNull Context context,
-            @NonNull LayoutInflater inflater,
-            @Nullable FingerprintManager fingerprintManager,
-            @NonNull WindowManager windowManager,
-            @Main DelayableExecutor fgExecutor,
-            @NonNull DisplayManager displayManager,
-            @Main Handler handler) {
-        mContext = context;
-        mInflater = inflater;
-        mFingerprintManager = checkNotNull(fingerprintManager);
-        mWindowManager = windowManager;
-        mFgExecutor = fgExecutor;
-        mOrientationListener = new BiometricOrientationEventListener(
-                context,
-                () -> {
-                    onOrientationChanged();
-                    return Unit.INSTANCE;
-                },
-                displayManager,
-                handler);
-
-        mSensorProps = findFirstSidefps();
-        checkArgument(mSensorProps != null);
-
-        mCoreLayoutParams = new WindowManager.LayoutParams(
-                WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
-                getCoreLayoutParamFlags(),
-                PixelFormat.TRANSLUCENT);
-        mCoreLayoutParams.setTitle(TAG);
-        // Overrides default, avoiding status bars during layout
-        mCoreLayoutParams.setFitInsetsTypes(0);
-        mCoreLayoutParams.gravity = Gravity.TOP | Gravity.LEFT;
-        mCoreLayoutParams.layoutInDisplayCutoutMode =
-                WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-        mCoreLayoutParams.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
-
-        DisplayMetrics displayMetrics = new DisplayMetrics();
-        windowManager.getDefaultDisplay().getMetrics(displayMetrics);
-        mDisplayHeight = displayMetrics.heightPixels;
-        mDisplayWidth = displayMetrics.widthPixels;
-
-        mFingerprintManager.setSidefpsController(mSidefpsControllerImpl);
-    }
-
-    private void show() {
-        mView = (SidefpsView) mInflater.inflate(R.layout.sidefps_view, null, false);
-        mView.setSensorProperties(mSensorProps);
-        mWindowManager.addView(mView, computeLayoutParams());
-
-        mOrientationListener.enable();
-    }
-
-    private void hide() {
-        if (mView != null) {
-            mWindowManager.removeView(mView);
-            mView.setOnTouchListener(null);
-            mView.setOnHoverListener(null);
-            mView = null;
-        } else {
-            Log.v(TAG, "hideUdfpsOverlay | the overlay is already hidden");
-        }
-
-        mOrientationListener.disable();
-    }
-
-    private void onOrientationChanged() {
-        // If mView is null or if view is hidden, then return.
-        if (mView == null || !mIsVisible) {
-            return;
-        }
-
-        // If the overlay needs to be displayed with a new configuration, destroy the current
-        // overlay, and re-create and show the overlay with the updated LayoutParams.
-        hide();
-        show();
-    }
-
-    @Nullable
-    private FingerprintSensorPropertiesInternal findFirstSidefps() {
-        for (FingerprintSensorPropertiesInternal props :
-                mFingerprintManager.getSensorPropertiesInternal()) {
-            if (props.isAnySidefpsType()) {
-                // TODO(b/188690214): L155-L173 can be removed once sensorLocationX,
-                //  sensorLocationY, and sensorRadius are defined in sensorProps by the HAL
-                int sensorLocationX = 25;
-                int sensorLocationY = 610;
-                int sensorRadius = 112;
-
-                FingerprintSensorPropertiesInternal tempProps =
-                        new FingerprintSensorPropertiesInternal(
-                                props.sensorId,
-                                props.sensorStrength,
-                                props.maxEnrollmentsPerUser,
-                                props.componentInfo,
-                                props.sensorType,
-                                props.resetLockoutRequiresHardwareAuthToken,
-                                sensorLocationX,
-                                sensorLocationY,
-                                sensorRadius
-                        );
-                props = tempProps;
-                return props;
-            }
-        }
-        return null;
-    }
-
-    private int getCoreLayoutParamFlags() {
-        return WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
-                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
-    }
-
-    /**
-     * Computes layout params depending on orientation & folding configuration of device
-     */
-    private WindowManager.LayoutParams computeLayoutParams() {
-        mCoreLayoutParams.flags = getCoreLayoutParamFlags();
-        // Y value of top of affordance in portrait mode, X value of left of affordance in landscape
-        int sfpsLocationY = mSensorProps.sensorLocationY - mSensorProps.sensorRadius;
-        int sfpsAffordanceHeight = mSensorProps.sensorRadius * 2;
-
-        // Calculate coordinates of drawable area for the fps affordance, accounting for orientation
-        switch (mContext.getDisplay().getRotation()) {
-            case Surface.ROTATION_90:
-                mCoreLayoutParams.x = sfpsLocationY;
-                mCoreLayoutParams.y = 0;
-                mCoreLayoutParams.height = SFPS_AFFORDANCE_WIDTH;
-                mCoreLayoutParams.width = sfpsAffordanceHeight;
-                break;
-            case Surface.ROTATION_270:
-                mCoreLayoutParams.x = mDisplayHeight - sfpsLocationY - sfpsAffordanceHeight;
-                mCoreLayoutParams.y = mDisplayWidth - SFPS_AFFORDANCE_WIDTH;
-                mCoreLayoutParams.height = SFPS_AFFORDANCE_WIDTH;
-                mCoreLayoutParams.width = sfpsAffordanceHeight;
-                break;
-            default: // Portrait
-                mCoreLayoutParams.x = mDisplayWidth - SFPS_AFFORDANCE_WIDTH;
-                mCoreLayoutParams.y = sfpsLocationY;
-                mCoreLayoutParams.height = sfpsAffordanceHeight;
-                mCoreLayoutParams.width = SFPS_AFFORDANCE_WIDTH;
-        }
-        return mCoreLayoutParams;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
new file mode 100644
index 0000000..c0731b2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.biometrics
+
+import android.content.Context
+import android.graphics.PixelFormat
+import android.graphics.Rect
+import android.hardware.display.DisplayManager
+import android.hardware.fingerprint.FingerprintManager
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
+import android.hardware.fingerprint.ISidefpsController
+import android.os.Handler
+import android.util.Log
+import android.view.Display
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.Surface
+import android.view.View
+import android.view.WindowManager
+import androidx.annotation.RawRes
+import com.airbnb.lottie.LottieAnimationView
+import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.util.concurrency.DelayableExecutor
+import javax.inject.Inject
+
+private const val TAG = "SidefpsController"
+
+/**
+ * Shows and hides the side fingerprint sensor (side-fps) overlay and handles side fps touch events.
+ */
+@SysUISingleton
+class SidefpsController @Inject constructor(
+    private val context: Context,
+    private val layoutInflater: LayoutInflater,
+    fingerprintManager: FingerprintManager?,
+    private val windowManager: WindowManager,
+    @Main mainExecutor: DelayableExecutor,
+    displayManager: DisplayManager,
+    @Main handler: Handler
+) {
+    @VisibleForTesting
+    val sensorProps: FingerprintSensorPropertiesInternal = fingerprintManager
+        ?.sensorPropertiesInternal
+        ?.firstOrNull { it.isAnySidefpsType }
+        ?: throw IllegalStateException("no side fingerprint sensor")
+
+    @VisibleForTesting
+    val orientationListener = BiometricDisplayListener(
+        context,
+        displayManager,
+        handler,
+        BiometricDisplayListener.SensorType.SideFingerprint(sensorProps)
+    ) { onOrientationChanged() }
+
+    private var overlayView: View? = null
+        set(value) {
+            field?.let { oldView ->
+                windowManager.removeView(oldView)
+                orientationListener.disable()
+            }
+            field = value
+            field?.let { newView ->
+                windowManager.addView(newView, overlayViewParams)
+                orientationListener.enable()
+            }
+        }
+
+    private val overlayViewParams = WindowManager.LayoutParams(
+        WindowManager.LayoutParams.WRAP_CONTENT,
+        WindowManager.LayoutParams.WRAP_CONTENT,
+        WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
+        WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                or WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
+        PixelFormat.TRANSLUCENT
+    ).apply {
+        title = TAG
+        fitInsetsTypes = 0 // overrides default, avoiding status bars during layout
+        gravity = Gravity.TOP or Gravity.LEFT
+        layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+        privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
+    }
+
+    init {
+        fingerprintManager?.setSidefpsController(object : ISidefpsController.Stub() {
+            override fun show() = mainExecutor.execute {
+                if (overlayView == null) {
+                    overlayView = createOverlayForDisplay()
+                } else {
+                    Log.v(TAG, "overlay already shown")
+                }
+            }
+            override fun hide() = mainExecutor.execute { overlayView = null }
+        })
+    }
+
+    private fun onOrientationChanged() {
+        if (overlayView != null) {
+            overlayView = createOverlayForDisplay()
+        }
+    }
+
+    private fun createOverlayForDisplay(): View {
+        val view = layoutInflater.inflate(R.layout.sidefps_view, null, false)
+        val display = context.display!!
+
+        val lottie = view.findViewById(R.id.sidefps_animation) as LottieAnimationView
+        lottie.setAnimation(display.asSideFpsAnimation())
+        view.rotation = display.asSideFpsAnimationRotation()
+
+        updateOverlayParams(display, lottie.composition?.bounds ?: Rect())
+        lottie.addLottieOnCompositionLoadedListener {
+            if (overlayView == view) {
+                updateOverlayParams(display, it.bounds)
+                windowManager.updateViewLayout(overlayView, overlayViewParams)
+            }
+        }
+
+        return view
+    }
+
+    private fun updateOverlayParams(display: Display, bounds: Rect) {
+        val isPortrait = display.isPortrait()
+        val size = windowManager.maximumWindowMetrics.bounds
+        val displayWidth = if (isPortrait) size.width() else size.height()
+        val displayHeight = if (isPortrait) size.height() else size.width()
+        val offsets = sensorProps.getLocation(display.uniqueId).let { location ->
+            if (location == null) {
+                Log.w(TAG, "No location specified for display: ${display.uniqueId}")
+            }
+            location ?: sensorProps.location
+        }
+
+        // ignore sensorLocationX and sensorRadius since it's assumed to be on the side
+        // of the device and centered at sensorLocationY
+        val (x, y) = when (display.rotation) {
+            Surface.ROTATION_90 ->
+                Pair(offsets.sensorLocationY, 0)
+            Surface.ROTATION_270 ->
+                Pair(displayHeight - offsets.sensorLocationY - bounds.width(), displayWidth)
+            Surface.ROTATION_180 ->
+                Pair(0, displayHeight - offsets.sensorLocationY - bounds.height())
+            else ->
+                Pair(displayWidth, offsets.sensorLocationY)
+        }
+        overlayViewParams.x = x
+        overlayViewParams.y = y
+    }
+}
+
+@RawRes
+private fun Display.asSideFpsAnimation(): Int = when (rotation) {
+    Surface.ROTATION_0 -> R.raw.sfps_pulse
+    Surface.ROTATION_180 -> R.raw.sfps_pulse
+    else -> R.raw.sfps_pulse_landscape
+}
+
+private fun Display.asSideFpsAnimationRotation(): Float = when (rotation) {
+    Surface.ROTATION_180 -> 180f
+    Surface.ROTATION_270 -> 180f
+    else -> 0f
+}
+
+private fun Display.isPortrait(): Boolean =
+    rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsView.java
deleted file mode 100644
index 4fc59d6..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsView.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import static com.android.systemui.biometrics.SidefpsController.SFPS_AFFORDANCE_WIDTH;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.RectF;
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
-import android.util.AttributeSet;
-import android.view.Surface;
-import android.widget.FrameLayout;
-
-/**
- * A view containing a normal drawable view for sidefps events.
- */
-public class SidefpsView extends FrameLayout {
-    private static final String TAG = "SidefpsView";
-    private static final int POINTER_SIZE_PX = 50;
-    private static final int ROUND_RADIUS = 15;
-
-    @NonNull private final RectF mSensorRect;
-    @NonNull private final Paint mSensorRectPaint;
-    @NonNull private final Paint mPointerText;
-    @NonNull private final Context mContext;
-
-    // Used to obtain the sensor location.
-    @NonNull private FingerprintSensorPropertiesInternal mSensorProps;
-    @Surface.Rotation private int mOrientation;
-
-    public SidefpsView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        super.setWillNotDraw(false);
-        mContext = context;
-        mPointerText = new Paint(0 /* flags */);
-        mPointerText.setAntiAlias(true);
-        mPointerText.setColor(Color.WHITE);
-        mPointerText.setTextSize(POINTER_SIZE_PX);
-
-        mSensorRect = new RectF();
-        mSensorRectPaint = new Paint(0 /* flags */);
-        mSensorRectPaint.setAntiAlias(true);
-        mSensorRectPaint.setColor(Color.BLUE); // TODO: Fix Color
-        mSensorRectPaint.setStyle(Paint.Style.FILL);
-    }
-
-    void setSensorProperties(@NonNull FingerprintSensorPropertiesInternal properties) {
-        mSensorProps = properties;
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        super.onDraw(canvas);
-        canvas.drawRoundRect(mSensorRect, ROUND_RADIUS, ROUND_RADIUS, mSensorRectPaint);
-        int x, y;
-        if (mOrientation == Surface.ROTATION_90 || mOrientation == Surface.ROTATION_270) {
-            x = mSensorProps.sensorRadius + 10;
-            y = SFPS_AFFORDANCE_WIDTH / 2 + 15;
-        } else {
-            x = SFPS_AFFORDANCE_WIDTH / 2 - 10;
-            y = mSensorProps.sensorRadius + 30;
-        }
-        canvas.drawText(
-                ">",
-                x,
-                y,
-                mPointerText
-        );
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        mOrientation = mContext.getDisplay().getRotation();
-        if (mOrientation == Surface.ROTATION_90 || mOrientation == Surface.ROTATION_270) {
-            right = mSensorProps.sensorRadius * 2;
-            bottom = SFPS_AFFORDANCE_WIDTH;
-        } else {
-            right = SFPS_AFFORDANCE_WIDTH;
-            bottom = mSensorProps.sensorRadius * 2;
-        }
-
-        mSensorRect.set(
-                0,
-                0,
-                right,
-                bottom);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 2ab833b..2ca6ad1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -33,6 +33,7 @@
 import android.graphics.Point;
 import android.graphics.RectF;
 import android.hardware.biometrics.BiometricOverlayConstants;
+import android.hardware.biometrics.SensorLocationInternal;
 import android.hardware.display.DisplayManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -128,7 +129,7 @@
     @NonNull private final SystemClock mSystemClock;
     @NonNull private final UnlockedScreenOffAnimationController
             mUnlockedScreenOffAnimationController;
-    @VisibleForTesting @NonNull final BiometricOrientationEventListener mOrientationListener;
+    @VisibleForTesting @NonNull final BiometricDisplayListener mOrientationListener;
     // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
     // sensors, this, in addition to a lot of the code here, will be updated.
     @VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps;
@@ -555,14 +556,6 @@
         mHbmProvider = hbmProvider.orElse(null);
         screenLifecycle.addObserver(mScreenObserver);
         mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON;
-        mOrientationListener = new BiometricOrientationEventListener(
-                context,
-                () -> {
-                    onOrientationChanged();
-                    return Unit.INSTANCE;
-                },
-                displayManager,
-                mainHandler);
         mKeyguardBypassController = keyguardBypassController;
         mConfigurationController = configurationController;
         mSystemClock = systemClock;
@@ -571,6 +564,15 @@
         mSensorProps = findFirstUdfps();
         // At least one UDFPS sensor exists
         checkArgument(mSensorProps != null);
+        mOrientationListener = new BiometricDisplayListener(
+                context,
+                displayManager,
+                mainHandler,
+                new BiometricDisplayListener.SensorType.UnderDisplayFingerprint(mSensorProps),
+                () -> {
+                    onOrientationChanged();
+                    return Unit.INSTANCE;
+                });
 
         mCoreLayoutParams = new WindowManager.LayoutParams(
                 WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
@@ -640,10 +642,11 @@
         // on lockscreen and for the udfps light reveal animation on keyguard.
         // Keyguard is only shown in portrait mode for now, so this will need to
         // be updated if that ever changes.
-        return new RectF(mSensorProps.sensorLocationX - mSensorProps.sensorRadius,
-                mSensorProps.sensorLocationY - mSensorProps.sensorRadius,
-                mSensorProps.sensorLocationX + mSensorProps.sensorRadius,
-                mSensorProps.sensorLocationY + mSensorProps.sensorRadius);
+        final SensorLocationInternal location = mSensorProps.getLocation();
+        return new RectF(location.sensorLocationX - location.sensorRadius,
+                location.sensorLocationY - location.sensorRadius,
+                location.sensorLocationX + location.sensorRadius,
+                location.sensorLocationY + location.sensorRadius);
     }
 
     private void updateOverlay() {
@@ -681,10 +684,11 @@
         }
 
         // Default dimensions assume portrait mode.
-        mCoreLayoutParams.x = mSensorProps.sensorLocationX - mSensorProps.sensorRadius - paddingX;
-        mCoreLayoutParams.y = mSensorProps.sensorLocationY - mSensorProps.sensorRadius - paddingY;
-        mCoreLayoutParams.height = 2 * mSensorProps.sensorRadius + 2 * paddingX;
-        mCoreLayoutParams.width = 2 * mSensorProps.sensorRadius + 2 * paddingY;
+        final SensorLocationInternal location = mSensorProps.getLocation();
+        mCoreLayoutParams.x = location.sensorLocationX - location.sensorRadius - paddingX;
+        mCoreLayoutParams.y = location.sensorLocationY - location.sensorRadius - paddingY;
+        mCoreLayoutParams.height = 2 * location.sensorRadius + 2 * paddingX;
+        mCoreLayoutParams.width = 2 * location.sensorRadius + 2 * paddingY;
 
         Point p = new Point();
         // Gets the size based on the current rotation of the display.
@@ -699,9 +703,9 @@
                 } else {
                     Log.v(TAG, "rotate udfps location ROTATION_90");
                 }
-                mCoreLayoutParams.x = mSensorProps.sensorLocationY - mSensorProps.sensorRadius
+                mCoreLayoutParams.x = location.sensorLocationY - location.sensorRadius
                         - paddingX;
-                mCoreLayoutParams.y = p.y - mSensorProps.sensorLocationX - mSensorProps.sensorRadius
+                mCoreLayoutParams.y = p.y - location.sensorLocationX - location.sensorRadius
                         - paddingY;
                 break;
 
@@ -712,9 +716,9 @@
                 } else {
                     Log.v(TAG, "rotate udfps location ROTATION_270");
                 }
-                mCoreLayoutParams.x = p.x - mSensorProps.sensorLocationY - mSensorProps.sensorRadius
+                mCoreLayoutParams.x = p.x - location.sensorLocationY - location.sensorRadius
                         - paddingX;
-                mCoreLayoutParams.y = mSensorProps.sensorLocationX - mSensorProps.sensorRadius
+                mCoreLayoutParams.y = location.sensorLocationX - location.sensorRadius
                         - paddingY;
                 break;
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
index 7ccfb86..6cc8acf 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
@@ -21,6 +21,7 @@
 import android.annotation.Nullable;
 import android.graphics.Insets;
 import android.graphics.Rect;
+import android.hardware.biometrics.SensorLocationInternal;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.util.Log;
 import android.view.Surface;
@@ -93,7 +94,7 @@
         // Go through each of the children and do the custom measurement.
         int totalHeight = 0;
         final int numChildren = mView.getChildCount();
-        final int sensorDiameter = mSensorProps.sensorRadius * 2;
+        final int sensorDiameter = mSensorProps.getLocation().sensorRadius * 2;
         for (int i = 0; i < numChildren; i++) {
             final View child = mView.getChildAt(i);
             if (child.getId() == R.id.biometric_icon_frame) {
@@ -160,7 +161,7 @@
         final int horizontalSpacerWidth = calculateHorizontalSpacerWidthForLandscape(
                 mSensorProps, displayWidth, dialogMargin, horizontalInset);
 
-        final int sensorDiameter = mSensorProps.sensorRadius * 2;
+        final int sensorDiameter = mSensorProps.getLocation().sensorRadius * 2;
         final int remeasuredWidth = sensorDiameter + 2 * horizontalSpacerWidth;
 
         int remeasuredHeight = 0;
@@ -257,10 +258,10 @@
             @NonNull FingerprintSensorPropertiesInternal sensorProperties, int displayHeightPx,
             int textIndicatorHeightPx, int buttonBarHeightPx, int dialogMarginPx,
             int navbarBottomInsetPx) {
-
+        final SensorLocationInternal location = sensorProperties.getLocation();
         final int sensorDistanceFromBottom = displayHeightPx
-                - sensorProperties.sensorLocationY
-                - sensorProperties.sensorRadius;
+                - location.sensorLocationY
+                - location.sensorRadius;
 
         final int spacerHeight = sensorDistanceFromBottom
                 - textIndicatorHeightPx
@@ -318,10 +319,10 @@
     static int calculateHorizontalSpacerWidthForLandscape(
             @NonNull FingerprintSensorPropertiesInternal sensorProperties, int displayWidthPx,
             int dialogMarginPx, int navbarHorizontalInsetPx) {
-
+        final SensorLocationInternal location = sensorProperties.getLocation();
         final int sensorDistanceFromEdge = displayWidthPx
-                - sensorProperties.sensorLocationY
-                - sensorProperties.sensorRadius;
+                - location.sensorLocationY
+                - location.sensorRadius;
 
         final int horizontalPadding = sensorDistanceFromEdge
                 - dialogMarginPx
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
index d9edef4..c83006d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
@@ -63,7 +63,7 @@
 
     void updateSensorLocation(@NonNull FingerprintSensorPropertiesInternal sensorProps) {
         View fingerprintAccessibilityView = findViewById(R.id.udfps_enroll_accessibility_view);
-        final int sensorHeight = sensorProps.sensorRadius * 2;
+        final int sensorHeight = sensorProps.getLocation().sensorRadius * 2;
         final int sensorWidth = sensorHeight;
         ViewGroup.LayoutParams params = fingerprintAccessibilityView.getLayoutParams();
         params.width = sensorWidth;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index 15f77ff..30e5aed 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -25,6 +25,7 @@
 import android.graphics.Paint;
 import android.graphics.PointF;
 import android.graphics.RectF;
+import android.hardware.biometrics.SensorLocationInternal;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.os.Build;
 import android.os.UserHandle;
@@ -141,11 +142,12 @@
                 : mAnimationViewController.getPaddingX();
         int paddingY = mAnimationViewController == null ? 0
                 : mAnimationViewController.getPaddingY();
+        final SensorLocationInternal location = mSensorProps.getLocation();
         mSensorRect.set(
                 paddingX,
                 paddingY,
-                2 * mSensorProps.sensorRadius + paddingX,
-                2 * mSensorProps.sensorRadius + paddingY);
+                2 * location.sensorRadius + paddingX,
+                2 * location.sensorRadius + paddingY);
 
         if (mAnimationViewController != null) {
             mAnimationViewController.onSensorRectUpdated(new RectF(mSensorRect));
@@ -239,18 +241,20 @@
         if (mGhbmView != null && surface == null) {
             Log.e(TAG, "doIlluminate | surface must be non-null for GHBM");
         }
-        mHbmProvider.enableHbm(mHbmType, surface, () -> {
-            if (mGhbmView != null) {
-                mGhbmView.drawIlluminationDot(mSensorRect);
-            }
-            if (onIlluminatedRunnable != null) {
-                // No framework API can reliably tell when a frame reaches the panel. A timeout
-                // is the safest solution.
-                postDelayed(onIlluminatedRunnable, mOnIlluminatedDelayMs);
-            } else {
-                Log.w(TAG, "doIlluminate | onIlluminatedRunnable is null");
-            }
-        });
+        if (mHbmProvider != null) {
+            mHbmProvider.enableHbm(mHbmType, surface, () -> {
+                if (mGhbmView != null) {
+                    mGhbmView.drawIlluminationDot(mSensorRect);
+                }
+                if (onIlluminatedRunnable != null) {
+                    // No framework API can reliably tell when a frame reaches the panel. A timeout
+                    // is the safest solution.
+                    postDelayed(onIlluminatedRunnable, mOnIlluminatedDelayMs);
+                } else {
+                    Log.w(TAG, "doIlluminate | onIlluminatedRunnable is null");
+                }
+            });
+        }
     }
 
     @Override
@@ -263,6 +267,8 @@
             mGhbmView.setGhbmIlluminationListener(null);
             mGhbmView.setVisibility(View.INVISIBLE);
         }
-        mHbmProvider.disableHbm(null /* onHbmDisabled */);
+        if (mHbmProvider != null) {
+            mHbmProvider.disableHbm(null /* onHbmDisabled */);
+        }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java
index b6d1e42..619d48d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java
@@ -29,6 +29,7 @@
 
 import android.content.Context;
 import android.hardware.biometrics.ComponentInfoInternal;
+import android.hardware.biometrics.SensorLocationInternal;
 import android.hardware.biometrics.SensorProperties;
 import android.hardware.fingerprint.FingerprintSensorProperties;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -257,9 +258,10 @@
                 componentInfo,
                 FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
                 true /* resetLockoutRequiresHardwareAuthToken */,
-                540 /* sensorLocationX */,
-                1600 /* sensorLocationY */,
-                100 /* sensorRadius */);
+                List.of(new SensorLocationInternal("" /* displayId */,
+                        540 /* sensorLocationX */,
+                        1600 /* sensorLocationY */,
+                        100 /* sensorRadius */)));
     }
 
     public class TestableView extends AuthBiometricFaceToFingerprintView {
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 977b05c..01e60e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.biometrics
 
+import android.graphics.Rect
 import android.hardware.biometrics.SensorProperties
 import android.hardware.display.DisplayManager
 import android.hardware.display.DisplayManagerGlobal
@@ -30,8 +31,12 @@
 import android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS
 import android.view.DisplayInfo
 import android.view.LayoutInflater
+import android.view.View
+import android.view.WindowInsets
 import android.view.WindowManager
+import android.view.WindowMetrics
 import androidx.test.filters.SmallTest
+import com.airbnb.lottie.LottieAnimationView
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.concurrency.FakeExecutor
@@ -42,9 +47,13 @@
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers.eq
+import org.mockito.Captor
 import org.mockito.Mock
 import org.mockito.Mockito.`when`
 import org.mockito.Mockito.any
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
 import org.mockito.junit.MockitoJUnit
 
@@ -66,11 +75,13 @@
     @Mock
     lateinit var windowManager: WindowManager
     @Mock
-    lateinit var sidefpsView: SidefpsView
+    lateinit var sidefpsView: View
     @Mock
     lateinit var displayManager: DisplayManager
     @Mock
     lateinit var handler: Handler
+    @Captor
+    lateinit var overlayCaptor: ArgumentCaptor<View>
 
     private val executor = FakeExecutor(FakeSystemClock())
     private lateinit var overlayController: ISidefpsController
@@ -79,6 +90,8 @@
     @Before
     fun setup() {
         `when`(layoutInflater.inflate(R.layout.sidefps_view, null, false)).thenReturn(sidefpsView)
+        `when`(sidefpsView.findViewById<LottieAnimationView>(eq(R.id.sidefps_animation)))
+            .thenReturn(mock(LottieAnimationView::class.java))
         `when`(fingerprintManager.sensorPropertiesInternal).thenReturn(
                 listOf(
                         FingerprintSensorPropertiesInternal(
@@ -99,6 +112,9 @@
                         DEFAULT_DISPLAY_ADJUSTMENTS
                 )
         )
+        `when`(windowManager.maximumWindowMetrics).thenReturn(
+            WindowMetrics(Rect(0, 0, 800, 800), WindowInsets.CONSUMED)
+        )
 
         sideFpsController = SidefpsController(
                 mContext, layoutInflater, fingerprintManager, windowManager, executor,
@@ -121,4 +137,44 @@
         executor.runAllReady()
         verify(displayManager).unregisterDisplayListener(any())
     }
+
+    @Test
+    fun testShowsAndHides() {
+        overlayController.show()
+        executor.runAllReady()
+
+        verify(windowManager).addView(overlayCaptor.capture(), any())
+
+        reset(windowManager)
+        overlayController.hide()
+        executor.runAllReady()
+
+        verify(windowManager, never()).addView(any(), any())
+        verify(windowManager).removeView(eq(overlayCaptor.value))
+    }
+
+    @Test
+    fun testShowsOnce() {
+        repeat(5) {
+            overlayController.show()
+            executor.runAllReady()
+        }
+
+        verify(windowManager).addView(any(), any())
+        verify(windowManager, never()).removeView(any())
+    }
+
+    @Test
+    fun testHidesOnce() {
+        overlayController.show()
+        executor.runAllReady()
+
+        repeat(5) {
+            overlayController.hide()
+            executor.runAllReady()
+        }
+
+        verify(windowManager).addView(any(), any())
+        verify(windowManager).removeView(any())
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java
index 88b4039..27755ede 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertEquals;
 
 import android.hardware.biometrics.ComponentInfoInternal;
+import android.hardware.biometrics.SensorLocationInternal;
 import android.hardware.biometrics.SensorProperties;
 import android.hardware.fingerprint.FingerprintSensorProperties;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -61,8 +62,9 @@
                 0 /* sensorId */, SensorProperties.STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */,
                 componentInfo,
                 FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
-                true /* resetLockoutRequiresHardwareAuthToken */, sensorLocationX, sensorLocationY,
-                sensorRadius);
+                true /* resetLockoutRequiresHardwareAuthToken */,
+                List.of(new SensorLocationInternal("" /* displayId */,
+                        sensorLocationX, sensorLocationY, sensorRadius)));
 
         assertEquals(970,
                 UdfpsDialogMeasureAdapter.calculateBottomSpacerHeightForPortrait(
@@ -125,8 +127,9 @@
                 0 /* sensorId */, SensorProperties.STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */,
                 componentInfo,
                 FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
-                true /* resetLockoutRequiresHardwareAuthToken */, sensorLocationX, sensorLocationY,
-                sensorRadius);
+                true /* resetLockoutRequiresHardwareAuthToken */,
+                List.of(new SensorLocationInternal("" /* displayId */,
+                        sensorLocationX, sensorLocationY, sensorRadius)));
 
         assertEquals(1205,
                 UdfpsDialogMeasureAdapter.calculateHorizontalSpacerWidthForLandscape(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
index d279bbb..3e9fbcf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
@@ -25,6 +25,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.PointF;
+import android.hardware.biometrics.SensorLocationInternal;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.os.Vibrator;
 import android.testing.AndroidTestingRunner;
@@ -132,7 +133,8 @@
                         /* component info */ new ArrayList<>(),
                         /* sensorType */ 3,
                         /* resetLockoutRequiresHwToken */ false,
-                        (int) udfpsLocation.x, (int) udfpsLocation.y, radius);
+                        List.of(new SensorLocationInternal("" /* displayId */,
+                                (int) udfpsLocation.x, (int) udfpsLocation.y, radius)));
         when(mAuthController.getUdfpsSensorLocation()).thenReturn(udfpsLocation);
         when(mAuthController.getUdfpsProps()).thenReturn(List.of(fpProps));
 
@@ -165,7 +167,8 @@
                         /* component info */ new ArrayList<>(),
                         /* sensorType */ 3,
                         /* resetLockoutRequiresHwToken */ false,
-                        (int) udfpsLocation.x, (int) udfpsLocation.y, radius);
+                        List.of(new SensorLocationInternal("" /* displayId */,
+                                (int) udfpsLocation.x, (int) udfpsLocation.y, radius)));
         when(mAuthController.getUdfpsSensorLocation()).thenReturn(udfpsLocation);
         when(mAuthController.getUdfpsProps()).thenReturn(List.of(fpProps));
 
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index baf5af5..2b30943 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -5221,7 +5221,9 @@
             sr.setProcess(null, null, 0, null);
             sr.isolatedProc = null;
             sr.executeNesting = 0;
-            sr.forceClearTracker();
+            synchronized (mAm.mProcessStats.mLock) {
+                sr.forceClearTracker();
+            }
             if (mDestroyingServices.remove(sr)) {
                 if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "killServices remove destroying " + sr);
             }
@@ -5371,7 +5373,9 @@
             i--;
             ServiceRecord sr = mDestroyingServices.get(i);
             if (sr.app == app) {
-                sr.forceClearTracker();
+                synchronized (mAm.mProcessStats.mLock) {
+                    sr.forceClearTracker();
+                }
                 mDestroyingServices.remove(i);
                 if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "killServices remove destroying " + sr);
             }
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index c915399..9c8ccd9 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -47,6 +47,7 @@
 import android.hardware.biometrics.ITestSession;
 import android.hardware.biometrics.ITestSessionCallback;
 import android.hardware.biometrics.PromptInfo;
+import android.hardware.biometrics.SensorLocationInternal;
 import android.hardware.biometrics.SensorPropertiesInternal;
 import android.hardware.face.FaceSensorProperties;
 import android.hardware.face.FaceSensorPropertiesInternal;
@@ -60,7 +61,6 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Slog;
@@ -82,7 +82,6 @@
     private static final String SETTING_HIDL_DISABLED =
             "com.android.server.biometrics.AuthService.hidlDisabled";
     private static final int DEFAULT_HIDL_DISABLED = 0;
-    private static final String SYSPROP_FIRST_API_LEVEL = "ro.board.first_api_level";
 
     private final Injector mInjector;
 
@@ -626,19 +625,9 @@
         final SensorConfig[] hidlConfigs;
         if (!mInjector.isHidlDisabled(getContext())) {
             final String[] configStrings = mInjector.getConfiguration(getContext());
-            final boolean isRVendor = SystemProperties.getInt(SYSPROP_FIRST_API_LEVEL, 0)
-                    == Build.VERSION_CODES.R;
-            if (configStrings.length == 0 && isRVendor) {
-                // For backwards compatibility with R where biometrics could work without being
-                // configured in config_biometric_sensors. In the absence of a vendor provided
-                // configuration, we assume the weakest biometric strength (i.e. convenience).
-                Slog.w(TAG, "Found R vendor partition without config_biometric_sensors");
-                hidlConfigs = generateRSdkCompatibleConfiguration();
-            } else {
-                hidlConfigs = new SensorConfig[configStrings.length];
-                for (int i = 0; i < configStrings.length; ++i) {
-                    hidlConfigs[i] = new SensorConfig(configStrings[i]);
-                }
+            hidlConfigs = new SensorConfig[configStrings.length];
+            for (int i = 0; i < configStrings.length; ++i) {
+                hidlConfigs[i] = new SensorConfig(configStrings[i]);
             }
         } else {
             hidlConfigs = null;
@@ -651,38 +640,6 @@
     }
 
     /**
-     * Generates SensorConfig[] with an entry that corresponds to the biometric feature declared on
-     * the device. Returns an empty SensorConfig[] if no biometric features are declared. If both
-     * fingerprint and face are declared, only the fingerprint config will be generated. Biometrics
-     * are assumed to be of the weakest strength class, i.e. convenience.
-     */
-    private @NonNull SensorConfig[] generateRSdkCompatibleConfiguration() {
-        final boolean hasFp = getContext().getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_FINGERPRINT);
-        final boolean hasFace = getContext().getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_FACE);
-        if (hasFp || hasFace) {
-            final String id = "0";
-            final String strength = String.valueOf(Authenticators.BIOMETRIC_CONVENIENCE);
-            final String modality;
-            final String logStr;
-            if (hasFp) {
-                modality = String.valueOf(BiometricAuthenticator.TYPE_FINGERPRINT);
-                logStr = "fingerprint";
-            } else {
-                modality = String.valueOf(BiometricAuthenticator.TYPE_FACE);
-                logStr = "face";
-            }
-            final String config = String.join(":" /* delimiter */, id, modality, strength);
-            Slog.d(TAG, "Generated config_biometric_sensors for " + logStr + ": " + config);
-            return new SensorConfig[]{new SensorConfig(config)};
-        } else {
-            Slog.d(TAG, "Couldn't generate config_biometric_sensors. No biometrics found.");
-            return new SensorConfig[0];
-        }
-    }
-
-    /**
      * Registers HIDL and AIDL authenticators for all of the available modalities.
      *
      * @param hidlSensors Array of {@link SensorConfig} configuration for all of the HIDL sensors
@@ -810,8 +767,9 @@
         if (isUdfps && udfpsProps.length == 3) {
             return new FingerprintSensorPropertiesInternal(sensorId,
                     Utils.authenticatorStrengthToPropertyStrength(strength), maxEnrollmentsPerUser,
-                    componentInfo, sensorType, resetLockoutRequiresHardwareAuthToken, udfpsProps[0],
-                    udfpsProps[1], udfpsProps[2]);
+                    componentInfo, sensorType, resetLockoutRequiresHardwareAuthToken,
+                    List.of(new SensorLocationInternal("" /* display */,
+                            udfpsProps[0], udfpsProps[1], udfpsProps[2])));
         } else {
             return new FingerprintSensorPropertiesInternal(sensorId,
                     Utils.authenticatorStrengthToPropertyStrength(strength), maxEnrollmentsPerUser,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index db7f3d7..8a11e8d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -27,6 +27,7 @@
 import android.hardware.biometrics.common.ICancellationSignal;
 import android.hardware.biometrics.fingerprint.ISession;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.ISidefpsController;
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -67,6 +68,7 @@
             int sensorId, boolean isStrongBiometric, int statsClient,
             @Nullable TaskStackListener taskStackListener, @NonNull LockoutCache lockoutCache,
             @Nullable IUdfpsOverlayController udfpsOverlayController,
+            @Nullable ISidefpsController sidefpsController,
             boolean allowBackgroundAuthentication,
             @NonNull FingerprintSensorPropertiesInternal sensorProps) {
         super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted, owner,
@@ -76,7 +78,7 @@
                 false /* isKeyguardBypassEnabled */);
         setRequestId(requestId);
         mLockoutCache = lockoutCache;
-        mSensorOverlays = new SensorOverlays(udfpsOverlayController, null /* sideFpsController */);
+        mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
         mSensorProps = sensorProps;
         mALSProbeCallback = createALSCallback(false /* startWithClient */);
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index ca83dda..0defc3f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -25,10 +25,12 @@
 import android.app.TaskStackListener;
 import android.content.Context;
 import android.content.pm.UserInfo;
+import android.content.res.TypedArray;
 import android.hardware.biometrics.ComponentInfoInternal;
 import android.hardware.biometrics.IInvalidationCallback;
 import android.hardware.biometrics.ITestSession;
 import android.hardware.biometrics.ITestSessionCallback;
+import android.hardware.biometrics.SensorLocationInternal;
 import android.hardware.biometrics.common.ComponentInfo;
 import android.hardware.biometrics.fingerprint.IFingerprint;
 import android.hardware.biometrics.fingerprint.SensorProps;
@@ -145,6 +147,8 @@
         mActivityTaskManager = ActivityTaskManager.getInstance();
         mTaskStackListener = new BiometricTaskStackListener();
 
+        final List<SensorLocationInternal> workaroundLocations = getWorkaroundSensorProps(context);
+
         for (SensorProps prop : props) {
             final int sensorId = prop.commonProps.sensorId;
 
@@ -164,9 +168,12 @@
                             componentInfo,
                             prop.sensorType,
                             true /* resetLockoutRequiresHardwareAuthToken */,
-                            prop.sensorLocations[0].sensorLocationX,
-                            prop.sensorLocations[0].sensorLocationY,
-                            prop.sensorLocations[0].sensorRadius);
+                            !workaroundLocations.isEmpty() ? workaroundLocations :
+                                    List.of(new SensorLocationInternal(
+                                        "" /* displayId */,
+                                        prop.sensorLocations[0].sensorLocationX,
+                                        prop.sensorLocations[0].sensorLocationY,
+                                        prop.sensorLocations[0].sensorRadius)));
             final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler,
                     internalProp, lockoutResetDispatcher, gestureAvailabilityDispatcher);
 
@@ -403,7 +410,7 @@
                     userId, operationId, restricted, opPackageName, cookie,
                     false /* requireConfirmation */, sensorId, isStrongBiometric, statsClient,
                     mTaskStackListener, mSensors.get(sensorId).getLockoutCache(),
-                    mUdfpsOverlayController, allowBackgroundAuthentication,
+                    mUdfpsOverlayController, mSidefpsController, allowBackgroundAuthentication,
                     mSensors.get(sensorId).getSensorProperties());
             scheduleForSensor(sensorId, client, mFingerprintStateCallback);
         });
@@ -647,4 +654,45 @@
     void setTestHalEnabled(boolean enabled) {
         mTestHalEnabled = enabled;
     }
+
+    // TODO(b/174868353): workaround for gaps in HAL interface (remove and get directly from HAL)
+    // reads values via an overlay instead of querying the HAL
+    @NonNull
+    private List<SensorLocationInternal> getWorkaroundSensorProps(@NonNull Context context) {
+        final List<SensorLocationInternal> sensorLocations = new ArrayList<>();
+
+        final TypedArray sfpsProps = context.getResources().obtainTypedArray(
+                com.android.internal.R.array.config_sfps_sensor_props);
+        for (int i = 0; i < sfpsProps.length(); i++) {
+            final int id = sfpsProps.getResourceId(i, -1);
+            if (id > 0) {
+                final SensorLocationInternal location = parseSensorLocation(
+                        context.getResources().obtainTypedArray(id));
+                if (location != null) {
+                    sensorLocations.add(location);
+                }
+            }
+        }
+        sfpsProps.recycle();
+
+        return sensorLocations;
+    }
+
+    @Nullable
+    private SensorLocationInternal parseSensorLocation(@Nullable TypedArray array) {
+        if (array == null) {
+            return null;
+        }
+
+        try {
+            return new SensorLocationInternal(
+                    array.getString(0),
+                    array.getInt(1, 0),
+                    array.getInt(2, 0),
+                    array.getInt(3, 0));
+        } catch (Exception e) {
+            Slog.w(getTag(), "malformed sensor location", e);
+        }
+        return null;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index d2882aa4..5f2f4cf 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -629,7 +629,8 @@
                     mContext, mLazyDaemon, token, requestId, listener, userId, operationId,
                     restricted, opPackageName, cookie, false /* requireConfirmation */,
                     mSensorProperties.sensorId, isStrongBiometric, statsClient,
-                    mTaskStackListener, mLockoutTracker, mUdfpsOverlayController,
+                    mTaskStackListener, mLockoutTracker,
+                    mUdfpsOverlayController, mSidefpsController,
                     allowBackgroundAuthentication, mSensorProperties);
             mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback);
         });
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
index 79ad8e1..dd68b4d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
@@ -426,8 +426,7 @@
         mSensorProperties = new FingerprintSensorPropertiesInternal(sensorProps.sensorId,
                 sensorProps.sensorStrength, maxTemplatesAllowed, sensorProps.componentInfo,
                 FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
-                resetLockoutRequiresHardwareAuthToken, sensorProps.sensorLocationX,
-                sensorProps.sensorLocationY, sensorProps.sensorRadius);
+                resetLockoutRequiresHardwareAuthToken, sensorProps.getAllLocations());
         mMockHalResultController = controller;
         mUserHasTrust = new SparseBooleanArray();
         mTrustManager = context.getSystemService(TrustManager.class);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
index afb4d16..3058e25 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
@@ -26,6 +26,7 @@
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.ISidefpsController;
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -67,6 +68,7 @@
             @NonNull TaskStackListener taskStackListener,
             @NonNull LockoutFrameworkImpl lockoutTracker,
             @Nullable IUdfpsOverlayController udfpsOverlayController,
+            @Nullable ISidefpsController sidefpsController,
             boolean allowBackgroundAuthentication,
             @NonNull FingerprintSensorPropertiesInternal sensorProps) {
         super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
@@ -76,7 +78,7 @@
                 false /* isKeyguardBypassEnabled */);
         setRequestId(requestId);
         mLockoutFrameworkImpl = lockoutTracker;
-        mSensorOverlays = new SensorOverlays(udfpsOverlayController, null /* sideFpsController */);
+        mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
         mSensorProps = sensorProps;
         mALSProbeCallback = createALSCallback(false /* startWithClient */);
     }
diff --git a/services/core/java/com/android/server/pm/InstallParams.java b/services/core/java/com/android/server/pm/InstallParams.java
index edb76a6..bc7d95e 100644
--- a/services/core/java/com/android/server/pm/InstallParams.java
+++ b/services/core/java/com/android/server/pm/InstallParams.java
@@ -28,8 +28,8 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
 import static android.content.pm.PackageManager.INSTALL_FAILED_SESSION_INVALID;
-import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_TEST_ONLY;
+import static android.content.pm.PackageManager.INSTALL_FAILED_UID_CHANGED;
 import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
 import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE;
 import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP;
@@ -1204,19 +1204,17 @@
                     }
 
                     // Check for shared user id changes
-                    String invalidPackageName = null;
                     if (!Objects.equals(oldPackage.getSharedUserId(),
                             parsedPackage.getSharedUserId())
                             // Don't mark as invalid if the app is trying to
                             // leave a sharedUserId
                             && parsedPackage.getSharedUserId() != null) {
-                        invalidPackageName = parsedPackage.getPackageName();
-                    }
-
-                    if (invalidPackageName != null) {
-                        throw new PrepareFailure(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
-                                "Package " + invalidPackageName + " tried to change user "
-                                        + oldPackage.getSharedUserId());
+                        throw new PrepareFailure(INSTALL_FAILED_UID_CHANGED,
+                                "Package " + parsedPackage.getPackageName()
+                                        + " shared user changed from "
+                                        + (oldPackage.getSharedUserId() != null
+                                        ? oldPackage.getSharedUserId() : "<nothing>")
+                                        + " to " + parsedPackage.getSharedUserId());
                     }
 
                     // In case of rollback, remember per-user/profile install state
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 95e2d0a..2c649d2 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -20,7 +20,7 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
-import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
+import static android.content.pm.PackageManager.INSTALL_FAILED_UID_CHANGED;
 import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
 import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
 import static android.content.pm.PackageManager.UNINSTALL_REASON_USER_TYPE;
@@ -1040,14 +1040,15 @@
             @Nullable Set<String> mimeGroupNames, @NonNull UUID domainSetId)
                     throws PackageManagerException {
         final String pkgName = pkgSetting.name;
-        if (pkgSetting.sharedUser != sharedUser) {
+        if (!Objects.equals(pkgSetting.sharedUser, sharedUser) && sharedUser != null) {
             PackageManagerService.reportSettingsProblem(Log.WARN,
                     "Package " + pkgName + " shared user changed from "
                     + (pkgSetting.sharedUser != null ? pkgSetting.sharedUser.name : "<nothing>")
-                    + " to " + (sharedUser != null ? sharedUser.name : "<nothing>"));
-            throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
+                    + " to " + sharedUser.name);
+            throw new PackageManagerException(INSTALL_FAILED_UID_CHANGED,
                     "Updating application package " + pkgName + " failed");
         }
+        pkgSetting.sharedUser = sharedUser;
 
         if (!pkgSetting.getPath().equals(codePath)) {
             final boolean isSystem = pkgSetting.isSystem();
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index a124f76..946fa44 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2607,6 +2607,11 @@
         return parent != null ? parent.getOrganizedTaskFragment() : null;
     }
 
+    boolean isEmbedded() {
+        final TaskFragment parent = getTaskFragment();
+        return parent != null && parent.isEmbedded();
+    }
+
     @Override
     @Nullable
     TaskDisplayArea getDisplayArea() {
@@ -2698,7 +2703,9 @@
     boolean isResizeable() {
         return mAtmService.mForceResizableActivities
                 || ActivityInfo.isResizeableMode(info.resizeMode)
-                || info.supportsPictureInPicture();
+                || info.supportsPictureInPicture()
+                // If the activity can be embedded, it should inherit the bounds of task fragment.
+                || isEmbedded();
     }
 
     /** @return whether this activity is non-resizeable but is forced to be resizable. */
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index c7ca180..4a1a922 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1095,29 +1095,27 @@
                 return rootTask;
             }
         } else if (candidateTask != null) {
-            final Task rootTask = candidateTask;
             final int position = onTop ? POSITION_TOP : POSITION_BOTTOM;
             final Task launchRootTask = getLaunchRootTask(windowingMode, activityType, options,
                     sourceTask, launchFlags);
-
             if (launchRootTask != null) {
-                if (rootTask.getParent() == null) {
-                    launchRootTask.addChild(rootTask, position);
-                } else if (rootTask.getParent() != launchRootTask) {
-                    rootTask.reparent(launchRootTask, position);
+                if (candidateTask.getParent() == null) {
+                    launchRootTask.addChild(candidateTask, position);
+                } else if (candidateTask.getParent() != launchRootTask) {
+                    candidateTask.reparent(launchRootTask, position);
                 }
-            } else if (rootTask.getDisplayArea() != this || !rootTask.isRootTask()) {
-                if (rootTask.getParent() == null) {
-                    addChild(rootTask, position);
+            } else if (candidateTask.getDisplayArea() != this || !candidateTask.isRootTask()) {
+                if (candidateTask.getParent() == null) {
+                    addChild(candidateTask, position);
                 } else {
-                    rootTask.reparent(this, onTop);
+                    candidateTask.reparent(this, onTop);
                 }
             }
             // Update windowing mode if necessary, e.g. moving a pinned task to fullscreen.
             if (candidateTask.getWindowingMode() != windowingMode) {
                 candidateTask.setWindowingMode(windowingMode);
             }
-            return rootTask;
+            return candidateTask.getRootTask();
         }
         return new Task.Builder(mAtmService)
                 .setWindowingMode(windowingMode)
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 31bf49d..c78e162 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -355,6 +355,10 @@
             throw new IllegalArgumentException("reparent: can't reparent to null " + this);
         }
 
+        if (newParent == this) {
+            throw new IllegalArgumentException("Can not reparent to itself " + this);
+        }
+
         final WindowContainer oldParent = mParent;
         if (mParent == newParent) {
             throw new IllegalArgumentException("WC=" + this + " already child of " + mParent);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
index b51918e..8b7c90d 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
@@ -19,10 +19,13 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.hardware.biometrics.common.CommonProps;
 import android.hardware.biometrics.fingerprint.IFingerprint;
 import android.hardware.biometrics.fingerprint.SensorLocation;
@@ -56,6 +59,8 @@
     @Mock
     private Context mContext;
     @Mock
+    private Resources mResources;
+    @Mock
     private UserManager mUserManager;
     @Mock
     private GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
@@ -74,19 +79,21 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mResources.obtainTypedArray(anyInt())).thenReturn(mock(TypedArray.class));
         when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
         when(mUserManager.getAliveUsers()).thenReturn(new ArrayList<>());
 
         final SensorProps sensor1 = new SensorProps();
         sensor1.commonProps = new CommonProps();
         sensor1.commonProps.sensorId = 0;
-        sensor1.sensorLocations = new SensorLocation[] {new SensorLocation()};
+        sensor1.sensorLocations = new SensorLocation[]{new SensorLocation()};
         final SensorProps sensor2 = new SensorProps();
         sensor2.commonProps = new CommonProps();
         sensor2.commonProps.sensorId = 1;
-        sensor2.sensorLocations = new SensorLocation[] {new SensorLocation()};
+        sensor2.sensorLocations = new SensorLocation[]{new SensorLocation()};
 
-        mSensorProps = new SensorProps[] {sensor1, sensor2};
+        mSensorProps = new SensorProps[]{sensor1, sensor2};
 
         mLockoutResetDispatcher = new LockoutResetDispatcher(mContext);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 9ad479a..9a14e7d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2724,9 +2724,11 @@
                 mAtm, null /* fragmentToken */, false /* createdByOrganizer */);
         fragmentSetup.accept(taskFragment2, new Rect(width / 2, 0, width, height));
         task.addChild(taskFragment2, POSITION_TOP);
-        final ActivityRecord activity2 = new ActivityBuilder(mAtm).build();
+        final ActivityRecord activity2 = new ActivityBuilder(mAtm)
+                .setResizeMode(ActivityInfo.RESIZE_MODE_UNRESIZEABLE).build();
         activity2.mVisibleRequested = true;
         taskFragment2.addChild(activity2);
+        assertTrue(activity2.isResizeable());
         activity1.reparent(taskFragment1, POSITION_TOP);
 
         assertEquals(task, activity1.mStartingData.mAssociatedTask);
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index 2753ae0..7981827 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -17,7 +17,6 @@
 @file:JvmName("CommonAssertions")
 package com.android.server.wm.flicker
 
-import android.view.Surface
 import com.android.server.wm.flicker.helpers.WindowUtils
 import com.android.server.wm.traces.common.FlickerComponentName
 
@@ -87,33 +86,35 @@
     }
 }
 
-fun FlickerTestParameter.navBarLayerRotatesAndScales() {
+@JvmOverloads
+fun FlickerTestParameter.navBarLayerRotatesAndScales(
+    beginRotation: Int,
+    endRotation: Int = beginRotation
+) {
+    val startingPos = WindowUtils.getNavigationBarPosition(beginRotation)
+    val endingPos = WindowUtils.getNavigationBarPosition(endRotation)
+
     assertLayersStart {
-        val rotation = this.entry.displays.sortedBy { it.id }
-            .firstOrNull()?.transform?.getRotation() ?: Surface.ROTATION_0
-        this.visibleRegion(FlickerComponentName.NAV_BAR)
-                .coversExactly(WindowUtils.getNavigationBarPosition(rotation))
+        this.visibleRegion(FlickerComponentName.NAV_BAR).coversExactly(startingPos)
     }
     assertLayersEnd {
-        val rotation = this.entry.displays.sortedBy { it.id }
-            .firstOrNull()?.transform?.getRotation() ?: Surface.ROTATION_0
-        this.visibleRegion(FlickerComponentName.NAV_BAR)
-                .coversExactly(WindowUtils.getNavigationBarPosition(rotation))
+        this.visibleRegion(FlickerComponentName.NAV_BAR).coversExactly(endingPos)
     }
 }
 
-fun FlickerTestParameter.statusBarLayerRotatesScales() {
+@JvmOverloads
+fun FlickerTestParameter.statusBarLayerRotatesScales(
+    beginRotation: Int,
+    endRotation: Int = beginRotation
+) {
+    val startingPos = WindowUtils.getStatusBarPosition(beginRotation)
+    val endingPos = WindowUtils.getStatusBarPosition(endRotation)
+
     assertLayersStart {
-        val rotation = this.entry.displays.sortedBy { it.id }
-            .firstOrNull()?.transform?.getRotation() ?: Surface.ROTATION_0
-        this.visibleRegion(FlickerComponentName.STATUS_BAR)
-            .coversExactly(WindowUtils.getStatusBarPosition(rotation))
+        this.visibleRegion(FlickerComponentName.STATUS_BAR).coversExactly(startingPos)
     }
     assertLayersEnd {
-        val rotation = this.entry.displays.sortedBy { it.id }
-            .firstOrNull()?.transform?.getRotation() ?: Surface.ROTATION_0
-        this.visibleRegion(FlickerComponentName.STATUS_BAR)
-            .coversExactly(WindowUtils.getStatusBarPosition(rotation))
+        this.visibleRegion(FlickerComponentName.STATUS_BAR).coversExactly(endingPos)
     }
 }
 
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index 3928476..3c610af 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -18,6 +18,7 @@
 
 import android.app.Instrumentation
 import android.platform.test.annotations.Presubmit
+import android.view.Surface
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerTestParameter
@@ -94,11 +95,15 @@
 
     @Presubmit
     @Test
-    open fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
+    open fun navBarLayerRotatesAndScales() {
+        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+    }
 
     @Presubmit
     @Test
-    open fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    open fun statusBarLayerRotatesScales() {
+        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+    }
 
     @Presubmit
     @Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
index 7b2bae5..3550536 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -152,13 +152,13 @@
     @Presubmit
     @Test
     fun navBarLayerRotatesAndScales() {
-        testSpec.navBarLayerRotatesAndScales()
+        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
     }
 
     @Presubmit
     @Test
     fun statusBarLayerRotatesScales() {
-        testSpec.statusBarLayerRotatesScales()
+        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation)
     }
 
     @Presubmit
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index a812b76..0352185 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -151,13 +151,13 @@
     @Presubmit
     @Test
     fun navBarLayerRotatesAndScales() {
-        testSpec.navBarLayerRotatesAndScales()
+        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
     }
 
     @Presubmit
     @Test
     fun statusBarLayerRotatesScales() {
-        testSpec.statusBarLayerRotatesScales()
+        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
     }
 
     @Presubmit
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index 586eb2b..11660df 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -32,6 +32,7 @@
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.entireScreenCovered
+import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.common.FlickerComponentName
@@ -120,20 +121,20 @@
     @Test
     fun navBarLayerRotatesAndScales() {
         Assume.assumeFalse(testSpec.isRotated)
-        testSpec.navBarLayerRotatesAndScales()
+        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
     }
 
     @FlakyTest
     @Test
     fun navBarLayerRotatesAndScales_Flaky() {
         Assume.assumeTrue(testSpec.isRotated)
-        testSpec.navBarLayerRotatesAndScales()
+        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
     }
 
     @Presubmit
     @Test
     fun statusBarLayerRotatesScales() {
-        testSpec.statusBarLayerRotatesScales()
+        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation)
     }
 
     @Presubmit
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index b589969..bb2ffbc 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -34,6 +34,7 @@
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.entireScreenCovered
+import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.common.FlickerComponentName
@@ -142,11 +143,14 @@
 
     @Presubmit
     @Test
-    fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
+    fun navBarLayerRotatesAndScales() =
+        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
 
     @Presubmit
     @Test
-    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    fun statusBarLayerRotatesScales() {
+        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+    }
 
     @Presubmit
     @Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index ab641a7..665204b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -33,6 +33,7 @@
 import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsVisible
@@ -123,11 +124,15 @@
 
     @Presubmit
     @Test
-    fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
+    fun navBarLayerRotatesAndScales() {
+        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
+    }
 
     @Presubmit
     @Test
-    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    fun statusBarLayerRotatesScales() {
+        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation)
+    }
 
     @Presubmit
     @Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index f82ab22..d31c9bc 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -38,6 +38,7 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsVisible
@@ -206,11 +207,15 @@
 
     @Presubmit
     @Test
-    fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
+    fun navBarLayerRotatesAndScales() {
+        testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation)
+    }
 
     @Presubmit
     @Test
-    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    fun statusBarLayerRotatesScales() {
+        testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation)
+    }
 
     @Presubmit
     @Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
index 785b8f4..b842681 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
@@ -172,7 +172,7 @@
     override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
             super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
-    @Postsubmit
+    @FlakyTest
     @Test
     override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
 
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
index ee6e87d..e6ce88e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
@@ -18,11 +18,13 @@
 
 import android.app.Instrumentation
 import android.platform.test.annotations.Presubmit
+import android.view.Surface
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.LAUNCHER_COMPONENT
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.helpers.SimpleAppHelper
 import com.android.server.wm.flicker.helpers.StandardAppHelper
@@ -93,7 +95,9 @@
      */
     @Presubmit
     @Test
-    open fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
+    open fun navBarLayerRotatesAndScales() {
+        testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation)
+    }
 
     /**
      * Checks that the status bar window is visible during the whole transition
@@ -118,7 +122,9 @@
      */
     @Presubmit
     @Test
-    open fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    open fun statusBarLayerRotatesScales() {
+        testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation)
+    }
 
     /**
      * Checks that all windows that are visible on the trace, are visible for at least 2
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
index 6da01fa..091180f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
@@ -297,7 +297,8 @@
      */
     @Postsubmit
     @Test
-    fun navbarIsAlwaysInRightPosition() = testSpec.navBarLayerRotatesAndScales()
+    fun navbarIsAlwaysInRightPosition() =
+            testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
 
     /**
      * Checks that the status bar window is visible throughout the entire transition.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
index a620e87..ed6f5c6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
@@ -314,7 +314,8 @@
      */
     @Postsubmit
     @Test
-    fun navbarIsAlwaysInRightPosition() = testSpec.navBarLayerRotatesAndScales()
+    fun navbarIsAlwaysInRightPosition() =
+            testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
 
     /**
      * Checks that the status bar window is visible throughout the entire transition.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
index 92e0391..edf4408 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
@@ -306,7 +306,7 @@
     @Presubmit
     @Test
     fun navbarIsAlwaysInRightPosition() =
-            testSpec.navBarLayerRotatesAndScales()
+            testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
 
     /**
      * Checks that the status bar window is visible throughout the entire transition.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 1ee8822..6f9171b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -24,7 +24,9 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsVisible
@@ -90,11 +92,16 @@
 
     @Presubmit
     @Test
-    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    fun statusBarLayerRotatesScales() {
+        testSpec.statusBarLayerRotatesScales(
+            testSpec.config.startRotation, testSpec.config.endRotation)
+    }
 
     @FlakyTest
     @Test
-    override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+    override fun navBarLayerRotatesAndScales() {
+        super.navBarLayerRotatesAndScales()
+    }
 
     @FlakyTest
     @Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index 7f0ac12..6966846 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -75,7 +75,10 @@
 
     @Presubmit
     @Test
-    open fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
+    open fun navBarLayerRotatesAndScales() {
+        testSpec.navBarLayerRotatesAndScales(
+            testSpec.config.startRotation, testSpec.config.endRotation)
+    }
 
     @Presubmit
     @Test