Merge changes from topic "sfps-updates" into sc-v2-dev
* changes: Update side fsp sensor position via overlay. Update side fingerprint sensor overlay assets.
This commit is contained in:
@@ -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;
|
||||
@@ -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 + "]";
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,9 @@ import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFP
|
||||
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 class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna
|
||||
*/
|
||||
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 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna
|
||||
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 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna
|
||||
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 class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna
|
||||
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 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
@@ -4576,6 +4576,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>
|
||||
|
||||
|
||||
@@ -2609,6 +2609,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" />
|
||||
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
1
packages/SystemUI/res/raw/sfps_pulse.json
Normal file
1
packages/SystemUI/res/raw/sfps_pulse.json
Normal file
File diff suppressed because one or more lines are too long
1
packages/SystemUI/res/raw/sfps_pulse_landscape.json
Normal file
1
packages/SystemUI/res/raw/sfps_pulse_landscape.json
Normal file
File diff suppressed because one or more lines are too long
@@ -27,6 +27,7 @@ import android.graphics.Rect;
|
||||
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 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
|
||||
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,
|
||||
|
||||
@@ -114,7 +114,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
|
||||
@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 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
|
||||
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;
|
||||
|
||||
|
||||
@@ -223,7 +223,7 @@ class AuthRippleController @Inject constructor(
|
||||
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) {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ import android.graphics.PixelFormat;
|
||||
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;
|
||||
@@ -129,7 +130,7 @@ public class UdfpsController implements DozeReceiver {
|
||||
@NonNull private final KeyguardBypassController mKeyguardBypassController;
|
||||
@NonNull private final ConfigurationController mConfigurationController;
|
||||
@NonNull private final SystemClock mSystemClock;
|
||||
@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;
|
||||
@@ -558,14 +559,6 @@ public class UdfpsController implements DozeReceiver {
|
||||
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;
|
||||
@@ -573,6 +566,15 @@ public class UdfpsController implements DozeReceiver {
|
||||
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,
|
||||
@@ -642,10 +644,11 @@ public class UdfpsController implements DozeReceiver {
|
||||
// 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() {
|
||||
@@ -669,10 +672,11 @@ public class UdfpsController implements DozeReceiver {
|
||||
}
|
||||
|
||||
// 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.
|
||||
@@ -685,9 +689,9 @@ public class UdfpsController implements DozeReceiver {
|
||||
&& mKeyguardUpdateMonitor.isGoingToSleep()) {
|
||||
break;
|
||||
}
|
||||
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;
|
||||
|
||||
@@ -696,9 +700,9 @@ public class UdfpsController implements DozeReceiver {
|
||||
&& mKeyguardUpdateMonitor.isGoingToSleep()) {
|
||||
break;
|
||||
}
|
||||
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;
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.annotation.NonNull;
|
||||
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 @@ public class UdfpsDialogMeasureAdapter {
|
||||
// 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 @@ public class UdfpsDialogMeasureAdapter {
|
||||
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 @@ public class UdfpsDialogMeasureAdapter {
|
||||
@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 @@ public class UdfpsDialogMeasureAdapter {
|
||||
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
|
||||
|
||||
@@ -63,7 +63,7 @@ public class UdfpsEnrollView extends UdfpsAnimationView {
|
||||
|
||||
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;
|
||||
|
||||
@@ -25,6 +25,7 @@ import android.graphics.Color;
|
||||
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 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
|
||||
: 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));
|
||||
|
||||
@@ -29,6 +29,7 @@ import static org.mockito.Mockito.verify;
|
||||
|
||||
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 @@ public class AuthBiometricFaceToFingerprintViewTest extends SysuiTestCase {
|
||||
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 {
|
||||
|
||||
@@ -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.Display
|
||||
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.Test
|
||||
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 @@ class SidefpsControllerTest : SysuiTestCase() {
|
||||
@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 @@ class SidefpsControllerTest : SysuiTestCase() {
|
||||
@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 @@ class SidefpsControllerTest : SysuiTestCase() {
|
||||
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 @@ class SidefpsControllerTest : SysuiTestCase() {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.systemui.biometrics;
|
||||
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 @@ public class UdfpsDialogMeasureAdapterTest extends SysuiTestCase {
|
||||
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 @@ public class UdfpsDialogMeasureAdapterTest extends SysuiTestCase {
|
||||
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(
|
||||
|
||||
@@ -25,6 +25,7 @@ import static org.mockito.Mockito.when;
|
||||
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 @@ public class LockIconViewControllerTest extends SysuiTestCase {
|
||||
/* 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 @@ public class LockIconViewControllerTest extends SysuiTestCase {
|
||||
/* 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));
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ import android.hardware.biometrics.IInvalidationCallback;
|
||||
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;
|
||||
@@ -766,8 +767,9 @@ public class AuthService extends SystemService {
|
||||
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,
|
||||
|
||||
@@ -27,6 +27,7 @@ import android.hardware.biometrics.BiometricsProtoEnums;
|
||||
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 @@ class FingerprintAuthenticationClient extends AuthenticationClient<ISession> imp
|
||||
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 @@ class FingerprintAuthenticationClient extends AuthenticationClient<ISession> imp
|
||||
false /* isKeyguardBypassEnabled */);
|
||||
setRequestId(requestId);
|
||||
mLockoutCache = lockoutCache;
|
||||
mSensorOverlays = new SensorOverlays(udfpsOverlayController, null /* sideFpsController */);
|
||||
mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
|
||||
mSensorProps = sensorProps;
|
||||
mALSProbeCallback = createALSCallback(false /* startWithClient */);
|
||||
}
|
||||
|
||||
@@ -25,10 +25,12 @@ import android.app.ActivityTaskManager;
|
||||
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 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
|
||||
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 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
|
||||
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 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
|
||||
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 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -629,7 +629,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
|
||||
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);
|
||||
});
|
||||
|
||||
@@ -426,8 +426,7 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage
|
||||
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);
|
||||
|
||||
@@ -26,6 +26,7 @@ import android.hardware.biometrics.BiometricFingerprintConstants;
|
||||
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 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
|
||||
@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 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
|
||||
false /* isKeyguardBypassEnabled */);
|
||||
setRequestId(requestId);
|
||||
mLockoutFrameworkImpl = lockoutTracker;
|
||||
mSensorOverlays = new SensorOverlays(udfpsOverlayController, null /* sideFpsController */);
|
||||
mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
|
||||
mSensorProps = sensorProps;
|
||||
mALSProbeCallback = createALSCallback(false /* startWithClient */);
|
||||
}
|
||||
|
||||
@@ -19,10 +19,13 @@ package com.android.server.biometrics.sensors.fingerprint.aidl;
|
||||
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 @@ public class FingerprintProviderTest {
|
||||
@Mock
|
||||
private Context mContext;
|
||||
@Mock
|
||||
private Resources mResources;
|
||||
@Mock
|
||||
private UserManager mUserManager;
|
||||
@Mock
|
||||
private GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
|
||||
@@ -74,19 +79,21 @@ public class FingerprintProviderTest {
|
||||
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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user