de-HIDL: Sensor configs & Provider merge
1. Map HIDL to AIDL fingerprint sensor configuration
2. Propagate that through FingerprintService and FingerprintProvider
3. Similar implementation for face sensors
4. Use only one provider
Test: atest FingerprintSensorConfigurationsTest
FaceSensorConfigurationsTest AuthServiceTest FaceServiceTest
FingerprintServiceTest FaceProviderTest FingerprintProviderTest
HidlToAidlSensorAdapterTest HidlToAidlSessionAdapterTest SensorTest
Bug: 303480396
bug: 303480830
Change-Id: I7a945e596c6331077e90cbe1500de0bcb212895c
diff --git a/Android.bp b/Android.bp
index 676a0f5..fec5951 100644
--- a/Android.bp
+++ b/Android.bp
@@ -97,6 +97,7 @@
// AIDL sources from external directories
":android.hardware.biometrics.common-V4-java-source",
":android.hardware.biometrics.fingerprint-V3-java-source",
+ ":android.hardware.biometrics.face-V4-java-source",
":android.hardware.gnss-V2-java-source",
":android.hardware.graphics.common-V3-java-source",
":android.hardware.keymaster-V4-java-source",
diff --git a/core/java/android/hardware/face/FaceSensorConfigurations.aidl b/core/java/android/hardware/face/FaceSensorConfigurations.aidl
new file mode 100644
index 0000000..26367b3
--- /dev/null
+++ b/core/java/android/hardware/face/FaceSensorConfigurations.aidl
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.face;
+
+parcelable FaceSensorConfigurations;
\ No newline at end of file
diff --git a/core/java/android/hardware/face/FaceSensorConfigurations.java b/core/java/android/hardware/face/FaceSensorConfigurations.java
new file mode 100644
index 0000000..6ef692f
--- /dev/null
+++ b/core/java/android/hardware/face/FaceSensorConfigurations.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.face;
+
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.biometrics.face.IFace;
+import android.hardware.biometrics.face.SensorProps;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Slog;
+
+import androidx.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+
+/**
+ * Provides the sensor props for face sensor, if available.
+ * @hide
+ */
+public class FaceSensorConfigurations implements Parcelable {
+ private static final String TAG = "FaceSensorConfigurations";
+
+ private final boolean mResetLockoutRequiresChallenge;
+ private final Map<String, SensorProps[]> mSensorPropsMap;
+
+ public static final Creator<FaceSensorConfigurations> CREATOR =
+ new Creator<FaceSensorConfigurations>() {
+ @Override
+ public FaceSensorConfigurations createFromParcel(Parcel in) {
+ return new FaceSensorConfigurations(in);
+ }
+
+ @Override
+ public FaceSensorConfigurations[] newArray(int size) {
+ return new FaceSensorConfigurations[size];
+ }
+ };
+
+ public FaceSensorConfigurations(boolean resetLockoutRequiresChallenge) {
+ mResetLockoutRequiresChallenge = resetLockoutRequiresChallenge;
+ mSensorPropsMap = new HashMap<>();
+ }
+
+ protected FaceSensorConfigurations(Parcel in) {
+ mResetLockoutRequiresChallenge = in.readByte() != 0;
+ mSensorPropsMap = in.readHashMap(null, String.class, SensorProps[].class);
+ }
+
+ /**
+ * Process AIDL instances to extract sensor props and add it to the sensor map.
+ * @param aidlInstances available face AIDL instances
+ * @param getIFace function that provides the daemon for the specific instance
+ */
+ public void addAidlConfigs(@NonNull String[] aidlInstances,
+ @NonNull Function<String, IFace> getIFace) {
+ for (String aidlInstance : aidlInstances) {
+ final String fqName = IFace.DESCRIPTOR + "/" + aidlInstance;
+ IFace face = getIFace.apply(fqName);
+ try {
+ if (face != null) {
+ mSensorPropsMap.put(aidlInstance, face.getSensorProps());
+ } else {
+ Slog.e(TAG, "Unable to get declared service: " + fqName);
+ }
+ } catch (RemoteException e) {
+ Log.d(TAG, "Unable to get sensor properties!");
+ }
+ }
+ }
+
+ /**
+ * Parse through HIDL configuration and add it to the sensor map.
+ */
+ public void addHidlConfigs(@NonNull String[] hidlConfigStrings,
+ @NonNull Context context) {
+ final List<HidlFaceSensorConfig> hidlFaceSensorConfigs = new ArrayList<>();
+ for (String hidlConfig: hidlConfigStrings) {
+ final HidlFaceSensorConfig hidlFaceSensorConfig = new HidlFaceSensorConfig();
+ try {
+ hidlFaceSensorConfig.parse(hidlConfig, context);
+ } catch (Exception e) {
+ Log.e(TAG, "HIDL sensor configuration format is incorrect.");
+ continue;
+ }
+ if (hidlFaceSensorConfig.getModality() == TYPE_FACE) {
+ hidlFaceSensorConfigs.add(hidlFaceSensorConfig);
+ }
+ }
+ final String hidlHalInstanceName = "defaultHIDL";
+ mSensorPropsMap.put(hidlHalInstanceName, hidlFaceSensorConfigs.toArray(
+ new SensorProps[hidlFaceSensorConfigs.size()]));
+ }
+
+ /**
+ * Returns true if any face sensors have been added.
+ */
+ public boolean hasSensorConfigurations() {
+ return mSensorPropsMap.size() > 0;
+ }
+
+ /**
+ * Returns true if there is only a single face sensor configuration available.
+ */
+ public boolean isSingleSensorConfigurationPresent() {
+ return mSensorPropsMap.size() == 1;
+ }
+
+ /**
+ * Return sensor props for the given instance. If instance is not available,
+ * then null is returned.
+ */
+ @Nullable
+ public Pair<String, SensorProps[]> getSensorPairForInstance(String instance) {
+ if (mSensorPropsMap.containsKey(instance)) {
+ return new Pair<>(instance, mSensorPropsMap.get(instance));
+ }
+
+ return null;
+ }
+
+ /**
+ * Return the first pair of instance and sensor props, which does not correspond to the given
+ * If instance is not available, then null is returned.
+ */
+ @Nullable
+ public Pair<String, SensorProps[]> getSensorPairNotForInstance(String instance) {
+ Optional<String> notAVirtualInstance = mSensorPropsMap.keySet().stream().filter(
+ (instanceName) -> !instanceName.equals(instance)).findFirst();
+ return notAVirtualInstance.map(this::getSensorPairForInstance).orElseGet(
+ this::getSensorPair);
+ }
+
+ /**
+ * Returns the first pair of instance and sensor props that has been added to the map.
+ */
+ @Nullable
+ public Pair<String, SensorProps[]> getSensorPair() {
+ Optional<String> optionalInstance = mSensorPropsMap.keySet().stream().findFirst();
+ return optionalInstance.map(this::getSensorPairForInstance).orElse(null);
+
+ }
+
+ public boolean getResetLockoutRequiresChallenge() {
+ return mResetLockoutRequiresChallenge;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeByte((byte) (mResetLockoutRequiresChallenge ? 1 : 0));
+ dest.writeMap(mSensorPropsMap);
+ }
+}
diff --git a/core/java/android/hardware/face/HidlFaceSensorConfig.java b/core/java/android/hardware/face/HidlFaceSensorConfig.java
new file mode 100644
index 0000000..cab146d
--- /dev/null
+++ b/core/java/android/hardware/face/HidlFaceSensorConfig.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.face;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.common.CommonProps;
+import android.hardware.biometrics.common.SensorStrength;
+import android.hardware.biometrics.face.SensorProps;
+
+import com.android.internal.R;
+
+/**
+ * Parse HIDL face sensor config and map it to SensorProps.aidl to match AIDL.
+ * See core/res/res/values/config.xml config_biometric_sensors
+ * @hide
+ */
+public final class HidlFaceSensorConfig extends SensorProps {
+ private int mSensorId;
+ private int mModality;
+ private int mStrength;
+
+ /**
+ * Parse through the config string and map it to SensorProps.aidl.
+ * @throws IllegalArgumentException when config string has unexpected format
+ */
+ public void parse(@NonNull String config, @NonNull Context context)
+ throws IllegalArgumentException {
+ final String[] elems = config.split(":");
+ if (elems.length < 3) {
+ throw new IllegalArgumentException();
+ }
+ mSensorId = Integer.parseInt(elems[0]);
+ mModality = Integer.parseInt(elems[1]);
+ mStrength = Integer.parseInt(elems[2]);
+ mapHidlToAidlFaceSensorConfigurations(context);
+ }
+
+ @BiometricAuthenticator.Modality
+ public int getModality() {
+ return mModality;
+ }
+
+ private void mapHidlToAidlFaceSensorConfigurations(@NonNull Context context) {
+ commonProps = new CommonProps();
+ commonProps.sensorId = mSensorId;
+ commonProps.sensorStrength = authenticatorStrengthToPropertyStrength(mStrength);
+ halControlsPreview = context.getResources().getBoolean(
+ R.bool.config_faceAuthSupportsSelfIllumination);
+ commonProps.maxEnrollmentsPerUser = context.getResources().getInteger(
+ R.integer.config_faceMaxTemplatesPerUser);
+ commonProps.componentInfo = null;
+ supportsDetectInteraction = false;
+ }
+
+ private byte authenticatorStrengthToPropertyStrength(
+ @BiometricManager.Authenticators.Types int strength) {
+ switch (strength) {
+ case BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE:
+ return SensorStrength.CONVENIENCE;
+ case BiometricManager.Authenticators.BIOMETRIC_WEAK:
+ return SensorStrength.WEAK;
+ case BiometricManager.Authenticators.BIOMETRIC_STRONG:
+ return SensorStrength.STRONG;
+ default:
+ throw new IllegalArgumentException("Unknown strength: " + strength);
+ }
+ }
+}
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index 7080133..0096877 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -26,6 +26,7 @@
import android.hardware.face.Face;
import android.hardware.face.FaceAuthenticateOptions;
import android.hardware.face.FaceSensorPropertiesInternal;
+import android.hardware.face.FaceSensorConfigurations;
import android.view.Surface;
/**
@@ -167,6 +168,10 @@
@EnforcePermission("USE_BIOMETRIC_INTERNAL")
void registerAuthenticators(in List<FaceSensorPropertiesInternal> hidlSensors);
+ //Register all available face sensors.
+ @EnforcePermission("USE_BIOMETRIC_INTERNAL")
+ void registerAuthenticatorsLegacy(in FaceSensorConfigurations faceSensorConfigurations);
+
// Adds a callback which gets called when the service registers all of the face
// authenticators. The callback is automatically removed after it's invoked.
void addAuthenticatorsRegisteredCallback(IFaceAuthenticatorsRegisteredCallback callback);
diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.aidl b/core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.aidl
new file mode 100644
index 0000000..ebb05dc
--- /dev/null
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.aidl
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.fingerprint;
+
+parcelable FingerprintSensorConfigurations;
\ No newline at end of file
diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.java b/core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.java
new file mode 100644
index 0000000..f214494a
--- /dev/null
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.fingerprint;
+
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.biometrics.fingerprint.IFingerprint;
+import android.hardware.biometrics.fingerprint.SensorProps;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+
+/**
+ * Provides the sensor props for fingerprint sensor, if available.
+ * @hide
+ */
+
+public class FingerprintSensorConfigurations implements Parcelable {
+ private static final String TAG = "FingerprintSensorConfigurations";
+
+ private final Map<String, SensorProps[]> mSensorPropsMap;
+ private final boolean mResetLockoutRequiresHardwareAuthToken;
+
+ public static final Creator<FingerprintSensorConfigurations> CREATOR =
+ new Creator<>() {
+ @Override
+ public FingerprintSensorConfigurations createFromParcel(Parcel in) {
+ return new FingerprintSensorConfigurations(in);
+ }
+
+ @Override
+ public FingerprintSensorConfigurations[] newArray(int size) {
+ return new FingerprintSensorConfigurations[size];
+ }
+ };
+
+ public FingerprintSensorConfigurations(boolean resetLockoutRequiresHardwareAuthToken) {
+ mResetLockoutRequiresHardwareAuthToken = resetLockoutRequiresHardwareAuthToken;
+ mSensorPropsMap = new HashMap<>();
+ }
+
+ /**
+ * Process AIDL instances to extract sensor props and add it to the sensor map.
+ * @param aidlInstances available face AIDL instances
+ * @param getIFingerprint function that provides the daemon for the specific instance
+ */
+ public void addAidlSensors(@NonNull String[] aidlInstances,
+ @NonNull Function<String, IFingerprint> getIFingerprint) {
+ for (String aidlInstance : aidlInstances) {
+ try {
+ final String fqName = IFingerprint.DESCRIPTOR + "/" + aidlInstance;
+ final IFingerprint fp = getIFingerprint.apply(fqName);
+ if (fp != null) {
+ SensorProps[] props = fp.getSensorProps();
+ mSensorPropsMap.put(aidlInstance, props);
+ } else {
+ Log.d(TAG, "IFingerprint null for instance " + aidlInstance);
+ }
+ } catch (RemoteException e) {
+ Log.d(TAG, "Unable to get sensor properties!");
+ }
+ }
+ }
+
+ /**
+ * Parse through HIDL configuration and add it to the sensor map.
+ */
+ public void addHidlSensors(@NonNull String[] hidlConfigStrings,
+ @NonNull Context context) {
+ final List<HidlFingerprintSensorConfig> hidlFingerprintSensorConfigs = new ArrayList<>();
+ for (String hidlConfigString : hidlConfigStrings) {
+ final HidlFingerprintSensorConfig hidlFingerprintSensorConfig =
+ new HidlFingerprintSensorConfig();
+ try {
+ hidlFingerprintSensorConfig.parse(hidlConfigString, context);
+ } catch (Exception e) {
+ Log.e(TAG, "HIDL sensor configuration format is incorrect.");
+ continue;
+ }
+ if (hidlFingerprintSensorConfig.getModality() == TYPE_FINGERPRINT) {
+ hidlFingerprintSensorConfigs.add(hidlFingerprintSensorConfig);
+ }
+ }
+ final String hidlHalInstanceName = "defaultHIDL";
+ mSensorPropsMap.put(hidlHalInstanceName,
+ hidlFingerprintSensorConfigs.toArray(
+ new HidlFingerprintSensorConfig[hidlFingerprintSensorConfigs.size()]));
+ }
+
+ protected FingerprintSensorConfigurations(Parcel in) {
+ mResetLockoutRequiresHardwareAuthToken = in.readByte() != 0;
+ mSensorPropsMap = in.readHashMap(null /* loader */, String.class, SensorProps[].class);
+ }
+
+ /**
+ * Returns true if any fingerprint sensors have been added.
+ */
+ public boolean hasSensorConfigurations() {
+ return mSensorPropsMap.size() > 0;
+ }
+
+ /**
+ * Returns true if there is only a single fingerprint sensor configuration available.
+ */
+ public boolean isSingleSensorConfigurationPresent() {
+ return mSensorPropsMap.size() == 1;
+ }
+
+ /**
+ * Return sensor props for the given instance. If instance is not available,
+ * then null is returned.
+ */
+ @Nullable
+ public Pair<String, SensorProps[]> getSensorPairForInstance(String instance) {
+ if (mSensorPropsMap.containsKey(instance)) {
+ return new Pair<>(instance, mSensorPropsMap.get(instance));
+ }
+
+ return null;
+ }
+
+ /**
+ * Return the first pair of instance and sensor props, which does not correspond to the given
+ * If instance is not available, then null is returned.
+ */
+ @Nullable
+ public Pair<String, SensorProps[]> getSensorPairNotForInstance(String instance) {
+ Optional<String> notAVirtualInstance = mSensorPropsMap.keySet().stream().filter(
+ (instanceName) -> !instanceName.equals(instance)).findFirst();
+ return notAVirtualInstance.map(this::getSensorPairForInstance).orElseGet(
+ this::getSensorPair);
+ }
+
+ /**
+ * Returns the first pair of instance and sensor props that has been added to the map.
+ */
+ @Nullable
+ public Pair<String, SensorProps[]> getSensorPair() {
+ Optional<String> optionalInstance = mSensorPropsMap.keySet().stream().findFirst();
+ return optionalInstance.map(this::getSensorPairForInstance).orElse(null);
+
+ }
+
+ public boolean getResetLockoutRequiresHardwareAuthToken() {
+ return mResetLockoutRequiresHardwareAuthToken;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeByte((byte) (mResetLockoutRequiresHardwareAuthToken ? 1 : 0));
+ dest.writeMap(mSensorPropsMap);
+ }
+}
diff --git a/core/java/android/hardware/fingerprint/HidlFingerprintSensorConfig.java b/core/java/android/hardware/fingerprint/HidlFingerprintSensorConfig.java
new file mode 100644
index 0000000..d481153
--- /dev/null
+++ b/core/java/android/hardware/fingerprint/HidlFingerprintSensorConfig.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.fingerprint;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.common.CommonProps;
+import android.hardware.biometrics.common.SensorStrength;
+import android.hardware.biometrics.fingerprint.FingerprintSensorType;
+import android.hardware.biometrics.fingerprint.SensorLocation;
+import android.hardware.biometrics.fingerprint.SensorProps;
+
+import com.android.internal.R;
+import com.android.internal.util.ArrayUtils;
+
+/**
+ * Parse HIDL fingerprint sensor config and map it to SensorProps.aidl to match AIDL.
+ * See core/res/res/values/config.xml config_biometric_sensors
+ * @hide
+ */
+public final class HidlFingerprintSensorConfig extends SensorProps {
+ private int mSensorId;
+ private int mModality;
+ private int mStrength;
+
+ /**
+ * Parse through the config string and map it to SensorProps.aidl.
+ * @throws IllegalArgumentException when config string has unexpected format
+ */
+ public void parse(@NonNull String config, @NonNull Context context)
+ throws IllegalArgumentException {
+ final String[] elems = config.split(":");
+ if (elems.length < 3) {
+ throw new IllegalArgumentException();
+ }
+ mSensorId = Integer.parseInt(elems[0]);
+ mModality = Integer.parseInt(elems[1]);
+ mStrength = Integer.parseInt(elems[2]);
+ mapHidlToAidlSensorConfiguration(context);
+ }
+
+ @BiometricAuthenticator.Modality
+ public int getModality() {
+ return mModality;
+ }
+
+ private void mapHidlToAidlSensorConfiguration(@NonNull Context context) {
+ commonProps = new CommonProps();
+ commonProps.componentInfo = null;
+ commonProps.sensorId = mSensorId;
+ commonProps.sensorStrength = authenticatorStrengthToPropertyStrength(mStrength);
+ commonProps.maxEnrollmentsPerUser = context.getResources().getInteger(
+ R.integer.config_fingerprintMaxTemplatesPerUser);
+ halControlsIllumination = false;
+ sensorLocations = new SensorLocation[1];
+
+ final int[] udfpsProps = context.getResources().getIntArray(
+ com.android.internal.R.array.config_udfps_sensor_props);
+ final boolean isUdfps = !ArrayUtils.isEmpty(udfpsProps);
+ // config_is_powerbutton_fps indicates whether device has a power button fingerprint sensor.
+ final boolean isPowerbuttonFps = context.getResources().getBoolean(
+ R.bool.config_is_powerbutton_fps);
+
+ if (isUdfps) {
+ sensorType = FingerprintSensorType.UNKNOWN;
+ } else if (isPowerbuttonFps) {
+ sensorType = FingerprintSensorType.POWER_BUTTON;
+ } else {
+ sensorType = FingerprintSensorType.REAR;
+ }
+
+ if (isUdfps && udfpsProps.length == 3) {
+ setSensorLocation(udfpsProps[0], udfpsProps[1], udfpsProps[2]);
+ } else {
+ setSensorLocation(540 /* sensorLocationX */, 1636 /* sensorLocationY */,
+ 130 /* sensorRadius */);
+ }
+
+ }
+
+ private void setSensorLocation(int sensorLocationX,
+ int sensorLocationY, int sensorRadius) {
+ sensorLocations[0] = new SensorLocation();
+ sensorLocations[0].display = "";
+ sensorLocations[0].sensorLocationX = sensorLocationX;
+ sensorLocations[0].sensorLocationY = sensorLocationY;
+ sensorLocations[0].sensorRadius = sensorRadius;
+ }
+
+ private byte authenticatorStrengthToPropertyStrength(
+ @BiometricManager.Authenticators.Types int strength) {
+ switch (strength) {
+ case BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE:
+ return SensorStrength.CONVENIENCE;
+ case BiometricManager.Authenticators.BIOMETRIC_WEAK:
+ return SensorStrength.WEAK;
+ case BiometricManager.Authenticators.BIOMETRIC_STRONG:
+ return SensorStrength.STRONG;
+ default:
+ throw new IllegalArgumentException("Unknown strength: " + strength);
+ }
+ }
+}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index f594c00..606b171 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -31,6 +31,7 @@
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintAuthenticateOptions;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.FingerprintSensorConfigurations;
import java.util.List;
/**
@@ -173,6 +174,10 @@
@EnforcePermission("MANAGE_FINGERPRINT")
void removeClientActiveCallback(IFingerprintClientActiveCallback callback);
+ //Register all available fingerprint sensors.
+ @EnforcePermission("USE_BIOMETRIC_INTERNAL")
+ void registerAuthenticatorsLegacy(in FingerprintSensorConfigurations fingerprintSensorConfigurations);
+
// Registers all HIDL and AIDL sensors. Only HIDL sensor properties need to be provided, because
// AIDL sensor properties are retrieved directly from the available HALs. If no HIDL HALs exist,
// hidlSensors must be non-null and empty. See AuthService.java
diff --git a/core/tests/coretests/src/android/hardware/face/FaceSensorConfigurationsTest.java b/core/tests/coretests/src/android/hardware/face/FaceSensorConfigurationsTest.java
new file mode 100644
index 0000000..da3a465
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/face/FaceSensorConfigurationsTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.face;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.hardware.biometrics.face.IFace;
+import android.hardware.biometrics.face.SensorProps;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.function.Function;
+
+@Presubmit
+@SmallTest
+public class FaceSensorConfigurationsTest {
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+ @Mock
+ private Context mContext;
+ @Mock
+ private Resources mResources;
+ @Mock
+ private IFace mFace;
+ @Mock
+ private Function<String, IFace> mGetIFace;
+
+ private final String[] mAidlInstances = new String[]{"default", "virtual"};
+ private String[] mHidlConfigStrings = new String[]{"0:2:15", "0:8:15"};
+ private FaceSensorConfigurations mFaceSensorConfigurations;
+
+ @Before
+ public void setUp() throws RemoteException {
+ when(mGetIFace.apply(anyString())).thenReturn(mFace);
+ when(mFace.getSensorProps()).thenReturn(new SensorProps[]{});
+ when(mContext.getResources()).thenReturn(mResources);
+ }
+
+ @Test
+ public void testAidlInstanceSensorProps() {
+ mFaceSensorConfigurations = new FaceSensorConfigurations(false);
+ mFaceSensorConfigurations.addAidlConfigs(mAidlInstances, mGetIFace);
+
+ assertThat(mFaceSensorConfigurations.hasSensorConfigurations()).isTrue();
+ assertThat(!mFaceSensorConfigurations.isSingleSensorConfigurationPresent()).isTrue();
+ assertThat(mFaceSensorConfigurations.getResetLockoutRequiresChallenge())
+ .isFalse();
+ }
+
+ @Test
+ public void testHidlConfigStrings() {
+ mFaceSensorConfigurations = new FaceSensorConfigurations(true);
+ mFaceSensorConfigurations.addHidlConfigs(mHidlConfigStrings, mContext);
+
+ assertThat(mFaceSensorConfigurations.isSingleSensorConfigurationPresent()).isTrue();
+ assertThat(mFaceSensorConfigurations.getResetLockoutRequiresChallenge())
+ .isTrue();
+ }
+
+ @Test
+ public void testHidlConfigStrings_incorrectFormat() {
+ mHidlConfigStrings = new String[]{"0:8:15", "0:2", "0:face:15"};
+ mFaceSensorConfigurations = new FaceSensorConfigurations(true);
+ mFaceSensorConfigurations.addHidlConfigs(mHidlConfigStrings, mContext);
+
+ assertThat(mFaceSensorConfigurations.isSingleSensorConfigurationPresent()).isTrue();
+ assertThat(mFaceSensorConfigurations.getResetLockoutRequiresChallenge())
+ .isTrue();
+ }
+}
diff --git a/core/tests/coretests/src/android/hardware/fingerprint/FingerprintSensorConfigurationsTest.java b/core/tests/coretests/src/android/hardware/fingerprint/FingerprintSensorConfigurationsTest.java
new file mode 100644
index 0000000..613089c
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/fingerprint/FingerprintSensorConfigurationsTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.fingerprint;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.hardware.biometrics.fingerprint.IFingerprint;
+import android.hardware.biometrics.fingerprint.SensorProps;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.function.Function;
+
+
+@Presubmit
+@SmallTest
+public class FingerprintSensorConfigurationsTest {
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+ @Mock
+ private Context mContext;
+ @Mock
+ private Resources mResources;
+ @Mock
+ private IFingerprint mFingerprint;
+ @Mock
+ private Function<String, IFingerprint> mGetIFingerprint;
+
+ private final String[] mAidlInstances = new String[]{"default", "virtual"};
+ private String[] mHidlConfigStrings = new String[]{"0:2:15", "0:8:15"};
+ private FingerprintSensorConfigurations mFingerprintSensorConfigurations;
+
+ @Before
+ public void setUp() throws RemoteException {
+ when(mGetIFingerprint.apply(anyString())).thenReturn(mFingerprint);
+ when(mFingerprint.getSensorProps()).thenReturn(new SensorProps[]{});
+ when(mContext.getResources()).thenReturn(mResources);
+ }
+
+ @Test
+ public void testAidlInstanceSensorProps() {
+ mFingerprintSensorConfigurations = new FingerprintSensorConfigurations(
+ true /* resetLockoutRequiresHardwareAuthToken */);
+ mFingerprintSensorConfigurations.addAidlSensors(mAidlInstances, mGetIFingerprint);
+
+ assertThat(mFingerprintSensorConfigurations.hasSensorConfigurations()).isTrue();
+ assertThat(!mFingerprintSensorConfigurations.isSingleSensorConfigurationPresent())
+ .isTrue();
+ assertThat(mFingerprintSensorConfigurations.getResetLockoutRequiresHardwareAuthToken())
+ .isTrue();
+ }
+
+ @Test
+ public void testHidlConfigStrings() {
+ mFingerprintSensorConfigurations = new FingerprintSensorConfigurations(
+ false /* resetLockoutRequiresHardwareAuthToken */);
+ mFingerprintSensorConfigurations.addHidlSensors(mHidlConfigStrings, mContext);
+
+ assertThat(mFingerprintSensorConfigurations.isSingleSensorConfigurationPresent()).isTrue();
+ assertThat(mFingerprintSensorConfigurations.getResetLockoutRequiresHardwareAuthToken())
+ .isFalse();
+ }
+
+ @Test
+ public void testHidlConfigStrings_incorrectFormat() {
+ mHidlConfigStrings = new String[]{"0:8:15", "0:2", "0:fingerprint:15"};
+ mFingerprintSensorConfigurations = new FingerprintSensorConfigurations(
+ false /* resetLockoutRequiresHardwareAuthToken */);
+ mFingerprintSensorConfigurations.addHidlSensors(mHidlConfigStrings, mContext);
+
+ assertThat(mFingerprintSensorConfigurations.isSingleSensorConfigurationPresent()).isTrue();
+ assertThat(mFingerprintSensorConfigurations.getResetLockoutRequiresHardwareAuthToken())
+ .isFalse();
+ }
+}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index f5a80d8..258e9b4 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -103,7 +103,6 @@
"android.hardware.power-java_static",
],
srcs: [
- ":android.hardware.biometrics.face-V4-java-source",
":android.hardware.tv.hdmi.connection-V1-java-source",
":android.hardware.tv.hdmi.earc-V1-java-source",
":statslog-art-java-gen",
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index dafea9a..d5d8fd2 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -51,9 +51,13 @@
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.biometrics.SensorPropertiesInternal;
+import android.hardware.biometrics.face.IFace;
+import android.hardware.biometrics.fingerprint.IFingerprint;
+import android.hardware.face.FaceSensorConfigurations;
import android.hardware.face.FaceSensorProperties;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.IFaceService;
+import android.hardware.fingerprint.FingerprintSensorConfigurations;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintService;
@@ -122,8 +126,6 @@
/**
* Allows to test with various device sensor configurations.
- * @param context
- * @return
*/
@VisibleForTesting
public String[] getConfiguration(Context context) {
@@ -131,6 +133,30 @@
}
/**
+ * Allows to test with various device sensor configurations.
+ */
+ @VisibleForTesting
+ public String[] getFingerprintConfiguration(Context context) {
+ return getConfiguration(context);
+ }
+
+ /**
+ * Allows to test with various device sensor configurations.
+ */
+ @VisibleForTesting
+ public String[] getFaceConfiguration(Context context) {
+ return getConfiguration(context);
+ }
+
+ /**
+ * Allows to test with various device sensor configurations.
+ */
+ @VisibleForTesting
+ public String[] getIrisConfiguration(Context context) {
+ return getConfiguration(context);
+ }
+
+ /**
* Allows us to mock FingerprintService for testing
*/
@VisibleForTesting
@@ -173,6 +199,22 @@
}
return false;
}
+
+ /**
+ * Allows to test with various fingerprint aidl instances.
+ */
+ @VisibleForTesting
+ public String[] getFingerprintAidlInstances() {
+ return ServiceManager.getDeclaredInstances(IFingerprint.DESCRIPTOR);
+ }
+
+ /**
+ * Allows to test with various face aidl instances.
+ */
+ @VisibleForTesting
+ public String[] getFaceAidlInstances() {
+ return ServiceManager.getDeclaredInstances(IFace.DESCRIPTOR);
+ }
}
private final class AuthServiceImpl extends IAuthService.Stub {
@@ -717,12 +759,129 @@
hidlConfigs = null;
}
- // Registers HIDL and AIDL authenticators, but only HIDL configs need to be provided.
- registerAuthenticators(hidlConfigs);
+ if (com.android.server.biometrics.Flags.deHidl()) {
+ Slog.d(TAG, "deHidl flag is on.");
+ registerAuthenticators();
+ } else {
+ // Registers HIDL and AIDL authenticators, but only HIDL configs need to be provided.
+ registerAuthenticators(hidlConfigs);
+ }
mInjector.publishBinderService(this, mImpl);
}
+ private void registerAuthenticators() {
+ registerFingerprintSensors(mInjector.getFingerprintAidlInstances(),
+ mInjector.getFingerprintConfiguration(getContext()));
+ registerFaceSensors(mInjector.getFaceAidlInstances(),
+ mInjector.getFaceConfiguration(getContext()));
+ registerIrisSensors(mInjector.getIrisConfiguration(getContext()));
+ }
+
+ private void registerIrisSensors(String[] hidlConfigStrings) {
+ final SensorConfig[] hidlConfigs;
+ if (!mInjector.isHidlDisabled(getContext())) {
+ final int firstApiLevel = SystemProperties.getInt(SYSPROP_FIRST_API_LEVEL, 0);
+ final int apiLevel = SystemProperties.getInt(SYSPROP_API_LEVEL, firstApiLevel);
+ if (hidlConfigStrings.length == 0 && apiLevel == Build.VERSION_CODES.R) {
+ // 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");
+ hidlConfigStrings = generateRSdkCompatibleConfiguration();
+ }
+ hidlConfigs = new SensorConfig[hidlConfigStrings.length];
+ for (int i = 0; i < hidlConfigStrings.length; ++i) {
+ hidlConfigs[i] = new SensorConfig(hidlConfigStrings[i]);
+ }
+ } else {
+ hidlConfigs = null;
+ }
+
+ final List<SensorPropertiesInternal> hidlIrisSensors = new ArrayList<>();
+
+ if (hidlConfigs != null) {
+ for (SensorConfig sensor : hidlConfigs) {
+ switch (sensor.modality) {
+ case TYPE_IRIS:
+ hidlIrisSensors.add(getHidlIrisSensorProps(sensor.id, sensor.strength));
+ break;
+
+ default:
+ Slog.e(TAG, "Unknown modality: " + sensor.modality);
+ }
+ }
+ }
+
+ final IIrisService irisService = mInjector.getIrisService();
+ if (irisService != null) {
+ try {
+ irisService.registerAuthenticators(hidlIrisSensors);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException when registering iris authenticators", e);
+ }
+ } else if (hidlIrisSensors.size() > 0) {
+ Slog.e(TAG, "HIDL iris configuration exists, but IrisService is null.");
+ }
+ }
+
+ private void registerFaceSensors(final String[] faceAidlInstances,
+ final String[] hidlConfigStrings) {
+ final FaceSensorConfigurations mFaceSensorConfigurations =
+ new FaceSensorConfigurations(hidlConfigStrings != null
+ && hidlConfigStrings.length > 0);
+
+ if (hidlConfigStrings != null && hidlConfigStrings.length > 0) {
+ mFaceSensorConfigurations.addHidlConfigs(
+ hidlConfigStrings, getContext());
+ }
+
+ if (faceAidlInstances != null && faceAidlInstances.length > 0) {
+ mFaceSensorConfigurations.addAidlConfigs(faceAidlInstances,
+ name -> IFace.Stub.asInterface(Binder.allowBlocking(
+ ServiceManager.waitForDeclaredService(name))));
+ }
+
+ final IFaceService faceService = mInjector.getFaceService();
+ if (faceService != null) {
+ try {
+ faceService.registerAuthenticatorsLegacy(mFaceSensorConfigurations);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException when registering face authenticators", e);
+ }
+ } else if (mFaceSensorConfigurations.hasSensorConfigurations()) {
+ Slog.e(TAG, "Face configuration exists, but FaceService is null.");
+ }
+ }
+
+ private void registerFingerprintSensors(final String[] fingerprintAidlInstances,
+ final String[] hidlConfigStrings) {
+ final FingerprintSensorConfigurations mFingerprintSensorConfigurations =
+ new FingerprintSensorConfigurations(!(hidlConfigStrings != null
+ && hidlConfigStrings.length > 0));
+
+ if (hidlConfigStrings != null && hidlConfigStrings.length > 0) {
+ mFingerprintSensorConfigurations.addHidlSensors(hidlConfigStrings, getContext());
+ }
+
+ if (fingerprintAidlInstances != null && fingerprintAidlInstances.length > 0) {
+ mFingerprintSensorConfigurations.addAidlSensors(fingerprintAidlInstances,
+ name -> IFingerprint.Stub.asInterface(Binder.allowBlocking(
+ ServiceManager.waitForDeclaredService(name))));
+ }
+
+ final IFingerprintService fingerprintService = mInjector.getFingerprintService();
+ if (fingerprintService != null) {
+ try {
+ fingerprintService.registerAuthenticatorsLegacy(mFingerprintSensorConfigurations);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException when registering fingerprint authenticators", e);
+ }
+ } else if (mFingerprintSensorConfigurations.hasSensorConfigurations()) {
+ Slog.e(TAG, "Fingerprint configuration exists, but FingerprintService is null.");
+ }
+ }
+
/**
* Generates an array of string configs with entries that correspond to the biometric features
* declared on the device. Returns an empty array if no biometric features are declared.
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index 037ea38a..89b638b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -202,7 +202,7 @@
};
@VisibleForTesting
- BiometricScheduler(@NonNull String tag,
+ public BiometricScheduler(@NonNull String tag,
@NonNull Handler handler,
@SensorType int sensorType,
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
index 57feedc..0c3dfa7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
@@ -387,6 +387,11 @@
return isAuthentication || isDetection;
}
+ /** If this operation is {@link StartUserClient}. */
+ public boolean isStartUserOperation() {
+ return mClientMonitor instanceof StartUserClient<?, ?>;
+ }
+
/** If this operation performs acquisition {@link AcquisitionClient}. */
public boolean isAcquisitionOperation() {
return mClientMonitor instanceof AcquisitionClient;
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
index 7f8f38f..6daaad1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
@@ -54,8 +54,6 @@
// is all done internally.
super(context, lazyDaemon, token, null /* ClientMonitorCallbackConverter */, userId, owner,
0 /* cookie */, sensorId, logger, biometricContext);
- //, BiometricsProtoEnums.ACTION_ENUMERATE,
- // BiometricsProtoEnums.CLIENT_UNKNOWN);
mEnrolledList = enrolledList;
mUtils = utils;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
index 8075470..3753bbd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
@@ -135,7 +135,7 @@
final int currentUserId = mCurrentUserRetriever.getCurrentUserId();
final int nextUserId = mPendingOperations.getFirst().getTargetUserId();
- if (nextUserId == currentUserId) {
+ if (nextUserId == currentUserId || mPendingOperations.getFirst().isStartUserOperation()) {
super.startNextOperationIfIdle();
} else if (currentUserId == UserHandle.USER_NULL) {
final BaseClientMonitor startClient =
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 578d9dc..6af223b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -36,6 +36,7 @@
import android.hardware.biometrics.face.SensorProps;
import android.hardware.face.Face;
import android.hardware.face.FaceAuthenticateOptions;
+import android.hardware.face.FaceSensorConfigurations;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.FaceServiceReceiver;
import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
@@ -55,6 +56,7 @@
import android.util.proto.ProtoOutputStream;
import android.view.Surface;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.SystemService;
@@ -76,6 +78,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.function.Supplier;
/**
* A service to manage multiple clients that want to access the face HAL API.
@@ -86,7 +89,7 @@
protected static final String TAG = "FaceService";
- private final FaceServiceWrapper mServiceWrapper;
+ @VisibleForTesting final FaceServiceWrapper mServiceWrapper;
private final LockoutResetDispatcher mLockoutResetDispatcher;
private final LockPatternUtils mLockPatternUtils;
@NonNull
@@ -94,11 +97,18 @@
@NonNull
private final BiometricStateCallback<ServiceProvider, FaceSensorPropertiesInternal>
mBiometricStateCallback;
+ @NonNull
+ private final FaceProviderFunction mFaceProviderFunction;
+
+ interface FaceProviderFunction {
+ FaceProvider getFaceProvider(Pair<String, SensorProps[]> filteredSensorProps,
+ boolean resetLockoutRequiresChallenge);
+ }
/**
* Receives the incoming binder calls from FaceManager.
*/
- private final class FaceServiceWrapper extends IFaceService.Stub {
+ @VisibleForTesting final class FaceServiceWrapper extends IFaceService.Stub {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
@@ -672,7 +682,8 @@
final SensorProps[] props = face.getSensorProps();
final FaceProvider provider = new FaceProvider(getContext(),
mBiometricStateCallback, props, instance, mLockoutResetDispatcher,
- BiometricContext.getInstance(getContext()));
+ BiometricContext.getInstance(getContext()),
+ false /* resetLockoutRequiresChallenge */);
providers.add(provider);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception in getSensorProps: " + fqName);
@@ -704,6 +715,55 @@
});
}
+ @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
+ public void registerAuthenticatorsLegacy(
+ FaceSensorConfigurations faceSensorConfigurations) {
+ super.registerAuthenticatorsLegacy_enforcePermission();
+
+ if (!faceSensorConfigurations.hasSensorConfigurations()) {
+ Slog.d(TAG, "No face sensors to register.");
+ return;
+ }
+ mRegistry.registerAll(() -> getProviders(faceSensorConfigurations));
+ }
+
+ private List<ServiceProvider> getProviders(
+ FaceSensorConfigurations faceSensorConfigurations) {
+ final List<ServiceProvider> providers = new ArrayList<>();
+ final Pair<String, SensorProps[]> filteredSensorProps =
+ filterAvailableHalInstances(faceSensorConfigurations);
+ providers.add(mFaceProviderFunction.getFaceProvider(filteredSensorProps,
+ faceSensorConfigurations.getResetLockoutRequiresChallenge()));
+ return providers;
+ }
+
+ @NonNull
+ private Pair<String, SensorProps[]> filterAvailableHalInstances(
+ FaceSensorConfigurations faceSensorConfigurations) {
+ Pair<String, SensorProps[]> finalSensorPair = faceSensorConfigurations.getSensorPair();
+
+ if (faceSensorConfigurations.isSingleSensorConfigurationPresent()) {
+ return finalSensorPair;
+ }
+
+ final Pair<String, SensorProps[]> virtualSensorProps = faceSensorConfigurations
+ .getSensorPairForInstance("virtual");
+
+ if (Utils.isVirtualEnabled(getContext())) {
+ if (virtualSensorProps != null) {
+ return virtualSensorProps;
+ } else {
+ Slog.e(TAG, "Could not find virtual interface while it is enabled");
+ return finalSensorPair;
+ }
+ } else {
+ if (virtualSensorProps != null) {
+ return faceSensorConfigurations.getSensorPairNotForInstance("virtual");
+ }
+ }
+ return finalSensorPair;
+ }
+
private Pair<List<FaceSensorPropertiesInternal>, List<String>>
filterAvailableHalInstances(
@NonNull List<FaceSensorPropertiesInternal> hidlInstances,
@@ -752,20 +812,36 @@
}
public FaceService(Context context) {
+ this(context, null /* faceProviderFunction */, () -> IBiometricService.Stub.asInterface(
+ ServiceManager.getService(Context.BIOMETRIC_SERVICE)));
+ }
+
+ @VisibleForTesting FaceService(Context context, FaceProviderFunction faceProviderFunction,
+ Supplier<IBiometricService> biometricServiceSupplier) {
super(context);
mServiceWrapper = new FaceServiceWrapper();
mLockoutResetDispatcher = new LockoutResetDispatcher(context);
mLockPatternUtils = new LockPatternUtils(context);
mBiometricStateCallback = new BiometricStateCallback<>(UserManager.get(context));
- mRegistry = new FaceServiceRegistry(mServiceWrapper,
- () -> IBiometricService.Stub.asInterface(
- ServiceManager.getService(Context.BIOMETRIC_SERVICE)));
+ mRegistry = new FaceServiceRegistry(mServiceWrapper, biometricServiceSupplier);
mRegistry.addAllRegisteredCallback(new IFaceAuthenticatorsRegisteredCallback.Stub() {
@Override
public void onAllAuthenticatorsRegistered(List<FaceSensorPropertiesInternal> sensors) {
mBiometricStateCallback.start(mRegistry.getProviders());
}
});
+
+ if (Flags.deHidl()) {
+ mFaceProviderFunction = faceProviderFunction != null ? faceProviderFunction :
+ ((filteredSensorProps, resetLockoutRequiresChallenge) -> new FaceProvider(
+ getContext(), mBiometricStateCallback,
+ filteredSensorProps.second,
+ filteredSensorProps.first, mLockoutResetDispatcher,
+ BiometricContext.getInstance(getContext()),
+ resetLockoutRequiresChallenge));
+ } else {
+ mFaceProviderFunction = ((filteredSensorProps, resetLockoutRequiresChallenge) -> null);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/LockoutHalImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/LockoutHalImpl.java
index e5d4a63..ef2be79 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/LockoutHalImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/LockoutHalImpl.java
@@ -24,7 +24,8 @@
* the user changes.
*/
public class LockoutHalImpl implements LockoutTracker {
- private @LockoutMode int mCurrentUserLockoutMode;
+ @LockoutMode
+ private int mCurrentUserLockoutMode;
@Override
public int getLockoutModeForUser(int userId) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java
index 57f5b34..098be21 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java
@@ -27,6 +27,7 @@
import android.hardware.keymaster.HardwareAuthToken;
import android.util.Slog;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AcquisitionClient;
@@ -59,6 +60,20 @@
void onHardwareUnavailable();
}
+ /**
+ * Interface to send results to the AidlResponseHandler's owner.
+ */
+ public interface AidlResponseHandlerCallback {
+ /**
+ * Invoked when enrollment is successful.
+ */
+ void onEnrollSuccess();
+ /**
+ * Invoked when the HAL sends ERROR_HW_UNAVAILABLE.
+ */
+ void onHardwareUnavailable();
+ }
+
private static final String TAG = "AidlResponseHandler";
@NonNull
@@ -68,7 +83,7 @@
private final int mSensorId;
private final int mUserId;
@NonNull
- private final LockoutTracker mLockoutCache;
+ private final LockoutTracker mLockoutTracker;
@NonNull
private final LockoutResetDispatcher mLockoutResetDispatcher;
@@ -76,6 +91,8 @@
private final AuthSessionCoordinator mAuthSessionCoordinator;
@NonNull
private final HardwareUnavailableCallback mHardwareUnavailableCallback;
+ @NonNull
+ private final AidlResponseHandlerCallback mAidlResponseHandlerCallback;
public AidlResponseHandler(@NonNull Context context,
@NonNull BiometricScheduler scheduler, int sensorId, int userId,
@@ -83,14 +100,33 @@
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull AuthSessionCoordinator authSessionCoordinator,
@NonNull HardwareUnavailableCallback hardwareUnavailableCallback) {
+ this(context, scheduler, sensorId, userId, lockoutTracker, lockoutResetDispatcher,
+ authSessionCoordinator, hardwareUnavailableCallback,
+ new AidlResponseHandlerCallback() {
+ @Override
+ public void onEnrollSuccess() {}
+
+ @Override
+ public void onHardwareUnavailable() {}
+ });
+ }
+
+ public AidlResponseHandler(@NonNull Context context,
+ @NonNull BiometricScheduler scheduler, int sensorId, int userId,
+ @NonNull LockoutTracker lockoutTracker,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull AuthSessionCoordinator authSessionCoordinator,
+ @NonNull HardwareUnavailableCallback hardwareUnavailableCallback,
+ @NonNull AidlResponseHandlerCallback aidlResponseHandlerCallback) {
mContext = context;
mScheduler = scheduler;
mSensorId = sensorId;
mUserId = userId;
- mLockoutCache = lockoutTracker;
+ mLockoutTracker = lockoutTracker;
mLockoutResetDispatcher = lockoutResetDispatcher;
mAuthSessionCoordinator = authSessionCoordinator;
mHardwareUnavailableCallback = hardwareUnavailableCallback;
+ mAidlResponseHandlerCallback = aidlResponseHandlerCallback;
}
@Override
@@ -106,13 +142,13 @@
@Override
public void onChallengeGenerated(long challenge) {
handleResponse(FaceGenerateChallengeClient.class, (c) -> c.onChallengeGenerated(mSensorId,
- mUserId, challenge), null);
+ mUserId, challenge));
}
@Override
public void onChallengeRevoked(long challenge) {
handleResponse(FaceRevokeChallengeClient.class, (c) -> c.onChallengeRevoked(mSensorId,
- mUserId, challenge), null);
+ mUserId, challenge));
}
@Override
@@ -123,7 +159,7 @@
return;
}
c.onAuthenticationFrame(AidlConversionUtils.toFrameworkAuthenticationFrame(frame));
- }, null);
+ });
}
@Override
@@ -134,7 +170,7 @@
return;
}
c.onEnrollmentFrame(AidlConversionUtils.toFrameworkEnrollmentFrame(frame));
- }, null);
+ });
}
@Override
@@ -149,9 +185,13 @@
handleResponse(ErrorConsumer.class, (c) -> {
c.onError(error, vendorCode);
if (error == Error.HW_UNAVAILABLE) {
- mHardwareUnavailableCallback.onHardwareUnavailable();
+ if (Flags.deHidl()) {
+ mAidlResponseHandlerCallback.onHardwareUnavailable();
+ } else {
+ mHardwareUnavailableCallback.onHardwareUnavailable();
+ }
}
- }, null);
+ });
}
@Override
@@ -167,7 +207,12 @@
.getUniqueName(mContext, currentUserId);
final Face face = new Face(name, enrollmentId, mSensorId);
- handleResponse(FaceEnrollClient.class, (c) -> c.onEnrollResult(face, remaining), null);
+ handleResponse(FaceEnrollClient.class, (c) -> {
+ c.onEnrollResult(face, remaining);
+ if (remaining == 0) {
+ mAidlResponseHandlerCallback.onEnrollSuccess();
+ }
+ });
}
@Override
@@ -179,37 +224,37 @@
byteList.add(b);
}
handleResponse(AuthenticationConsumer.class, (c) -> c.onAuthenticated(face,
- true /* authenticated */, byteList), null);
+ true /* authenticated */, byteList));
}
@Override
public void onAuthenticationFailed() {
final Face face = new Face("" /* name */, 0 /* faceId */, mSensorId);
handleResponse(AuthenticationConsumer.class, (c) -> c.onAuthenticated(face,
- false /* authenticated */, null /* hat */), null);
+ false /* authenticated */, null /* hat */));
}
@Override
public void onLockoutTimed(long durationMillis) {
- handleResponse(LockoutConsumer.class, (c) -> c.onLockoutTimed(durationMillis), null);
+ handleResponse(LockoutConsumer.class, (c) -> c.onLockoutTimed(durationMillis));
}
@Override
public void onLockoutPermanent() {
- handleResponse(LockoutConsumer.class, LockoutConsumer::onLockoutPermanent, null);
+ handleResponse(LockoutConsumer.class, LockoutConsumer::onLockoutPermanent);
}
@Override
public void onLockoutCleared() {
handleResponse(FaceResetLockoutClient.class, FaceResetLockoutClient::onLockoutCleared,
(c) -> FaceResetLockoutClient.resetLocalLockoutStateToNone(mSensorId, mUserId,
- mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
- Utils.getCurrentStrength(mSensorId), -1 /* requestId */));
+ mLockoutTracker, mLockoutResetDispatcher, mAuthSessionCoordinator,
+ Utils.getCurrentStrength(mSensorId), -1 /* requestId */));
}
@Override
public void onInteractionDetected() {
- handleResponse(FaceDetectClient.class, FaceDetectClient::onInteractionDetected, null);
+ handleResponse(FaceDetectClient.class, FaceDetectClient::onInteractionDetected);
}
@Override
@@ -219,23 +264,23 @@
final Face face = new Face("" /* name */, enrollmentIds[i], mSensorId);
final int finalI = i;
handleResponse(EnumerateConsumer.class, (c) -> c.onEnumerationResult(face,
- enrollmentIds.length - finalI - 1), null);
+ enrollmentIds.length - finalI - 1 /* remaining */));
}
} else {
handleResponse(EnumerateConsumer.class, (c) -> c.onEnumerationResult(
- null /* identifier */, 0 /* remaining */), null);
+ null /* identifier */, 0 /* remaining */));
}
}
@Override
public void onFeaturesRetrieved(byte[] features) {
handleResponse(FaceGetFeatureClient.class, (c) -> c.onFeatureGet(true /* success */,
- features), null);
+ features));
}
@Override
public void onFeatureSet(byte feature) {
- handleResponse(FaceSetFeatureClient.class, (c) -> c.onFeatureSet(true /* success */), null);
+ handleResponse(FaceSetFeatureClient.class, (c) -> c.onFeatureSet(true /* success */));
}
@Override
@@ -245,33 +290,32 @@
final Face face = new Face("" /* name */, enrollmentIds[i], mSensorId);
final int finalI = i;
handleResponse(RemovalConsumer.class,
- (c) -> c.onRemoved(face, enrollmentIds.length - finalI - 1),
- null);
+ (c) -> c.onRemoved(face,
+ enrollmentIds.length - finalI - 1 /* remaining */));
}
} else {
handleResponse(RemovalConsumer.class, (c) -> c.onRemoved(null /* identifier */,
- 0 /* remaining */), null);
+ 0 /* remaining */));
}
}
@Override
public void onAuthenticatorIdRetrieved(long authenticatorId) {
handleResponse(FaceGetAuthenticatorIdClient.class, (c) -> c.onAuthenticatorIdRetrieved(
- authenticatorId), null);
+ authenticatorId));
}
@Override
public void onAuthenticatorIdInvalidated(long newAuthenticatorId) {
handleResponse(FaceInvalidationClient.class, (c) -> c.onAuthenticatorIdInvalidated(
- newAuthenticatorId), null);
+ newAuthenticatorId));
}
/**
* Handles acquired messages sent by the HAL (specifically for HIDL HAL).
*/
public void onAcquired(int acquiredInfo, int vendorCode) {
- handleResponse(AcquisitionClient.class, (c) -> c.onAcquired(acquiredInfo, vendorCode),
- null);
+ handleResponse(AcquisitionClient.class, (c) -> c.onAcquired(acquiredInfo, vendorCode));
}
/**
@@ -288,7 +332,7 @@
lockoutMode = LockoutTracker.LOCKOUT_TIMED;
}
- mLockoutCache.setLockoutModeForUser(mUserId, lockoutMode);
+ mLockoutTracker.setLockoutModeForUser(mUserId, lockoutMode);
if (duration == 0) {
mLockoutResetDispatcher.notifyLockoutResetCallbacks(mSensorId);
@@ -296,6 +340,20 @@
});
}
+ /**
+ * Handle clients which are not supported in HIDL HAL. For face, FaceInvalidationClient
+ * is the only AIDL client which is not supported in HIDL.
+ */
+ public void onUnsupportedClientScheduled() {
+ Slog.e(TAG, "FaceInvalidationClient is not supported in the HAL.");
+ handleResponse(FaceInvalidationClient.class, BaseClientMonitor::cancel);
+ }
+
+ private <T> void handleResponse(@NonNull Class<T> className,
+ @NonNull Consumer<T> action) {
+ handleResponse(className, action, null /* alternateAction */);
+ }
+
private <T> void handleResponse(@NonNull Class<T> className,
@NonNull Consumer<T> actionIfClassMatchesClient,
@Nullable Consumer<BaseClientMonitor> alternateAction) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java
index e70e25a..af46f44 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java
@@ -21,7 +21,7 @@
import android.hardware.biometrics.face.ISession;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
-import com.android.server.biometrics.sensors.face.hidl.AidlToHidlAdapter;
+import com.android.server.biometrics.sensors.face.hidl.HidlToAidlSessionAdapter;
import java.util.function.Supplier;
@@ -47,7 +47,7 @@
public AidlSession(Context context, Supplier<IBiometricsFace> session, int userId,
AidlResponseHandler aidlResponseHandler) {
- mSession = new AidlToHidlAdapter(context, session, userId, aidlResponseHandler);
+ mSession = new HidlToAidlSessionAdapter(context, session, userId, aidlResponseHandler);
mHalInterfaceVersion = 0;
mUserId = userId;
mAidlResponseHandler = aidlResponseHandler;
@@ -64,7 +64,7 @@
}
/** The HAL callback, which should only be used in tests {@See BiometricTestSessionImpl}. */
- AidlResponseHandler getHalSessionCallback() {
+ public AidlResponseHandler getHalSessionCallback() {
return mAidlResponseHandler;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java
index c15049b..c41b706 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java
@@ -34,7 +34,7 @@
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.ErrorConsumer;
import com.android.server.biometrics.sensors.HalClientMonitor;
-import com.android.server.biometrics.sensors.face.hidl.AidlToHidlAdapter;
+import com.android.server.biometrics.sensors.face.hidl.HidlToAidlSessionAdapter;
import java.util.HashMap;
import java.util.Map;
@@ -75,8 +75,8 @@
protected void startHalOperation() {
try {
ISession session = getFreshDaemon().getSession();
- if (session instanceof AidlToHidlAdapter) {
- ((AidlToHidlAdapter) session).setFeature(mFeature);
+ if (session instanceof HidlToAidlSessionAdapter) {
+ ((HidlToAidlSessionAdapter) session).setFeature(mFeature);
}
session.getFeatures();
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index dd9c6d5..9fa15b8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -24,6 +24,7 @@
import android.app.TaskStackListener;
import android.content.Context;
import android.content.pm.UserInfo;
+import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.IInvalidationCallback;
@@ -51,6 +52,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.AuthenticationStatsBroadcastReceiver;
import com.android.server.biometrics.AuthenticationStatsCollector;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
@@ -64,11 +66,13 @@
import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
import com.android.server.biometrics.sensors.InvalidationRequesterClient;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.PerformanceTracker;
import com.android.server.biometrics.sensors.SensorList;
import com.android.server.biometrics.sensors.face.FaceUtils;
import com.android.server.biometrics.sensors.face.ServiceProvider;
import com.android.server.biometrics.sensors.face.UsageStats;
+import com.android.server.biometrics.sensors.face.hidl.HidlToAidlSensorAdapter;
import org.json.JSONArray;
import org.json.JSONException;
@@ -152,9 +156,11 @@
@NonNull SensorProps[] props,
@NonNull String halInstanceName,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull BiometricContext biometricContext) {
+ @NonNull BiometricContext biometricContext,
+ boolean resetLockoutRequiresChallenge) {
this(context, biometricStateCallback, props, halInstanceName, lockoutResetDispatcher,
- biometricContext, null /* daemon */);
+ biometricContext, null /* daemon */, resetLockoutRequiresChallenge,
+ false /* testHalEnabled */);
}
@VisibleForTesting FaceProvider(@NonNull Context context,
@@ -163,7 +169,8 @@
@NonNull String halInstanceName,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull BiometricContext biometricContext,
- IFace daemon) {
+ @Nullable IFace daemon, boolean resetLockoutRequiresChallenge,
+ boolean testHalEnabled) {
mContext = context;
mBiometricStateCallback = biometricStateCallback;
mHalInstanceName = halInstanceName;
@@ -176,48 +183,118 @@
mBiometricContext = biometricContext;
mAuthSessionCoordinator = mBiometricContext.getAuthSessionCoordinator();
mDaemon = daemon;
+ mTestHalEnabled = testHalEnabled;
- AuthenticationStatsBroadcastReceiver mBroadcastReceiver =
- new AuthenticationStatsBroadcastReceiver(
- mContext,
- BiometricsProtoEnums.MODALITY_FACE,
- (AuthenticationStatsCollector collector) -> {
- Slog.d(getTag(), "Initializing AuthenticationStatsCollector");
- mAuthenticationStatsCollector = collector;
- });
+ initAuthenticationBroadcastReceiver();
+ initSensors(resetLockoutRequiresChallenge, props);
+ }
- for (SensorProps prop : props) {
- final int sensorId = prop.commonProps.sensorId;
+ private void initAuthenticationBroadcastReceiver() {
+ new AuthenticationStatsBroadcastReceiver(
+ mContext,
+ BiometricsProtoEnums.MODALITY_FACE,
+ (AuthenticationStatsCollector collector) -> {
+ Slog.d(getTag(), "Initializing AuthenticationStatsCollector");
+ mAuthenticationStatsCollector = collector;
+ });
+ }
- final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
- if (prop.commonProps.componentInfo != null) {
- for (ComponentInfo info : prop.commonProps.componentInfo) {
- componentInfo.add(new ComponentInfoInternal(info.componentId,
- info.hardwareVersion, info.firmwareVersion, info.serialNumber,
- info.softwareVersion));
+ private void initSensors(boolean resetLockoutRequiresChallenge, SensorProps[] props) {
+ if (Flags.deHidl()) {
+ if (resetLockoutRequiresChallenge) {
+ Slog.d(getTag(), "Adding HIDL configs");
+ for (SensorProps prop : props) {
+ addHidlSensors(prop, resetLockoutRequiresChallenge);
+ }
+ } else {
+ Slog.d(getTag(), "Adding AIDL configs");
+ for (SensorProps prop : props) {
+ addAidlSensors(prop, resetLockoutRequiresChallenge);
}
}
+ } else {
+ for (SensorProps prop : props) {
+ final int sensorId = prop.commonProps.sensorId;
- final FaceSensorPropertiesInternal internalProp = new FaceSensorPropertiesInternal(
- prop.commonProps.sensorId, prop.commonProps.sensorStrength,
- prop.commonProps.maxEnrollmentsPerUser, componentInfo, prop.sensorType,
- prop.supportsDetectInteraction, prop.halControlsPreview,
- false /* resetLockoutRequiresChallenge */);
- final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler,
- internalProp, lockoutResetDispatcher, mBiometricContext);
- final int userId = sensor.getLazySession().get() == null ? UserHandle.USER_NULL :
- sensor.getLazySession().get().getUserId();
- mFaceSensors.addSensor(sensorId, sensor, userId,
- new SynchronousUserSwitchObserver() {
- @Override
- public void onUserSwitching(int newUserId) {
- scheduleInternalCleanup(sensorId, newUserId, null /* callback */);
- }
- });
- Slog.d(getTag(), "Added: " + internalProp);
+ final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
+ if (prop.commonProps.componentInfo != null) {
+ for (ComponentInfo info : prop.commonProps.componentInfo) {
+ componentInfo.add(new ComponentInfoInternal(info.componentId,
+ info.hardwareVersion, info.firmwareVersion, info.serialNumber,
+ info.softwareVersion));
+ }
+ }
+
+ final FaceSensorPropertiesInternal internalProp = new FaceSensorPropertiesInternal(
+ prop.commonProps.sensorId, prop.commonProps.sensorStrength,
+ prop.commonProps.maxEnrollmentsPerUser, componentInfo, prop.sensorType,
+ prop.supportsDetectInteraction, prop.halControlsPreview,
+ false /* resetLockoutRequiresChallenge */);
+ final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this,
+ mContext, mHandler, internalProp, mLockoutResetDispatcher,
+ mBiometricContext);
+ sensor.init(mLockoutResetDispatcher, this);
+ final int userId = sensor.getLazySession().get() == null ? UserHandle.USER_NULL :
+ sensor.getLazySession().get().getUserId();
+ mFaceSensors.addSensor(sensorId, sensor, userId,
+ new SynchronousUserSwitchObserver() {
+ @Override
+ public void onUserSwitching(int newUserId) {
+ scheduleInternalCleanup(sensorId, newUserId, null /* callback */);
+ }
+ });
+ Slog.d(getTag(), "Added: " + internalProp);
+ }
}
}
+ private void addHidlSensors(SensorProps prop, boolean resetLockoutRequiresChallenge) {
+ final int sensorId = prop.commonProps.sensorId;
+ final Sensor sensor = new HidlToAidlSensorAdapter(getTag() + "/" + sensorId, this,
+ mContext, mHandler, prop, mLockoutResetDispatcher,
+ mBiometricContext, resetLockoutRequiresChallenge,
+ () -> {
+ //TODO: update to make this testable
+ scheduleInternalCleanup(sensorId, ActivityManager.getCurrentUser(),
+ null /* callback */);
+ scheduleGetFeature(sensorId, new Binder(), ActivityManager.getCurrentUser(),
+ BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION, null,
+ mContext.getOpPackageName());
+ });
+ sensor.init(mLockoutResetDispatcher, this);
+ final int userId = sensor.getLazySession().get() == null ? UserHandle.USER_NULL :
+ sensor.getLazySession().get().getUserId();
+ mFaceSensors.addSensor(sensorId, sensor, userId,
+ new SynchronousUserSwitchObserver() {
+ @Override
+ public void onUserSwitching(int newUserId) {
+ scheduleInternalCleanup(sensorId, newUserId, null /* callback */);
+ scheduleGetFeature(sensorId, new Binder(), newUserId,
+ BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION,
+ null, mContext.getOpPackageName());
+ }
+ });
+ Slog.d(getTag(), "Added: " + mFaceSensors.get(sensorId));
+ }
+
+ private void addAidlSensors(SensorProps prop, boolean resetLockoutRequiresChallenge) {
+ final int sensorId = prop.commonProps.sensorId;
+ final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext,
+ mHandler, prop, mLockoutResetDispatcher, mBiometricContext,
+ resetLockoutRequiresChallenge);
+ sensor.init(mLockoutResetDispatcher, this);
+ final int userId = sensor.getLazySession().get() == null ? UserHandle.USER_NULL :
+ sensor.getLazySession().get().getUserId();
+ mFaceSensors.addSensor(sensorId, sensor, userId,
+ new SynchronousUserSwitchObserver() {
+ @Override
+ public void onUserSwitching(int newUserId) {
+ scheduleInternalCleanup(sensorId, newUserId, null /* callback */);
+ }
+ });
+ Slog.d(getTag(), "Added: " + mFaceSensors.get(sensorId));
+ }
+
private String getTag() {
return "FaceProvider/" + mHalInstanceName;
}
@@ -290,7 +367,10 @@
}
}
- private void scheduleLoadAuthenticatorIdsForUser(int sensorId, int userId) {
+ /**
+ * Schedules FaceGetAuthenticatorIdClient for specific sensor and user.
+ */
+ protected void scheduleLoadAuthenticatorIdsForUser(int sensorId, int userId) {
mHandler.post(() -> {
final FaceGetAuthenticatorIdClient client = new FaceGetAuthenticatorIdClient(
mContext, mFaceSensors.get(sensorId).getLazySession(), userId,
@@ -365,8 +445,12 @@
@Override
public int getLockoutModeForUser(int sensorId, int userId) {
- return mBiometricContext.getAuthSessionCoordinator().getLockoutStateFor(userId,
- Utils.getCurrentStrength(sensorId));
+ if (Flags.deHidl()) {
+ return mFaceSensors.get(sensorId).getLockoutModeForUser(userId);
+ } else {
+ return mBiometricContext.getAuthSessionCoordinator().getLockoutStateFor(userId,
+ Utils.getCurrentStrength(sensorId));
+ }
}
@Override
@@ -376,13 +460,18 @@
@Override
public boolean isHardwareDetected(int sensorId) {
- return hasHalInstance();
+ if (Flags.deHidl()) {
+ return mFaceSensors.get(sensorId).isHardwareDetected(mHalInstanceName);
+ } else {
+ return hasHalInstance();
+ }
}
@Override
public void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token,
@NonNull IFaceServiceReceiver receiver, String opPackageName) {
mHandler.post(() -> {
+ mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId);
final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext,
mFaceSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), userId, opPackageName, sensorId,
@@ -416,6 +505,7 @@
@Nullable Surface previewSurface, boolean debugConsent) {
final long id = mRequestCounter.incrementAndGet();
mHandler.post(() -> {
+ mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId);
final int maxTemplatesPerUser = mFaceSensors.get(
sensorId).getSensorProperties().maxEnrollmentsPerUser;
final FaceEnrollClient client = new FaceEnrollClient(mContext,
@@ -427,18 +517,23 @@
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
mBiometricContext, maxTemplatesPerUser, debugConsent);
- scheduleForSensor(sensorId, client, new ClientMonitorCompositeCallback(
- mBiometricStateCallback, new ClientMonitorCallback() {
- @Override
- public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
- boolean success) {
- ClientMonitorCallback.super.onClientFinished(clientMonitor, success);
- if (success) {
- scheduleLoadAuthenticatorIdsForUser(sensorId, userId);
- scheduleInvalidationRequest(sensorId, userId);
+ if (Flags.deHidl()) {
+ scheduleForSensor(sensorId, client, mBiometricStateCallback);
+ } else {
+ scheduleForSensor(sensorId, client, new ClientMonitorCompositeCallback(
+ mBiometricStateCallback, new ClientMonitorCallback() {
+ @Override
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
+ boolean success) {
+ ClientMonitorCallback.super.onClientFinished(clientMonitor,
+ success);
+ if (success) {
+ scheduleLoadAuthenticatorIdsForUser(sensorId, userId);
+ scheduleInvalidationRequest(sensorId, userId);
+ }
}
- }
- }));
+ }));
+ }
});
return id;
}
@@ -486,6 +581,13 @@
final int userId = options.getUserId();
final int sensorId = options.getSensorId();
final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
+ mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId);
+ final LockoutTracker lockoutTracker;
+ if (Flags.deHidl()) {
+ lockoutTracker = mFaceSensors.get(sensorId).getLockoutTracker(true /* forAuth */);
+ } else {
+ lockoutTracker = null;
+ }
final FaceAuthenticationClient client = new FaceAuthenticationClient(
mContext, mFaceSensors.get(sensorId).getLazySession(), token, requestId,
callback, operationId, restricted, options, cookie,
@@ -493,7 +595,7 @@
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
mAuthenticationStatsCollector),
mBiometricContext, isStrongBiometric,
- mUsageStats, null /* lockoutTracker */,
+ mUsageStats, lockoutTracker,
allowBackgroundAuthentication, Utils.getCurrentStrength(sensorId));
scheduleForSensor(sensorId, client, new ClientMonitorCallback() {
@Override
@@ -555,6 +657,7 @@
private void scheduleRemoveSpecifiedIds(int sensorId, @NonNull IBinder token, int[] faceIds,
int userId, @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
mHandler.post(() -> {
+ mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId);
final FaceRemovalClient client = new FaceRemovalClient(mContext,
mFaceSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), faceIds, userId,
@@ -571,6 +674,7 @@
@Override
public void scheduleResetLockout(int sensorId, int userId, @NonNull byte[] hardwareAuthToken) {
mHandler.post(() -> {
+ mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId);
final FaceResetLockoutClient client = new FaceResetLockoutClient(
mContext, mFaceSensors.get(sensorId).getLazySession(), userId,
mContext.getOpPackageName(), sensorId,
@@ -578,8 +682,8 @@
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
mBiometricContext, hardwareAuthToken,
- mFaceSensors.get(sensorId).getLockoutCache(), mLockoutResetDispatcher,
- Utils.getCurrentStrength(sensorId));
+ mFaceSensors.get(sensorId).getLockoutTracker(false/* forAuth */),
+ mLockoutResetDispatcher, Utils.getCurrentStrength(sensorId));
scheduleForSensor(sensorId, client);
});
@@ -590,6 +694,7 @@
boolean enabled, @NonNull byte[] hardwareAuthToken,
@NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
mHandler.post(() -> {
+ mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId);
final List<Face> faces = FaceUtils.getInstance(sensorId)
.getBiometricsForUser(mContext, userId);
if (faces.isEmpty()) {
@@ -610,6 +715,7 @@
public void scheduleGetFeature(int sensorId, @NonNull IBinder token, int userId, int feature,
@NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName) {
mHandler.post(() -> {
+ mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId);
final List<Face> faces = FaceUtils.getInstance(sensorId)
.getBiometricsForUser(mContext, userId);
if (faces.isEmpty()) {
@@ -641,6 +747,7 @@
public void scheduleInternalCleanup(int sensorId, int userId,
@Nullable ClientMonitorCallback callback, boolean favorHalEnrollments) {
mHandler.post(() -> {
+ mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId);
final FaceInternalCleanupClient client =
new FaceInternalCleanupClient(mContext,
mFaceSensors.get(sensorId).getLazySession(), userId,
@@ -760,4 +867,8 @@
}
biometricScheduler.startWatchdog();
}
+
+ public boolean getTestHalEnabled() {
+ return mTestHalEnabled;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
index 77b5592..d02eefa 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.face.IFace;
+import android.hardware.biometrics.face.ISession;
import android.hardware.keymaster.HardwareAuthToken;
import android.os.RemoteException;
import android.util.Slog;
@@ -34,6 +35,7 @@
import com.android.server.biometrics.sensors.HalClientMonitor;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.face.hidl.HidlToAidlSessionAdapter;
import java.util.function.Supplier;
@@ -47,7 +49,7 @@
private static final String TAG = "FaceResetLockoutClient";
private final HardwareAuthToken mHardwareAuthToken;
- private final LockoutTracker mLockoutCache;
+ private final LockoutTracker mLockoutTracker;
private final LockoutResetDispatcher mLockoutResetDispatcher;
private final int mBiometricStrength;
@@ -60,7 +62,7 @@
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
0 /* cookie */, sensorId, logger, biometricContext);
mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken);
- mLockoutCache = lockoutTracker;
+ mLockoutTracker = lockoutTracker;
mLockoutResetDispatcher = lockoutResetDispatcher;
mBiometricStrength = biometricStrength;
}
@@ -79,7 +81,11 @@
@Override
protected void startHalOperation() {
try {
- getFreshDaemon().getSession().resetLockout(mHardwareAuthToken);
+ final ISession session = getFreshDaemon().getSession();
+ session.resetLockout(mHardwareAuthToken);
+ if (session instanceof HidlToAidlSessionAdapter) {
+ mCallback.onClientFinished(this, true /* success */);
+ }
} catch (RemoteException e) {
Slog.e(TAG, "Unable to reset lockout", e);
mCallback.onClientFinished(this, false /* success */);
@@ -87,7 +93,7 @@
}
void onLockoutCleared() {
- resetLocalLockoutStateToNone(getSensorId(), getTargetUserId(), mLockoutCache,
+ resetLocalLockoutStateToNone(getSensorId(), getTargetUserId(), mLockoutTracker,
mLockoutResetDispatcher, getBiometricContext().getAuthSessionCoordinator(),
mBiometricStrength, getRequestId());
mCallback.onClientFinished(this, true /* success */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index 54e66eb..3e5c599 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -21,16 +21,20 @@
import android.content.Context;
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
+import android.hardware.biometrics.common.ComponentInfo;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.ISession;
+import android.hardware.biometrics.face.SensorProps;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
@@ -38,6 +42,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.SensorServiceStateProto;
import com.android.server.biometrics.SensorStateProto;
import com.android.server.biometrics.UserStateProto;
@@ -49,12 +54,15 @@
import com.android.server.biometrics.sensors.ErrorConsumer;
import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.StartUserClient;
import com.android.server.biometrics.sensors.StopUserClient;
import com.android.server.biometrics.sensors.UserAwareBiometricScheduler;
import com.android.server.biometrics.sensors.face.FaceUtils;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
@@ -71,25 +79,53 @@
@NonNull private final IBinder mToken;
@NonNull private final Handler mHandler;
@NonNull private final FaceSensorPropertiesInternal mSensorProperties;
- @NonNull private final UserAwareBiometricScheduler mScheduler;
- @NonNull private final LockoutCache mLockoutCache;
+ @NonNull private BiometricScheduler mScheduler;
+ @Nullable private LockoutTracker mLockoutTracker;
@NonNull private final Map<Integer, Long> mAuthenticatorIds;
- @NonNull private final Supplier<AidlSession> mLazySession;
+ @NonNull private Supplier<AidlSession> mLazySession;
@Nullable AidlSession mCurrentSession;
+ @NonNull BiometricContext mBiometricContext;
Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context,
@NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull BiometricContext biometricContext, AidlSession session) {
+ @NonNull BiometricContext biometricContext, @Nullable AidlSession session) {
mTag = tag;
mProvider = provider;
mContext = context;
mToken = new Binder();
mHandler = handler;
mSensorProperties = sensorProperties;
- mScheduler = new UserAwareBiometricScheduler(tag,
+ mBiometricContext = biometricContext;
+ mAuthenticatorIds = new HashMap<>();
+ }
+
+ Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context,
+ @NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull BiometricContext biometricContext) {
+ this(tag, provider, context, handler, sensorProperties, lockoutResetDispatcher,
+ biometricContext, null);
+ }
+
+ public Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context,
+ @NonNull Handler handler, @NonNull SensorProps prop,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull BiometricContext biometricContext,
+ boolean resetLockoutRequiresChallenge) {
+ this(tag, provider, context, handler,
+ getFaceSensorPropertiesInternal(prop, resetLockoutRequiresChallenge),
+ lockoutResetDispatcher, biometricContext, null);
+ }
+
+ /**
+ * Initialize biometric scheduler, lockout tracker and session for the sensor.
+ */
+ public void init(LockoutResetDispatcher lockoutResetDispatcher,
+ FaceProvider provider) {
+ mScheduler = new UserAwareBiometricScheduler(mTag,
BiometricScheduler.SENSOR_TYPE_FACE, null /* gestureAvailabilityDispatcher */,
() -> mCurrentSession != null ? mCurrentSession.getUserId() : UserHandle.USER_NULL,
new UserAwareBiometricScheduler.UserSwitchCallback() {
@@ -98,7 +134,7 @@
public StopUserClient<?> getStopUserClient(int userId) {
return new FaceStopUserClient(mContext, mLazySession, mToken, userId,
mSensorProperties.sensorId,
- BiometricLogger.ofUnknown(mContext), biometricContext,
+ BiometricLogger.ofUnknown(mContext), mBiometricContext,
() -> mCurrentSession = null);
}
@@ -107,13 +143,36 @@
public StartUserClient<?, ?> getStartUserClient(int newUserId) {
final int sensorId = mSensorProperties.sensorId;
- final AidlResponseHandler resultController = new AidlResponseHandler(
- mContext, mScheduler, sensorId, newUserId,
- mLockoutCache, lockoutResetDispatcher,
- biometricContext.getAuthSessionCoordinator(), () -> {
- Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE");
- mCurrentSession = null;
- });
+ final AidlResponseHandler resultController;
+ if (Flags.deHidl()) {
+ resultController = new AidlResponseHandler(
+ mContext, mScheduler, sensorId, newUserId,
+ mLockoutTracker, lockoutResetDispatcher,
+ mBiometricContext.getAuthSessionCoordinator(), () -> {},
+ new AidlResponseHandler.AidlResponseHandlerCallback() {
+ @Override
+ public void onEnrollSuccess() {
+ mProvider.scheduleLoadAuthenticatorIdsForUser(sensorId,
+ newUserId);
+ mProvider.scheduleInvalidationRequest(sensorId,
+ newUserId);
+ }
+
+ @Override
+ public void onHardwareUnavailable() {
+ Slog.e(mTag, "Face sensor hardware unavailable.");
+ mCurrentSession = null;
+ }
+ });
+ } else {
+ resultController = new AidlResponseHandler(
+ mContext, mScheduler, sensorId, newUserId,
+ mLockoutTracker, lockoutResetDispatcher,
+ mBiometricContext.getAuthSessionCoordinator(), () -> {
+ Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE");
+ mCurrentSession = null;
+ });
+ }
final StartUserClient.UserStartedCallback<ISession> userStartedCallback =
(userIdStarted, newSession, halInterfaceVersion) -> {
@@ -136,32 +195,42 @@
return new FaceStartUserClient(mContext, provider::getHalInstance,
mToken, newUserId, mSensorProperties.sensorId,
- BiometricLogger.ofUnknown(mContext), biometricContext,
+ BiometricLogger.ofUnknown(mContext), mBiometricContext,
resultController, userStartedCallback);
}
});
- mLockoutCache = new LockoutCache();
- mAuthenticatorIds = new HashMap<>();
mLazySession = () -> mCurrentSession != null ? mCurrentSession : null;
+ mLockoutTracker = new LockoutCache();
}
- Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context,
- @NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties,
- @NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull BiometricContext biometricContext) {
- this(tag, provider, context, handler, sensorProperties, lockoutResetDispatcher,
- biometricContext, null);
+ private static FaceSensorPropertiesInternal getFaceSensorPropertiesInternal(SensorProps prop,
+ boolean resetLockoutRequiresChallenge) {
+ final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
+ if (prop.commonProps.componentInfo != null) {
+ for (ComponentInfo info : prop.commonProps.componentInfo) {
+ componentInfo.add(new ComponentInfoInternal(info.componentId,
+ info.hardwareVersion, info.firmwareVersion, info.serialNumber,
+ info.softwareVersion));
+ }
+ }
+ final FaceSensorPropertiesInternal internalProp = new FaceSensorPropertiesInternal(
+ prop.commonProps.sensorId, prop.commonProps.sensorStrength,
+ prop.commonProps.maxEnrollmentsPerUser, componentInfo, prop.sensorType,
+ prop.supportsDetectInteraction, prop.halControlsPreview,
+ resetLockoutRequiresChallenge);
+
+ return internalProp;
}
- @NonNull Supplier<AidlSession> getLazySession() {
+ @NonNull public Supplier<AidlSession> getLazySession() {
return mLazySession;
}
- @NonNull FaceSensorPropertiesInternal getSensorProperties() {
+ @NonNull protected FaceSensorPropertiesInternal getSensorProperties() {
return mSensorProperties;
}
- @VisibleForTesting @Nullable AidlSession getSessionForUser(int userId) {
+ @VisibleForTesting @Nullable protected AidlSession getSessionForUser(int userId) {
if (mCurrentSession != null && mCurrentSession.getUserId() == userId) {
return mCurrentSession;
} else {
@@ -174,15 +243,18 @@
mProvider, this);
}
- @NonNull BiometricScheduler getScheduler() {
+ @NonNull public BiometricScheduler getScheduler() {
return mScheduler;
}
- @NonNull LockoutCache getLockoutCache() {
- return mLockoutCache;
+ @NonNull protected LockoutTracker getLockoutTracker(boolean forAuth) {
+ if (forAuth) {
+ return null;
+ }
+ return mLockoutTracker;
}
- @NonNull Map<Integer, Long> getAuthenticatorIds() {
+ @NonNull protected Map<Integer, Long> getAuthenticatorIds() {
return mAuthenticatorIds;
}
@@ -253,4 +325,49 @@
mScheduler.reset();
mCurrentSession = null;
}
+
+ protected BiometricContext getBiometricContext() {
+ return mBiometricContext;
+ }
+
+ protected Handler getHandler() {
+ return mHandler;
+ }
+
+ protected Context getContext() {
+ return mContext;
+ }
+
+ /**
+ * Schedules FaceUpdateActiveUserClient for user id.
+ */
+ public void scheduleFaceUpdateActiveUserClient(int userId) {}
+
+ /**
+ * Returns true if the sensor hardware is detected.
+ */
+ public boolean isHardwareDetected(String halInstanceName) {
+ if (mTestHalEnabled) {
+ return true;
+ }
+ return ServiceManager.checkService(IFace.DESCRIPTOR + "/" + halInstanceName) != null;
+ }
+
+ /**
+ * Returns lockout mode of this sensor.
+ */
+ @LockoutTracker.LockoutMode
+ public int getLockoutModeForUser(int userId) {
+ return mBiometricContext.getAuthSessionCoordinator().getLockoutStateFor(userId,
+ Utils.getCurrentStrength(mSensorProperties.sensorId));
+ }
+
+ public void setScheduler(BiometricScheduler scheduler) {
+ mScheduler = scheduler;
+ }
+
+ public void setLazySession(
+ Supplier<AidlSession> lazySession) {
+ mLazySession = lazySession;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
index 8385c3f..0c34d70 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
@@ -27,13 +27,14 @@
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
-import com.android.server.biometrics.sensors.HalClientMonitor;
+import com.android.server.biometrics.sensors.StartUserClient;
+import com.android.server.biometrics.sensors.face.aidl.AidlSession;
import java.io.File;
import java.util.Map;
import java.util.function.Supplier;
-public class FaceUpdateActiveUserClient extends HalClientMonitor<IBiometricsFace> {
+public class FaceUpdateActiveUserClient extends StartUserClient<IBiometricsFace, AidlSession> {
private static final String TAG = "FaceUpdateActiveUserClient";
private static final String FACE_DATA_DIR = "facedata";
@@ -45,8 +46,18 @@
int sensorId, @NonNull BiometricLogger logger,
@NonNull BiometricContext biometricContext, boolean hasEnrolledBiometrics,
@NonNull Map<Integer, Long> authenticatorIds) {
- super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
- 0 /* cookie */, sensorId, logger, biometricContext);
+ this(context, lazyDaemon, (newUserId, newUser, halInterfaceVersion) -> {},
+ userId, owner, sensorId, logger, biometricContext, hasEnrolledBiometrics,
+ authenticatorIds);
+ }
+
+ FaceUpdateActiveUserClient(@NonNull Context context,
+ @NonNull Supplier<IBiometricsFace> lazyDaemon, UserStartedCallback userStartedCallback,
+ int userId, @NonNull String owner, int sensorId, @NonNull BiometricLogger logger,
+ @NonNull BiometricContext biometricContext, boolean hasEnrolledBiometrics,
+ @NonNull Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, null /* token */, userId, sensorId, logger, biometricContext,
+ userStartedCallback);
mHasEnrolledBiometrics = hasEnrolledBiometrics;
mAuthenticatorIds = authenticatorIds;
}
@@ -77,6 +88,7 @@
daemon.setActiveUser(getTargetUserId(), storePath.getAbsolutePath());
mAuthenticatorIds.put(getTargetUserId(),
mHasEnrolledBiometrics ? daemon.getAuthenticatorId().value : 0L);
+ mUserStartedCallback.onUserStarted(getTargetUserId(), null, 0);
mCallback.onClientFinished(this, true /* success */);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to setActiveUser: " + e);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlCallbackConverter.java
index 36a9790..7a574ce 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlCallbackConverter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlCallbackConverter.java
@@ -112,4 +112,8 @@
void onAuthenticatorIdRetrieved(long authenticatorId) {
mAidlResponseHandler.onAuthenticatorIdRetrieved(authenticatorId);
}
+
+ void onUnsupportedClientScheduled() {
+ mAidlResponseHandler.onUnsupportedClientScheduled();
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java
new file mode 100644
index 0000000..6355cb5
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face.hidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.hardware.biometrics.face.SensorProps;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.os.Handler;
+import android.os.IHwBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.AuthSessionCoordinator;
+import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.StartUserClient;
+import com.android.server.biometrics.sensors.face.FaceUtils;
+import com.android.server.biometrics.sensors.face.LockoutHalImpl;
+import com.android.server.biometrics.sensors.face.aidl.AidlResponseHandler;
+import com.android.server.biometrics.sensors.face.aidl.AidlSession;
+import com.android.server.biometrics.sensors.face.aidl.FaceProvider;
+import com.android.server.biometrics.sensors.face.aidl.Sensor;
+
+/**
+ * Convert HIDL sensor configurations to an AIDL Sensor.
+ */
+public class HidlToAidlSensorAdapter extends Sensor implements IHwBinder.DeathRecipient{
+
+ private static final String TAG = "HidlToAidlSensorAdapter";
+
+ private IBiometricsFace mDaemon;
+ private AidlSession mSession;
+ private int mCurrentUserId = UserHandle.USER_NULL;
+ private final Runnable mInternalCleanupAndGetFeatureRunnable;
+ private final FaceProvider mFaceProvider;
+ private final LockoutResetDispatcher mLockoutResetDispatcher;
+ private final AuthSessionCoordinator mAuthSessionCoordinator;
+ private final AidlResponseHandler.AidlResponseHandlerCallback mAidlResponseHandlerCallback;
+ private final StartUserClient.UserStartedCallback<AidlSession> mUserStartedCallback =
+ (newUserId, newUser, halInterfaceVersion) -> {
+ if (newUserId != mCurrentUserId) {
+ handleUserChanged(newUserId);
+ }
+ };
+ private LockoutHalImpl mLockoutTracker;
+
+ public HidlToAidlSensorAdapter(@NonNull String tag,
+ @NonNull FaceProvider provider,
+ @NonNull Context context,
+ @NonNull Handler handler,
+ @NonNull SensorProps prop,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull BiometricContext biometricContext,
+ boolean resetLockoutRequiresChallenge,
+ @NonNull Runnable internalCleanupAndGetFeatureRunnable) {
+ this(tag, provider, context, handler, prop, lockoutResetDispatcher, biometricContext,
+ resetLockoutRequiresChallenge, internalCleanupAndGetFeatureRunnable,
+ new AuthSessionCoordinator(), null /* daemon */,
+ null /* onEnrollSuccessCallback */);
+ }
+
+ @VisibleForTesting
+ HidlToAidlSensorAdapter(@NonNull String tag,
+ @NonNull FaceProvider provider,
+ @NonNull Context context,
+ @NonNull Handler handler,
+ @NonNull SensorProps prop,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull BiometricContext biometricContext,
+ boolean resetLockoutRequiresChallenge,
+ @NonNull Runnable internalCleanupAndGetFeatureRunnable,
+ @NonNull AuthSessionCoordinator authSessionCoordinator,
+ @Nullable IBiometricsFace daemon,
+ @Nullable AidlResponseHandler.AidlResponseHandlerCallback aidlResponseHandlerCallback) {
+ super(tag, provider, context, handler, prop, lockoutResetDispatcher, biometricContext,
+ resetLockoutRequiresChallenge);
+ mInternalCleanupAndGetFeatureRunnable = internalCleanupAndGetFeatureRunnable;
+ mFaceProvider = provider;
+ mLockoutResetDispatcher = lockoutResetDispatcher;
+ mAuthSessionCoordinator = authSessionCoordinator;
+ mDaemon = daemon;
+ mAidlResponseHandlerCallback = aidlResponseHandlerCallback == null
+ ? new AidlResponseHandler.AidlResponseHandlerCallback() {
+ @Override
+ public void onEnrollSuccess() {
+ scheduleFaceUpdateActiveUserClient(mCurrentUserId);
+ }
+
+ @Override
+ public void onHardwareUnavailable() {
+ mDaemon = null;
+ mCurrentUserId = UserHandle.USER_NULL;
+ }
+ } : aidlResponseHandlerCallback;
+ }
+
+ @Override
+ public void scheduleFaceUpdateActiveUserClient(int userId) {
+ getScheduler().scheduleClientMonitor(getFaceUpdateActiveUserClient(userId));
+ }
+
+ @Override
+ public void serviceDied(long cookie) {
+ Slog.d(TAG, "HAL died.");
+ mDaemon = null;
+ }
+
+ @Override
+ public boolean isHardwareDetected(String halInstanceName) {
+ return getIBiometricsFace() != null;
+ }
+
+ @Override
+ @LockoutTracker.LockoutMode
+ public int getLockoutModeForUser(int userId) {
+ return mLockoutTracker.getLockoutModeForUser(userId);
+ }
+
+ @Override
+ public void init(LockoutResetDispatcher lockoutResetDispatcher,
+ FaceProvider provider) {
+ setScheduler(new BiometricScheduler(TAG, BiometricScheduler.SENSOR_TYPE_FACE,
+ null /* gestureAvailabilityTracker */));
+ setLazySession(this::getSession);
+ mLockoutTracker = new LockoutHalImpl();
+ }
+
+ @Override
+ @VisibleForTesting @Nullable protected AidlSession getSessionForUser(int userId) {
+ if (mSession != null && mSession.getUserId() == userId) {
+ return mSession;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ protected LockoutTracker getLockoutTracker(boolean forAuth) {
+ return mLockoutTracker;
+ }
+
+ @NonNull AidlSession getSession() {
+ if (mDaemon != null && mSession != null) {
+ return mSession;
+ } else {
+ return mSession = new AidlSession(getContext(), this::getIBiometricsFace,
+ mCurrentUserId, getAidlResponseHandler());
+ }
+ }
+
+ private AidlResponseHandler getAidlResponseHandler() {
+ return new AidlResponseHandler(getContext(), getScheduler(), getSensorProperties().sensorId,
+ mCurrentUserId, mLockoutTracker, mLockoutResetDispatcher,
+ mAuthSessionCoordinator, () -> {}, mAidlResponseHandlerCallback);
+ }
+
+ private IBiometricsFace getIBiometricsFace() {
+ if (mFaceProvider.getTestHalEnabled()) {
+ final TestHal testHal = new TestHal(getContext(), getSensorProperties().sensorId);
+ testHal.setCallback(new HidlToAidlCallbackConverter(getAidlResponseHandler()));
+ return testHal;
+ }
+
+ if (mDaemon != null) {
+ return mDaemon;
+ }
+
+ Slog.d(TAG, "Daemon was null, reconnecting, current operation: "
+ + getScheduler().getCurrentClient());
+
+ try {
+ mDaemon = IBiometricsFace.getService();
+ } catch (java.util.NoSuchElementException e) {
+ // Service doesn't exist or cannot be opened.
+ Slog.w(TAG, "NoSuchElementException", e);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get face HAL", e);
+ }
+
+ if (mDaemon == null) {
+ Slog.w(TAG, "Face HAL not available");
+ return null;
+ }
+
+ mDaemon.asBinder().linkToDeath(this, 0 /* flags */);
+
+ scheduleLoadAuthenticatorIds();
+ mInternalCleanupAndGetFeatureRunnable.run();
+ return mDaemon;
+ }
+
+ @VisibleForTesting void handleUserChanged(int newUserId) {
+ Slog.d(TAG, "User changed. Current user is " + newUserId);
+ mSession = null;
+ mCurrentUserId = newUserId;
+ }
+
+ private void scheduleLoadAuthenticatorIds() {
+ // Note that this can be performed on the scheduler (as opposed to being done immediately
+ // when the HAL is (re)loaded, since
+ // 1) If this is truly the first time it's being performed (e.g. system has just started),
+ // this will be run very early and way before any applications need to generate keys.
+ // 2) If this is being performed to refresh the authenticatorIds (e.g. HAL crashed and has
+ // just been reloaded), the framework already has a cache of the authenticatorIds. This
+ // is safe because authenticatorIds only change when A) new template has been enrolled,
+ // or B) all templates are removed.
+ getHandler().post(() -> {
+ for (UserInfo user : UserManager.get(getContext()).getAliveUsers()) {
+ final int targetUserId = user.id;
+ if (!getAuthenticatorIds().containsKey(targetUserId)) {
+ scheduleFaceUpdateActiveUserClient(targetUserId);
+ }
+ }
+ });
+ }
+
+ private FaceUpdateActiveUserClient getFaceUpdateActiveUserClient(int userId) {
+ return new FaceUpdateActiveUserClient(getContext(), this::getIBiometricsFace,
+ mUserStartedCallback, userId, TAG, getSensorProperties().sensorId,
+ BiometricLogger.ofUnknown(getContext()), getBiometricContext(),
+ !FaceUtils.getInstance(getSensorProperties().sensorId).getBiometricsForUser(
+ getContext(), userId).isEmpty(),
+ getAuthenticatorIds());
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapter.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapter.java
similarity index 88%
rename from services/core/java/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapter.java
rename to services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapter.java
index 489b213..5daf2d4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapter.java
@@ -47,34 +47,37 @@
import java.util.function.Supplier;
/**
- * Adapter to convert AIDL-specific interface {@link ISession} methods to HIDL implementation.
+ * Adapter to convert HIDL methods into AIDL interface {@link ISession}.
*/
-public class AidlToHidlAdapter implements ISession {
+public class HidlToAidlSessionAdapter implements ISession {
- private final String TAG = "AidlToHidlAdapter";
+ private static final String TAG = "HidlToAidlSessionAdapter";
+
private static final int CHALLENGE_TIMEOUT_SEC = 600;
@DurationMillisLong
private static final int GENERATE_CHALLENGE_REUSE_INTERVAL_MILLIS = 60 * 1000;
@DurationMillisLong
private static final int GENERATE_CHALLENGE_COUNTER_TTL_MILLIS = CHALLENGE_TIMEOUT_SEC * 1000;
private static final int INVALID_VALUE = -1;
+ @VisibleForTesting static final int ENROLL_TIMEOUT_SEC = 75;
+
private final Clock mClock;
private final List<Long> mGeneratedChallengeCount = new ArrayList<>();
- @VisibleForTesting static final int ENROLL_TIMEOUT_SEC = 75;
+ private final int mUserId;
+ private final Context mContext;
+
private long mGenerateChallengeCreatedAt = INVALID_VALUE;
private long mGenerateChallengeResult = INVALID_VALUE;
@NonNull private Supplier<IBiometricsFace> mSession;
- private final int mUserId;
private HidlToAidlCallbackConverter mHidlToAidlCallbackConverter;
- private final Context mContext;
private int mFeature = INVALID_VALUE;
- public AidlToHidlAdapter(Context context, Supplier<IBiometricsFace> session,
+ public HidlToAidlSessionAdapter(Context context, Supplier<IBiometricsFace> session,
int userId, AidlResponseHandler aidlResponseHandler) {
this(context, session, userId, aidlResponseHandler, Clock.systemUTC());
}
- AidlToHidlAdapter(Context context, Supplier<IBiometricsFace> session, int userId,
+ HidlToAidlSessionAdapter(Context context, Supplier<IBiometricsFace> session, int userId,
AidlResponseHandler aidlResponseHandler, Clock clock) {
mSession = session;
mUserId = userId;
@@ -83,42 +86,11 @@
setCallback(aidlResponseHandler);
}
- private void setCallback(AidlResponseHandler aidlResponseHandler) {
- mHidlToAidlCallbackConverter = new HidlToAidlCallbackConverter(aidlResponseHandler);
- try {
- mSession.get().setCallback(mHidlToAidlCallbackConverter);
- } catch (RemoteException e) {
- Slog.d(TAG, "Failed to set callback");
- }
- }
-
@Override
public IBinder asBinder() {
return null;
}
- private boolean isGeneratedChallengeCacheValid() {
- return mGenerateChallengeCreatedAt != INVALID_VALUE
- && mGenerateChallengeResult != INVALID_VALUE
- && mClock.millis() - mGenerateChallengeCreatedAt
- < GENERATE_CHALLENGE_REUSE_INTERVAL_MILLIS;
- }
-
- private void incrementChallengeCount() {
- mGeneratedChallengeCount.add(0, mClock.millis());
- }
-
- private int decrementChallengeCount() {
- final long now = mClock.millis();
- // ignore values that are old in case generate/revoke calls are not matched
- // this doesn't ensure revoke if calls are mismatched but it keeps the list from growing
- mGeneratedChallengeCount.removeIf(x -> now - x > GENERATE_CHALLENGE_COUNTER_TTL_MILLIS);
- if (!mGeneratedChallengeCount.isEmpty()) {
- mGeneratedChallengeCount.remove(0);
- }
- return mGeneratedChallengeCount.size();
- }
-
@Override
public void generateChallenge() throws RemoteException {
incrementChallengeCount();
@@ -150,7 +122,7 @@
@Override
public EnrollmentStageConfig[] getEnrollmentConfig(byte enrollmentType) throws RemoteException {
- //unsupported in HIDL
+ Slog.e(TAG, "getEnrollmentConfig unsupported in HIDL");
return null;
}
@@ -244,19 +216,6 @@
}
}
- private int getFaceId() {
- FaceManager faceManager = mContext.getSystemService(FaceManager.class);
- List<Face> faces = faceManager.getEnrolledFaces(mUserId);
- if (faces.isEmpty()) {
- Slog.d(TAG, "No faces to get feature from.");
- mHidlToAidlCallbackConverter.onError(0 /* deviceId */, mUserId,
- BiometricFaceConstants.FACE_ERROR_NOT_ENROLLED, 0 /* vendorCode */);
- return INVALID_VALUE;
- }
-
- return faces.get(0).getBiometricId();
- }
-
@Override
public void getAuthenticatorId() throws RemoteException {
long authenticatorId = mSession.get().getAuthenticatorId().value;
@@ -265,7 +224,8 @@
@Override
public void invalidateAuthenticatorId() throws RemoteException {
- //unsupported in HIDL
+ Slog.e(TAG, "invalidateAuthenticatorId unsupported in HIDL");
+ mHidlToAidlCallbackConverter.onUnsupportedClientScheduled();
}
@Override
@@ -279,47 +239,105 @@
@Override
public void close() throws RemoteException {
- //Unsupported in HIDL
+ Slog.e(TAG, "close unsupported in HIDL");
}
@Override
public ICancellationSignal authenticateWithContext(long operationId, OperationContext context)
throws RemoteException {
- //Unsupported in HIDL
- return null;
+ Slog.e(TAG, "authenticateWithContext unsupported in HIDL");
+ return authenticate(operationId);
}
@Override
public ICancellationSignal enrollWithContext(HardwareAuthToken hat, byte type, byte[] features,
NativeHandle previewSurface, OperationContext context) throws RemoteException {
- //Unsupported in HIDL
- return null;
+ Slog.e(TAG, "enrollWithContext unsupported in HIDL");
+ return enroll(hat, type, features, previewSurface);
}
@Override
public ICancellationSignal detectInteractionWithContext(OperationContext context)
throws RemoteException {
- //Unsupported in HIDL
- return null;
+ Slog.e(TAG, "detectInteractionWithContext unsupported in HIDL");
+ return detectInteraction();
}
@Override
public void onContextChanged(OperationContext context) throws RemoteException {
- //Unsupported in HIDL
+ Slog.e(TAG, "onContextChanged unsupported in HIDL");
}
@Override
public int getInterfaceVersion() throws RemoteException {
- //Unsupported in HIDL
+ Slog.e(TAG, "getInterfaceVersion unsupported in HIDL");
return 0;
}
@Override
public String getInterfaceHash() throws RemoteException {
+ Slog.e(TAG, "getInterfaceHash unsupported in HIDL");
+ return null;
+ }
+
+ @Override
+ public ICancellationSignal enrollWithOptions(FaceEnrollOptions options) {
//Unsupported in HIDL
return null;
}
+ private boolean isGeneratedChallengeCacheValid() {
+ return mGenerateChallengeCreatedAt != INVALID_VALUE
+ && mGenerateChallengeResult != INVALID_VALUE
+ && mClock.millis() - mGenerateChallengeCreatedAt
+ < GENERATE_CHALLENGE_REUSE_INTERVAL_MILLIS;
+ }
+
+ private void incrementChallengeCount() {
+ mGeneratedChallengeCount.add(0, mClock.millis());
+ }
+
+ private int decrementChallengeCount() {
+ final long now = mClock.millis();
+ // ignore values that are old in case generate/revoke calls are not matched
+ // this doesn't ensure revoke if calls are mismatched but it keeps the list from growing
+ mGeneratedChallengeCount.removeIf(x -> now - x > GENERATE_CHALLENGE_COUNTER_TTL_MILLIS);
+ if (!mGeneratedChallengeCount.isEmpty()) {
+ mGeneratedChallengeCount.remove(0);
+ }
+ return mGeneratedChallengeCount.size();
+ }
+
+ private void setCallback(AidlResponseHandler aidlResponseHandler) {
+ mHidlToAidlCallbackConverter = new HidlToAidlCallbackConverter(aidlResponseHandler);
+ try {
+ if (mSession.get() != null) {
+ long halId = mSession.get().setCallback(mHidlToAidlCallbackConverter).value;
+ Slog.d(TAG, "Face HAL ready, HAL ID: " + halId);
+ if (halId == 0) {
+ Slog.d(TAG, "Unable to set HIDL callback.");
+ }
+ } else {
+ Slog.e(TAG, "Unable to set HIDL callback. HIDL daemon is null.");
+ }
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Failed to set callback");
+ }
+ }
+
+ private int getFaceId() {
+ FaceManager faceManager = mContext.getSystemService(FaceManager.class);
+ List<Face> faces = faceManager.getEnrolledFaces(mUserId);
+ if (faces.isEmpty()) {
+ Slog.d(TAG, "No faces to get feature from.");
+ mHidlToAidlCallbackConverter.onError(0 /* deviceId */, mUserId,
+ BiometricFaceConstants.FACE_ERROR_NOT_ENROLLED, 0 /* vendorCode */);
+ return INVALID_VALUE;
+ }
+
+ return faces.get(0).getBiometricId();
+ }
+
/**
* Cancellation in HIDL occurs for the entire session, instead of a specific client.
*/
@@ -345,10 +363,4 @@
return null;
}
}
-
- @Override
- public ICancellationSignal enrollWithOptions(FaceEnrollOptions options) {
- //Unsupported in HIDL
- return null;
- }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 83b306b..e01d672 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -45,9 +45,11 @@
import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.PointerContext;
+import android.hardware.biometrics.fingerprint.SensorProps;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintAuthenticateOptions;
import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintSensorConfigurations;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.FingerprintServiceReceiver;
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
@@ -80,6 +82,7 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.SystemService;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.AuthenticationStateListeners;
@@ -127,6 +130,8 @@
@NonNull
private final Function<String, FingerprintProvider> mFingerprintProvider;
@NonNull
+ private final FingerprintProviderFunction mFingerprintProviderFunction;
+ @NonNull
private final BiometricStateCallback<ServiceProvider, FingerprintSensorPropertiesInternal>
mBiometricStateCallback;
@NonNull
@@ -136,6 +141,11 @@
@NonNull
private final FingerprintServiceRegistry mRegistry;
+ interface FingerprintProviderFunction {
+ FingerprintProvider getFingerprintProvider(Pair<String, SensorProps[]> filteredSensorProp,
+ boolean resetLockoutRequiresHardwareAuthToken);
+ }
+
/** Receives the incoming binder calls from FingerprintManager. */
@VisibleForTesting
final IFingerprintService.Stub mServiceWrapper = new IFingerprintService.Stub() {
@@ -874,6 +884,18 @@
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override // Binder call
+ public void registerAuthenticatorsLegacy(
+ @NonNull FingerprintSensorConfigurations fingerprintSensorConfigurations) {
+ super.registerAuthenticatorsLegacy_enforcePermission();
+ if (!fingerprintSensorConfigurations.hasSensorConfigurations()) {
+ Slog.d(TAG, "No fingerprint sensors available.");
+ return;
+ }
+ mRegistry.registerAll(() -> getProviders(fingerprintSensorConfigurations));
+ }
+
+ @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
+ @Override // Binder call
public void registerAuthenticators(
@NonNull List<FingerprintSensorPropertiesInternal> hidlSensors) {
super.registerAuthenticators_enforcePermission();
@@ -1021,7 +1043,8 @@
() -> IBiometricService.Stub.asInterface(
ServiceManager.getService(Context.BIOMETRIC_SERVICE)),
() -> ServiceManager.getDeclaredInstances(IFingerprint.DESCRIPTOR),
- null /* fingerprintProvider */);
+ null /* fingerprintProvider */,
+ null /* fingerprintProviderFunction */);
}
@VisibleForTesting
@@ -1029,7 +1052,8 @@
BiometricContext biometricContext,
Supplier<IBiometricService> biometricServiceSupplier,
Supplier<String[]> aidlInstanceNameSupplier,
- Function<String, FingerprintProvider> fingerprintProvider) {
+ Function<String, FingerprintProvider> fingerprintProvider,
+ FingerprintProviderFunction fingerprintProviderFunction) {
super(context);
mBiometricContext = biometricContext;
mAidlInstanceNameSupplier = aidlInstanceNameSupplier;
@@ -1049,7 +1073,8 @@
return new FingerprintProvider(getContext(),
mBiometricStateCallback, mAuthenticationStateListeners,
fp.getSensorProps(), name, mLockoutResetDispatcher,
- mGestureAvailabilityDispatcher, mBiometricContext);
+ mGestureAvailabilityDispatcher, mBiometricContext,
+ true /* resetLockoutRequiresHardwareAuthToken */);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception in getSensorProps: " + fqName);
}
@@ -1059,6 +1084,22 @@
return null;
};
+ if (Flags.deHidl()) {
+ mFingerprintProviderFunction = fingerprintProviderFunction == null
+ ? (filteredSensorProps, resetLockoutRequiresHardwareAuthToken) ->
+ new FingerprintProvider(
+ getContext(), mBiometricStateCallback,
+ mAuthenticationStateListeners,
+ filteredSensorProps.second,
+ filteredSensorProps.first, mLockoutResetDispatcher,
+ mGestureAvailabilityDispatcher,
+ mBiometricContext,
+ resetLockoutRequiresHardwareAuthToken)
+ : fingerprintProviderFunction;
+ } else {
+ mFingerprintProviderFunction =
+ (filteredSensorProps, resetLockoutRequiresHardwareAuthToken) -> null;
+ }
mHandler = new Handler(Looper.getMainLooper());
mRegistry = new FingerprintServiceRegistry(mServiceWrapper, biometricServiceSupplier);
mRegistry.addAllRegisteredCallback(new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
@@ -1070,6 +1111,44 @@
});
}
+ @NonNull
+ private List<ServiceProvider> getProviders(@NonNull FingerprintSensorConfigurations
+ fingerprintSensorConfigurations) {
+ final List<ServiceProvider> providers = new ArrayList<>();
+ final Pair<String, SensorProps[]> filteredSensorProps = filterAvailableHalInstances(
+ fingerprintSensorConfigurations);
+ providers.add(mFingerprintProviderFunction.getFingerprintProvider(filteredSensorProps,
+ fingerprintSensorConfigurations.getResetLockoutRequiresHardwareAuthToken()));
+
+ return providers;
+ }
+
+ @NonNull
+ private Pair<String, SensorProps[]> filterAvailableHalInstances(
+ FingerprintSensorConfigurations fingerprintSensorConfigurations) {
+ Pair<String, SensorProps[]> finalSensorPair =
+ fingerprintSensorConfigurations.getSensorPair();
+ if (fingerprintSensorConfigurations.isSingleSensorConfigurationPresent()) {
+ return finalSensorPair;
+ }
+
+ final Pair<String, SensorProps[]> virtualSensorPropsPair = fingerprintSensorConfigurations
+ .getSensorPairForInstance("virtual");
+ if (Utils.isVirtualEnabled(getContext())) {
+ if (virtualSensorPropsPair != null) {
+ return virtualSensorPropsPair;
+ } else {
+ Slog.e(TAG, "Could not find virtual interface while it is enabled");
+ return finalSensorPair;
+ }
+ } else {
+ if (virtualSensorPropsPair != null) {
+ return fingerprintSensorConfigurations.getSensorPairNotForInstance("virtual");
+ }
+ }
+ return finalSensorPair;
+ }
+
private Pair<List<FingerprintSensorPropertiesInternal>, List<String>>
filterAvailableHalInstances(
@NonNull List<FingerprintSensorPropertiesInternal> hidlInstances,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java
index 4a01943..bd21cf4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java
@@ -25,6 +25,7 @@
import android.hardware.keymaster.HardwareAuthToken;
import android.util.Slog;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AcquisitionClient;
@@ -34,9 +35,9 @@
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.EnumerateConsumer;
import com.android.server.biometrics.sensors.ErrorConsumer;
-import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutConsumer;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.RemovalConsumer;
import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
@@ -59,6 +60,21 @@
void onHardwareUnavailable();
}
+ /**
+ * Interface to send results to the AidlResponseHandler's owner.
+ */
+ public interface AidlResponseHandlerCallback {
+ /**
+ * Invoked when enrollment is successful.
+ */
+ void onEnrollSuccess();
+
+ /**
+ * Invoked when the HAL sends ERROR_HW_UNAVAILABLE.
+ */
+ void onHardwareUnavailable();
+ }
+
private static final String TAG = "AidlResponseHandler";
@NonNull
@@ -68,28 +84,49 @@
private final int mSensorId;
private final int mUserId;
@NonNull
- private final LockoutCache mLockoutCache;
+ private final LockoutTracker mLockoutTracker;
@NonNull
private final LockoutResetDispatcher mLockoutResetDispatcher;
@NonNull
private final AuthSessionCoordinator mAuthSessionCoordinator;
@NonNull
private final HardwareUnavailableCallback mHardwareUnavailableCallback;
+ @NonNull
+ private final AidlResponseHandlerCallback mAidlResponseHandlerCallback;
public AidlResponseHandler(@NonNull Context context,
@NonNull BiometricScheduler scheduler, int sensorId, int userId,
- @NonNull LockoutCache lockoutTracker,
+ @NonNull LockoutTracker lockoutTracker,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull AuthSessionCoordinator authSessionCoordinator,
@NonNull HardwareUnavailableCallback hardwareUnavailableCallback) {
+ this(context, scheduler, sensorId, userId, lockoutTracker, lockoutResetDispatcher,
+ authSessionCoordinator, hardwareUnavailableCallback,
+ new AidlResponseHandlerCallback() {
+ @Override
+ public void onEnrollSuccess() {}
+
+ @Override
+ public void onHardwareUnavailable() {}
+ });
+ }
+
+ public AidlResponseHandler(@NonNull Context context,
+ @NonNull BiometricScheduler scheduler, int sensorId, int userId,
+ @NonNull LockoutTracker lockoutTracker,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull AuthSessionCoordinator authSessionCoordinator,
+ @NonNull HardwareUnavailableCallback hardwareUnavailableCallback,
+ @NonNull AidlResponseHandlerCallback aidlResponseHandlerCallback) {
mContext = context;
mScheduler = scheduler;
mSensorId = sensorId;
mUserId = userId;
- mLockoutCache = lockoutTracker;
+ mLockoutTracker = lockoutTracker;
mLockoutResetDispatcher = lockoutResetDispatcher;
mAuthSessionCoordinator = authSessionCoordinator;
mHardwareUnavailableCallback = hardwareUnavailableCallback;
+ mAidlResponseHandlerCallback = aidlResponseHandlerCallback;
}
@Override
@@ -105,27 +142,26 @@
@Override
public void onChallengeGenerated(long challenge) {
handleResponse(FingerprintGenerateChallengeClient.class, (c) -> c.onChallengeGenerated(
- mSensorId, mUserId, challenge), null);
+ mSensorId, mUserId, challenge));
}
@Override
public void onChallengeRevoked(long challenge) {
handleResponse(FingerprintRevokeChallengeClient.class, (c) -> c.onChallengeRevoked(
- challenge), null);
+ challenge));
}
/**
* Handles acquired messages sent by the HAL (specifically for HIDL HAL).
*/
public void onAcquired(int acquiredInfo, int vendorCode) {
- handleResponse(AcquisitionClient.class, (c) -> c.onAcquired(acquiredInfo, vendorCode),
- null);
+ handleResponse(AcquisitionClient.class, (c) -> c.onAcquired(acquiredInfo, vendorCode));
}
@Override
public void onAcquired(byte info, int vendorCode) {
handleResponse(AcquisitionClient.class, (c) -> c.onAcquired(
- AidlConversionUtils.toFrameworkAcquiredInfo(info), vendorCode), null);
+ AidlConversionUtils.toFrameworkAcquiredInfo(info), vendorCode));
}
/**
@@ -135,9 +171,13 @@
handleResponse(ErrorConsumer.class, (c) -> {
c.onError(error, vendorCode);
if (error == Error.HW_UNAVAILABLE) {
- mHardwareUnavailableCallback.onHardwareUnavailable();
+ if (Flags.deHidl()) {
+ mAidlResponseHandlerCallback.onHardwareUnavailable();
+ } else {
+ mHardwareUnavailableCallback.onHardwareUnavailable();
+ }
}
- }, null);
+ });
}
@Override
@@ -158,8 +198,12 @@
.getUniqueName(mContext, currentUserId);
final Fingerprint fingerprint = new Fingerprint(name, currentUserId,
enrollmentId, mSensorId);
- handleResponse(FingerprintEnrollClient.class, (c) -> c.onEnrollResult(fingerprint,
- remaining), null);
+ handleResponse(FingerprintEnrollClient.class, (c) -> {
+ c.onEnrollResult(fingerprint, remaining);
+ if (remaining == 0) {
+ mAidlResponseHandlerCallback.onEnrollSuccess();
+ }
+ });
}
@Override
@@ -184,13 +228,12 @@
@Override
public void onLockoutTimed(long durationMillis) {
- handleResponse(LockoutConsumer.class, (c) -> c.onLockoutTimed(durationMillis),
- null);
+ handleResponse(LockoutConsumer.class, (c) -> c.onLockoutTimed(durationMillis));
}
@Override
public void onLockoutPermanent() {
- handleResponse(LockoutConsumer.class, LockoutConsumer::onLockoutPermanent, null);
+ handleResponse(LockoutConsumer.class, LockoutConsumer::onLockoutPermanent);
}
@Override
@@ -198,7 +241,7 @@
handleResponse(FingerprintResetLockoutClient.class,
FingerprintResetLockoutClient::onLockoutCleared,
(c) -> FingerprintResetLockoutClient.resetLocalLockoutStateToNone(
- mSensorId, mUserId, mLockoutCache, mLockoutResetDispatcher,
+ mSensorId, mUserId, mLockoutTracker, mLockoutResetDispatcher,
mAuthSessionCoordinator, Utils.getCurrentStrength(mSensorId),
-1 /* requestId */));
}
@@ -206,49 +249,74 @@
@Override
public void onInteractionDetected() {
handleResponse(FingerprintDetectClient.class,
- FingerprintDetectClient::onInteractionDetected, null);
+ FingerprintDetectClient::onInteractionDetected);
}
@Override
public void onEnrollmentsEnumerated(int[] enrollmentIds) {
if (enrollmentIds.length > 0) {
for (int i = 0; i < enrollmentIds.length; i++) {
- final Fingerprint fp = new Fingerprint("", enrollmentIds[i], mSensorId);
- int finalI = i;
- handleResponse(EnumerateConsumer.class, (c) -> c.onEnumerationResult(fp,
- enrollmentIds.length - finalI - 1), null);
+ onEnrollmentEnumerated(enrollmentIds[i],
+ enrollmentIds.length - i - 1 /* remaining */);
}
} else {
- handleResponse(EnumerateConsumer.class, (c) -> c.onEnumerationResult(null,
- 0), null);
+ handleResponse(EnumerateConsumer.class, (c) -> c.onEnumerationResult(
+ null /* identifier */,
+ 0 /* remaining */));
}
}
+ /**
+ * Handle enumerated fingerprint.
+ */
+ public void onEnrollmentEnumerated(int enrollmentId, int remaining) {
+ final Fingerprint fp = new Fingerprint("", enrollmentId, mSensorId);
+ handleResponse(EnumerateConsumer.class, (c) -> c.onEnumerationResult(fp, remaining));
+ }
+
+ /**
+ * Handle removal of fingerprint.
+ */
+ public void onEnrollmentRemoved(int enrollmentId, int remaining) {
+ final Fingerprint fp = new Fingerprint("", enrollmentId, mSensorId);
+ handleResponse(RemovalConsumer.class, (c) -> c.onRemoved(fp, remaining));
+ }
+
@Override
public void onEnrollmentsRemoved(int[] enrollmentIds) {
if (enrollmentIds.length > 0) {
for (int i = 0; i < enrollmentIds.length; i++) {
- final Fingerprint fp = new Fingerprint("", enrollmentIds[i], mSensorId);
- int finalI = i;
- handleResponse(RemovalConsumer.class, (c) -> c.onRemoved(fp,
- enrollmentIds.length - finalI - 1), null);
+ onEnrollmentRemoved(enrollmentIds[i], enrollmentIds.length - i - 1 /* remaining */);
}
} else {
- handleResponse(RemovalConsumer.class, (c) -> c.onRemoved(null, 0),
- null);
+ handleResponse(RemovalConsumer.class, (c) -> c.onRemoved(null /* identifier */,
+ 0 /* remaining */));
}
}
@Override
public void onAuthenticatorIdRetrieved(long authenticatorId) {
handleResponse(FingerprintGetAuthenticatorIdClient.class,
- (c) -> c.onAuthenticatorIdRetrieved(authenticatorId), null);
+ (c) -> c.onAuthenticatorIdRetrieved(authenticatorId));
}
@Override
public void onAuthenticatorIdInvalidated(long newAuthenticatorId) {
handleResponse(FingerprintInvalidationClient.class, (c) -> c.onAuthenticatorIdInvalidated(
- newAuthenticatorId), null);
+ newAuthenticatorId));
+ }
+
+ /**
+ * Handle clients which are not supported in HIDL HAL.
+ */
+ public <T extends BaseClientMonitor> void onUnsupportedClientScheduled(Class<T> className) {
+ Slog.e(TAG, className + " is not supported in the HAL.");
+ handleResponse(className, (c) -> c.cancel());
+ }
+
+ private <T> void handleResponse(@NonNull Class<T> className,
+ @NonNull Consumer<T> action) {
+ handleResponse(className, action, null /* alternateAction */);
}
private <T> void handleResponse(@NonNull Class<T> className,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java
index 299a310..8ff105b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java
@@ -20,7 +20,7 @@
import android.hardware.biometrics.fingerprint.ISession;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import com.android.server.biometrics.sensors.fingerprint.hidl.AidlToHidlAdapter;
+import com.android.server.biometrics.sensors.fingerprint.hidl.HidlToAidlSessionAdapter;
import java.util.function.Supplier;
@@ -45,7 +45,7 @@
public AidlSession(@NonNull Supplier<IBiometricsFingerprint> session,
int userId, AidlResponseHandler aidlResponseHandler) {
- mSession = new AidlToHidlAdapter(session, userId, aidlResponseHandler);
+ mSession = new HidlToAidlSessionAdapter(session, userId, aidlResponseHandler);
mHalInterfaceVersion = 0;
mUserId = userId;
mAidlResponseHandler = aidlResponseHandler;
@@ -62,7 +62,7 @@
}
/** The HAL callback, which should only be used in tests {@See BiometricTestSessionImpl}. */
- AidlResponseHandler getHalSessionCallback() {
+ public AidlResponseHandler getHalSessionCallback() {
return mAidlResponseHandler;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
index ea1a622..0353969 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
@@ -30,7 +30,7 @@
import java.util.Map;
import java.util.function.Supplier;
-class FingerprintGetAuthenticatorIdClient extends HalClientMonitor<AidlSession> {
+public class FingerprintGetAuthenticatorIdClient extends HalClientMonitor<AidlSession> {
private static final String TAG = "FingerprintGetAuthenticatorIdClient";
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 032ab87..88a11d9 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
@@ -59,6 +59,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.AuthenticationStatsBroadcastReceiver;
import com.android.server.biometrics.AuthenticationStatsCollector;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
@@ -73,6 +74,7 @@
import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
import com.android.server.biometrics.sensors.InvalidationRequesterClient;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.PerformanceTracker;
import com.android.server.biometrics.sensors.SensorList;
import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
@@ -80,6 +82,7 @@
import com.android.server.biometrics.sensors.fingerprint.PowerPressHandler;
import com.android.server.biometrics.sensors.fingerprint.ServiceProvider;
import com.android.server.biometrics.sensors.fingerprint.Udfps;
+import com.android.server.biometrics.sensors.fingerprint.hidl.HidlToAidlSensorAdapter;
import org.json.JSONArray;
import org.json.JSONException;
@@ -165,10 +168,12 @@
@NonNull SensorProps[] props, @NonNull String halInstanceName,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
- @NonNull BiometricContext biometricContext) {
+ @NonNull BiometricContext biometricContext,
+ boolean resetLockoutRequiresHardwareAuthToken) {
this(context, biometricStateCallback, authenticationStateListeners, props, halInstanceName,
lockoutResetDispatcher, gestureAvailabilityDispatcher, biometricContext,
- null /* daemon */);
+ null /* daemon */, resetLockoutRequiresHardwareAuthToken,
+ false /* testHalEnabled */);
}
@VisibleForTesting FingerprintProvider(@NonNull Context context,
@@ -178,7 +183,9 @@
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
@NonNull BiometricContext biometricContext,
- IFingerprint daemon) {
+ @Nullable IFingerprint daemon,
+ boolean resetLockoutRequiresHardwareAuthToken,
+ boolean testHalEnabled) {
mContext = context;
mBiometricStateCallback = biometricStateCallback;
mAuthenticationStateListeners = authenticationStateListeners;
@@ -191,62 +198,136 @@
mBiometricContext = biometricContext;
mAuthSessionCoordinator = mBiometricContext.getAuthSessionCoordinator();
mDaemon = daemon;
+ mTestHalEnabled = testHalEnabled;
- AuthenticationStatsBroadcastReceiver mBroadcastReceiver =
- new AuthenticationStatsBroadcastReceiver(
- mContext,
- BiometricsProtoEnums.MODALITY_FINGERPRINT,
- (AuthenticationStatsCollector collector) -> {
- Slog.d(getTag(), "Initializing AuthenticationStatsCollector");
- mAuthenticationStatsCollector = collector;
- });
+ initAuthenticationBroadcastReceiver();
+ initSensors(resetLockoutRequiresHardwareAuthToken, props, gestureAvailabilityDispatcher);
+ }
- final List<SensorLocationInternal> workaroundLocations = getWorkaroundSensorProps(context);
+ private void initAuthenticationBroadcastReceiver() {
+ new AuthenticationStatsBroadcastReceiver(
+ mContext,
+ BiometricsProtoEnums.MODALITY_FINGERPRINT,
+ (AuthenticationStatsCollector collector) -> {
+ Slog.d(getTag(), "Initializing AuthenticationStatsCollector");
+ mAuthenticationStatsCollector = collector;
+ });
+ }
- for (SensorProps prop : props) {
- final int sensorId = prop.commonProps.sensorId;
-
- final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
- if (prop.commonProps.componentInfo != null) {
- for (ComponentInfo info : prop.commonProps.componentInfo) {
- componentInfo.add(new ComponentInfoInternal(info.componentId,
- info.hardwareVersion, info.firmwareVersion, info.serialNumber,
- info.softwareVersion));
+ private void initSensors(boolean resetLockoutRequiresHardwareAuthToken, SensorProps[] props,
+ GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
+ if (Flags.deHidl()) {
+ if (!resetLockoutRequiresHardwareAuthToken) {
+ Slog.d(getTag(), "Adding HIDL configs");
+ for (SensorProps sensorConfig: props) {
+ addHidlSensors(sensorConfig, gestureAvailabilityDispatcher,
+ resetLockoutRequiresHardwareAuthToken);
+ }
+ } else {
+ Slog.d(getTag(), "Adding AIDL configs");
+ final List<SensorLocationInternal> workaroundLocations =
+ getWorkaroundSensorProps(mContext);
+ for (SensorProps prop : props) {
+ addAidlSensors(prop, gestureAvailabilityDispatcher, workaroundLocations,
+ resetLockoutRequiresHardwareAuthToken);
}
}
+ } else {
+ final List<SensorLocationInternal> workaroundLocations =
+ getWorkaroundSensorProps(mContext);
- final FingerprintSensorPropertiesInternal internalProp =
- new FingerprintSensorPropertiesInternal(prop.commonProps.sensorId,
- prop.commonProps.sensorStrength,
- prop.commonProps.maxEnrollmentsPerUser,
- componentInfo,
- prop.sensorType,
- prop.halControlsIllumination,
- true /* resetLockoutRequiresHardwareAuthToken */,
- !workaroundLocations.isEmpty() ? workaroundLocations :
- Arrays.stream(prop.sensorLocations).map(location ->
- new SensorLocationInternal(
- location.display,
- location.sensorLocationX,
- location.sensorLocationY,
- location.sensorRadius))
- .collect(Collectors.toList()));
- final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler,
- internalProp, lockoutResetDispatcher, gestureAvailabilityDispatcher,
- mBiometricContext);
- final int sessionUserId = sensor.getLazySession().get() == null ? UserHandle.USER_NULL :
- sensor.getLazySession().get().getUserId();
- mFingerprintSensors.addSensor(sensorId, sensor, sessionUserId,
- new SynchronousUserSwitchObserver() {
- @Override
- public void onUserSwitching(int newUserId) {
- scheduleInternalCleanup(sensorId, newUserId, null /* callback */);
- }
- });
- Slog.d(getTag(), "Added: " + internalProp);
+ for (SensorProps prop : props) {
+ final int sensorId = prop.commonProps.sensorId;
+ final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
+ if (prop.commonProps.componentInfo != null) {
+ for (ComponentInfo info : prop.commonProps.componentInfo) {
+ componentInfo.add(new ComponentInfoInternal(info.componentId,
+ info.hardwareVersion, info.firmwareVersion, info.serialNumber,
+ info.softwareVersion));
+ }
+ }
+ final FingerprintSensorPropertiesInternal internalProp =
+ new FingerprintSensorPropertiesInternal(prop.commonProps.sensorId,
+ prop.commonProps.sensorStrength,
+ prop.commonProps.maxEnrollmentsPerUser,
+ componentInfo,
+ prop.sensorType,
+ prop.halControlsIllumination,
+ true /* resetLockoutRequiresHardwareAuthToken */,
+ !workaroundLocations.isEmpty() ? workaroundLocations :
+ Arrays.stream(prop.sensorLocations).map(
+ location -> new SensorLocationInternal(
+ location.display,
+ location.sensorLocationX,
+ location.sensorLocationY,
+ location.sensorRadius))
+ .collect(Collectors.toList()));
+ final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext,
+ mHandler, internalProp, mLockoutResetDispatcher,
+ gestureAvailabilityDispatcher, mBiometricContext);
+ sensor.init(gestureAvailabilityDispatcher,
+ mLockoutResetDispatcher);
+ final int sessionUserId =
+ sensor.getLazySession().get() == null ? UserHandle.USER_NULL :
+ sensor.getLazySession().get().getUserId();
+ mFingerprintSensors.addSensor(sensorId, sensor, sessionUserId,
+ new SynchronousUserSwitchObserver() {
+ @Override
+ public void onUserSwitching(int newUserId) {
+ scheduleInternalCleanup(sensorId, newUserId, null /* callback */);
+ }
+ });
+ Slog.d(getTag(), "Added: " + mFingerprintSensors.get(sensorId).toString());
+ }
}
}
+ private void addHidlSensors(@NonNull SensorProps prop,
+ @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+ boolean resetLockoutRequiresHardwareAuthToken) {
+ final int sensorId = prop.commonProps.sensorId;
+ final Sensor sensor = new HidlToAidlSensorAdapter(getTag() + "/"
+ + sensorId, this, mContext, mHandler,
+ prop, mLockoutResetDispatcher, gestureAvailabilityDispatcher,
+ mBiometricContext, resetLockoutRequiresHardwareAuthToken,
+ () -> scheduleInternalCleanup(sensorId, ActivityManager.getCurrentUser(),
+ null /* callback */));
+ sensor.init(gestureAvailabilityDispatcher, mLockoutResetDispatcher);
+ final int sessionUserId = sensor.getLazySession().get() == null ? UserHandle.USER_NULL :
+ sensor.getLazySession().get().getUserId();
+ mFingerprintSensors.addSensor(sensorId, sensor, sessionUserId,
+ new SynchronousUserSwitchObserver() {
+ @Override
+ public void onUserSwitching(int newUserId) {
+ scheduleInternalCleanup(sensorId, newUserId, null /* callback */);
+ }
+ });
+ Slog.d(getTag(), "Added: " + mFingerprintSensors.get(sensorId).toString());
+ }
+
+ private void addAidlSensors(@NonNull SensorProps prop,
+ @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+ List<SensorLocationInternal> workaroundLocations,
+ boolean resetLockoutRequiresHardwareAuthToken) {
+ final int sensorId = prop.commonProps.sensorId;
+ final Sensor sensor = new Sensor(getTag() + "/" + sensorId,
+ this, mContext, mHandler,
+ prop, mLockoutResetDispatcher, gestureAvailabilityDispatcher,
+ mBiometricContext, workaroundLocations,
+ resetLockoutRequiresHardwareAuthToken);
+ sensor.init(gestureAvailabilityDispatcher, mLockoutResetDispatcher);
+ final int sessionUserId = sensor.getLazySession().get() == null ? UserHandle.USER_NULL :
+ sensor.getLazySession().get().getUserId();
+ mFingerprintSensors.addSensor(sensorId, sensor, sessionUserId,
+ new SynchronousUserSwitchObserver() {
+ @Override
+ public void onUserSwitching(int newUserId) {
+ scheduleInternalCleanup(sensorId, newUserId, null /* callback */);
+ }
+ });
+ Slog.d(getTag(), "Added: " + mFingerprintSensors.get(sensorId).toString());
+ }
+
private String getTag() {
return "FingerprintProvider/" + mHalInstanceName;
}
@@ -351,7 +432,10 @@
}
}
- private void scheduleLoadAuthenticatorIdsForUser(int sensorId, int userId) {
+ /**
+ * Schedules FingerprintGetAuthenticatorIdClient for specific sensor and user.
+ */
+ protected void scheduleLoadAuthenticatorIdsForUser(int sensorId, int userId) {
mHandler.post(() -> {
final FingerprintGetAuthenticatorIdClient client =
new FingerprintGetAuthenticatorIdClient(mContext,
@@ -387,8 +471,8 @@
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
mBiometricContext, hardwareAuthToken,
- mFingerprintSensors.get(sensorId).getLockoutCache(), mLockoutResetDispatcher,
- Utils.getCurrentStrength(sensorId));
+ mFingerprintSensors.get(sensorId).getLockoutTracker(false /* forAuth */),
+ mLockoutResetDispatcher, Utils.getCurrentStrength(sensorId));
scheduleForSensor(sensorId, client);
});
}
@@ -443,18 +527,23 @@
mFingerprintSensors.get(sensorId).getSensorProperties(),
mUdfpsOverlayController, mSidefpsController,
mAuthenticationStateListeners, maxTemplatesPerUser, enrollReason);
- scheduleForSensor(sensorId, client, new ClientMonitorCompositeCallback(
- mBiometricStateCallback, new ClientMonitorCallback() {
- @Override
- public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
- boolean success) {
- ClientMonitorCallback.super.onClientFinished(clientMonitor, success);
- if (success) {
- scheduleLoadAuthenticatorIdsForUser(sensorId, userId);
- scheduleInvalidationRequest(sensorId, userId);
- }
- }
- }));
+ if (Flags.deHidl()) {
+ scheduleForSensor(sensorId, client, mBiometricStateCallback);
+ } else {
+ scheduleForSensor(sensorId, client, new ClientMonitorCompositeCallback(
+ mBiometricStateCallback, new ClientMonitorCallback() {
+ @Override
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
+ boolean success) {
+ ClientMonitorCallback.super.onClientFinished(
+ clientMonitor, success);
+ if (success) {
+ scheduleLoadAuthenticatorIdsForUser(sensorId, userId);
+ scheduleInvalidationRequest(sensorId, userId);
+ }
+ }
+ }));
+ }
});
return id;
}
@@ -497,6 +586,13 @@
final int userId = options.getUserId();
final int sensorId = options.getSensorId();
final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
+ final LockoutTracker lockoutTracker;
+ if (Flags.deHidl()) {
+ lockoutTracker = mFingerprintSensors.get(sensorId)
+ .getLockoutTracker(true /* forAuth */);
+ } else {
+ lockoutTracker = null;
+ }
final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
mContext, mFingerprintSensors.get(sensorId).getLazySession(), token, requestId,
callback, operationId, restricted, options, cookie,
@@ -510,7 +606,7 @@
mFingerprintSensors.get(sensorId).getSensorProperties(), mHandler,
Utils.getCurrentStrength(sensorId),
SystemClock.elapsedRealtimeClock(),
- null /* lockoutTracker */);
+ lockoutTracker);
scheduleForSensor(sensorId, client, new ClientMonitorCallback() {
@Override
@@ -636,6 +732,9 @@
@Override
public boolean isHardwareDetected(int sensorId) {
+ if (Flags.deHidl()) {
+ return mFingerprintSensors.get(sensorId).isHardwareDetected(mHalInstanceName);
+ }
return hasHalInstance();
}
@@ -674,8 +773,12 @@
@Override
public int getLockoutModeForUser(int sensorId, int userId) {
- return mBiometricContext.getAuthSessionCoordinator().getLockoutStateFor(userId,
- Utils.getCurrentStrength(sensorId));
+ if (Flags.deHidl()) {
+ return mFingerprintSensors.get(sensorId).getLockoutModeForUser(userId);
+ } else {
+ return mBiometricContext.getAuthSessionCoordinator().getLockoutStateFor(userId,
+ Utils.getCurrentStrength(sensorId));
+ }
}
@Override
@@ -829,6 +932,10 @@
mTestHalEnabled = enabled;
}
+ public boolean getTestHalEnabled() {
+ return mTestHalEnabled;
+ }
+
// 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
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
index ec225a6..387ae12 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
@@ -60,7 +60,8 @@
@Authenticators.Types int biometricStrength) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
0 /* cookie */, sensorId, biometricLogger, biometricContext);
- mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken);
+ mHardwareAuthToken = hardwareAuthToken == null ? null :
+ HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken);
mLockoutCache = lockoutTracker;
mLockoutResetDispatcher = lockoutResetDispatcher;
mBiometricStrength = biometricStrength;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index 893cb8f..dd887bb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -21,21 +21,28 @@
import android.content.Context;
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.ComponentInfoInternal;
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.ISession;
+import android.hardware.biometrics.fingerprint.SensorProps;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.SensorServiceStateProto;
import com.android.server.biometrics.SensorStateProto;
import com.android.server.biometrics.UserStateProto;
@@ -48,15 +55,20 @@
import com.android.server.biometrics.sensors.ErrorConsumer;
import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.StartUserClient;
import com.android.server.biometrics.sensors.StopUserClient;
import com.android.server.biometrics.sensors.UserAwareBiometricScheduler;
import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
+import java.util.stream.Collectors;
/**
* Maintains the state of a single sensor within an instance of the
@@ -73,15 +85,17 @@
@NonNull private final IBinder mToken;
@NonNull private final Handler mHandler;
@NonNull private final FingerprintSensorPropertiesInternal mSensorProperties;
- @NonNull private final UserAwareBiometricScheduler mScheduler;
- @NonNull private final LockoutCache mLockoutCache;
+ @NonNull private BiometricScheduler mScheduler;
+ @NonNull private LockoutTracker mLockoutTracker;
@NonNull private final Map<Integer, Long> mAuthenticatorIds;
+ @NonNull private final BiometricContext mBiometricContext;
@Nullable AidlSession mCurrentSession;
- @NonNull private final Supplier<AidlSession> mLazySession;
+ @NonNull private Supplier<AidlSession> mLazySession;
- Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, @NonNull Context context,
- @NonNull Handler handler, @NonNull FingerprintSensorPropertiesInternal sensorProperties,
+ public Sensor(@NonNull String tag, @NonNull FingerprintProvider provider,
+ @NonNull Context context, @NonNull Handler handler,
+ @NonNull FingerprintSensorPropertiesInternal sensorProperties,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
@NonNull BiometricContext biometricContext, AidlSession session) {
@@ -91,8 +105,38 @@
mToken = new Binder();
mHandler = handler;
mSensorProperties = sensorProperties;
- mLockoutCache = new LockoutCache();
- mScheduler = new UserAwareBiometricScheduler(tag,
+ mBiometricContext = biometricContext;
+ mAuthenticatorIds = new HashMap<>();
+ mCurrentSession = session;
+ }
+
+ Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, @NonNull Context context,
+ @NonNull Handler handler, @NonNull FingerprintSensorPropertiesInternal sensorProperties,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+ @NonNull BiometricContext biometricContext) {
+ this(tag, provider, context, handler, sensorProperties, lockoutResetDispatcher,
+ gestureAvailabilityDispatcher, biometricContext, null);
+ }
+
+ Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, @NonNull Context context,
+ @NonNull Handler handler, @NonNull SensorProps sensorProp,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+ @NonNull BiometricContext biometricContext,
+ @NonNull List<SensorLocationInternal> workaroundLocation,
+ boolean resetLockoutRequiresHardwareAuthToken) {
+ this(tag, provider, context, handler, getFingerprintSensorPropertiesInternal(sensorProp,
+ workaroundLocation, resetLockoutRequiresHardwareAuthToken),
+ lockoutResetDispatcher, gestureAvailabilityDispatcher, biometricContext, null);
+ }
+
+ /**
+ * Initialize biometric scheduler, lockout tracker and session for the sensor.
+ */
+ public void init(GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+ LockoutResetDispatcher lockoutResetDispatcher) {
+ mScheduler = new UserAwareBiometricScheduler(mTag,
BiometricScheduler.sensorTypeFromFingerprintProperties(mSensorProperties),
gestureAvailabilityDispatcher,
() -> mCurrentSession != null ? mCurrentSession.getUserId() : UserHandle.USER_NULL,
@@ -102,7 +146,7 @@
public StopUserClient<?> getStopUserClient(int userId) {
return new FingerprintStopUserClient(mContext, mLazySession, mToken,
userId, mSensorProperties.sensorId,
- BiometricLogger.ofUnknown(mContext), biometricContext,
+ BiometricLogger.ofUnknown(mContext), mBiometricContext,
() -> mCurrentSession = null);
}
@@ -111,13 +155,38 @@
public StartUserClient<?, ?> getStartUserClient(int newUserId) {
final int sensorId = mSensorProperties.sensorId;
- final AidlResponseHandler resultController = new AidlResponseHandler(
- mContext, mScheduler, sensorId, newUserId,
- mLockoutCache, lockoutResetDispatcher,
- biometricContext.getAuthSessionCoordinator(), () -> {
- Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE");
- mCurrentSession = null;
- });
+ final AidlResponseHandler resultController;
+
+ if (Flags.deHidl()) {
+ resultController = new AidlResponseHandler(
+ mContext, mScheduler, sensorId, newUserId,
+ mLockoutTracker, lockoutResetDispatcher,
+ mBiometricContext.getAuthSessionCoordinator(), () -> {},
+ new AidlResponseHandler.AidlResponseHandlerCallback() {
+ @Override
+ public void onEnrollSuccess() {
+ mProvider.scheduleLoadAuthenticatorIdsForUser(sensorId,
+ newUserId);
+ mProvider.scheduleInvalidationRequest(sensorId,
+ newUserId);
+ }
+
+ @Override
+ public void onHardwareUnavailable() {
+ Slog.e(mTag,
+ "Fingerprint sensor hardware unavailable.");
+ mCurrentSession = null;
+ }
+ });
+ } else {
+ resultController = new AidlResponseHandler(
+ mContext, mScheduler, sensorId, newUserId,
+ mLockoutTracker, lockoutResetDispatcher,
+ mBiometricContext.getAuthSessionCoordinator(), () -> {
+ Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE");
+ mCurrentSession = null;
+ });
+ }
final StartUserClient.UserStartedCallback<ISession> userStartedCallback =
(userIdStarted, newSession, halInterfaceVersion) -> {
@@ -133,40 +202,58 @@
+ "sensor: "
+ sensorId
+ ", user: " + userIdStarted);
- provider.scheduleInvalidationRequest(sensorId,
+ mProvider.scheduleInvalidationRequest(sensorId,
userIdStarted);
}
};
- return new FingerprintStartUserClient(mContext, provider::getHalInstance,
+ return new FingerprintStartUserClient(mContext, mProvider::getHalInstance,
mToken, newUserId, mSensorProperties.sensorId,
- BiometricLogger.ofUnknown(mContext), biometricContext,
+ BiometricLogger.ofUnknown(mContext), mBiometricContext,
resultController, userStartedCallback);
}
});
- mAuthenticatorIds = new HashMap<>();
+ mLockoutTracker = new LockoutCache();
mLazySession = () -> mCurrentSession != null ? mCurrentSession : null;
- mCurrentSession = session;
}
- Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, @NonNull Context context,
- @NonNull Handler handler, @NonNull FingerprintSensorPropertiesInternal sensorProperties,
- @NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
- @NonNull BiometricContext biometricContext) {
- this(tag, provider, context, handler, sensorProperties, lockoutResetDispatcher,
- gestureAvailabilityDispatcher, biometricContext, null);
+ protected static FingerprintSensorPropertiesInternal getFingerprintSensorPropertiesInternal(
+ SensorProps prop, List<SensorLocationInternal> workaroundLocations,
+ boolean resetLockoutRequiresHardwareAuthToken) {
+ final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
+ if (prop.commonProps.componentInfo != null) {
+ for (ComponentInfo info : prop.commonProps.componentInfo) {
+ componentInfo.add(new ComponentInfoInternal(info.componentId,
+ info.hardwareVersion, info.firmwareVersion, info.serialNumber,
+ info.softwareVersion));
+ }
+ }
+ return new FingerprintSensorPropertiesInternal(prop.commonProps.sensorId,
+ prop.commonProps.sensorStrength,
+ prop.commonProps.maxEnrollmentsPerUser,
+ componentInfo,
+ prop.sensorType,
+ prop.halControlsIllumination,
+ resetLockoutRequiresHardwareAuthToken,
+ !workaroundLocations.isEmpty() ? workaroundLocations :
+ Arrays.stream(prop.sensorLocations).map(location ->
+ new SensorLocationInternal(
+ location.display,
+ location.sensorLocationX,
+ location.sensorLocationY,
+ location.sensorRadius))
+ .collect(Collectors.toList()));
}
- @NonNull Supplier<AidlSession> getLazySession() {
+ @NonNull public Supplier<AidlSession> getLazySession() {
return mLazySession;
}
- @NonNull FingerprintSensorPropertiesInternal getSensorProperties() {
+ @NonNull public FingerprintSensorPropertiesInternal getSensorProperties() {
return mSensorProperties;
}
- @Nullable AidlSession getSessionForUser(int userId) {
+ @Nullable protected AidlSession getSessionForUser(int userId) {
if (mCurrentSession != null && mCurrentSession.getUserId() == userId) {
return mCurrentSession;
} else {
@@ -180,15 +267,18 @@
biometricStateCallback, mProvider, this);
}
- @NonNull BiometricScheduler getScheduler() {
+ @NonNull public BiometricScheduler getScheduler() {
return mScheduler;
}
- @NonNull LockoutCache getLockoutCache() {
- return mLockoutCache;
+ @NonNull protected LockoutTracker getLockoutTracker(boolean forAuth) {
+ if (forAuth) {
+ return null;
+ }
+ return mLockoutTracker;
}
- @NonNull Map<Integer, Long> getAuthenticatorIds() {
+ @NonNull public Map<Integer, Long> getAuthenticatorIds() {
return mAuthenticatorIds;
}
@@ -262,4 +352,49 @@
mScheduler.reset();
mCurrentSession = null;
}
+
+ @NonNull protected Handler getHandler() {
+ return mHandler;
+ }
+
+ @NonNull protected Context getContext() {
+ return mContext;
+ }
+
+ /**
+ * Returns true if the sensor hardware is detected.
+ */
+ protected boolean isHardwareDetected(String halInstance) {
+ if (mTestHalEnabled) {
+ return true;
+ }
+ return (ServiceManager.checkService(IFingerprint.DESCRIPTOR + "/" + halInstance)
+ != null);
+ }
+
+ @NonNull protected BiometricContext getBiometricContext() {
+ return mBiometricContext;
+ }
+
+ /**
+ * Returns lockout mode of this sensor.
+ */
+ @LockoutTracker.LockoutMode
+ public int getLockoutModeForUser(int userId) {
+ return mBiometricContext.getAuthSessionCoordinator().getLockoutStateFor(userId,
+ Utils.getCurrentStrength(mSensorProperties.sensorId));
+ }
+
+ public void setScheduler(BiometricScheduler scheduler) {
+ mScheduler = scheduler;
+ }
+
+ public void setLazySession(
+ Supplier<AidlSession> lazySession) {
+ mLazySession = lazySession;
+ }
+
+ public FingerprintProvider getProvider() {
+ return mProvider;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
index a4e6025..5c5b992 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
@@ -29,7 +29,8 @@
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
-import com.android.server.biometrics.sensors.HalClientMonitor;
+import com.android.server.biometrics.sensors.StartUserClient;
+import com.android.server.biometrics.sensors.fingerprint.aidl.AidlSession;
import java.io.File;
import java.util.Map;
@@ -38,7 +39,8 @@
/**
* Sets the HAL's current active user, and updates the framework's authenticatorId cache.
*/
-public class FingerprintUpdateActiveUserClient extends HalClientMonitor<IBiometricsFingerprint> {
+public class FingerprintUpdateActiveUserClient extends
+ StartUserClient<IBiometricsFingerprint, AidlSession> {
private static final String TAG = "FingerprintUpdateActiveUserClient";
private static final String FP_DATA_DIR = "fpdata";
@@ -53,11 +55,24 @@
@NonNull Supplier<IBiometricsFingerprint> lazyDaemon, int userId,
@NonNull String owner, int sensorId,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
- Supplier<Integer> currentUserId,
+ @NonNull Supplier<Integer> currentUserId,
boolean hasEnrolledBiometrics, @NonNull Map<Integer, Long> authenticatorIds,
boolean forceUpdateAuthenticatorId) {
- super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
- 0 /* cookie */, sensorId, logger, biometricContext);
+ this(context, lazyDaemon, userId, owner, sensorId, logger, biometricContext, currentUserId,
+ hasEnrolledBiometrics, authenticatorIds, forceUpdateAuthenticatorId,
+ (newUserId, newUser, halInterfaceVersion) -> {});
+ }
+
+ FingerprintUpdateActiveUserClient(@NonNull Context context,
+ @NonNull Supplier<IBiometricsFingerprint> lazyDaemon, int userId,
+ @NonNull String owner, int sensorId,
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ @NonNull Supplier<Integer> currentUserId,
+ boolean hasEnrolledBiometrics, @NonNull Map<Integer, Long> authenticatorIds,
+ boolean forceUpdateAuthenticatorId,
+ @NonNull UserStartedCallback<AidlSession> userStartedCallback) {
+ super(context, lazyDaemon, null /* token */, userId, sensorId, logger, biometricContext,
+ userStartedCallback);
mCurrentUserId = currentUserId;
mForceUpdateAuthenticatorId = forceUpdateAuthenticatorId;
mHasEnrolledBiometrics = hasEnrolledBiometrics;
@@ -70,6 +85,7 @@
if (mCurrentUserId.get() == getTargetUserId() && !mForceUpdateAuthenticatorId) {
Slog.d(TAG, "Already user: " + mCurrentUserId + ", returning");
+ mUserStartedCallback.onUserStarted(getTargetUserId(), null, 0);
callback.onClientFinished(this, true /* success */);
return;
}
@@ -119,6 +135,7 @@
getFreshDaemon().setActiveGroup(targetId, mDirectory.getAbsolutePath());
mAuthenticatorIds.put(targetId, mHasEnrolledBiometrics
? getFreshDaemon().getAuthenticatorId() : 0L);
+ mUserStartedCallback.onUserStarted(targetId, null, 0);
mCallback.onClientFinished(this, true /* success */);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to setActiveGroup: " + e);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlCallbackConverter.java
index c3e5cbe..e9a48e7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlCallbackConverter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlCallbackConverter.java
@@ -20,6 +20,7 @@
import android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprintClientCallback;
import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.fingerprint.aidl.AidlResponseHandler;
import java.util.ArrayList;
@@ -73,12 +74,12 @@
@Override
public void onRemoved(long deviceId, int fingerId, int groupId, int remaining) {
- mAidlResponseHandler.onEnrollmentsRemoved(new int[]{fingerId});
+ mAidlResponseHandler.onEnrollmentRemoved(fingerId, remaining);
}
@Override
public void onEnumerate(long deviceId, int fingerId, int groupId, int remaining) {
- mAidlResponseHandler.onEnrollmentsEnumerated(new int[]{fingerId});
+ mAidlResponseHandler.onEnrollmentEnumerated(fingerId, remaining);
}
void onChallengeGenerated(long challenge) {
@@ -92,4 +93,8 @@
void onResetLockout() {
mAidlResponseHandler.onLockoutCleared();
}
+
+ <T extends BaseClientMonitor> void unsupportedClientScheduled(Class<T> className) {
+ mAidlResponseHandler.onUnsupportedClientScheduled(className);
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java
new file mode 100644
index 0000000..0bb61415
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint.hidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.hardware.biometrics.fingerprint.SensorProps;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.os.Handler;
+import android.os.IHwBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.AuthSessionCoordinator;
+import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.StartUserClient;
+import com.android.server.biometrics.sensors.StopUserClient;
+import com.android.server.biometrics.sensors.UserAwareBiometricScheduler;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
+import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
+import com.android.server.biometrics.sensors.fingerprint.aidl.AidlResponseHandler;
+import com.android.server.biometrics.sensors.fingerprint.aidl.AidlSession;
+import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintProvider;
+import com.android.server.biometrics.sensors.fingerprint.aidl.Sensor;
+
+import java.util.ArrayList;
+
+/**
+ * Convert HIDL sensor configurations to an AIDL Sensor.
+ */
+public class HidlToAidlSensorAdapter extends Sensor implements IHwBinder.DeathRecipient {
+ private static final String TAG = "HidlToAidlSensorAdapter";
+
+ private final Runnable mInternalCleanupRunnable;
+ private final LockoutResetDispatcher mLockoutResetDispatcher;
+ private LockoutFrameworkImpl mLockoutTracker;
+ private final AuthSessionCoordinator mAuthSessionCoordinator;
+ private final AidlResponseHandler.AidlResponseHandlerCallback mAidlResponseHandlerCallback;
+ private int mCurrentUserId = UserHandle.USER_NULL;
+ private IBiometricsFingerprint mDaemon;
+ private AidlSession mSession;
+
+ private final StartUserClient.UserStartedCallback<AidlSession> mUserStartedCallback =
+ (newUserId, newUser, halInterfaceVersion) -> {
+ if (mCurrentUserId != newUserId) {
+ handleUserChanged(newUserId);
+ }
+ };
+
+ public HidlToAidlSensorAdapter(@NonNull String tag, @NonNull FingerprintProvider provider,
+ @NonNull Context context, @NonNull Handler handler,
+ @NonNull SensorProps prop,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+ @NonNull BiometricContext biometricContext,
+ boolean resetLockoutRequiresHardwareAuthToken,
+ @NonNull Runnable internalCleanupRunnable) {
+ this(tag, provider, context, handler, prop, lockoutResetDispatcher,
+ gestureAvailabilityDispatcher, biometricContext,
+ resetLockoutRequiresHardwareAuthToken, internalCleanupRunnable,
+ new AuthSessionCoordinator(), null /* daemon */,
+ null /* onEnrollSuccessCallback */);
+ }
+
+ @VisibleForTesting
+ HidlToAidlSensorAdapter(@NonNull String tag, @NonNull FingerprintProvider provider,
+ @NonNull Context context, @NonNull Handler handler,
+ @NonNull SensorProps prop,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+ @NonNull BiometricContext biometricContext,
+ boolean resetLockoutRequiresHardwareAuthToken,
+ @NonNull Runnable internalCleanupRunnable,
+ @NonNull AuthSessionCoordinator authSessionCoordinator,
+ @Nullable IBiometricsFingerprint daemon,
+ @Nullable AidlResponseHandler.AidlResponseHandlerCallback aidlResponseHandlerCallback) {
+ super(tag, provider, context, handler, getFingerprintSensorPropertiesInternal(prop,
+ new ArrayList<>(), resetLockoutRequiresHardwareAuthToken),
+ lockoutResetDispatcher,
+ gestureAvailabilityDispatcher,
+ biometricContext, null /* session */);
+ mLockoutResetDispatcher = lockoutResetDispatcher;
+ mInternalCleanupRunnable = internalCleanupRunnable;
+ mAuthSessionCoordinator = authSessionCoordinator;
+ mDaemon = daemon;
+ mAidlResponseHandlerCallback = aidlResponseHandlerCallback == null
+ ? new AidlResponseHandler.AidlResponseHandlerCallback() {
+ @Override
+ public void onEnrollSuccess() {
+ getScheduler()
+ .scheduleClientMonitor(getFingerprintUpdateActiveUserClient(
+ mCurrentUserId, true /* forceUpdateAuthenticatorIds */));
+ }
+
+ @Override
+ public void onHardwareUnavailable() {
+ mDaemon = null;
+ mSession = null;
+ mCurrentUserId = UserHandle.USER_NULL;
+ }
+ } : aidlResponseHandlerCallback;
+ }
+
+ @Override
+ public void serviceDied(long cookie) {
+ Slog.d(TAG, "HAL died.");
+ mSession = null;
+ mDaemon = null;
+ }
+
+ @Override
+ @LockoutTracker.LockoutMode
+ public int getLockoutModeForUser(int userId) {
+ return mLockoutTracker.getLockoutModeForUser(userId);
+ }
+
+ @Override
+ public void init(GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+ LockoutResetDispatcher lockoutResetDispatcher) {
+ setLazySession(this::getSession);
+ setScheduler(new UserAwareBiometricScheduler(TAG,
+ BiometricScheduler.sensorTypeFromFingerprintProperties(getSensorProperties()),
+ gestureAvailabilityDispatcher, () -> mCurrentUserId, getUserSwitchCallback()));
+ mLockoutTracker = new LockoutFrameworkImpl(getContext(),
+ userId -> mLockoutResetDispatcher.notifyLockoutResetCallbacks(
+ getSensorProperties().sensorId));
+ }
+
+ @Override
+ @Nullable
+ protected AidlSession getSessionForUser(int userId) {
+ if (mSession != null && mSession.getUserId() == userId) {
+ return mSession;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ protected boolean isHardwareDetected(String halInstance) {
+ return getIBiometricsFingerprint() != null;
+ }
+
+ @NonNull
+ @Override
+ protected LockoutTracker getLockoutTracker(boolean forAuth) {
+ return mLockoutTracker;
+ }
+
+ private synchronized AidlSession getSession() {
+ if (mSession != null && mDaemon != null) {
+ return mSession;
+ } else {
+ return mSession = new AidlSession(this::getIBiometricsFingerprint,
+ mCurrentUserId, getAidlResponseHandler());
+ }
+ }
+
+ private AidlResponseHandler getAidlResponseHandler() {
+ return new AidlResponseHandler(getContext(),
+ getScheduler(),
+ getSensorProperties().sensorId,
+ mCurrentUserId,
+ mLockoutTracker,
+ mLockoutResetDispatcher,
+ mAuthSessionCoordinator,
+ () -> {}, mAidlResponseHandlerCallback);
+ }
+
+ @VisibleForTesting IBiometricsFingerprint getIBiometricsFingerprint() {
+ if (getProvider().getTestHalEnabled()) {
+ final TestHal testHal = new TestHal(getContext(), getSensorProperties().sensorId);
+ testHal.setNotify(new HidlToAidlCallbackConverter(getAidlResponseHandler()));
+ return testHal;
+ }
+
+ if (mDaemon != null) {
+ return mDaemon;
+ }
+
+ try {
+ mDaemon = IBiometricsFingerprint.getService();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get fingerprint HAL", e);
+ } catch (java.util.NoSuchElementException e) {
+ // Service doesn't exist or cannot be opened.
+ Slog.w(TAG, "NoSuchElementException", e);
+ }
+
+ if (mDaemon == null) {
+ Slog.w(TAG, "Fingerprint HAL not available");
+ return null;
+ }
+
+ mDaemon.asBinder().linkToDeath(this, 0 /* flags */);
+
+ Slog.d(TAG, "Fingerprint HAL ready");
+
+ scheduleLoadAuthenticatorIds();
+ mInternalCleanupRunnable.run();
+ return mDaemon;
+ }
+
+ private UserAwareBiometricScheduler.UserSwitchCallback getUserSwitchCallback() {
+ return new UserAwareBiometricScheduler.UserSwitchCallback() {
+ @NonNull
+ @Override
+ public StopUserClient<?> getStopUserClient(int userId) {
+ return new StopUserClient<IBiometricsFingerprint>(getContext(),
+ HidlToAidlSensorAdapter.this::getIBiometricsFingerprint,
+ null /* token */, userId, getSensorProperties().sensorId,
+ BiometricLogger.ofUnknown(getContext()), getBiometricContext(),
+ () -> {
+ mCurrentUserId = UserHandle.USER_NULL;
+ mSession = null;
+ }) {
+ @Override
+ public void start(@NonNull ClientMonitorCallback callback) {
+ super.start(callback);
+ startHalOperation();
+ }
+
+ @Override
+ protected void startHalOperation() {
+ onUserStopped();
+ }
+
+ @Override
+ public void unableToStart() {
+ getCallback().onClientFinished(this, false /* success */);
+ }
+ };
+ }
+
+ @NonNull
+ @Override
+ public StartUserClient<?, ?> getStartUserClient(int newUserId) {
+ return getFingerprintUpdateActiveUserClient(newUserId,
+ false /* forceUpdateAuthenticatorId */);
+ }
+ };
+ }
+
+ private FingerprintUpdateActiveUserClient getFingerprintUpdateActiveUserClient(int newUserId,
+ boolean forceUpdateAuthenticatorIds) {
+ return new FingerprintUpdateActiveUserClient(getContext(),
+ this::getIBiometricsFingerprint, newUserId, TAG,
+ getSensorProperties().sensorId, BiometricLogger.ofUnknown(getContext()),
+ getBiometricContext(), () -> mCurrentUserId,
+ !FingerprintUtils.getInstance(getSensorProperties().sensorId)
+ .getBiometricsForUser(getContext(),
+ newUserId).isEmpty(), getAuthenticatorIds(), forceUpdateAuthenticatorIds,
+ mUserStartedCallback);
+ }
+
+ private void scheduleLoadAuthenticatorIds() {
+ getHandler().post(() -> {
+ for (UserInfo user : UserManager.get(getContext()).getAliveUsers()) {
+ final int targetUserId = user.id;
+ if (!getAuthenticatorIds().containsKey(targetUserId)) {
+ getScheduler().scheduleClientMonitor(getFingerprintUpdateActiveUserClient(
+ targetUserId, true /* forceUpdateAuthenticatorIds */));
+ }
+ }
+ });
+ }
+
+ @VisibleForTesting void handleUserChanged(int newUserId) {
+ Slog.d(TAG, "User changed. Current user is " + newUserId);
+ mSession = null;
+ mCurrentUserId = newUserId;
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/AidlToHidlAdapter.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapter.java
similarity index 73%
rename from services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/AidlToHidlAdapter.java
rename to services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapter.java
index b48d232..2fc00e1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/AidlToHidlAdapter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapter.java
@@ -25,20 +25,25 @@
import android.hardware.keymaster.HardwareAuthToken;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.Log;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
import com.android.server.biometrics.sensors.fingerprint.aidl.AidlResponseHandler;
+import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintGetAuthenticatorIdClient;
+import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintInvalidationClient;
import java.util.function.Supplier;
/**
- * Adapter to convert AIDL-specific interface {@link ISession} methods to HIDL implementation.
+ * Adapter to convert HIDL methods into AIDL interface {@link ISession}.
*/
-public class AidlToHidlAdapter implements ISession {
- private final String TAG = "AidlToHidlAdapter";
+public class HidlToAidlSessionAdapter implements ISession {
+
+ private final String TAG = "HidlToAidlSessionAdapter";
+
@VisibleForTesting
static final int ENROLL_TIMEOUT_SEC = 60;
@NonNull
@@ -46,22 +51,13 @@
private final int mUserId;
private HidlToAidlCallbackConverter mHidlToAidlCallbackConverter;
- public AidlToHidlAdapter(Supplier<IBiometricsFingerprint> session, int userId,
+ public HidlToAidlSessionAdapter(Supplier<IBiometricsFingerprint> session, int userId,
AidlResponseHandler aidlResponseHandler) {
mSession = session;
mUserId = userId;
setCallback(aidlResponseHandler);
}
- private void setCallback(AidlResponseHandler aidlResponseHandler) {
- mHidlToAidlCallbackConverter = new HidlToAidlCallbackConverter(aidlResponseHandler);
- try {
- mSession.get().setNotify(mHidlToAidlCallbackConverter);
- } catch (RemoteException e) {
- Slog.d(TAG, "Failed to set callback");
- }
- }
-
@Override
public IBinder asBinder() {
return null;
@@ -125,12 +121,16 @@
@Override
public void getAuthenticatorId() throws RemoteException {
- //Unsupported in HIDL
+ Log.e(TAG, "getAuthenticatorId unsupported in HIDL");
+ mHidlToAidlCallbackConverter.unsupportedClientScheduled(
+ FingerprintGetAuthenticatorIdClient.class);
}
@Override
public void invalidateAuthenticatorId() throws RemoteException {
- //Unsupported in HIDL
+ Log.e(TAG, "invalidateAuthenticatorId unsupported in HIDL");
+ mHidlToAidlCallbackConverter.unsupportedClientScheduled(
+ FingerprintInvalidationClient.class);
}
@Override
@@ -140,72 +140,92 @@
@Override
public void close() throws RemoteException {
- //Unsupported in HIDL
+ Log.e(TAG, "close unsupported in HIDL");
}
@Override
public void onUiReady() throws RemoteException {
- //Unsupported in HIDL
+ Log.e(TAG, "onUiReady unsupported in HIDL");
}
@Override
public ICancellationSignal authenticateWithContext(long operationId, OperationContext context)
throws RemoteException {
- //Unsupported in HIDL
- return null;
+ Log.e(TAG, "authenticateWithContext unsupported in HIDL");
+ return authenticate(operationId);
}
@Override
public ICancellationSignal enrollWithContext(HardwareAuthToken hat, OperationContext context)
throws RemoteException {
- //Unsupported in HIDL
- return null;
+ Log.e(TAG, "enrollWithContext unsupported in HIDL");
+ return enroll(hat);
}
@Override
public ICancellationSignal detectInteractionWithContext(OperationContext context)
throws RemoteException {
- //Unsupported in HIDL
- return null;
+ Log.e(TAG, "enrollWithContext unsupported in HIDL");
+ return detectInteraction();
}
@Override
public void onPointerDownWithContext(PointerContext context) throws RemoteException {
- //Unsupported in HIDL
+ Log.e(TAG, "onPointerDownWithContext unsupported in HIDL");
+ onPointerDown(context.pointerId, (int) context.x, (int) context.y, context.minor,
+ context.major);
}
@Override
public void onPointerUpWithContext(PointerContext context) throws RemoteException {
- //Unsupported in HIDL
+ Log.e(TAG, "onPointerUpWithContext unsupported in HIDL");
+ onPointerUp(context.pointerId);
}
@Override
public void onContextChanged(OperationContext context) throws RemoteException {
- //Unsupported in HIDL
+ Log.e(TAG, "onContextChanged unsupported in HIDL");
}
@Override
public void onPointerCancelWithContext(PointerContext context) throws RemoteException {
- //Unsupported in HIDL
+ Log.e(TAG, "onPointerCancelWithContext unsupported in HIDL");
}
@Override
public void setIgnoreDisplayTouches(boolean shouldIgnore) throws RemoteException {
- //Unsupported in HIDL
+ Log.e(TAG, "setIgnoreDisplayTouches unsupported in HIDL");
}
@Override
public int getInterfaceVersion() throws RemoteException {
- //Unsupported in HIDL
+ Log.e(TAG, "getInterfaceVersion unsupported in HIDL");
return 0;
}
@Override
public String getInterfaceHash() throws RemoteException {
- //Unsupported in HIDL
+ Log.e(TAG, "getInterfaceHash unsupported in HIDL");
return null;
}
+ private void setCallback(AidlResponseHandler aidlResponseHandler) {
+ mHidlToAidlCallbackConverter = new HidlToAidlCallbackConverter(aidlResponseHandler);
+ try {
+ if (mSession.get() != null) {
+ long halId = mSession.get().setNotify(mHidlToAidlCallbackConverter);
+ Slog.d(TAG, "Fingerprint HAL ready, HAL ID: " + halId);
+ if (halId == 0) {
+ Slog.d(TAG, "Unable to set HIDL callback.");
+ }
+ } else {
+ Slog.e(TAG, "Unable to set HIDL callback. HIDL daemon is null.");
+ }
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Failed to set callback");
+ }
+ }
+
private class Cancellation extends ICancellationSignal.Stub {
Cancellation() {}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java
index 0730c67..2f77275 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java
@@ -18,6 +18,7 @@
import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
+import android.annotation.NonNull;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -31,15 +32,18 @@
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.sensors.LockoutTracker;
+import java.util.function.Function;
+
/**
* Tracks and enforces biometric lockout for biometric sensors that do not support lockout in the
* HAL.
*/
public class LockoutFrameworkImpl implements LockoutTracker {
- private static final String TAG = "LockoutTracker";
+ private static final String TAG = "LockoutFrameworkImpl";
private static final String ACTION_LOCKOUT_RESET =
"com.android.server.biometrics.sensors.fingerprint.ACTION_LOCKOUT_RESET";
private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED = 5;
@@ -65,22 +69,32 @@
void onLockoutReset(int userId);
}
- private final Context mContext;
private final LockoutResetCallback mLockoutResetCallback;
private final SparseBooleanArray mTimedLockoutCleared;
private final SparseIntArray mFailedAttempts;
private final AlarmManager mAlarmManager;
private final LockoutReceiver mLockoutReceiver;
private final Handler mHandler;
+ private final Function<Integer, PendingIntent> mLockoutResetIntent;
- public LockoutFrameworkImpl(Context context, LockoutResetCallback lockoutResetCallback) {
- mContext = context;
+ public LockoutFrameworkImpl(@NonNull Context context,
+ @NonNull LockoutResetCallback lockoutResetCallback) {
+ this(context, lockoutResetCallback, (userId) -> PendingIntent.getBroadcast(context, userId,
+ new Intent(ACTION_LOCKOUT_RESET).putExtra(KEY_LOCKOUT_RESET_USER, userId),
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE));
+ }
+
+ @VisibleForTesting
+ LockoutFrameworkImpl(@NonNull Context context,
+ @NonNull LockoutResetCallback lockoutResetCallback,
+ @NonNull Function<Integer, PendingIntent> lockoutResetIntent) {
mLockoutResetCallback = lockoutResetCallback;
mTimedLockoutCleared = new SparseBooleanArray();
mFailedAttempts = new SparseIntArray();
mAlarmManager = context.getSystemService(AlarmManager.class);
mLockoutReceiver = new LockoutReceiver();
mHandler = new Handler(Looper.getMainLooper());
+ mLockoutResetIntent = lockoutResetIntent;
context.registerReceiver(mLockoutReceiver, new IntentFilter(ACTION_LOCKOUT_RESET),
RESET_FINGERPRINT_LOCKOUT, null /* handler */, Context.RECEIVER_EXPORTED);
@@ -129,34 +143,18 @@
return LOCKOUT_NONE;
}
- /**
- * Clears lockout for Fingerprint HIDL HAL
- */
@Override
- public void setLockoutModeForUser(int userId, int mode) {
- mFailedAttempts.put(userId, 0);
- mTimedLockoutCleared.put(userId, true);
- // If we're asked to reset failed attempts externally (i.e. from Keyguard),
- // the alarm might still be pending; remove it.
- cancelLockoutResetForUser(userId);
- mLockoutResetCallback.onLockoutReset(userId);
- }
+ public void setLockoutModeForUser(int userId, int mode) {}
private void cancelLockoutResetForUser(int userId) {
- mAlarmManager.cancel(getLockoutResetIntentForUser(userId));
+ mAlarmManager.cancel(mLockoutResetIntent.apply(userId));
}
private void scheduleLockoutResetForUser(int userId) {
mHandler.post(() -> {
mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS,
- getLockoutResetIntentForUser(userId));
+ SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS,
+ mLockoutResetIntent.apply(userId));
});
}
-
- private PendingIntent getLockoutResetIntentForUser(int userId) {
- return PendingIntent.getBroadcast(mContext, userId,
- new Intent(ACTION_LOCKOUT_RESET).putExtra(KEY_LOCKOUT_RESET_USER, userId),
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index 3b5cae3..88b2ed4 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -50,14 +50,22 @@
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.PromptInfo;
+import android.hardware.biometrics.fingerprint.SensorProps;
+import android.hardware.face.FaceSensorConfigurations;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.IFaceService;
+import android.hardware.fingerprint.FingerprintSensorConfigurations;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintService;
import android.hardware.iris.IIrisService;
import android.os.Binder;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.InstrumentationRegistry;
@@ -89,6 +97,9 @@
@Rule
public MockitoRule mockitorule = MockitoJUnit.rule();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -118,6 +129,10 @@
@Captor
private ArgumentCaptor<List<FingerprintSensorPropertiesInternal>> mFingerprintPropsCaptor;
@Captor
+ private ArgumentCaptor<FingerprintSensorConfigurations> mFingerprintSensorConfigurationsCaptor;
+ @Captor
+ private ArgumentCaptor<FaceSensorConfigurations> mFaceSensorConfigurationsCaptor;
+ @Captor
private ArgumentCaptor<List<FaceSensorPropertiesInternal>> mFacePropsCaptor;
@Before
@@ -143,6 +158,9 @@
when(mContext.getResources()).thenReturn(mResources);
when(mInjector.getBiometricService()).thenReturn(mBiometricService);
when(mInjector.getConfiguration(any())).thenReturn(config);
+ when(mInjector.getFaceConfiguration(any())).thenReturn(config);
+ when(mInjector.getFingerprintConfiguration(any())).thenReturn(config);
+ when(mInjector.getIrisConfiguration(any())).thenReturn(config);
when(mInjector.getFingerprintService()).thenReturn(mFingerprintService);
when(mInjector.getFaceService()).thenReturn(mFaceService);
when(mInjector.getIrisService()).thenReturn(mIrisService);
@@ -173,12 +191,13 @@
}
@Test
+ @RequiresFlagsDisabled(com.android.server.biometrics.Flags.FLAG_DE_HIDL)
public void testRegisterAuthenticator_registerAuthenticators() throws Exception {
final int fingerprintId = 0;
final int fingerprintStrength = 15;
final int faceId = 1;
- final int faceStrength = 4095;
+ final int faceStrength = 15;
final String[] config = {
// ID0:Fingerprint:Strong
@@ -206,6 +225,51 @@
Utils.authenticatorStrengthToPropertyStrength(faceStrength));
}
+ @Test
+ @RequiresFlagsEnabled(com.android.server.biometrics.Flags.FLAG_DE_HIDL)
+ public void testRegisterAuthenticator_registerAuthenticatorsLegacy() throws RemoteException {
+ final int fingerprintId = 0;
+ final int fingerprintStrength = 15;
+
+ final int faceId = 1;
+ final int faceStrength = 4095;
+
+ final String[] config = {
+ // ID0:Fingerprint:Strong
+ String.format("%d:2:%d", fingerprintId, fingerprintStrength),
+ // ID2:Face:Convenience
+ String.format("%d:8:%d", faceId, faceStrength)
+ };
+
+ when(mInjector.getFingerprintConfiguration(any())).thenReturn(config);
+ when(mInjector.getFaceConfiguration(any())).thenReturn(config);
+ when(mInjector.getFingerprintAidlInstances()).thenReturn(new String[]{});
+ when(mInjector.getFaceAidlInstances()).thenReturn(new String[]{});
+
+ mAuthService = new AuthService(mContext, mInjector);
+ mAuthService.onStart();
+
+ verify(mFingerprintService).registerAuthenticatorsLegacy(
+ mFingerprintSensorConfigurationsCaptor.capture());
+
+ final SensorProps[] fingerprintProp = mFingerprintSensorConfigurationsCaptor.getValue()
+ .getSensorPairForInstance("defaultHIDL").second;
+
+ assertEquals(fingerprintProp[0].commonProps.sensorId, fingerprintId);
+ assertEquals(fingerprintProp[0].commonProps.sensorStrength,
+ Utils.authenticatorStrengthToPropertyStrength(fingerprintStrength));
+
+ verify(mFaceService).registerAuthenticatorsLegacy(
+ mFaceSensorConfigurationsCaptor.capture());
+
+ final android.hardware.biometrics.face.SensorProps[] faceProp =
+ mFaceSensorConfigurationsCaptor.getValue()
+ .getSensorPairForInstance("defaultHIDL").second;
+
+ assertEquals(faceProp[0].commonProps.sensorId, faceId);
+ assertEquals(faceProp[0].commonProps.sensorStrength,
+ Utils.authenticatorStrengthToPropertyStrength(faceStrength));
+ }
// TODO(b/141025588): Check that an exception is thrown when the userId != callingUserId
@Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceTest.java
new file mode 100644
index 0000000..c9e1c4a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceTest.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face;
+
+import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
+import static android.hardware.face.FaceSensorProperties.TYPE_UNKNOWN;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.IBiometricService;
+import android.hardware.biometrics.face.IFace;
+import android.hardware.biometrics.face.SensorProps;
+import android.hardware.face.FaceSensorConfigurations;
+import android.hardware.face.FaceSensorPropertiesInternal;
+import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.provider.Settings;
+import android.testing.TestableContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.internal.util.test.FakeSettingsProviderRule;
+import com.android.server.biometrics.Flags;
+import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.sensors.face.aidl.FaceProvider;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@Presubmit
+@SmallTest
+public class FaceServiceTest {
+ private static final int ID_DEFAULT = 2;
+ private static final int ID_VIRTUAL = 6;
+ private static final String NAME_DEFAULT = "default";
+ private static final String NAME_VIRTUAL = "virtual";
+
+ @Rule
+ public final MockitoRule mMockito = MockitoJUnit.rule();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
+ @Rule
+ public final FakeSettingsProviderRule mSettingsRule = FakeSettingsProvider.rule();
+ @Mock
+ FaceProvider mFaceProviderDefault;
+ @Mock
+ FaceProvider mFaceProviderVirtual;
+ @Mock
+ IFace mDefaultFaceDaemon;
+ @Mock
+ IFace mVirtualFaceDaemon;
+ @Mock
+ IBiometricService mIBiometricService;
+
+ private final SensorProps mDefaultSensorProps = new SensorProps();
+ private final SensorProps mVirtualSensorProps = new SensorProps();
+ private FaceService mFaceService;
+ private final FaceSensorPropertiesInternal mSensorPropsDefault =
+ new FaceSensorPropertiesInternal(ID_DEFAULT, STRENGTH_STRONG,
+ 2 /* maxEnrollmentsPerUser */,
+ List.of(),
+ TYPE_UNKNOWN,
+ true /* supportsFaceDetection */,
+ true /* supportsSelfIllumination */,
+ false /* resetLockoutRequiresChallenge */);
+ private final FaceSensorPropertiesInternal mSensorPropsVirtual =
+ new FaceSensorPropertiesInternal(ID_VIRTUAL, STRENGTH_STRONG,
+ 2 /* maxEnrollmentsPerUser */,
+ List.of(),
+ TYPE_UNKNOWN,
+ true /* supportsFaceDetection */,
+ true /* supportsSelfIllumination */,
+ false /* resetLockoutRequiresChallenge */);
+ private FaceSensorConfigurations mFaceSensorConfigurations;
+
+ @Before
+ public void setUp() throws RemoteException {
+ when(mDefaultFaceDaemon.getSensorProps()).thenReturn(
+ new SensorProps[]{mDefaultSensorProps});
+ when(mVirtualFaceDaemon.getSensorProps()).thenReturn(
+ new SensorProps[]{mVirtualSensorProps});
+ when(mFaceProviderDefault.getSensorProperties()).thenReturn(List.of(mSensorPropsDefault));
+ when(mFaceProviderVirtual.getSensorProperties()).thenReturn(List.of(mSensorPropsVirtual));
+
+ mFaceSensorConfigurations = new FaceSensorConfigurations(false);
+ mFaceSensorConfigurations.addAidlConfigs(new String[]{NAME_DEFAULT, NAME_VIRTUAL},
+ (name) -> {
+ if (name.equals(IFace.DESCRIPTOR + "/" + NAME_DEFAULT)) {
+ return mDefaultFaceDaemon;
+ } else if (name.equals(IFace.DESCRIPTOR + "/" + NAME_VIRTUAL)) {
+ return mVirtualFaceDaemon;
+ }
+ return null;
+ });
+ }
+
+ private void initService() {
+ mFaceService = new FaceService(mContext,
+ (filteredSensorProps, resetLockoutRequiresChallenge) -> {
+ if (NAME_DEFAULT.equals(filteredSensorProps.first)) return mFaceProviderDefault;
+ if (NAME_VIRTUAL.equals(filteredSensorProps.first)) return mFaceProviderVirtual;
+ return null;
+ }, () -> mIBiometricService);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
+ public void registerAuthenticatorsLegacy_defaultOnly() throws Exception {
+ initService();
+
+ mFaceService.mServiceWrapper.registerAuthenticatorsLegacy(mFaceSensorConfigurations);
+ waitForRegistration();
+
+ verify(mIBiometricService).registerAuthenticator(eq(ID_DEFAULT),
+ eq(BiometricAuthenticator.TYPE_FACE),
+ eq(Utils.propertyStrengthToAuthenticatorStrength(STRENGTH_STRONG)),
+ any());
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
+ public void registerAuthenticatorsLegacy_virtualOnly() throws Exception {
+ initService();
+ Settings.Secure.putInt(mSettingsRule.mockContentResolver(mContext),
+ Settings.Secure.BIOMETRIC_VIRTUAL_ENABLED, 1);
+
+ mFaceService.mServiceWrapper.registerAuthenticatorsLegacy(mFaceSensorConfigurations);
+ waitForRegistration();
+
+ verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL),
+ eq(BiometricAuthenticator.TYPE_FACE),
+ eq(Utils.propertyStrengthToAuthenticatorStrength(STRENGTH_STRONG)), any());
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
+ public void registerAuthenticatorsLegacy_virtualAlwaysWhenNoOther() throws Exception {
+ mFaceSensorConfigurations = new FaceSensorConfigurations(false);
+ mFaceSensorConfigurations.addAidlConfigs(new String[]{NAME_VIRTUAL},
+ (name) -> {
+ if (name.equals(IFace.DESCRIPTOR + "/" + NAME_DEFAULT)) {
+ return mDefaultFaceDaemon;
+ } else if (name.equals(IFace.DESCRIPTOR + "/" + NAME_VIRTUAL)) {
+ return mVirtualFaceDaemon;
+ }
+ return null;
+ });
+ initService();
+
+ mFaceService.mServiceWrapper.registerAuthenticatorsLegacy(mFaceSensorConfigurations);
+ waitForRegistration();
+
+ verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL),
+ eq(BiometricAuthenticator.TYPE_FACE),
+ eq(Utils.propertyStrengthToAuthenticatorStrength(STRENGTH_STRONG)), any());
+ }
+
+ private void waitForRegistration() throws Exception {
+ final CountDownLatch latch = new CountDownLatch(1);
+ mFaceService.mServiceWrapper.addAuthenticatorsRegisteredCallback(
+ new IFaceAuthenticatorsRegisteredCallback.Stub() {
+ public void onAllAuthenticatorsRegistered(
+ List<FaceSensorPropertiesInternal> sensors) {
+ latch.countDown();
+ }
+ });
+ latch.await(10, TimeUnit.SECONDS);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
index f43120d..8b1a291 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
@@ -16,6 +16,9 @@
package com.android.server.biometrics.sensors.face.aidl;
+import static android.os.UserHandle.USER_NULL;
+import static android.os.UserHandle.USER_SYSTEM;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
@@ -32,15 +35,19 @@
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.ISession;
import android.hardware.biometrics.face.SensorProps;
+import android.hardware.face.HidlFaceSensorConfig;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
@@ -49,6 +56,7 @@
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -59,6 +67,10 @@
@SmallTest
public class FaceProviderTest {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
private static final String TAG = "FaceProviderTest";
private static final float FRR_THRESHOLD = 0.2f;
@@ -109,7 +121,7 @@
mFaceProvider = new FaceProvider(mContext, mBiometricStateCallback,
mSensorProps, TAG, mLockoutResetDispatcher, mBiometricContext,
- mDaemon);
+ mDaemon, false /* resetLockoutRequiresChallenge */, false /* testHalEnabled */);
}
@Test
@@ -124,10 +136,38 @@
assertThat(currentClient).isInstanceOf(FaceInternalCleanupClient.class);
assertThat(currentClient.getSensorId()).isEqualTo(prop.commonProps.sensorId);
- assertThat(currentClient.getTargetUserId()).isEqualTo(UserHandle.USER_SYSTEM);
+ assertThat(currentClient.getTargetUserId()).isEqualTo(USER_SYSTEM);
}
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
+ public void testAddingHidlSensors() {
+ when(mResources.getIntArray(anyInt())).thenReturn(new int[]{});
+ when(mResources.getBoolean(anyInt())).thenReturn(false);
+
+ final int faceId = 0;
+ final int faceStrength = 15;
+ final String config = String.format("%d:8:%d", faceId, faceStrength);
+ final HidlFaceSensorConfig faceSensorConfig = new HidlFaceSensorConfig();
+ faceSensorConfig.parse(config, mContext);
+ final HidlFaceSensorConfig[] hidlFaceSensorConfig =
+ new HidlFaceSensorConfig[]{faceSensorConfig};
+ mFaceProvider = new FaceProvider(mContext,
+ mBiometricStateCallback, hidlFaceSensorConfig, TAG,
+ mLockoutResetDispatcher, mBiometricContext, mDaemon,
+ true /* resetLockoutRequiresChallenge */,
+ true /* testHalEnabled */);
+
+ assertThat(mFaceProvider.mFaceSensors.get(faceId)
+ .getLazySession().get().getUserId()).isEqualTo(USER_NULL);
+
+ waitForIdle();
+
+ assertThat(mFaceProvider.mFaceSensors.get(faceId)
+ .getLazySession().get().getUserId()).isEqualTo(USER_SYSTEM);
+ }
+
@SuppressWarnings("rawtypes")
@Test
public void halServiceDied_resetsAllSchedulers() {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
index 7a293e8..e7f7195 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
@@ -157,7 +157,7 @@
sensorProps.halControlsPreview, false /* resetLockoutRequiresChallenge */);
final Sensor sensor = new Sensor("SensorTest", mFaceProvider, mContext,
null /* handler */, internalProp, mLockoutResetDispatcher, mBiometricContext);
-
+ sensor.init(mLockoutResetDispatcher, mFaceProvider);
mScheduler.reset();
assertNull(mScheduler.getCurrentClient());
@@ -185,6 +185,7 @@
sensorProps.halControlsPreview, false /* resetLockoutRequiresChallenge */);
final Sensor sensor = new Sensor("SensorTest", mFaceProvider, mContext, null,
internalProp, mLockoutResetDispatcher, mBiometricContext, mCurrentSession);
+ sensor.init(mLockoutResetDispatcher, mFaceProvider);
mScheduler = (UserAwareBiometricScheduler) sensor.getScheduler();
sensor.mCurrentSession = new AidlSession(0, mock(ISession.class),
USER_ID, mHalCallback);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java
new file mode 100644
index 0000000..4e43332
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face.hidl;
+
+import static com.android.server.biometrics.sensors.face.hidl.HidlToAidlSessionAdapter.ENROLL_TIMEOUT_SEC;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.IBiometricService;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.biometrics.face.V1_0.OptionalUint64;
+import android.hardware.biometrics.face.V1_0.Status;
+import android.hardware.face.Face;
+import android.hardware.face.HidlFaceSensorConfig;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.test.TestLooper;
+import android.testing.TestableContext;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.AuthSessionCoordinator;
+import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.face.aidl.AidlResponseHandler;
+import com.android.server.biometrics.sensors.face.aidl.FaceEnrollClient;
+import com.android.server.biometrics.sensors.face.aidl.FaceProvider;
+import com.android.server.biometrics.sensors.face.aidl.FaceResetLockoutClient;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+public class HidlToAidlSensorAdapterTest {
+ private static final String TAG = "HidlToAidlSensorAdapterTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+ private static final byte[] HAT = new byte[69];
+ @Rule
+ public final MockitoRule mMockito = MockitoJUnit.rule();
+
+ @Mock
+ private IBiometricService mBiometricService;
+ @Mock
+ private LockoutResetDispatcher mLockoutResetDispatcherForSensor;
+ @Mock
+ private LockoutResetDispatcher mLockoutResetDispatcherForClient;
+ @Mock
+ private BiometricLogger mLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ @Mock
+ private AuthSessionCoordinator mAuthSessionCoordinator;
+ @Mock
+ private FaceProvider mFaceProvider;
+ @Mock
+ private Runnable mInternalCleanupAndGetFeatureRunnable;
+ @Mock
+ private IBiometricsFace mDaemon;
+ @Mock
+ AidlResponseHandler.AidlResponseHandlerCallback mAidlResponseHandlerCallback;
+ @Mock
+ BiometricUtils<Face> mBiometricUtils;
+
+ private final TestLooper mLooper = new TestLooper();
+ private HidlToAidlSensorAdapter mHidlToAidlSensorAdapter;
+ private final TestableContext mContext = new TestableContext(
+ ApplicationProvider.getApplicationContext());
+
+ @Before
+ public void setUp() throws RemoteException {
+ final OptionalUint64 result = new OptionalUint64();
+ result.status = Status.OK;
+
+ when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator);
+ when(mDaemon.setCallback(any())).thenReturn(result);
+ doAnswer((answer) -> {
+ mHidlToAidlSensorAdapter.getLazySession().get().getHalSessionCallback()
+ .onLockoutChanged(0);
+ return null;
+ }).when(mDaemon).resetLockout(any());
+ doAnswer((answer) -> {
+ mHidlToAidlSensorAdapter.getLazySession().get().getHalSessionCallback()
+ .onEnrollmentProgress(1, 0);
+ return null;
+ }).when(mDaemon).enroll(any(), anyInt(), any());
+
+ mContext.getOrCreateTestableResources();
+
+ final String config = String.format("%d:8:15", SENSOR_ID);
+ final BiometricScheduler scheduler = new BiometricScheduler(TAG,
+ new Handler(mLooper.getLooper()),
+ BiometricScheduler.SENSOR_TYPE_FACE,
+ null /* gestureAvailabilityTracker */,
+ mBiometricService, 10 /* recentOperationsLimit */);
+ final HidlFaceSensorConfig faceSensorConfig = new HidlFaceSensorConfig();
+ faceSensorConfig.parse(config, mContext);
+ mHidlToAidlSensorAdapter = new HidlToAidlSensorAdapter(TAG, mFaceProvider,
+ mContext, new Handler(mLooper.getLooper()), faceSensorConfig,
+ mLockoutResetDispatcherForSensor, mBiometricContext,
+ false /* resetLockoutRequiresChallenge */, mInternalCleanupAndGetFeatureRunnable,
+ mAuthSessionCoordinator, mDaemon, mAidlResponseHandlerCallback);
+ mHidlToAidlSensorAdapter.init(mLockoutResetDispatcherForSensor, mFaceProvider);
+ mHidlToAidlSensorAdapter.setScheduler(scheduler);
+ mHidlToAidlSensorAdapter.handleUserChanged(USER_ID);
+ }
+
+ @Test
+ public void lockoutTimedResetViaClient() {
+ setLockoutTimed();
+
+ mHidlToAidlSensorAdapter.getScheduler().scheduleClientMonitor(
+ new FaceResetLockoutClient(mContext, mHidlToAidlSensorAdapter.getLazySession(),
+ USER_ID, TAG, SENSOR_ID, mLogger, mBiometricContext, HAT,
+ mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */),
+ mLockoutResetDispatcherForClient, 0 /* biometricStrength */));
+ mLooper.dispatchAll();
+
+ assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false/* forAuth */)
+ .getLockoutModeForUser(USER_ID)).isEqualTo(LockoutTracker.LOCKOUT_NONE);
+ verify(mLockoutResetDispatcherForClient, never()).notifyLockoutResetCallbacks(SENSOR_ID);
+ verify(mLockoutResetDispatcherForSensor).notifyLockoutResetCallbacks(SENSOR_ID);
+ verify(mAuthSessionCoordinator, never()).resetLockoutFor(eq(USER_ID), anyInt(), anyLong());
+ }
+
+ @Test
+ public void lockoutTimedResetViaCallback() {
+ setLockoutTimed();
+
+ mHidlToAidlSensorAdapter.getLazySession().get().getHalSessionCallback().onLockoutChanged(0);
+ mLooper.dispatchAll();
+
+ verify(mLockoutResetDispatcherForSensor).notifyLockoutResetCallbacks(eq(
+ SENSOR_ID));
+ assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */)
+ .getLockoutModeForUser(USER_ID))
+ .isEqualTo(LockoutTracker.LOCKOUT_NONE);
+ verify(mAuthSessionCoordinator, never()).resetLockoutFor(eq(USER_ID), anyInt(), anyLong());
+ }
+
+ @Test
+ public void lockoutPermanentResetViaCallback() {
+ setLockoutPermanent();
+
+ mHidlToAidlSensorAdapter.getLazySession().get().getHalSessionCallback().onLockoutChanged(0);
+ mLooper.dispatchAll();
+
+ verify(mLockoutResetDispatcherForSensor).notifyLockoutResetCallbacks(eq(
+ SENSOR_ID));
+ assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */)
+ .getLockoutModeForUser(USER_ID)).isEqualTo(LockoutTracker.LOCKOUT_NONE);
+ verify(mAuthSessionCoordinator, never()).resetLockoutFor(eq(USER_ID), anyInt(), anyLong());
+ }
+
+ @Test
+ public void lockoutPermanentResetViaClient() {
+ setLockoutPermanent();
+
+ mHidlToAidlSensorAdapter.getScheduler().scheduleClientMonitor(
+ new FaceResetLockoutClient(mContext,
+ mHidlToAidlSensorAdapter.getLazySession(),
+ USER_ID, TAG, SENSOR_ID, mLogger, mBiometricContext, HAT,
+ mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */),
+ mLockoutResetDispatcherForClient, 0 /* biometricStrength */));
+ mLooper.dispatchAll();
+
+ verify(mLockoutResetDispatcherForClient, never()).notifyLockoutResetCallbacks(SENSOR_ID);
+ verify(mLockoutResetDispatcherForSensor).notifyLockoutResetCallbacks(SENSOR_ID);
+ assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */)
+ .getLockoutModeForUser(USER_ID)).isEqualTo(LockoutTracker.LOCKOUT_NONE);
+ verify(mAuthSessionCoordinator, never()).resetLockoutFor(eq(USER_ID), anyInt(), anyLong());
+ }
+
+ @Test
+ public void verifyOnEnrollSuccessCallback() {
+ mHidlToAidlSensorAdapter.getScheduler().scheduleClientMonitor(new FaceEnrollClient(mContext,
+ mHidlToAidlSensorAdapter.getLazySession(), null /* token */, null /* listener */,
+ USER_ID, HAT, TAG, 1 /* requestId */, mBiometricUtils,
+ new int[]{} /* disabledFeatures */, ENROLL_TIMEOUT_SEC, null /* previewSurface */,
+ SENSOR_ID, mLogger, mBiometricContext, 1 /* maxTemplatesPerUser */,
+ false /* debugConsent */));
+ mLooper.dispatchAll();
+
+ verify(mAidlResponseHandlerCallback).onEnrollSuccess();
+ }
+
+ private void setLockoutTimed() {
+ mHidlToAidlSensorAdapter.getLazySession().get().getHalSessionCallback()
+ .onLockoutChanged(1);
+ mLooper.dispatchAll();
+
+ assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */)
+ .getLockoutModeForUser(USER_ID))
+ .isEqualTo(LockoutTracker.LOCKOUT_TIMED);
+ }
+
+ private void setLockoutPermanent() {
+ mHidlToAidlSensorAdapter.getLazySession().get().getHalSessionCallback()
+ .onLockoutChanged(-1);
+ mLooper.dispatchAll();
+
+ assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */)
+ .getLockoutModeForUser(USER_ID)).isEqualTo(LockoutTracker.LOCKOUT_PERMANENT);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapterTest.java
similarity index 80%
rename from services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapterTest.java
rename to services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapterTest.java
index 9a40e8a..b9a4fb4 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapterTest.java
@@ -16,7 +16,7 @@
package com.android.server.biometrics.sensors.face.hidl;
-import static com.android.server.biometrics.sensors.face.hidl.AidlToHidlAdapter.ENROLL_TIMEOUT_SEC;
+import static com.android.server.biometrics.sensors.face.hidl.HidlToAidlSessionAdapter.ENROLL_TIMEOUT_SEC;
import static com.android.server.biometrics.sensors.face.hidl.FaceGenerateChallengeClient.CHALLENGE_TIMEOUT_SEC;
import static com.google.common.truth.Truth.assertThat;
@@ -68,7 +68,7 @@
@Presubmit
@SmallTest
-public class AidlToHidlAdapterTest {
+public class HidlToAidlSessionAdapterTest {
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
@@ -84,25 +84,32 @@
private Clock mClock;
private final long mChallenge = 100L;
- private AidlToHidlAdapter mAidlToHidlAdapter;
+ private HidlToAidlSessionAdapter mHidlToAidlSessionAdapter;
private final Face mFace = new Face("face" /* name */, 1 /* faceId */, 0 /* deviceId */);
private final int mFeature = BiometricFaceConstants.FEATURE_REQUIRE_REQUIRE_DIVERSITY;
private final byte[] mFeatures = new byte[]{Feature.REQUIRE_ATTENTION};
@Before
public void setUp() throws RemoteException {
+ final OptionalUint64 setCallbackResult = new OptionalUint64();
+ setCallbackResult.value = 1;
+
+ when(mSession.setCallback(any())).thenReturn(setCallbackResult);
+
TestableContext testableContext = new TestableContext(
InstrumentationRegistry.getInstrumentation().getContext());
testableContext.addMockSystemService(FaceManager.class, mFaceManager);
- mAidlToHidlAdapter = new AidlToHidlAdapter(testableContext, () -> mSession, 0 /* userId */,
- mAidlResponseHandler, mClock);
+
+ mHidlToAidlSessionAdapter = new HidlToAidlSessionAdapter(testableContext, () -> mSession,
+ 0 /* userId */, mAidlResponseHandler, mClock);
mHardwareAuthToken.timestamp = new Timestamp();
mHardwareAuthToken.mac = new byte[10];
- final OptionalUint64 result = new OptionalUint64();
- result.status = Status.OK;
- result.value = mChallenge;
- when(mSession.generateChallenge(anyInt())).thenReturn(result);
+ final OptionalUint64 generateChallengeResult = new OptionalUint64();
+ generateChallengeResult.status = Status.OK;
+ generateChallengeResult.value = mChallenge;
+
+ when(mSession.generateChallenge(anyInt())).thenReturn(generateChallengeResult);
when(mFaceManager.getEnrolledFaces(anyInt())).thenReturn(List.of(mFace));
}
@@ -112,16 +119,16 @@
final ArgumentCaptor<Long> challengeCaptor = ArgumentCaptor.forClass(Long.class);
- mAidlToHidlAdapter.generateChallenge();
+ mHidlToAidlSessionAdapter.generateChallenge();
verify(mSession).generateChallenge(CHALLENGE_TIMEOUT_SEC);
verify(mAidlResponseHandler).onChallengeGenerated(challengeCaptor.capture());
assertThat(challengeCaptor.getValue()).isEqualTo(mChallenge);
forwardTime(10 /* seconds */);
- mAidlToHidlAdapter.generateChallenge();
+ mHidlToAidlSessionAdapter.generateChallenge();
forwardTime(20 /* seconds */);
- mAidlToHidlAdapter.generateChallenge();
+ mHidlToAidlSessionAdapter.generateChallenge();
//Confirms that the challenge is cached and the hal method is not called again
verifyNoMoreInteractions(mSession);
@@ -129,7 +136,7 @@
.onChallengeGenerated(mChallenge);
forwardTime(60 /* seconds */);
- mAidlToHidlAdapter.generateChallenge();
+ mHidlToAidlSessionAdapter.generateChallenge();
//HAL method called after challenge has timed out
verify(mSession, times(2)).generateChallenge(CHALLENGE_TIMEOUT_SEC);
@@ -138,11 +145,11 @@
@Test
public void testRevokeChallenge_waitsUntilEmpty() throws RemoteException {
for (int i = 0; i < 3; i++) {
- mAidlToHidlAdapter.generateChallenge();
+ mHidlToAidlSessionAdapter.generateChallenge();
forwardTime(10 /* seconds */);
}
for (int i = 0; i < 3; i++) {
- mAidlToHidlAdapter.revokeChallenge(0);
+ mHidlToAidlSessionAdapter.revokeChallenge(0);
forwardTime((i + 1) * 10 /* seconds */);
}
@@ -151,20 +158,19 @@
@Test
public void testRevokeChallenge_timeout() throws RemoteException {
- mAidlToHidlAdapter.generateChallenge();
- mAidlToHidlAdapter.generateChallenge();
+ mHidlToAidlSessionAdapter.generateChallenge();
+ mHidlToAidlSessionAdapter.generateChallenge();
forwardTime(700);
- mAidlToHidlAdapter.generateChallenge();
- mAidlToHidlAdapter.revokeChallenge(0);
+ mHidlToAidlSessionAdapter.generateChallenge();
+ mHidlToAidlSessionAdapter.revokeChallenge(0);
verify(mSession).revokeChallenge();
}
@Test
public void testEnroll() throws RemoteException {
- ICancellationSignal cancellationSignal = mAidlToHidlAdapter.enroll(mHardwareAuthToken,
- EnrollmentType.DEFAULT, mFeatures,
- null /* previewSurface */);
+ ICancellationSignal cancellationSignal = mHidlToAidlSessionAdapter.enroll(
+ mHardwareAuthToken, EnrollmentType.DEFAULT, mFeatures, null /* previewSurface */);
ArgumentCaptor<ArrayList<Integer>> featureCaptor = ArgumentCaptor.forClass(ArrayList.class);
verify(mSession).enroll(any(), eq(ENROLL_TIMEOUT_SEC), featureCaptor.capture());
@@ -182,7 +188,8 @@
@Test
public void testAuthenticate() throws RemoteException {
final int operationId = 2;
- ICancellationSignal cancellationSignal = mAidlToHidlAdapter.authenticate(operationId);
+ ICancellationSignal cancellationSignal = mHidlToAidlSessionAdapter.authenticate(
+ operationId);
verify(mSession).authenticate(operationId);
@@ -193,7 +200,7 @@
@Test
public void testDetectInteraction() throws RemoteException {
- ICancellationSignal cancellationSignal = mAidlToHidlAdapter.detectInteraction();
+ ICancellationSignal cancellationSignal = mHidlToAidlSessionAdapter.detectInteraction();
verify(mSession).authenticate(0);
@@ -204,7 +211,7 @@
@Test
public void testEnumerateEnrollments() throws RemoteException {
- mAidlToHidlAdapter.enumerateEnrollments();
+ mHidlToAidlSessionAdapter.enumerateEnrollments();
verify(mSession).enumerate();
}
@@ -212,7 +219,7 @@
@Test
public void testRemoveEnrollment() throws RemoteException {
final int[] enrollments = new int[]{1};
- mAidlToHidlAdapter.removeEnrollments(enrollments);
+ mHidlToAidlSessionAdapter.removeEnrollments(enrollments);
verify(mSession).remove(enrollments[0]);
}
@@ -226,8 +233,8 @@
when(mSession.getFeature(eq(mFeature), anyInt())).thenReturn(result);
- mAidlToHidlAdapter.setFeature(mFeature);
- mAidlToHidlAdapter.getFeatures();
+ mHidlToAidlSessionAdapter.setFeature(mFeature);
+ mHidlToAidlSessionAdapter.getFeatures();
verify(mSession).getFeature(eq(mFeature), anyInt());
verify(mAidlResponseHandler).onFeaturesRetrieved(featureRetrieved.capture());
@@ -244,8 +251,8 @@
when(mSession.getFeature(eq(mFeature), anyInt())).thenReturn(result);
- mAidlToHidlAdapter.setFeature(mFeature);
- mAidlToHidlAdapter.getFeatures();
+ mHidlToAidlSessionAdapter.setFeature(mFeature);
+ mHidlToAidlSessionAdapter.getFeatures();
verify(mSession).getFeature(eq(mFeature), anyInt());
verify(mAidlResponseHandler).onFeaturesRetrieved(featureRetrieved.capture());
@@ -260,8 +267,8 @@
when(mSession.getFeature(eq(mFeature), anyInt())).thenReturn(result);
- mAidlToHidlAdapter.setFeature(mFeature);
- mAidlToHidlAdapter.getFeatures();
+ mHidlToAidlSessionAdapter.setFeature(mFeature);
+ mHidlToAidlSessionAdapter.getFeatures();
verify(mSession).getFeature(eq(mFeature), anyInt());
verify(mAidlResponseHandler, never()).onFeaturesRetrieved(any());
@@ -270,7 +277,7 @@
@Test
public void testGetFeatures_featureNotSet() throws RemoteException {
- mAidlToHidlAdapter.getFeatures();
+ mHidlToAidlSessionAdapter.getFeatures();
verify(mSession, never()).getFeature(eq(mFeature), anyInt());
verify(mAidlResponseHandler, never()).onFeaturesRetrieved(any());
@@ -283,7 +290,7 @@
when(mSession.setFeature(anyInt(), anyBoolean(), any(), anyInt())).thenReturn(Status.OK);
- mAidlToHidlAdapter.setFeature(mHardwareAuthToken, feature, enabled);
+ mHidlToAidlSessionAdapter.setFeature(mHardwareAuthToken, feature, enabled);
verify(mAidlResponseHandler).onFeatureSet(feature);
}
@@ -296,7 +303,7 @@
when(mSession.setFeature(anyInt(), anyBoolean(), any(), anyInt()))
.thenReturn(Status.INTERNAL_ERROR);
- mAidlToHidlAdapter.setFeature(mHardwareAuthToken, feature, enabled);
+ mHidlToAidlSessionAdapter.setFeature(mHardwareAuthToken, feature, enabled);
verify(mAidlResponseHandler).onError(BiometricFaceConstants.FACE_ERROR_UNKNOWN,
0 /* vendorCode */);
@@ -311,7 +318,7 @@
when(mSession.getAuthenticatorId()).thenReturn(result);
- mAidlToHidlAdapter.getAuthenticatorId();
+ mHidlToAidlSessionAdapter.getAuthenticatorId();
verify(mSession).getAuthenticatorId();
verify(mAidlResponseHandler).onAuthenticatorIdRetrieved(authenticatorId);
@@ -319,7 +326,7 @@
@Test
public void testResetLockout() throws RemoteException {
- mAidlToHidlAdapter.resetLockout(mHardwareAuthToken);
+ mHidlToAidlSessionAdapter.resetLockout(mHardwareAuthToken);
ArgumentCaptor<ArrayList> hatCaptor = ArgumentCaptor.forClass(ArrayList.class);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
index 2aa62d9..f570ba2 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
@@ -42,13 +42,19 @@
import android.app.AppOpsManager;
import android.content.pm.PackageManager;
import android.hardware.biometrics.IBiometricService;
+import android.hardware.biometrics.fingerprint.IFingerprint;
+import android.hardware.biometrics.fingerprint.SensorProps;
import android.hardware.fingerprint.FingerprintAuthenticateOptions;
+import android.hardware.fingerprint.FingerprintSensorConfigurations;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.os.Binder;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import android.testing.TestableContext;
@@ -58,6 +64,7 @@
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
import com.android.server.LocalServices;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintProvider;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
@@ -89,6 +96,9 @@
@Rule
public final MockitoRule mMockito = MockitoJUnit.rule();
@Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+ @Rule
public final TestableContext mContext = new TestableContext(
InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
@Rule
@@ -110,6 +120,10 @@
private IBinder mToken;
@Mock
private VirtualDeviceManagerInternal mVdmInternal;
+ @Mock
+ private IFingerprint mDefaultFingerprintDaemon;
+ @Mock
+ private IFingerprint mVirtualFingerprintDaemon;
@Captor
private ArgumentCaptor<FingerprintAuthenticateOptions> mAuthenticateOptionsCaptor;
@@ -126,7 +140,10 @@
List.of(),
TYPE_UDFPS_OPTICAL,
false /* resetLockoutRequiresHardwareAuthToken */);
+ private FingerprintSensorConfigurations mFingerprintSensorConfigurations;
private FingerprintService mService;
+ private final SensorProps mDefaultSensorProps = new SensorProps();
+ private final SensorProps mVirtualSensorProps = new SensorProps();
@Before
public void setup() throws Exception {
@@ -139,6 +156,10 @@
.thenAnswer(i -> i.getArguments()[0].equals(ID_DEFAULT));
when(mFingerprintVirtual.containsSensor(anyInt()))
.thenAnswer(i -> i.getArguments()[0].equals(ID_VIRTUAL));
+ when(mDefaultFingerprintDaemon.getSensorProps()).thenReturn(
+ new SensorProps[]{mDefaultSensorProps});
+ when(mVirtualFingerprintDaemon.getSensorProps()).thenReturn(
+ new SensorProps[]{mVirtualSensorProps});
mContext.addMockSystemService(AppOpsManager.class, mAppOpsManager);
for (int permission : List.of(OP_USE_BIOMETRIC, OP_USE_FINGERPRINT)) {
@@ -150,6 +171,18 @@
mContext.getTestablePermissions().setPermission(
permission, PackageManager.PERMISSION_GRANTED);
}
+
+ mFingerprintSensorConfigurations = new FingerprintSensorConfigurations(
+ true /* resetLockoutRequiresHardwareAuthToken */);
+ mFingerprintSensorConfigurations.addAidlSensors(new String[]{NAME_DEFAULT, NAME_VIRTUAL},
+ (name) -> {
+ if (name.equals(IFingerprint.DESCRIPTOR + "/" + NAME_DEFAULT)) {
+ return mDefaultFingerprintDaemon;
+ } else if (name.equals(IFingerprint.DESCRIPTOR + "/" + NAME_VIRTUAL)) {
+ return mVirtualFingerprintDaemon;
+ }
+ return null;
+ });
}
private void initServiceWith(String... aidlInstances) {
@@ -160,6 +193,10 @@
if (NAME_DEFAULT.equals(name)) return mFingerprintDefault;
if (NAME_VIRTUAL.equals(name)) return mFingerprintVirtual;
return null;
+ }, (sensorPropsPair, resetLockoutRequiresHardwareAuthToken) -> {
+ if (NAME_DEFAULT.equals(sensorPropsPair.first)) return mFingerprintDefault;
+ if (NAME_VIRTUAL.equals(sensorPropsPair.first)) return mFingerprintVirtual;
+ return null;
});
}
@@ -180,6 +217,17 @@
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
+ public void registerAuthenticatorsLegacy_defaultOnly() throws Exception {
+ initServiceWith(NAME_DEFAULT, NAME_VIRTUAL);
+
+ mService.mServiceWrapper.registerAuthenticatorsLegacy(mFingerprintSensorConfigurations);
+ waitForRegistration();
+
+ verify(mIBiometricService).registerAuthenticator(eq(ID_DEFAULT), anyInt(), anyInt(), any());
+ }
+
+ @Test
public void registerAuthenticators_virtualOnly() throws Exception {
initServiceWith(NAME_DEFAULT, NAME_VIRTUAL);
Settings.Secure.putInt(mSettingsRule.mockContentResolver(mContext),
@@ -192,6 +240,19 @@
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
+ public void registerAuthenticatorsLegacy_virtualOnly() throws Exception {
+ initServiceWith(NAME_DEFAULT, NAME_VIRTUAL);
+ Settings.Secure.putInt(mSettingsRule.mockContentResolver(mContext),
+ Settings.Secure.BIOMETRIC_VIRTUAL_ENABLED, 1);
+
+ mService.mServiceWrapper.registerAuthenticatorsLegacy(mFingerprintSensorConfigurations);
+ waitForRegistration();
+
+ verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), anyInt(), anyInt(), any());
+ }
+
+ @Test
public void registerAuthenticators_virtualAlwaysWhenNoOther() throws Exception {
initServiceWith(NAME_VIRTUAL);
@@ -201,6 +262,28 @@
verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), anyInt(), anyInt(), any());
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
+ public void registerAuthenticatorsLegacy_virtualAlwaysWhenNoOther() throws Exception {
+ mFingerprintSensorConfigurations =
+ new FingerprintSensorConfigurations(true);
+ mFingerprintSensorConfigurations.addAidlSensors(new String[]{NAME_VIRTUAL},
+ (name) -> {
+ if (name.equals(IFingerprint.DESCRIPTOR + "/" + NAME_DEFAULT)) {
+ return mDefaultFingerprintDaemon;
+ } else if (name.equals(IFingerprint.DESCRIPTOR + "/" + NAME_VIRTUAL)) {
+ return mVirtualFingerprintDaemon;
+ }
+ return null;
+ });
+ initServiceWith(NAME_VIRTUAL);
+
+ mService.mServiceWrapper.registerAuthenticatorsLegacy(mFingerprintSensorConfigurations);
+ waitForRegistration();
+
+ verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), anyInt(), anyInt(), any());
+ }
+
private void waitForRegistration() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
mService.mServiceWrapper.addAuthenticatorsRegisteredCallback(
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 4cfb83f..bf5986c 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
@@ -16,6 +16,9 @@
package com.android.server.biometrics.sensors.fingerprint.aidl;
+import static android.os.UserHandle.USER_NULL;
+import static android.os.UserHandle.USER_SYSTEM;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
@@ -34,17 +37,20 @@
import android.hardware.biometrics.fingerprint.ISession;
import android.hardware.biometrics.fingerprint.SensorLocation;
import android.hardware.biometrics.fingerprint.SensorProps;
+import android.hardware.fingerprint.HidlFingerprintSensorConfig;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.AuthenticationStateListeners;
-import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.BiometricStateCallback;
import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -52,6 +58,7 @@
import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -64,6 +71,10 @@
private static final String TAG = "FingerprintProviderTest";
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
@Mock
private Context mContext;
@Mock
@@ -115,7 +126,8 @@
mFingerprintProvider = new FingerprintProvider(mContext,
mBiometricStateCallback, mAuthenticationStateListeners, mSensorProps, TAG,
mLockoutResetDispatcher, mGestureAvailabilityDispatcher, mBiometricContext,
- mDaemon);
+ mDaemon, false /* resetLockoutRequiresHardwareAuthToken */,
+ true /* testHalEnabled */);
}
@Test
@@ -123,17 +135,42 @@
waitForIdle();
for (SensorProps prop : mSensorProps) {
- final BiometricScheduler scheduler =
- mFingerprintProvider.mFingerprintSensors.get(prop.commonProps.sensorId)
- .getScheduler();
- BaseClientMonitor currentClient = scheduler.getCurrentClient();
-
- assertThat(currentClient).isInstanceOf(FingerprintInternalCleanupClient.class);
- assertThat(currentClient.getSensorId()).isEqualTo(prop.commonProps.sensorId);
- assertThat(currentClient.getTargetUserId()).isEqualTo(UserHandle.USER_SYSTEM);
+ final Sensor sensor =
+ mFingerprintProvider.mFingerprintSensors.get(prop.commonProps.sensorId);
+ assertThat(sensor.getLazySession().get().getUserId()).isEqualTo(USER_SYSTEM);
}
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
+ public void testAddingHidlSensors() {
+ when(mResources.getIntArray(anyInt())).thenReturn(new int[]{});
+ when(mResources.getBoolean(anyInt())).thenReturn(false);
+
+ final int fingerprintId = 0;
+ final int fingerprintStrength = 15;
+ final String config = String.format("%d:2:%d", fingerprintId, fingerprintStrength);
+ final HidlFingerprintSensorConfig fingerprintSensorConfig =
+ new HidlFingerprintSensorConfig();
+ fingerprintSensorConfig.parse(config, mContext);
+ HidlFingerprintSensorConfig[] hidlFingerprintSensorConfigs =
+ new HidlFingerprintSensorConfig[]{fingerprintSensorConfig};
+ mFingerprintProvider = new FingerprintProvider(mContext,
+ mBiometricStateCallback, mAuthenticationStateListeners,
+ hidlFingerprintSensorConfigs, TAG, mLockoutResetDispatcher,
+ mGestureAvailabilityDispatcher, mBiometricContext, mDaemon,
+ false /* resetLockoutRequiresHardwareAuthToken */,
+ true /* testHalEnabled */);
+
+ assertThat(mFingerprintProvider.mFingerprintSensors.get(fingerprintId)
+ .getLazySession().get().getUserId()).isEqualTo(USER_NULL);
+
+ waitForIdle();
+
+ assertThat(mFingerprintProvider.mFingerprintSensors.get(fingerprintId)
+ .getLazySession().get().getUserId()).isEqualTo(USER_SYSTEM);
+ }
+
@SuppressWarnings("rawtypes")
@Test
public void halServiceDied_resetsAllSchedulers() {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
index 4102600..126a05e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
@@ -96,7 +96,7 @@
private final TestLooper mLooper = new TestLooper();
private final LockoutCache mLockoutCache = new LockoutCache();
- private UserAwareBiometricScheduler mScheduler;
+ private BiometricScheduler mScheduler;
private AidlResponseHandler mHalCallback;
@Before
@@ -164,7 +164,8 @@
final Sensor sensor = new Sensor("SensorTest", mFingerprintProvider, mContext,
null /* handler */, internalProp, mLockoutResetDispatcher,
mGestureAvailabilityDispatcher, mBiometricContext, mCurrentSession);
- mScheduler = (UserAwareBiometricScheduler) sensor.getScheduler();
+ sensor.init(mGestureAvailabilityDispatcher, mLockoutResetDispatcher);
+ mScheduler = sensor.getScheduler();
sensor.mCurrentSession = new AidlSession(0, mock(ISession.class),
USER_ID, mHalCallback);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapterTest.java
new file mode 100644
index 0000000..89a4961
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapterTest.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint.hidl;
+
+import static android.hardware.fingerprint.FingerprintManager.ENROLL_ENROLL;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.AlarmManager;
+import android.hardware.biometrics.IBiometricService;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.HidlFingerprintSensorConfig;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.test.TestLooper;
+import android.platform.test.annotations.Presubmit;
+import android.testing.TestableContext;
+
+import androidx.annotation.NonNull;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.AuthSessionCoordinator;
+import com.android.server.biometrics.sensors.AuthenticationStateListeners;
+import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.StartUserClient;
+import com.android.server.biometrics.sensors.StopUserClient;
+import com.android.server.biometrics.sensors.UserAwareBiometricScheduler;
+import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
+import com.android.server.biometrics.sensors.fingerprint.aidl.AidlResponseHandler;
+import com.android.server.biometrics.sensors.fingerprint.aidl.AidlSession;
+import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintEnrollClient;
+import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintProvider;
+import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintResetLockoutClient;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@Presubmit
+@SmallTest
+public class HidlToAidlSensorAdapterTest {
+
+ private static final String TAG = "HidlToAidlSensorAdapterTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+ private static final byte[] HAT = new byte[69];
+
+ @Rule
+ public final MockitoRule mMockito = MockitoJUnit.rule();
+
+ @Mock
+ private IBiometricService mBiometricService;
+ @Mock
+ private LockoutResetDispatcher mLockoutResetDispatcherForSensor;
+ @Mock
+ private LockoutResetDispatcher mLockoutResetDispatcherForClient;
+ @Mock
+ private BiometricLogger mLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ @Mock
+ private AuthSessionCoordinator mAuthSessionCoordinator;
+ @Mock
+ private FingerprintProvider mFingerprintProvider;
+ @Mock
+ private GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
+ @Mock
+ private Runnable mInternalCleanupRunnable;
+ @Mock
+ private AlarmManager mAlarmManager;
+ @Mock
+ private IBiometricsFingerprint mDaemon;
+ @Mock
+ private AidlResponseHandler.AidlResponseHandlerCallback mAidlResponseHandlerCallback;
+ @Mock
+ private BiometricUtils<Fingerprint> mBiometricUtils;
+ @Mock
+ private AuthenticationStateListeners mAuthenticationStateListeners;
+
+ private final TestLooper mLooper = new TestLooper();
+ private HidlToAidlSensorAdapter mHidlToAidlSensorAdapter;
+ private final TestableContext mContext = new TestableContext(
+ ApplicationProvider.getApplicationContext());
+
+ private final UserAwareBiometricScheduler.UserSwitchCallback mUserSwitchCallback =
+ new UserAwareBiometricScheduler.UserSwitchCallback() {
+ @NonNull
+ @Override
+ public StopUserClient<?> getStopUserClient(int userId) {
+ return new StopUserClient<IBiometricsFingerprint>(mContext,
+ mHidlToAidlSensorAdapter::getIBiometricsFingerprint, null, USER_ID,
+ SENSOR_ID, mLogger, mBiometricContext, () -> {}) {
+ @Override
+ protected void startHalOperation() {
+ getCallback().onClientFinished(this, true /* success */);
+ }
+
+ @Override
+ public void unableToStart() {}
+ };
+ }
+
+ @NonNull
+ @Override
+ public StartUserClient<?, ?> getStartUserClient(int newUserId) {
+ return new StartUserClient<IBiometricsFingerprint, AidlSession>(mContext,
+ mHidlToAidlSensorAdapter::getIBiometricsFingerprint, null,
+ USER_ID, SENSOR_ID,
+ mLogger, mBiometricContext,
+ (newUserId1, newUser, halInterfaceVersion) ->
+ mHidlToAidlSensorAdapter.handleUserChanged(newUserId1)) {
+ @Override
+ public void start(@NonNull ClientMonitorCallback callback) {
+ super.start(callback);
+ startHalOperation();
+ }
+
+ @Override
+ protected void startHalOperation() {
+ mUserStartedCallback.onUserStarted(USER_ID, null, 0);
+ getCallback().onClientFinished(this, true /* success */);
+ }
+
+ @Override
+ public void unableToStart() {}
+ };
+ }
+ };;
+
+ @Before
+ public void setUp() throws RemoteException {
+ when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator);
+ doAnswer((answer) -> {
+ mHidlToAidlSensorAdapter.getLazySession().get().getHalSessionCallback()
+ .onEnrollmentProgress(1 /* enrollmentId */, 0 /* remaining */);
+ return null;
+ }).when(mDaemon).enroll(any(), anyInt(), anyInt());
+
+ mContext.addMockSystemService(AlarmManager.class, mAlarmManager);
+ mContext.getOrCreateTestableResources();
+
+ final String config = String.format("%d:2:15", SENSOR_ID);
+ final UserAwareBiometricScheduler scheduler = new UserAwareBiometricScheduler(TAG,
+ new Handler(mLooper.getLooper()),
+ BiometricScheduler.SENSOR_TYPE_FP_OTHER,
+ null /* gestureAvailabilityDispatcher */,
+ mBiometricService,
+ () -> USER_ID,
+ mUserSwitchCallback);
+ final HidlFingerprintSensorConfig fingerprintSensorConfig =
+ new HidlFingerprintSensorConfig();
+ fingerprintSensorConfig.parse(config, mContext);
+ mHidlToAidlSensorAdapter = new HidlToAidlSensorAdapter(TAG,
+ mFingerprintProvider, mContext, new Handler(mLooper.getLooper()),
+ fingerprintSensorConfig, mLockoutResetDispatcherForSensor,
+ mGestureAvailabilityDispatcher, mBiometricContext,
+ false /* resetLockoutRequiresHardwareAuthToken */,
+ mInternalCleanupRunnable, mAuthSessionCoordinator, mDaemon,
+ mAidlResponseHandlerCallback);
+ mHidlToAidlSensorAdapter.init(mGestureAvailabilityDispatcher,
+ mLockoutResetDispatcherForSensor);
+ mHidlToAidlSensorAdapter.setScheduler(scheduler);
+ mHidlToAidlSensorAdapter.handleUserChanged(USER_ID);
+ }
+
+ @Test
+ public void lockoutTimedResetViaClient() {
+ setLockoutTimed();
+
+ mHidlToAidlSensorAdapter.getScheduler().scheduleClientMonitor(
+ new FingerprintResetLockoutClient(mContext,
+ mHidlToAidlSensorAdapter.getLazySession(),
+ USER_ID, TAG, SENSOR_ID, mLogger, mBiometricContext, HAT,
+ mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */),
+ mLockoutResetDispatcherForClient, 0 /* biometricStrength */));
+ mLooper.dispatchAll();
+
+ verify(mAlarmManager).setExact(anyInt(), anyLong(), any());
+ verify(mLockoutResetDispatcherForClient).notifyLockoutResetCallbacks(SENSOR_ID);
+ verify(mLockoutResetDispatcherForSensor).notifyLockoutResetCallbacks(SENSOR_ID);
+ assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */)
+ .getLockoutModeForUser(USER_ID)).isEqualTo(LockoutTracker.LOCKOUT_NONE);
+ verify(mAuthSessionCoordinator).resetLockoutFor(eq(USER_ID), anyInt(), anyLong());
+ }
+
+ @Test
+ public void lockoutTimedResetViaCallback() {
+ setLockoutTimed();
+
+ mHidlToAidlSensorAdapter.getLazySession().get().getHalSessionCallback().onLockoutCleared();
+ mLooper.dispatchAll();
+
+ verify(mLockoutResetDispatcherForSensor, times(2)).notifyLockoutResetCallbacks(eq(
+ SENSOR_ID));
+ assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */)
+ .getLockoutModeForUser(USER_ID)).isEqualTo(LockoutTracker.LOCKOUT_NONE);
+ verify(mAuthSessionCoordinator).resetLockoutFor(eq(USER_ID), anyInt(), anyLong());
+ }
+
+ @Test
+ public void lockoutPermanentResetViaCallback() {
+ setLockoutPermanent();
+
+ mHidlToAidlSensorAdapter.getLazySession().get().getHalSessionCallback().onLockoutCleared();
+ mLooper.dispatchAll();
+
+ verify(mLockoutResetDispatcherForSensor, times(2)).notifyLockoutResetCallbacks(eq(
+ SENSOR_ID));
+ assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */)
+ .getLockoutModeForUser(USER_ID)).isEqualTo(LockoutTracker.LOCKOUT_NONE);
+ verify(mAuthSessionCoordinator).resetLockoutFor(eq(USER_ID), anyInt(), anyLong());
+ }
+
+ @Test
+ public void lockoutPermanentResetViaClient() {
+ setLockoutPermanent();
+
+ mHidlToAidlSensorAdapter.getScheduler().scheduleClientMonitor(
+ new FingerprintResetLockoutClient(mContext,
+ mHidlToAidlSensorAdapter.getLazySession(),
+ USER_ID, TAG, SENSOR_ID, mLogger, mBiometricContext, HAT,
+ mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */),
+ mLockoutResetDispatcherForClient, 0 /* biometricStrength */));
+ mLooper.dispatchAll();
+
+ verify(mAlarmManager, atLeast(1)).setExact(anyInt(), anyLong(), any());
+ verify(mLockoutResetDispatcherForClient).notifyLockoutResetCallbacks(SENSOR_ID);
+ verify(mLockoutResetDispatcherForSensor).notifyLockoutResetCallbacks(SENSOR_ID);
+ assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */)
+ .getLockoutModeForUser(USER_ID)).isEqualTo(LockoutTracker.LOCKOUT_NONE);
+ verify(mAuthSessionCoordinator).resetLockoutFor(eq(USER_ID), anyInt(), anyLong());
+ }
+
+ @Test
+ public void verifyOnEnrollSuccessCallback() {
+ mHidlToAidlSensorAdapter.getScheduler().scheduleClientMonitor(new FingerprintEnrollClient(
+ mContext, mHidlToAidlSensorAdapter.getLazySession(), null /* token */,
+ 1 /* requestId */, null /* listener */, USER_ID, HAT, TAG, mBiometricUtils,
+ SENSOR_ID, mLogger, mBiometricContext,
+ mHidlToAidlSensorAdapter.getSensorProperties(), null, null,
+ mAuthenticationStateListeners, 5 /* maxTemplatesPerUser */, ENROLL_ENROLL));
+ mLooper.dispatchAll();
+
+ verify(mAidlResponseHandlerCallback).onEnrollSuccess();
+ }
+
+ private void setLockoutPermanent() {
+ for (int i = 0; i < 20; i++) {
+ mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */)
+ .addFailedAttemptForUser(USER_ID);
+ }
+
+ assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */)
+ .getLockoutModeForUser(USER_ID)).isEqualTo(LockoutTracker.LOCKOUT_PERMANENT);
+ }
+
+ private void setLockoutTimed() {
+ for (int i = 0; i < 5; i++) {
+ mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */)
+ .addFailedAttemptForUser(USER_ID);
+ }
+
+ assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */)
+ .getLockoutModeForUser(USER_ID)).isEqualTo(LockoutTracker.LOCKOUT_TIMED);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/AidlToHidlAdapterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapterTest.java
similarity index 79%
rename from services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/AidlToHidlAdapterTest.java
rename to services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapterTest.java
index b78ba82..d723e87 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/AidlToHidlAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapterTest.java
@@ -41,7 +41,7 @@
@Presubmit
@SmallTest
-public class AidlToHidlAdapterTest {
+public class HidlToAidlSessionAdapterTest {
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
@@ -54,11 +54,11 @@
private final long mChallenge = 100L;
private final int mUserId = 0;
- private AidlToHidlAdapter mAidlToHidlAdapter;
+ private HidlToAidlSessionAdapter mHidlToAidlSessionAdapter;
@Before
public void setUp() {
- mAidlToHidlAdapter = new AidlToHidlAdapter(() -> mSession, mUserId,
+ mHidlToAidlSessionAdapter = new HidlToAidlSessionAdapter(() -> mSession, mUserId,
mAidlResponseHandler);
mHardwareAuthToken.timestamp = new Timestamp();
mHardwareAuthToken.mac = new byte[10];
@@ -67,7 +67,7 @@
@Test
public void testGenerateChallenge() throws RemoteException {
when(mSession.preEnroll()).thenReturn(mChallenge);
- mAidlToHidlAdapter.generateChallenge();
+ mHidlToAidlSessionAdapter.generateChallenge();
verify(mSession).preEnroll();
verify(mAidlResponseHandler).onChallengeGenerated(mChallenge);
@@ -75,7 +75,7 @@
@Test
public void testRevokeChallenge() throws RemoteException {
- mAidlToHidlAdapter.revokeChallenge(mChallenge);
+ mHidlToAidlSessionAdapter.revokeChallenge(mChallenge);
verify(mSession).postEnroll();
verify(mAidlResponseHandler).onChallengeRevoked(0L);
@@ -84,9 +84,9 @@
@Test
public void testEnroll() throws RemoteException {
final ICancellationSignal cancellationSignal =
- mAidlToHidlAdapter.enroll(mHardwareAuthToken);
+ mHidlToAidlSessionAdapter.enroll(mHardwareAuthToken);
- verify(mSession).enroll(any(), anyInt(), eq(AidlToHidlAdapter.ENROLL_TIMEOUT_SEC));
+ verify(mSession).enroll(any(), anyInt(), eq(HidlToAidlSessionAdapter.ENROLL_TIMEOUT_SEC));
cancellationSignal.cancel();
@@ -96,7 +96,8 @@
@Test
public void testAuthenticate() throws RemoteException {
final int operationId = 2;
- final ICancellationSignal cancellationSignal = mAidlToHidlAdapter.authenticate(operationId);
+ final ICancellationSignal cancellationSignal = mHidlToAidlSessionAdapter.authenticate(
+ operationId);
verify(mSession).authenticate(operationId, mUserId);
@@ -107,7 +108,8 @@
@Test
public void testDetectInteraction() throws RemoteException {
- final ICancellationSignal cancellationSignal = mAidlToHidlAdapter.detectInteraction();
+ final ICancellationSignal cancellationSignal = mHidlToAidlSessionAdapter
+ .detectInteraction();
verify(mSession).authenticate(0 /* operationId */, mUserId);
@@ -118,7 +120,7 @@
@Test
public void testEnumerateEnrollments() throws RemoteException {
- mAidlToHidlAdapter.enumerateEnrollments();
+ mHidlToAidlSessionAdapter.enumerateEnrollments();
verify(mSession).enumerate();
}
@@ -126,7 +128,7 @@
@Test
public void testRemoveEnrollment() throws RemoteException {
final int[] enrollmentIds = new int[]{1};
- mAidlToHidlAdapter.removeEnrollments(enrollmentIds);
+ mHidlToAidlSessionAdapter.removeEnrollments(enrollmentIds);
verify(mSession).remove(mUserId, enrollmentIds[0]);
}
@@ -134,14 +136,14 @@
@Test
public void testRemoveMultipleEnrollments() throws RemoteException {
final int[] enrollmentIds = new int[]{1, 2};
- mAidlToHidlAdapter.removeEnrollments(enrollmentIds);
+ mHidlToAidlSessionAdapter.removeEnrollments(enrollmentIds);
verify(mSession).remove(mUserId, 0);
}
@Test
public void testResetLockout() throws RemoteException {
- mAidlToHidlAdapter.resetLockout(mHardwareAuthToken);
+ mHidlToAidlSessionAdapter.resetLockout(mHardwareAuthToken);
verify(mAidlResponseHandler).onLockoutCleared();
}