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