summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/biometrics/biometrics.aconfig7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java20
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/LockoutCache.java1
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/LockoutTracker.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java1
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/LockoutHalImpl.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java319
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java26
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java24
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java14
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java410
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapter.java347
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java524
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlCallbackConverter.java115
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java274
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java25
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java40
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java24
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java13
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java361
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/AidlToHidlAdapter.java231
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java445
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlCallbackConverter.java95
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java22
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java54
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClientTest.java102
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClientTest.java105
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClientTest.java187
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClientTest.java169
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClientTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClientTest.java121
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClientTest.java88
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClientTest.java120
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java19
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapterTest.java342
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java61
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClientTest.java92
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java19
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClientTest.java123
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClientTest.java147
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClientTest.java105
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClientTest.java101
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/AidlToHidlAdapterTest.java148
64 files changed, 4442 insertions, 1116 deletions
diff --git a/services/core/java/com/android/server/biometrics/biometrics.aconfig b/services/core/java/com/android/server/biometrics/biometrics.aconfig
index b537e0eae548..b12d831ffe24 100644
--- a/services/core/java/com/android/server/biometrics/biometrics.aconfig
+++ b/services/core/java/com/android/server/biometrics/biometrics.aconfig
@@ -6,3 +6,10 @@ flag {
description: "This flag controls tunscany virtual HAL feature"
bug: "294254230"
}
+
+flag {
+ name: "de_hidl"
+ namespace: "biometrics_framework"
+ description: "feature flag for biometrics de-hidl"
+ bug: "287332354"
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 78c95ad4576b..a47135fd032f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -35,6 +35,7 @@ import android.util.EventLog;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+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;
@@ -116,7 +117,24 @@ public abstract class AuthenticationClient<T, O extends AuthenticateOptions>
@LockoutTracker.LockoutMode
public int handleFailedAttempt(int userId) {
- return LockoutTracker.LOCKOUT_NONE;
+ if (Flags.deHidl()) {
+ if (mLockoutTracker != null) {
+ mLockoutTracker.addFailedAttemptForUser(getTargetUserId());
+ }
+ @LockoutTracker.LockoutMode final int lockoutMode =
+ getLockoutTracker().getLockoutModeForUser(userId);
+ final PerformanceTracker performanceTracker =
+ PerformanceTracker.getInstanceForSensorId(getSensorId());
+ if (lockoutMode == LockoutTracker.LOCKOUT_PERMANENT) {
+ performanceTracker.incrementPermanentLockoutForUser(userId);
+ } else if (lockoutMode == LockoutTracker.LOCKOUT_TIMED) {
+ performanceTracker.incrementTimedLockoutForUser(userId);
+ }
+
+ return lockoutMode;
+ } else {
+ return LockoutTracker.LOCKOUT_NONE;
+ }
}
protected long getStartTimeMs() {
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 8a54ae5a6fea..037ea38a2d17 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -586,4 +586,13 @@ public class BiometricScheduler {
}
}, 10000);
}
+
+ /**
+ * Handle stop user client when user switching occurs.
+ */
+ public void onUserStopped() {}
+
+ public Handler getHandler() {
+ return mHandler;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/LockoutCache.java b/services/core/java/com/android/server/biometrics/sensors/LockoutCache.java
index 95c49032c029..35e9bcbbf085 100644
--- a/services/core/java/com/android/server/biometrics/sensors/LockoutCache.java
+++ b/services/core/java/com/android/server/biometrics/sensors/LockoutCache.java
@@ -32,6 +32,7 @@ public class LockoutCache implements LockoutTracker {
mUserLockoutStates = new SparseIntArray();
}
+ @Override
public void setLockoutModeForUser(int userId, @LockoutMode int mode) {
Slog.d(TAG, "Lockout for user: " + userId + " is " + mode);
synchronized (this) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/LockoutTracker.java b/services/core/java/com/android/server/biometrics/sensors/LockoutTracker.java
index 4a59c9df9bef..8271380010e2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/LockoutTracker.java
+++ b/services/core/java/com/android/server/biometrics/sensors/LockoutTracker.java
@@ -35,4 +35,7 @@ public interface LockoutTracker {
@interface LockoutMode {}
@LockoutMode int getLockoutModeForUser(int userId);
+ void setLockoutModeForUser(int userId, @LockoutMode int mode);
+ default void resetFailedAttemptsForUser(boolean clearAttemptCounter, int userId) {}
+ default void addFailedAttemptForUser(int userId) {}
}
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 694dfd28d0cc..80754702415a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
@@ -165,6 +165,7 @@ public class UserAwareBiometricScheduler extends BiometricScheduler {
}
}
+ @Override
public void onUserStopped() {
if (mStopUserClient == null) {
Slog.e(getTag(), "Unexpected onUserStopped");
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 cc0022703745..e5d4a635876d 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
@@ -31,6 +31,11 @@ public class LockoutHalImpl implements LockoutTracker {
return mCurrentUserLockoutMode;
}
+ @Override
+ public void setLockoutModeForUser(int userId, @LockoutMode int mode) {
+ setCurrentUserLockoutMode(mode);
+ }
+
public void setCurrentUserLockoutMode(@LockoutMode int lockoutMode) {
mCurrentUserLockoutMode = lockoutMode;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java
index 573c20fe041b..d149f5215a7a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java
@@ -38,7 +38,7 @@ import android.util.Slog;
/**
* Utilities for converting from hardware to framework-defined AIDL models.
*/
-final class AidlConversionUtils {
+public final class AidlConversionUtils {
private static final String TAG = "AidlConversionUtils";
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
new file mode 100644
index 000000000000..57f5b34c197a
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java
@@ -0,0 +1,319 @@
+/*
+ * 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.aidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.biometrics.face.AuthenticationFrame;
+import android.hardware.biometrics.face.EnrollmentFrame;
+import android.hardware.biometrics.face.Error;
+import android.hardware.biometrics.face.ISessionCallback;
+import android.hardware.face.Face;
+import android.hardware.keymaster.HardwareAuthToken;
+import android.util.Slog;
+
+import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.sensors.AcquisitionClient;
+import com.android.server.biometrics.sensors.AuthSessionCoordinator;
+import com.android.server.biometrics.sensors.AuthenticationConsumer;
+import com.android.server.biometrics.sensors.BaseClientMonitor;
+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.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.face.FaceUtils;
+
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+/**
+ * Response handler for the {@link ISessionCallback} HAL AIDL interface.
+ */
+public class AidlResponseHandler extends ISessionCallback.Stub {
+ /**
+ * Interface to send results to the AidlResponseHandler's owner.
+ */
+ public interface HardwareUnavailableCallback {
+ /**
+ * Invoked when the HAL sends ERROR_HW_UNAVAILABLE.
+ */
+ void onHardwareUnavailable();
+ }
+
+ private static final String TAG = "AidlResponseHandler";
+
+ @NonNull
+ private final Context mContext;
+ @NonNull
+ private final BiometricScheduler mScheduler;
+ private final int mSensorId;
+ private final int mUserId;
+ @NonNull
+ private final LockoutTracker mLockoutCache;
+ @NonNull
+ private final LockoutResetDispatcher mLockoutResetDispatcher;
+
+ @NonNull
+ private final AuthSessionCoordinator mAuthSessionCoordinator;
+ @NonNull
+ private final HardwareUnavailableCallback mHardwareUnavailableCallback;
+
+ public AidlResponseHandler(@NonNull Context context,
+ @NonNull BiometricScheduler scheduler, int sensorId, int userId,
+ @NonNull LockoutTracker lockoutTracker,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull AuthSessionCoordinator authSessionCoordinator,
+ @NonNull HardwareUnavailableCallback hardwareUnavailableCallback) {
+ mContext = context;
+ mScheduler = scheduler;
+ mSensorId = sensorId;
+ mUserId = userId;
+ mLockoutCache = lockoutTracker;
+ mLockoutResetDispatcher = lockoutResetDispatcher;
+ mAuthSessionCoordinator = authSessionCoordinator;
+ mHardwareUnavailableCallback = hardwareUnavailableCallback;
+ }
+
+ @Override
+ public int getInterfaceVersion() {
+ return this.VERSION;
+ }
+
+ @Override
+ public String getInterfaceHash() {
+ return this.HASH;
+ }
+
+ @Override
+ public void onChallengeGenerated(long challenge) {
+ handleResponse(FaceGenerateChallengeClient.class, (c) -> c.onChallengeGenerated(mSensorId,
+ mUserId, challenge), null);
+ }
+
+ @Override
+ public void onChallengeRevoked(long challenge) {
+ handleResponse(FaceRevokeChallengeClient.class, (c) -> c.onChallengeRevoked(mSensorId,
+ mUserId, challenge), null);
+ }
+
+ @Override
+ public void onAuthenticationFrame(AuthenticationFrame frame) {
+ handleResponse(FaceAuthenticationClient.class, (c) -> {
+ if (frame == null) {
+ Slog.e(TAG, "Received null enrollment frame for face authentication client.");
+ return;
+ }
+ c.onAuthenticationFrame(AidlConversionUtils.toFrameworkAuthenticationFrame(frame));
+ }, null);
+ }
+
+ @Override
+ public void onEnrollmentFrame(EnrollmentFrame frame) {
+ handleResponse(FaceEnrollClient.class, (c) -> {
+ if (frame == null) {
+ Slog.e(TAG, "Received null enrollment frame for face enroll client.");
+ return;
+ }
+ c.onEnrollmentFrame(AidlConversionUtils.toFrameworkEnrollmentFrame(frame));
+ }, null);
+ }
+
+ @Override
+ public void onError(byte error, int vendorCode) {
+ onError(AidlConversionUtils.toFrameworkError(error), vendorCode);
+ }
+
+ /**
+ * Handle error messages from the HAL.
+ */
+ public void onError(int error, int vendorCode) {
+ handleResponse(ErrorConsumer.class, (c) -> {
+ c.onError(error, vendorCode);
+ if (error == Error.HW_UNAVAILABLE) {
+ mHardwareUnavailableCallback.onHardwareUnavailable();
+ }
+ }, null);
+ }
+
+ @Override
+ public void onEnrollmentProgress(int enrollmentId, int remaining) {
+ BaseClientMonitor client = mScheduler.getCurrentClient();
+ final int currentUserId;
+ if (client == null) {
+ return;
+ } else {
+ currentUserId = client.getTargetUserId();
+ }
+ final CharSequence name = FaceUtils.getInstance(mSensorId)
+ .getUniqueName(mContext, currentUserId);
+ final Face face = new Face(name, enrollmentId, mSensorId);
+
+ handleResponse(FaceEnrollClient.class, (c) -> c.onEnrollResult(face, remaining), null);
+ }
+
+ @Override
+ public void onAuthenticationSucceeded(int enrollmentId, HardwareAuthToken hat) {
+ final Face face = new Face("" /* name */, enrollmentId, mSensorId);
+ final byte[] byteArray = HardwareAuthTokenUtils.toByteArray(hat);
+ final ArrayList<Byte> byteList = new ArrayList<>();
+ for (byte b : byteArray) {
+ byteList.add(b);
+ }
+ handleResponse(AuthenticationConsumer.class, (c) -> c.onAuthenticated(face,
+ true /* authenticated */, byteList), null);
+ }
+
+ @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);
+ }
+
+ @Override
+ public void onLockoutTimed(long durationMillis) {
+ handleResponse(LockoutConsumer.class, (c) -> c.onLockoutTimed(durationMillis), null);
+ }
+
+ @Override
+ public void onLockoutPermanent() {
+ handleResponse(LockoutConsumer.class, LockoutConsumer::onLockoutPermanent, null);
+ }
+
+ @Override
+ public void onLockoutCleared() {
+ handleResponse(FaceResetLockoutClient.class, FaceResetLockoutClient::onLockoutCleared,
+ (c) -> FaceResetLockoutClient.resetLocalLockoutStateToNone(mSensorId, mUserId,
+ mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
+ Utils.getCurrentStrength(mSensorId), -1 /* requestId */));
+ }
+
+ @Override
+ public void onInteractionDetected() {
+ handleResponse(FaceDetectClient.class, FaceDetectClient::onInteractionDetected, null);
+ }
+
+ @Override
+ public void onEnrollmentsEnumerated(int[] enrollmentIds) {
+ if (enrollmentIds.length > 0) {
+ for (int i = 0; i < enrollmentIds.length; ++i) {
+ 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);
+ }
+ } else {
+ handleResponse(EnumerateConsumer.class, (c) -> c.onEnumerationResult(
+ null /* identifier */, 0 /* remaining */), null);
+ }
+ }
+
+ @Override
+ public void onFeaturesRetrieved(byte[] features) {
+ handleResponse(FaceGetFeatureClient.class, (c) -> c.onFeatureGet(true /* success */,
+ features), null);
+ }
+
+ @Override
+ public void onFeatureSet(byte feature) {
+ handleResponse(FaceSetFeatureClient.class, (c) -> c.onFeatureSet(true /* success */), null);
+ }
+
+ @Override
+ public void onEnrollmentsRemoved(int[] enrollmentIds) {
+ if (enrollmentIds.length > 0) {
+ for (int i = 0; i < enrollmentIds.length; i++) {
+ 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);
+ }
+ } else {
+ handleResponse(RemovalConsumer.class, (c) -> c.onRemoved(null /* identifier */,
+ 0 /* remaining */), null);
+ }
+ }
+
+ @Override
+ public void onAuthenticatorIdRetrieved(long authenticatorId) {
+ handleResponse(FaceGetAuthenticatorIdClient.class, (c) -> c.onAuthenticatorIdRetrieved(
+ authenticatorId), null);
+ }
+
+ @Override
+ public void onAuthenticatorIdInvalidated(long newAuthenticatorId) {
+ handleResponse(FaceInvalidationClient.class, (c) -> c.onAuthenticatorIdInvalidated(
+ newAuthenticatorId), null);
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Handles lockout changed messages sent by the HAL (specifically for HIDL HAL).
+ */
+ public void onLockoutChanged(long duration) {
+ mScheduler.getHandler().post(() -> {
+ @LockoutTracker.LockoutMode final int lockoutMode;
+ if (duration == 0) {
+ lockoutMode = LockoutTracker.LOCKOUT_NONE;
+ } else if (duration == -1 || duration == Long.MAX_VALUE) {
+ lockoutMode = LockoutTracker.LOCKOUT_PERMANENT;
+ } else {
+ lockoutMode = LockoutTracker.LOCKOUT_TIMED;
+ }
+
+ mLockoutCache.setLockoutModeForUser(mUserId, lockoutMode);
+
+ if (duration == 0) {
+ mLockoutResetDispatcher.notifyLockoutResetCallbacks(mSensorId);
+ }
+ });
+ }
+
+ private <T> void handleResponse(@NonNull Class<T> className,
+ @NonNull Consumer<T> actionIfClassMatchesClient,
+ @Nullable Consumer<BaseClientMonitor> alternateAction) {
+ mScheduler.getHandler().post(() -> {
+ final BaseClientMonitor client = mScheduler.getCurrentClient();
+ if (className.isInstance(client)) {
+ actionIfClassMatchesClient.accept((T) client);
+ } else {
+ Slog.d(TAG, "Current client is not an instance of " + className.getName());
+ if (alternateAction != null) {
+ alternateAction.accept(client);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onSessionClosed() {
+ mScheduler.getHandler().post(mScheduler::onUserStopped);
+ }
+}
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 29eee6b5bb06..858bb864d4db 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
@@ -16,10 +16,14 @@
package com.android.server.biometrics.sensors.face.aidl;
-import static com.android.server.biometrics.sensors.face.aidl.Sensor.HalSessionCallback;
-
import android.annotation.NonNull;
+import android.content.Context;
import android.hardware.biometrics.face.ISession;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+
+import com.android.server.biometrics.sensors.face.hidl.AidlToHidlAdapter;
+
+import java.util.function.Supplier;
/**
* A holder for an AIDL {@link ISession} with additional metadata about the current user
@@ -31,14 +35,22 @@ public class AidlSession {
@NonNull
private final ISession mSession;
private final int mUserId;
- @NonNull private final HalSessionCallback mHalSessionCallback;
+ @NonNull private final AidlResponseHandler mAidlResponseHandler;
public AidlSession(int halInterfaceVersion, @NonNull ISession session, int userId,
- HalSessionCallback halSessionCallback) {
+ AidlResponseHandler aidlResponseHandler) {
mHalInterfaceVersion = halInterfaceVersion;
mSession = session;
mUserId = userId;
- mHalSessionCallback = halSessionCallback;
+ mAidlResponseHandler = aidlResponseHandler;
+ }
+
+ public AidlSession(Context context, Supplier<IBiometricsFace> session, int userId,
+ AidlResponseHandler aidlResponseHandler) {
+ mSession = new AidlToHidlAdapter(context, session, userId, aidlResponseHandler);
+ mHalInterfaceVersion = 0;
+ mUserId = userId;
+ mAidlResponseHandler = aidlResponseHandler;
}
/** The underlying {@link ISession}. */
@@ -52,8 +64,8 @@ public class AidlSession {
}
/** The HAL callback, which should only be used in tests {@See BiometricTestSessionImpl}. */
- HalSessionCallback getHalSessionCallback() {
- return mHalSessionCallback;
+ AidlResponseHandler getHalSessionCallback() {
+ return mAidlResponseHandler;
}
/**
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index 35fc43ae5f74..470dc4b7172c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -46,8 +46,8 @@ import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
-import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutConsumer;
+import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.PerformanceTracker;
import com.android.server.biometrics.sensors.face.UsageStats;
@@ -57,7 +57,8 @@ import java.util.function.Supplier;
/**
* Face-specific authentication client for the {@link IFace} AIDL HAL interface.
*/
-class FaceAuthenticationClient extends AuthenticationClient<AidlSession, FaceAuthenticateOptions>
+public class FaceAuthenticationClient
+ extends AuthenticationClient<AidlSession, FaceAuthenticateOptions>
implements LockoutConsumer {
private static final String TAG = "FaceAuthenticationClient";
@@ -74,11 +75,11 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession, FaceAut
@Nullable
private ICancellationSignal mCancellationSignal;
@Nullable
- private SensorPrivacyManager mSensorPrivacyManager;
+ private final SensorPrivacyManager mSensorPrivacyManager;
@FaceManager.FaceAcquired
private int mLastAcquire = FaceManager.FACE_ACQUIRED_UNKNOWN;
- FaceAuthenticationClient(@NonNull Context context,
+ public FaceAuthenticationClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon,
@NonNull IBinder token, long requestId,
@NonNull ClientMonitorCallbackConverter listener, long operationId,
@@ -86,7 +87,7 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession, FaceAut
boolean requireConfirmation,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
boolean isStrongBiometric, @NonNull UsageStats usageStats,
- @NonNull LockoutCache lockoutCache, boolean allowBackgroundAuthentication,
+ @NonNull LockoutTracker lockoutCache, boolean allowBackgroundAuthentication,
@Authenticators.Types int sensorStrength) {
this(context, lazyDaemon, token, requestId, listener, operationId,
restricted, options, cookie, requireConfirmation, logger, biometricContext,
@@ -103,12 +104,12 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession, FaceAut
boolean requireConfirmation,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
boolean isStrongBiometric, @NonNull UsageStats usageStats,
- @NonNull LockoutCache lockoutCache, boolean allowBackgroundAuthentication,
+ @NonNull LockoutTracker lockoutTracker, boolean allowBackgroundAuthentication,
SensorPrivacyManager sensorPrivacyManager,
@Authenticators.Types int biometricStrength) {
super(context, lazyDaemon, token, listener, operationId, restricted,
options, cookie, requireConfirmation, logger, biometricContext,
- isStrongBiometric, null /* taskStackListener */, null /* lockoutCache */,
+ isStrongBiometric, null /* taskStackListener */, lockoutTracker,
allowBackgroundAuthentication, false /* shouldVibrate */,
biometricStrength);
setRequestId(requestId);
@@ -263,8 +264,13 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession, FaceAut
mLastAcquire = acquireInfo;
final boolean shouldSend = shouldSendAcquiredMessage(acquireInfo, vendorCode);
onAcquiredInternal(acquireInfo, vendorCode, shouldSend);
- PerformanceTracker pt = PerformanceTracker.getInstanceForSensorId(getSensorId());
- pt.incrementAcquireForUser(getTargetUserId(), isCryptoOperation());
+
+ //Check if it is AIDL (lockoutTracker = null) or if it there is no lockout for HIDL
+ if (getLockoutTracker() == null || getLockoutTracker().getLockoutModeForUser(
+ getTargetUserId()) == LockoutTracker.LOCKOUT_NONE) {
+ PerformanceTracker pt = PerformanceTracker.getInstanceForSensorId(getSensorId());
+ pt.incrementAcquireForUser(getTargetUserId(), isCryptoOperation());
+ }
}
/**
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index f55cf0549382..dbed1f7a8f9d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -85,7 +85,7 @@ public class FaceEnrollClient extends EnrollClient<AidlSession> {
}
};
- FaceEnrollClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
+ public FaceEnrollClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String opPackageName, long requestId,
@NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec,
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
index 165c3a241043..e404bd2be31e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
@@ -36,7 +36,7 @@ import java.util.function.Supplier;
public class FaceGenerateChallengeClient extends GenerateChallengeClient<AidlSession> {
private static final String TAG = "FaceGenerateChallengeClient";
- FaceGenerateChallengeClient(@NonNull Context context,
+ public FaceGenerateChallengeClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
int sensorId, @NonNull BiometricLogger logger,
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 ef3b345402bf..c15049b48bb2 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
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.face.IFace;
+import android.hardware.biometrics.face.ISession;
import android.os.IBinder;
import android.os.RemoteException;
import android.provider.Settings;
@@ -33,6 +34,7 @@ import com.android.server.biometrics.sensors.ClientMonitorCallback;
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 java.util.HashMap;
import java.util.Map;
@@ -46,14 +48,16 @@ public class FaceGetFeatureClient extends HalClientMonitor<AidlSession> implemen
private static final String TAG = "FaceGetFeatureClient";
private final int mUserId;
+ private final int mFeature;
- FaceGetFeatureClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
+ public FaceGetFeatureClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
@NonNull IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId,
@NonNull String owner, int sensorId, @NonNull BiometricLogger logger,
- @NonNull BiometricContext biometricContext) {
+ @NonNull BiometricContext biometricContext, int feature) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
logger, biometricContext);
mUserId = userId;
+ mFeature = feature;
}
@Override
@@ -70,7 +74,11 @@ public class FaceGetFeatureClient extends HalClientMonitor<AidlSession> implemen
@Override
protected void startHalOperation() {
try {
- getFreshDaemon().getSession().getFeatures();
+ ISession session = getFreshDaemon().getSession();
+ if (session instanceof AidlToHidlAdapter) {
+ ((AidlToHidlAdapter) session).setFeature(mFeature);
+ }
+ session.getFeatures();
} catch (RemoteException e) {
Slog.e(TAG, "Unable to getFeature", e);
mCallback.onClientFinished(this, false /* success */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
index f09d192966f1..e75c6aba1489 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
@@ -38,9 +38,9 @@ import java.util.function.Supplier;
/**
* Face-specific internal cleanup client for the {@link IFace} AIDL HAL interface.
*/
-class FaceInternalCleanupClient extends InternalCleanupClient<Face, AidlSession> {
+public class FaceInternalCleanupClient extends InternalCleanupClient<Face, AidlSession> {
- FaceInternalCleanupClient(@NonNull Context context,
+ public FaceInternalCleanupClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, int userId, @NonNull String owner,
int sensorId, @NonNull BiometricLogger logger,
@NonNull BiometricContext biometricContext,
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 cc3118cc3433..dd9c6d50ae9e 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
@@ -493,7 +493,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
mAuthenticationStatsCollector),
mBiometricContext, isStrongBiometric,
- mUsageStats, mFaceSensors.get(sensorId).getLockoutCache(),
+ mUsageStats, null /* lockoutTracker */,
allowBackgroundAuthentication, Utils.getCurrentStrength(sensorId));
scheduleForSensor(sensorId, client, new ClientMonitorCallback() {
@Override
@@ -619,7 +619,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext,
mFaceSensors.get(sensorId).getLazySession(), token, callback, userId,
mContext.getOpPackageName(), sensorId, BiometricLogger.ofUnknown(mContext),
- mBiometricContext);
+ mBiometricContext, feature);
scheduleForSensor(sensorId, client);
});
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java
index 0512017394af..079388822def 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java
@@ -36,12 +36,12 @@ import java.util.function.Supplier;
/**
* Face-specific removal client for the {@link IFace} AIDL HAL interface.
*/
-class FaceRemovalClient extends RemovalClient<Face, AidlSession> {
+public class FaceRemovalClient extends RemovalClient<Face, AidlSession> {
private static final String TAG = "FaceRemovalClient";
final int[] mBiometricIds;
- FaceRemovalClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
+ public FaceRemovalClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
int[] biometricIds, int userId, @NonNull String owner,
@NonNull BiometricUtils<Face> utils, int sensorId,
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 1a12fcdf5010..77b5592c5064 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
@@ -32,7 +32,6 @@ import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ErrorConsumer;
import com.android.server.biometrics.sensors.HalClientMonitor;
-import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
@@ -48,14 +47,14 @@ public class FaceResetLockoutClient extends HalClientMonitor<AidlSession> implem
private static final String TAG = "FaceResetLockoutClient";
private final HardwareAuthToken mHardwareAuthToken;
- private final LockoutCache mLockoutCache;
+ private final LockoutTracker mLockoutCache;
private final LockoutResetDispatcher mLockoutResetDispatcher;
private final int mBiometricStrength;
- FaceResetLockoutClient(@NonNull Context context,
+ public FaceResetLockoutClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, int userId, String owner, int sensorId,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
- @NonNull byte[] hardwareAuthToken, @NonNull LockoutCache lockoutTracker,
+ @NonNull byte[] hardwareAuthToken, @NonNull LockoutTracker lockoutTracker,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@Authenticators.Types int biometricStrength) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
@@ -107,7 +106,7 @@ public class FaceResetLockoutClient extends HalClientMonitor<AidlSession> implem
* be used instead.
*/
static void resetLocalLockoutStateToNone(int sensorId, int userId,
- @NonNull LockoutCache lockoutTracker,
+ @NonNull LockoutTracker lockoutTracker,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull AuthSessionCoordinator authSessionCoordinator,
@Authenticators.Types int biometricStrength, long requestId) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java
index 8838345de4d6..0d6143a7d0f0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java
@@ -38,7 +38,7 @@ public class FaceRevokeChallengeClient extends RevokeChallengeClient<AidlSession
private final long mChallenge;
- FaceRevokeChallengeClient(@NonNull Context context,
+ public FaceRevokeChallengeClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token,
int userId, @NonNull String owner, int sensorId,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java
index 6c143872ff8c..f6da8726564f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java
@@ -46,7 +46,7 @@ public class FaceSetFeatureClient extends HalClientMonitor<AidlSession> implemen
private final boolean mEnabled;
private final HardwareAuthToken mHardwareAuthToken;
- FaceSetFeatureClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
+ public FaceSetFeatureClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull String owner, int sensorId,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
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 2ad41c2a7a02..54e66eb4cca4 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
@@ -23,16 +23,10 @@ import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
-import android.hardware.biometrics.face.AuthenticationFrame;
-import android.hardware.biometrics.face.EnrollmentFrame;
-import android.hardware.biometrics.face.Error;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.ISession;
-import android.hardware.biometrics.face.ISessionCallback;
-import android.hardware.face.Face;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal;
-import android.hardware.keymaster.HardwareAuthToken;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -44,29 +38,22 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
-import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.SensorServiceStateProto;
import com.android.server.biometrics.SensorStateProto;
import com.android.server.biometrics.UserStateProto;
import com.android.server.biometrics.Utils;
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.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BaseClientMonitor;
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.RemovalConsumer;
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.Map;
import java.util.function.Supplier;
@@ -91,397 +78,6 @@ public class Sensor {
@NonNull private final Supplier<AidlSession> mLazySession;
@Nullable AidlSession mCurrentSession;
- @VisibleForTesting
- public static class HalSessionCallback extends ISessionCallback.Stub {
- /**
- * Interface to sends results to the HalSessionCallback's owner.
- */
- public interface Callback {
- /**
- * Invoked when the HAL sends ERROR_HW_UNAVAILABLE.
- */
- void onHardwareUnavailable();
- }
-
- @NonNull
- private final Context mContext;
- @NonNull
- private final Handler mHandler;
- @NonNull
- private final String mTag;
- @NonNull
- private final UserAwareBiometricScheduler mScheduler;
- private final int mSensorId;
- private final int mUserId;
- @NonNull
- private final LockoutCache mLockoutCache;
- @NonNull
- private final LockoutResetDispatcher mLockoutResetDispatcher;
-
- @NonNull
- private AuthSessionCoordinator mAuthSessionCoordinator;
- @NonNull
- private final Callback mCallback;
-
- HalSessionCallback(@NonNull Context context, @NonNull Handler handler, @NonNull String tag,
- @NonNull UserAwareBiometricScheduler scheduler, int sensorId, int userId,
- @NonNull LockoutCache lockoutTracker,
- @NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull AuthSessionCoordinator authSessionCoordinator,
- @NonNull Callback callback) {
- mContext = context;
- mHandler = handler;
- mTag = tag;
- mScheduler = scheduler;
- mSensorId = sensorId;
- mUserId = userId;
- mLockoutCache = lockoutTracker;
- mLockoutResetDispatcher = lockoutResetDispatcher;
- mAuthSessionCoordinator = authSessionCoordinator;
- mCallback = callback;
- }
-
- @Override
- public int getInterfaceVersion() {
- return this.VERSION;
- }
-
- @Override
- public String getInterfaceHash() {
- return this.HASH;
- }
-
- @Override
- public void onChallengeGenerated(long challenge) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FaceGenerateChallengeClient)) {
- Slog.e(mTag, "onChallengeGenerated for wrong client: "
- + Utils.getClientName(client));
- return;
- }
-
- final FaceGenerateChallengeClient generateChallengeClient =
- (FaceGenerateChallengeClient) client;
- generateChallengeClient.onChallengeGenerated(mSensorId, mUserId, challenge);
- });
- }
-
- @Override
- public void onChallengeRevoked(long challenge) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FaceRevokeChallengeClient)) {
- Slog.e(mTag, "onChallengeRevoked for wrong client: "
- + Utils.getClientName(client));
- return;
- }
-
- final FaceRevokeChallengeClient revokeChallengeClient =
- (FaceRevokeChallengeClient) client;
- revokeChallengeClient.onChallengeRevoked(mSensorId, mUserId, challenge);
- });
- }
-
- @Override
- public void onAuthenticationFrame(AuthenticationFrame frame) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FaceAuthenticationClient)) {
- Slog.e(mTag, "onAuthenticationFrame for incompatible client: "
- + Utils.getClientName(client));
- return;
-
- }
- if (frame == null) {
- Slog.e(mTag, "Received null authentication frame for client: "
- + Utils.getClientName(client));
- return;
- }
- ((FaceAuthenticationClient) client).onAuthenticationFrame(
- AidlConversionUtils.toFrameworkAuthenticationFrame(frame));
- });
- }
-
- @Override
- public void onEnrollmentFrame(EnrollmentFrame frame) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FaceEnrollClient)) {
- Slog.e(mTag, "onEnrollmentFrame for incompatible client: "
- + Utils.getClientName(client));
- return;
- }
- if (frame == null) {
- Slog.e(mTag, "Received null enrollment frame for client: "
- + Utils.getClientName(client));
- return;
- }
- ((FaceEnrollClient) client).onEnrollmentFrame(
- AidlConversionUtils.toFrameworkEnrollmentFrame(frame));
- });
- }
-
- @Override
- public void onError(byte error, int vendorCode) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- Slog.d(mTag, "onError"
- + ", client: " + Utils.getClientName(client)
- + ", error: " + error
- + ", vendorCode: " + vendorCode);
- if (!(client instanceof ErrorConsumer)) {
- Slog.e(mTag, "onError for non-error consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final ErrorConsumer errorConsumer = (ErrorConsumer) client;
- errorConsumer.onError(AidlConversionUtils.toFrameworkError(error), vendorCode);
-
- if (error == Error.HW_UNAVAILABLE) {
- mCallback.onHardwareUnavailable();
- }
- });
- }
-
- @Override
- public void onEnrollmentProgress(int enrollmentId, int remaining) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FaceEnrollClient)) {
- Slog.e(mTag, "onEnrollmentProgress for non-enroll client: "
- + Utils.getClientName(client));
- return;
- }
-
- final int currentUserId = client.getTargetUserId();
- final CharSequence name = FaceUtils.getInstance(mSensorId)
- .getUniqueName(mContext, currentUserId);
- final Face face = new Face(name, enrollmentId, mSensorId);
-
- final FaceEnrollClient enrollClient = (FaceEnrollClient) client;
- enrollClient.onEnrollResult(face, remaining);
- });
- }
-
- @Override
- public void onAuthenticationSucceeded(int enrollmentId, HardwareAuthToken hat) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof AuthenticationConsumer)) {
- Slog.e(mTag, "onAuthenticationSucceeded for non-authentication consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final AuthenticationConsumer authenticationConsumer =
- (AuthenticationConsumer) client;
- final Face face = new Face("" /* name */, enrollmentId, mSensorId);
- final byte[] byteArray = HardwareAuthTokenUtils.toByteArray(hat);
- final ArrayList<Byte> byteList = new ArrayList<>();
- for (byte b : byteArray) {
- byteList.add(b);
- }
- authenticationConsumer.onAuthenticated(face, true /* authenticated */, byteList);
- });
- }
-
- @Override
- public void onAuthenticationFailed() {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof AuthenticationConsumer)) {
- Slog.e(mTag, "onAuthenticationFailed for non-authentication consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final AuthenticationConsumer authenticationConsumer =
- (AuthenticationConsumer) client;
- final Face face = new Face("" /* name */, 0 /* faceId */, mSensorId);
- authenticationConsumer.onAuthenticated(face, false /* authenticated */,
- null /* hat */);
- });
- }
-
- @Override
- public void onLockoutTimed(long durationMillis) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof LockoutConsumer)) {
- Slog.e(mTag, "onLockoutTimed for non-lockout consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final LockoutConsumer lockoutConsumer = (LockoutConsumer) client;
- lockoutConsumer.onLockoutTimed(durationMillis);
- });
- }
-
- @Override
- public void onLockoutPermanent() {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof LockoutConsumer)) {
- Slog.e(mTag, "onLockoutPermanent for non-lockout consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final LockoutConsumer lockoutConsumer = (LockoutConsumer) client;
- lockoutConsumer.onLockoutPermanent();
- });
- }
-
- @Override
- public void onLockoutCleared() {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FaceResetLockoutClient)) {
- Slog.d(mTag, "onLockoutCleared outside of resetLockout by HAL");
- // Given that onLockoutCleared() can happen at any time, and is not necessarily
- // coming from a specific client, set this to -1 to indicate it wasn't for a
- // specific request.
- FaceResetLockoutClient.resetLocalLockoutStateToNone(mSensorId, mUserId,
- mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
- Utils.getCurrentStrength(mSensorId), -1 /* requestId */);
- } else {
- Slog.d(mTag, "onLockoutCleared after resetLockout");
- final FaceResetLockoutClient resetLockoutClient =
- (FaceResetLockoutClient) client;
- resetLockoutClient.onLockoutCleared();
- }
- });
- }
-
- @Override
- public void onInteractionDetected() {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FaceDetectClient)) {
- Slog.e(mTag, "onInteractionDetected for wrong client: "
- + Utils.getClientName(client));
- return;
- }
-
- final FaceDetectClient detectClient = (FaceDetectClient) client;
- detectClient.onInteractionDetected();
- });
- }
-
- @Override
- public void onEnrollmentsEnumerated(int[] enrollmentIds) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof EnumerateConsumer)) {
- Slog.e(mTag, "onEnrollmentsEnumerated for non-enumerate consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final EnumerateConsumer enumerateConsumer =
- (EnumerateConsumer) client;
- if (enrollmentIds.length > 0) {
- for (int i = 0; i < enrollmentIds.length; ++i) {
- final Face face = new Face("" /* name */, enrollmentIds[i], mSensorId);
- enumerateConsumer.onEnumerationResult(face, enrollmentIds.length - i - 1);
- }
- } else {
- enumerateConsumer.onEnumerationResult(null /* identifier */, 0 /* remaining */);
- }
- });
- }
-
- @Override
- public void onFeaturesRetrieved(byte[] features) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FaceGetFeatureClient)) {
- Slog.e(mTag, "onFeaturesRetrieved for non-get feature consumer: "
- + Utils.getClientName(client));
- return;
- }
- final FaceGetFeatureClient faceGetFeatureClient = (FaceGetFeatureClient) client;
- faceGetFeatureClient.onFeatureGet(true /* success */, features);
- });
-
- }
-
- @Override
- public void onFeatureSet(byte feature) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FaceSetFeatureClient)) {
- Slog.e(mTag, "onFeatureSet for non-set consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final FaceSetFeatureClient faceSetFeatureClient = (FaceSetFeatureClient) client;
- faceSetFeatureClient.onFeatureSet(true /* success */);
- });
- }
-
- @Override
- public void onEnrollmentsRemoved(int[] enrollmentIds) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof RemovalConsumer)) {
- Slog.e(mTag, "onRemoved for non-removal consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final RemovalConsumer removalConsumer = (RemovalConsumer) client;
- if (enrollmentIds.length > 0) {
- for (int i = 0; i < enrollmentIds.length; i++) {
- final Face face = new Face("" /* name */, enrollmentIds[i], mSensorId);
- removalConsumer.onRemoved(face, enrollmentIds.length - i - 1);
- }
- } else {
- removalConsumer.onRemoved(null /* identifier */, 0 /* remaining */);
- }
- });
- }
-
- @Override
- public void onAuthenticatorIdRetrieved(long authenticatorId) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FaceGetAuthenticatorIdClient)) {
- Slog.e(mTag, "onAuthenticatorIdRetrieved for wrong consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final FaceGetAuthenticatorIdClient getAuthenticatorIdClient =
- (FaceGetAuthenticatorIdClient) client;
- getAuthenticatorIdClient.onAuthenticatorIdRetrieved(authenticatorId);
- });
- }
-
- @Override
- public void onAuthenticatorIdInvalidated(long newAuthenticatorId) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FaceInvalidationClient)) {
- Slog.e(mTag, "onAuthenticatorIdInvalidated for wrong consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final FaceInvalidationClient invalidationClient = (FaceInvalidationClient) client;
- invalidationClient.onAuthenticatorIdInvalidated(newAuthenticatorId);
- });
- }
-
- @Override
- public void onSessionClosed() {
- mHandler.post(mScheduler::onUserStopped);
- }
- }
Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context,
@NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties,
@@ -511,9 +107,9 @@ public class Sensor {
public StartUserClient<?, ?> getStartUserClient(int newUserId) {
final int sensorId = mSensorProperties.sensorId;
- final HalSessionCallback resultController = new HalSessionCallback(mContext,
- mHandler, mTag, mScheduler, sensorId, newUserId, mLockoutCache,
- lockoutResetDispatcher,
+ final AidlResponseHandler resultController = new AidlResponseHandler(
+ mContext, mScheduler, sensorId, newUserId,
+ mLockoutCache, lockoutResetDispatcher,
biometricContext.getAuthSessionCoordinator(), () -> {
Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE");
mCurrentSession = null;
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/AidlToHidlAdapter.java
new file mode 100644
index 000000000000..eecf44b92918
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapter.java
@@ -0,0 +1,347 @@
+/*
+ * 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.DurationMillisLong;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricFaceConstants;
+import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.common.OperationContext;
+import android.hardware.biometrics.face.EnrollmentStageConfig;
+import android.hardware.biometrics.face.ISession;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.biometrics.face.V1_0.OptionalBool;
+import android.hardware.biometrics.face.V1_0.Status;
+import android.hardware.common.NativeHandle;
+import android.hardware.face.Face;
+import android.hardware.face.FaceManager;
+import android.hardware.keymaster.HardwareAuthToken;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.sensors.face.aidl.AidlConversionUtils;
+import com.android.server.biometrics.sensors.face.aidl.AidlResponseHandler;
+
+import java.time.Clock;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Supplier;
+
+/**
+ * Adapter to convert AIDL-specific interface {@link ISession} methods to HIDL implementation.
+ */
+public class AidlToHidlAdapter implements ISession {
+
+ private final String TAG = "AidlToHidlAdapter";
+ 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;
+ private final Clock mClock;
+ private final List<Long> mGeneratedChallengeCount = new ArrayList<>();
+ @VisibleForTesting static final int ENROLL_TIMEOUT_SEC = 75;
+ 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,
+ int userId, AidlResponseHandler aidlResponseHandler) {
+ this(context, session, userId, aidlResponseHandler, Clock.systemUTC());
+ }
+
+ AidlToHidlAdapter(Context context, Supplier<IBiometricsFace> session, int userId,
+ AidlResponseHandler aidlResponseHandler, Clock clock) {
+ mSession = session;
+ mUserId = userId;
+ mContext = context;
+ mClock = clock;
+ 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();
+ if (isGeneratedChallengeCacheValid()) {
+ Slog.d(TAG, "Current challenge is cached and will be reused");
+ mHidlToAidlCallbackConverter.onChallengeGenerated(mGenerateChallengeResult);
+ return;
+ }
+ mGenerateChallengeCreatedAt = mClock.millis();
+ mGenerateChallengeResult = mSession.get().generateChallenge(CHALLENGE_TIMEOUT_SEC).value;
+ mHidlToAidlCallbackConverter.onChallengeGenerated(mGenerateChallengeResult);
+ }
+
+ @Override
+ public void revokeChallenge(long challenge) throws RemoteException {
+ final boolean shouldRevoke = decrementChallengeCount() == 0;
+ if (!shouldRevoke) {
+ Slog.w(TAG, "scheduleRevokeChallenge skipped - challenge still in use: "
+ + mGeneratedChallengeCount);
+ mHidlToAidlCallbackConverter.onError(0 /* deviceId */, mUserId,
+ BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */);
+ return;
+ }
+ mGenerateChallengeCreatedAt = INVALID_VALUE;
+ mGenerateChallengeResult = INVALID_VALUE;
+ mSession.get().revokeChallenge();
+ mHidlToAidlCallbackConverter.onChallengeRevoked(0L);
+ }
+
+ @Override
+ public EnrollmentStageConfig[] getEnrollmentConfig(byte enrollmentType) throws RemoteException {
+ //unsupported in HIDL
+ return null;
+ }
+
+ @Override
+ public ICancellationSignal enroll(HardwareAuthToken hat, byte type, byte[] features,
+ NativeHandle previewSurface) throws RemoteException {
+ final ArrayList<Byte> token = new ArrayList<>();
+ final byte[] hardwareAuthTokenArray = HardwareAuthTokenUtils.toByteArray(hat);
+ for (byte b : hardwareAuthTokenArray) {
+ token.add(b);
+ }
+ final ArrayList<Integer> disabledFeatures = new ArrayList<>();
+ for (byte b: features) {
+ disabledFeatures.add(AidlConversionUtils.convertAidlToFrameworkFeature(b));
+ }
+ mSession.get().enroll(token, ENROLL_TIMEOUT_SEC, disabledFeatures);
+ return new Cancellation();
+ }
+
+ @Override
+ public ICancellationSignal authenticate(long operationId) throws RemoteException {
+ mSession.get().authenticate(operationId);
+ return new Cancellation();
+ }
+
+ @Override
+ public ICancellationSignal detectInteraction() throws RemoteException {
+ mSession.get().authenticate(0);
+ return new Cancellation();
+ }
+
+ @Override
+ public void enumerateEnrollments() throws RemoteException {
+ mSession.get().enumerate();
+ }
+
+ @Override
+ public void removeEnrollments(int[] enrollmentIds) throws RemoteException {
+ mSession.get().remove(enrollmentIds[0]);
+ }
+
+ /**
+ * Needs to be called before getFeatures is invoked.
+ */
+ public void setFeature(int feature) {
+ mFeature = feature;
+ }
+
+ @Override
+ public void getFeatures() throws RemoteException {
+ final int faceId = getFaceId();
+ if (faceId == INVALID_VALUE || mFeature == INVALID_VALUE) {
+ return;
+ }
+
+ final OptionalBool result = mSession.get()
+ .getFeature(mFeature, faceId);
+
+ if (result.status == Status.OK && result.value) {
+ mHidlToAidlCallbackConverter.onFeatureGet(new byte[]{AidlConversionUtils
+ .convertFrameworkToAidlFeature(mFeature)});
+ } else if (result.status == Status.OK) {
+ mHidlToAidlCallbackConverter.onFeatureGet(new byte[]{});
+ } else {
+ mHidlToAidlCallbackConverter.onError(0 /* deviceId */, mUserId,
+ BiometricFaceConstants.FACE_ERROR_UNKNOWN, 0 /* vendorCode */);
+ }
+
+ mFeature = INVALID_VALUE;
+ }
+
+ @Override
+ public void setFeature(HardwareAuthToken hat, byte feature, boolean enabled)
+ throws RemoteException {
+ final int faceId = getFaceId();
+ if (faceId == INVALID_VALUE) {
+ return;
+ }
+ ArrayList<Byte> hardwareAuthTokenList = new ArrayList<>();
+ for (byte b: HardwareAuthTokenUtils.toByteArray(hat)) {
+ hardwareAuthTokenList.add(b);
+ }
+ final int result = mSession.get().setFeature(
+ AidlConversionUtils.convertAidlToFrameworkFeature(feature),
+ enabled, hardwareAuthTokenList, faceId);
+ if (result == Status.OK) {
+ mHidlToAidlCallbackConverter.onFeatureSet(feature);
+ } else {
+ mHidlToAidlCallbackConverter.onError(0 /* deviceId */, mUserId,
+ BiometricFaceConstants.FACE_ERROR_UNKNOWN, 0 /* vendorCode */);
+ }
+ }
+
+ 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;
+ mHidlToAidlCallbackConverter.onAuthenticatorIdRetrieved(authenticatorId);
+ }
+
+ @Override
+ public void invalidateAuthenticatorId() throws RemoteException {
+ //unsupported in HIDL
+ }
+
+ @Override
+ public void resetLockout(HardwareAuthToken hat) throws RemoteException {
+ ArrayList<Byte> hardwareAuthToken = new ArrayList<>();
+ for (byte b : HardwareAuthTokenUtils.toByteArray(hat)) {
+ hardwareAuthToken.add(b);
+ }
+ mSession.get().resetLockout(hardwareAuthToken);
+ }
+
+ @Override
+ public void close() throws RemoteException {
+ //Unsupported in HIDL
+ }
+
+ @Override
+ public ICancellationSignal authenticateWithContext(long operationId, OperationContext context)
+ throws RemoteException {
+ //Unsupported in HIDL
+ return null;
+ }
+
+ @Override
+ public ICancellationSignal enrollWithContext(HardwareAuthToken hat, byte type, byte[] features,
+ NativeHandle previewSurface, OperationContext context) throws RemoteException {
+ //Unsupported in HIDL
+ return null;
+ }
+
+ @Override
+ public ICancellationSignal detectInteractionWithContext(OperationContext context)
+ throws RemoteException {
+ //Unsupported in HIDL
+ return null;
+ }
+
+ @Override
+ public void onContextChanged(OperationContext context) throws RemoteException {
+ //Unsupported in HIDL
+ }
+
+ @Override
+ public int getInterfaceVersion() throws RemoteException {
+ //Unsupported in HIDL
+ return 0;
+ }
+
+ @Override
+ public String getInterfaceHash() throws RemoteException {
+ //Unsupported in HIDL
+ return null;
+ }
+
+ /**
+ * Cancellation in HIDL occurs for the entire session, instead of a specific client.
+ */
+ private class Cancellation extends ICancellationSignal.Stub {
+
+ Cancellation() {}
+ @Override
+ public void cancel() throws RemoteException {
+ try {
+ mSession.get().cancel();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when requesting cancel", e);
+ }
+ }
+
+ @Override
+ public int getInterfaceVersion() throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public String getInterfaceHash() throws RemoteException {
+ return null;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index 1499317478aa..46ce0b62e6d5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -54,6 +54,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.AuthenticationStatsBroadcastReceiver;
import com.android.server.biometrics.AuthenticationStatsCollector;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.SensorServiceStateProto;
import com.android.server.biometrics.SensorStateProto;
import com.android.server.biometrics.UserStateProto;
@@ -61,6 +62,7 @@ import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AcquisitionClient;
+import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
@@ -78,6 +80,8 @@ import com.android.server.biometrics.sensors.face.FaceUtils;
import com.android.server.biometrics.sensors.face.LockoutHalImpl;
import com.android.server.biometrics.sensors.face.ServiceProvider;
import com.android.server.biometrics.sensors.face.UsageStats;
+import com.android.server.biometrics.sensors.face.aidl.AidlResponseHandler;
+import com.android.server.biometrics.sensors.face.aidl.AidlSession;
import org.json.JSONArray;
import org.json.JSONException;
@@ -131,7 +135,9 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
private int mCurrentUserId = UserHandle.USER_NULL;
private final int mSensorId;
private final List<Long> mGeneratedChallengeCount = new ArrayList<>();
+ private final LockoutResetDispatcher mLockoutResetDispatcher;
private FaceGenerateChallengeClient mGeneratedChallengeCache = null;
+ private AidlSession mSession;
private final UserSwitchObserver mUserSwitchObserver = new SynchronousUserSwitchObserver() {
@Override
@@ -361,6 +367,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
mLockoutTracker = new LockoutHalImpl();
mHalResultController = new HalResultController(sensorProps.sensorId, context, mHandler,
mScheduler, mLockoutTracker, lockoutResetDispatcher);
+ mLockoutResetDispatcher = lockoutResetDispatcher;
mHalResultController.setCallback(() -> {
mDaemon = null;
mCurrentUserId = UserHandle.USER_NULL;
@@ -420,6 +427,24 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
});
}
+ public int getCurrentUserId() {
+ return mCurrentUserId;
+ }
+
+ synchronized AidlSession getSession() {
+ if (mDaemon != null && mSession != null) {
+ return mSession;
+ } else {
+ return mSession = new AidlSession(mContext, this::getDaemon, mCurrentUserId,
+ new AidlResponseHandler(mContext, mScheduler, mSensorId,
+ mCurrentUserId, mLockoutTracker, mLockoutResetDispatcher,
+ new AuthSessionCoordinator(), () -> {
+ mDaemon = null;
+ mCurrentUserId = UserHandle.USER_NULL;
+ }));
+ }
+ }
+
private synchronized IBiometricsFace getDaemon() {
if (mTestHalEnabled) {
final TestHal testHal = new TestHal(mContext, mSensorId);
@@ -551,32 +576,63 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
public void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token,
@NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
mHandler.post(() -> {
- incrementChallengeCount();
+ scheduleUpdateActiveUserWithoutHandler(userId);
- if (isGeneratedChallengeCacheValid()) {
- Slog.d(TAG, "Current challenge is cached and will be reused");
- mGeneratedChallengeCache.reuseResult(receiver);
- return;
+ if (Flags.deHidl()) {
+ scheduleGenerateChallengeAidl(userId, token, receiver, opPackageName);
+ } else {
+ scheduleGenerateChallengeHidl(userId, token, receiver, opPackageName);
}
+ });
+ }
- scheduleUpdateActiveUserWithoutHandler(userId);
+ private void scheduleGenerateChallengeAidl(int userId, @NonNull IBinder token,
+ @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
+ final com.android.server.biometrics.sensors.face.aidl.FaceGenerateChallengeClient client =
+ new com.android.server.biometrics.sensors.face.aidl.FaceGenerateChallengeClient(
+ mContext, this::getSession, token,
+ new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
+ mSensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
+ mBiometricContext);
+ mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
+ @Override
+ public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
+ if (client != clientMonitor) {
+ Slog.e(TAG,
+ "scheduleGenerateChallenge onClientStarted, mismatched client."
+ + " Expecting: " + client + ", received: "
+ + clientMonitor);
+ }
+ }
+ });
+ }
- final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext,
- mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
- opPackageName, mSensorId,
- createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext, sSystemClock.millis());
- mGeneratedChallengeCache = client;
- mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
- @Override
- public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
- if (client != clientMonitor) {
- Slog.e(TAG, "scheduleGenerateChallenge onClientStarted, mismatched client."
- + " Expecting: " + client + ", received: " + clientMonitor);
- }
+ private void scheduleGenerateChallengeHidl(int userId, @NonNull IBinder token,
+ @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
+ incrementChallengeCount();
+ if (isGeneratedChallengeCacheValid()) {
+ Slog.d(TAG, "Current challenge is cached and will be reused");
+ mGeneratedChallengeCache.reuseResult(receiver);
+ return;
+ }
+
+ final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext,
+ mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
+ opPackageName, mSensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
+ mBiometricContext, sSystemClock.millis());
+ mGeneratedChallengeCache = client;
+ mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
+ @Override
+ public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
+ if (client != clientMonitor) {
+ Slog.e(TAG,
+ "scheduleGenerateChallenge onClientStarted, mismatched client."
+ + " Expecting: " + client + ", received: "
+ + clientMonitor);
}
- });
+ }
});
}
@@ -584,31 +640,62 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
public void scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token,
@NonNull String opPackageName, long challenge) {
mHandler.post(() -> {
- final boolean shouldRevoke = decrementChallengeCount() == 0;
- if (!shouldRevoke) {
- Slog.w(TAG, "scheduleRevokeChallenge skipped - challenge still in use: "
- + mGeneratedChallengeCount);
- return;
+ if (Flags.deHidl()) {
+ scheduleRevokeChallengeAidl(userId, token, opPackageName);
+ } else {
+ scheduleRevokeChallengeHidl(userId, token, opPackageName);
+ }
+ });
+ }
+
+ private void scheduleRevokeChallengeAidl(int userId, @NonNull IBinder token,
+ @NonNull String opPackageName) {
+ final com.android.server.biometrics.sensors.face.aidl.FaceRevokeChallengeClient
+ client =
+ new com.android.server.biometrics.sensors.face.aidl.FaceRevokeChallengeClient(
+ mContext, this::getSession, token, userId, opPackageName, mSensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector), mBiometricContext, 0L);
+ mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
+ @Override
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
+ boolean success) {
+ if (client != clientMonitor) {
+ Slog.e(TAG,
+ "scheduleRevokeChallenge, mismatched client." + "Expecting: "
+ + client + ", received: " + clientMonitor);
+ }
}
+ });
+ }
- Slog.d(TAG, "scheduleRevokeChallenge executing - no active clients");
- mGeneratedChallengeCache = null;
+ private void scheduleRevokeChallengeHidl(int userId, @NonNull IBinder token,
+ @NonNull String opPackageName) {
+ final boolean shouldRevoke = decrementChallengeCount() == 0;
+ if (!shouldRevoke) {
+ Slog.w(TAG, "scheduleRevokeChallenge skipped - challenge still in use: "
+ + mGeneratedChallengeCount);
+ return;
+ }
- final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext,
- mLazyDaemon, token, userId, opPackageName, mSensorId,
- createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext);
- mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
- @Override
- public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
- boolean success) {
- if (client != clientMonitor) {
- Slog.e(TAG, "scheduleRevokeChallenge, mismatched client."
- + "Expecting: " + client + ", received: " + clientMonitor);
- }
+ Slog.d(TAG, "scheduleRevokeChallenge executing - no active clients");
+ mGeneratedChallengeCache = null;
+ final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext,
+ mLazyDaemon, token, userId, opPackageName, mSensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
+ mBiometricContext);
+ mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
+ @Override
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
+ boolean success) {
+ if (client != clientMonitor) {
+ Slog.e(TAG,
+ "scheduleRevokeChallenge, mismatched client." + "Expecting: "
+ + client + ", received: " + clientMonitor);
}
- });
+ }
});
}
@@ -620,7 +707,62 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final long id = mRequestCounter.incrementAndGet();
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
+ if (Flags.deHidl()) {
+ scheduleEnrollAidl(token, hardwareAuthToken, userId, receiver,
+ opPackageName, disabledFeatures, previewSurface, id);
+ } else {
+ scheduleEnrollHidl(token, hardwareAuthToken, userId, receiver,
+ opPackageName, disabledFeatures, previewSurface, id);
+ }
+ });
+ return id;
+ }
+
+ private void scheduleEnrollAidl(@NonNull IBinder token,
+ @NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver,
+ @NonNull String opPackageName, @NonNull int[] disabledFeatures,
+ @Nullable Surface previewSurface, long id) {
+ final com.android.server.biometrics.sensors.face.aidl.FaceEnrollClient client =
+ new com.android.server.biometrics.sensors.face.aidl.FaceEnrollClient(
+ mContext, this::getSession, token,
+ new ClientMonitorCallbackConverter(receiver), userId,
+ hardwareAuthToken, opPackageName, id,
+ FaceUtils.getLegacyInstance(mSensorId), disabledFeatures,
+ ENROLL_TIMEOUT_SEC, previewSurface, mSensorId,
+ createLogger(BiometricsProtoEnums.ACTION_ENROLL,
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector), mBiometricContext,
+ mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_faceMaxTemplatesPerUser),
+ false);
+
+ mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
+ @Override
+ public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
+ mBiometricStateCallback.onClientStarted(clientMonitor);
+ }
+
+ @Override
+ public void onBiometricAction(int action) {
+ mBiometricStateCallback.onBiometricAction(action);
+ }
+ @Override
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
+ boolean success) {
+ mBiometricStateCallback.onClientFinished(clientMonitor, success);
+ if (success) {
+ // Update authenticatorIds
+ scheduleUpdateActiveUserWithoutHandler(client.getTargetUserId());
+ }
+ }
+ });
+ }
+
+ private void scheduleEnrollHidl(@NonNull IBinder token,
+ @NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver,
+ @NonNull String opPackageName, @NonNull int[] disabledFeatures,
+ @Nullable Surface previewSurface, long id) {
final FaceEnrollClient client = new FaceEnrollClient(mContext, mLazyDaemon, token,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
opPackageName, id, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures,
@@ -628,7 +770,6 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
createLogger(BiometricsProtoEnums.ACTION_ENROLL,
BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext);
-
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
@@ -650,8 +791,6 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
}
}
});
- });
- return id;
}
@Override
@@ -683,18 +822,46 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
scheduleUpdateActiveUserWithoutHandler(userId);
final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorId);
- final FaceAuthenticationClient client = new FaceAuthenticationClient(mContext,
- mLazyDaemon, token, requestId, receiver, operationId, restricted,
- options, cookie, false /* requireConfirmation */,
- createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
- mAuthenticationStatsCollector),
- mBiometricContext, isStrongBiometric, mLockoutTracker,
- mUsageStats, allowBackgroundAuthentication,
- Utils.getCurrentStrength(mSensorId));
- mScheduler.scheduleClientMonitor(client);
+ if (Flags.deHidl()) {
+ scheduleAuthenticateAidl(token, operationId, cookie, receiver, options, requestId,
+ restricted, statsClient, allowBackgroundAuthentication, isStrongBiometric);
+ } else {
+ scheduleAuthenticateHidl(token, operationId, cookie, receiver, options, requestId,
+ restricted, statsClient, allowBackgroundAuthentication, isStrongBiometric);
+ }
});
}
+ private void scheduleAuthenticateAidl(@NonNull IBinder token, long operationId,
+ int cookie, @NonNull ClientMonitorCallbackConverter receiver,
+ @NonNull FaceAuthenticateOptions options, long requestId, boolean restricted,
+ int statsClient, boolean allowBackgroundAuthentication, boolean isStrongBiometric) {
+ final com.android.server.biometrics.sensors.face.aidl.FaceAuthenticationClient
+ client =
+ new com.android.server.biometrics.sensors.face.aidl.FaceAuthenticationClient(
+ mContext, this::getSession, token, requestId, receiver, operationId,
+ restricted, options, cookie, false /* requireConfirmation */,
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
+ mAuthenticationStatsCollector), mBiometricContext,
+ isStrongBiometric, mUsageStats, mLockoutTracker,
+ allowBackgroundAuthentication, Utils.getCurrentStrength(mSensorId));
+ mScheduler.scheduleClientMonitor(client);
+ }
+
+ private void scheduleAuthenticateHidl(@NonNull IBinder token, long operationId,
+ int cookie, @NonNull ClientMonitorCallbackConverter receiver,
+ @NonNull FaceAuthenticateOptions options, long requestId, boolean restricted,
+ int statsClient, boolean allowBackgroundAuthentication, boolean isStrongBiometric) {
+ final FaceAuthenticationClient client = new FaceAuthenticationClient(mContext,
+ mLazyDaemon, token, requestId, receiver, operationId, restricted, options,
+ cookie, false /* requireConfirmation */,
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
+ mAuthenticationStatsCollector), mBiometricContext,
+ isStrongBiometric, mLockoutTracker, mUsageStats,
+ allowBackgroundAuthentication, Utils.getCurrentStrength(mSensorId));
+ mScheduler.scheduleClientMonitor(client);
+ }
+
@Override
public long scheduleAuthenticate(@NonNull IBinder token, long operationId,
int cookie, @NonNull ClientMonitorCallbackConverter receiver,
@@ -719,13 +886,11 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
- final FaceRemovalClient client = new FaceRemovalClient(mContext, mLazyDaemon, token,
- new ClientMonitorCallbackConverter(receiver), faceId, userId, opPackageName,
- FaceUtils.getLegacyInstance(mSensorId), mSensorId,
- createLogger(BiometricsProtoEnums.ACTION_REMOVE,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext, mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
+ if (Flags.deHidl()) {
+ scheduleRemoveAidl(token, userId, receiver, opPackageName, faceId);
+ } else {
+ scheduleRemoveHidl(token, userId, receiver, opPackageName, faceId);
+ }
});
}
@@ -736,17 +901,39 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
scheduleUpdateActiveUserWithoutHandler(userId);
// For IBiometricsFace@1.0, remove(0) means remove all enrollments
- final FaceRemovalClient client = new FaceRemovalClient(mContext, mLazyDaemon, token,
- new ClientMonitorCallbackConverter(receiver), 0 /* faceId */, userId,
- opPackageName,
- FaceUtils.getLegacyInstance(mSensorId), mSensorId,
- createLogger(BiometricsProtoEnums.ACTION_REMOVE,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext, mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
+ if (Flags.deHidl()) {
+ scheduleRemoveAidl(token, userId, receiver, opPackageName, 0);
+ } else {
+ scheduleRemoveHidl(token, userId, receiver, opPackageName, 0);
+ }
});
}
+ private void scheduleRemoveAidl(@NonNull IBinder token, int userId,
+ @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName, int faceId) {
+ final com.android.server.biometrics.sensors.face.aidl.FaceRemovalClient client =
+ new com.android.server.biometrics.sensors.face.aidl.FaceRemovalClient(
+ mContext, this::getSession, token,
+ new ClientMonitorCallbackConverter(receiver), new int[]{faceId}, userId,
+ opPackageName, FaceUtils.getLegacyInstance(mSensorId), mSensorId,
+ createLogger(BiometricsProtoEnums.ACTION_REMOVE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector), mBiometricContext,
+ mAuthenticatorIds);
+ mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
+ }
+
+ private void scheduleRemoveHidl(@NonNull IBinder token, int userId,
+ @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName, int faceId) {
+ final FaceRemovalClient client = new FaceRemovalClient(mContext, mLazyDaemon, token,
+ new ClientMonitorCallbackConverter(receiver), faceId, userId,
+ opPackageName, FaceUtils.getLegacyInstance(mSensorId), mSensorId,
+ createLogger(BiometricsProtoEnums.ACTION_REMOVE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
+ mBiometricContext, mAuthenticatorIds);
+ mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
+ }
+
@Override
public void scheduleResetLockout(int sensorId, int userId, @NonNull byte[] hardwareAuthToken) {
mHandler.post(() -> {
@@ -756,89 +943,180 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
}
scheduleUpdateActiveUserWithoutHandler(userId);
-
- final FaceResetLockoutClient client = new FaceResetLockoutClient(mContext,
- mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId,
- createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext, hardwareAuthToken);
- mScheduler.scheduleClientMonitor(client);
+ if (Flags.deHidl()) {
+ scheduleResetLockoutAidl(userId, hardwareAuthToken);
+ } else {
+ scheduleResetLockoutHidl(userId, hardwareAuthToken);
+ }
});
}
+ private void scheduleResetLockoutAidl(int userId,
+ @NonNull byte[] hardwareAuthToken) {
+ final com.android.server.biometrics.sensors.face.aidl.FaceResetLockoutClient client =
+ new com.android.server.biometrics.sensors.face.aidl.FaceResetLockoutClient(
+ mContext, this::getSession, userId, mContext.getOpPackageName(),
+ mSensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
+ mBiometricContext, hardwareAuthToken, mLockoutTracker,
+ mLockoutResetDispatcher, mSensorProperties.sensorStrength);
+ mScheduler.scheduleClientMonitor(client);
+ }
+
+ private void scheduleResetLockoutHidl(int userId,
+ @NonNull byte[] hardwareAuthToken) {
+ final FaceResetLockoutClient client = new FaceResetLockoutClient(mContext,
+ mLazyDaemon,
+ userId, mContext.getOpPackageName(), mSensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
+ mBiometricContext, hardwareAuthToken);
+ mScheduler.scheduleClientMonitor(client);
+ }
+
@Override
public void scheduleSetFeature(int sensorId, @NonNull IBinder token, int userId, int feature,
boolean enabled, @NonNull byte[] hardwareAuthToken,
@NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
mHandler.post(() -> {
- final List<Face> faces = getEnrolledFaces(sensorId, userId);
- if (faces.isEmpty()) {
- Slog.w(TAG, "Ignoring setFeature, no templates enrolled for user: " + userId);
- return;
+ scheduleUpdateActiveUserWithoutHandler(userId);
+ if (Flags.deHidl()) {
+ scheduleSetFeatureAidl(sensorId, token, userId, feature, enabled, hardwareAuthToken,
+ receiver, opPackageName);
+ } else {
+ scheduleSetFeatureHidl(sensorId, token, userId, feature, enabled, hardwareAuthToken,
+ receiver, opPackageName);
}
+ });
+ }
- scheduleUpdateActiveUserWithoutHandler(userId);
+ private void scheduleSetFeatureHidl(int sensorId, @NonNull IBinder token, int userId,
+ int feature, boolean enabled, @NonNull byte[] hardwareAuthToken,
+ @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
+ final List<Face> faces = getEnrolledFaces(sensorId, userId);
+ if (faces.isEmpty()) {
+ Slog.w(TAG, "Ignoring setFeature, no templates enrolled for user: " + userId);
+ return;
+ }
+ final int faceId = faces.get(0).getBiometricId();
+ final FaceSetFeatureClient client = new FaceSetFeatureClient(mContext, mLazyDaemon,
+ token, new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
+ mSensorId, BiometricLogger.ofUnknown(mContext), mBiometricContext, feature,
+ enabled, hardwareAuthToken, faceId);
+ mScheduler.scheduleClientMonitor(client);
+ }
- final int faceId = faces.get(0).getBiometricId();
- final FaceSetFeatureClient client = new FaceSetFeatureClient(mContext,
- mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
- opPackageName, mSensorId, BiometricLogger.ofUnknown(mContext),
- mBiometricContext,
- feature, enabled, hardwareAuthToken, faceId);
- mScheduler.scheduleClientMonitor(client);
- });
+ private void scheduleSetFeatureAidl(int sensorId, @NonNull IBinder token, int userId,
+ int feature, boolean enabled, @NonNull byte[] hardwareAuthToken,
+ @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
+ final com.android.server.biometrics.sensors.face.aidl.FaceSetFeatureClient client =
+ new com.android.server.biometrics.sensors.face.aidl.FaceSetFeatureClient(
+ mContext, this::getSession, token,
+ new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
+ mSensorId, BiometricLogger.ofUnknown(mContext), mBiometricContext,
+ feature, enabled, hardwareAuthToken);
+ mScheduler.scheduleClientMonitor(client);
}
+
@Override
public void scheduleGetFeature(int sensorId, @NonNull IBinder token, int userId, int feature,
@Nullable ClientMonitorCallbackConverter listener, @NonNull String opPackageName) {
mHandler.post(() -> {
- final List<Face> faces = getEnrolledFaces(sensorId, userId);
- if (faces.isEmpty()) {
- Slog.w(TAG, "Ignoring getFeature, no templates enrolled for user: " + userId);
- return;
+ scheduleUpdateActiveUserWithoutHandler(userId);
+
+ if (Flags.deHidl()) {
+ scheduleGetFeatureAidl(token, userId, feature, listener,
+ opPackageName);
+ } else {
+ scheduleGetFeatureHidl(sensorId, token, userId, feature, listener,
+ opPackageName);
}
+ });
+ }
- scheduleUpdateActiveUserWithoutHandler(userId);
+ private void scheduleGetFeatureHidl(int sensorId, @NonNull IBinder token, int userId,
+ int feature, @Nullable ClientMonitorCallbackConverter listener,
+ @NonNull String opPackageName) {
+ final List<Face> faces = getEnrolledFaces(sensorId, userId);
+ if (faces.isEmpty()) {
+ Slog.w(TAG, "Ignoring getFeature, no templates enrolled for user: " + userId);
+ return;
+ }
- final int faceId = faces.get(0).getBiometricId();
- final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext, mLazyDaemon,
- token, listener, userId, opPackageName, mSensorId,
- BiometricLogger.ofUnknown(mContext), mBiometricContext,
- feature, faceId);
- mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
- @Override
- public void onClientFinished(
- @NonNull BaseClientMonitor clientMonitor, boolean success) {
- if (success && feature == BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION) {
- final int settingsValue = client.getValue() ? 1 : 0;
- Slog.d(TAG, "Updating attention value for user: " + userId
- + " to value: " + settingsValue);
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED,
- settingsValue, userId);
- }
+ final int faceId = faces.get(0).getBiometricId();
+ final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext, mLazyDaemon,
+ token, listener, userId, opPackageName, mSensorId,
+ BiometricLogger.ofUnknown(mContext), mBiometricContext, feature, faceId);
+ mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
+ @Override
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
+ boolean success) {
+ if (success
+ && feature == BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION) {
+ final int settingsValue = client.getValue() ? 1 : 0;
+ Slog.d(TAG,
+ "Updating attention value for user: " + userId + " to value: "
+ + settingsValue);
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED, settingsValue,
+ userId);
}
- });
+ }
});
}
+ private void scheduleGetFeatureAidl(@NonNull IBinder token, int userId,
+ int feature, @Nullable ClientMonitorCallbackConverter listener,
+ @NonNull String opPackageName) {
+ final com.android.server.biometrics.sensors.face.aidl.FaceGetFeatureClient client =
+ new com.android.server.biometrics.sensors.face.aidl.FaceGetFeatureClient(
+ mContext, this::getSession, token, listener, userId, opPackageName,
+ mSensorId, BiometricLogger.ofUnknown(mContext), mBiometricContext,
+ feature);
+ mScheduler.scheduleClientMonitor(client);
+ }
+
private void scheduleInternalCleanup(int userId,
@Nullable ClientMonitorCallback callback) {
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
-
- final FaceInternalCleanupClient client = new FaceInternalCleanupClient(mContext,
- mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId,
- createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext,
- FaceUtils.getLegacyInstance(mSensorId), mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client, new ClientMonitorCompositeCallback(callback,
- mBiometricStateCallback));
+ if (Flags.deHidl()) {
+ scheduleInternalCleanupAidl(userId, callback);
+ } else {
+ scheduleInternalCleanupHidl(userId, callback);
+ }
});
}
+ private void scheduleInternalCleanupHidl(int userId,
+ @Nullable ClientMonitorCallback callback) {
+ final FaceInternalCleanupClient client = new FaceInternalCleanupClient(mContext,
+ mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId,
+ createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
+ mBiometricContext, FaceUtils.getLegacyInstance(mSensorId),
+ mAuthenticatorIds);
+ mScheduler.scheduleClientMonitor(client,
+ new ClientMonitorCompositeCallback(callback, mBiometricStateCallback));
+ }
+
+ private void scheduleInternalCleanupAidl(int userId,
+ @Nullable ClientMonitorCallback callback) {
+ final com.android.server.biometrics.sensors.face.aidl.FaceInternalCleanupClient
+ client =
+ new com.android.server.biometrics.sensors.face.aidl.FaceInternalCleanupClient(
+ mContext, this::getSession, userId, mContext.getOpPackageName(),
+ mSensorId, createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
+ mBiometricContext, FaceUtils.getLegacyInstance(mSensorId),
+ mAuthenticatorIds);
+ mScheduler.scheduleClientMonitor(client,
+ new ClientMonitorCompositeCallback(callback, mBiometricStateCallback));
+ }
+
@Override
public void scheduleInternalCleanup(int sensorId, int userId,
@Nullable ClientMonitorCallback callback) {
@@ -970,6 +1248,10 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
boolean success) {
if (success) {
+ if (mCurrentUserId != targetUserId) {
+ // Create new session with updated user ID
+ mSession = null;
+ }
mCurrentUserId = targetUserId;
} else {
Slog.w(TAG, "Failed to change user, still: " + mCurrentUserId);
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
new file mode 100644
index 000000000000..36a9790d2d4b
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlCallbackConverter.java
@@ -0,0 +1,115 @@
+/*
+ * 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.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
+
+import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.sensors.face.aidl.AidlResponseHandler;
+
+import java.util.ArrayList;
+
+/**
+ * Convert HIDL-specific callback interface {@link IBiometricsFaceClientCallback} to AIDL
+ * response handler.
+ */
+public class HidlToAidlCallbackConverter extends IBiometricsFaceClientCallback.Stub {
+
+ private final AidlResponseHandler mAidlResponseHandler;
+
+ public HidlToAidlCallbackConverter(AidlResponseHandler aidlResponseHandler) {
+ mAidlResponseHandler = aidlResponseHandler;
+ }
+
+ @Override
+ public void onEnrollResult(
+ long deviceId, int faceId, int userId, int remaining) {
+ mAidlResponseHandler.onEnrollmentProgress(faceId, remaining);
+ }
+
+ @Override
+ public void onAuthenticated(long deviceId, int faceId, int userId,
+ ArrayList<Byte> token) {
+ final boolean authenticated = faceId != 0;
+ byte[] hardwareAuthToken = new byte[token.size()];
+
+ for (int i = 0; i < token.size(); i++) {
+ hardwareAuthToken[i] = token.get(i);
+ }
+
+ if (authenticated) {
+ mAidlResponseHandler.onAuthenticationSucceeded(faceId,
+ HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken));
+ } else {
+ mAidlResponseHandler.onAuthenticationFailed();
+ }
+ }
+
+ @Override
+ public void onAcquired(long deviceId, int userId, int acquiredInfo,
+ int vendorCode) {
+ mAidlResponseHandler.onAcquired(acquiredInfo, vendorCode);
+ }
+
+ @Override
+ public void onError(long deviceId, int userId, int error, int vendorCode) {
+ mAidlResponseHandler.onError(error, vendorCode);
+ }
+
+ @Override
+ public void onRemoved(long deviceId, ArrayList<Integer> removed, int userId) {
+ int[] enrollmentIds = new int[removed.size()];
+ for (int i = 0; i < removed.size(); i++) {
+ enrollmentIds[i] = removed.get(i);
+ }
+ mAidlResponseHandler.onEnrollmentsRemoved(enrollmentIds);
+ }
+
+ @Override
+ public void onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId) {
+ int[] enrollmentIds = new int[faceIds.size()];
+ for (int i = 0; i < faceIds.size(); i++) {
+ enrollmentIds[i] = faceIds.get(i);
+ }
+ mAidlResponseHandler.onEnrollmentsEnumerated(enrollmentIds);
+ }
+
+ @Override
+ public void onLockoutChanged(long duration) {
+ mAidlResponseHandler.onLockoutChanged(duration);
+ }
+
+ void onChallengeGenerated(long challenge) {
+ mAidlResponseHandler.onChallengeGenerated(challenge);
+ }
+
+ void onChallengeRevoked(long challenge) {
+ mAidlResponseHandler.onChallengeRevoked(challenge);
+ }
+
+ void onFeatureGet(byte[] features) {
+ mAidlResponseHandler.onFeaturesRetrieved(features);
+ }
+
+ void onFeatureSet(byte feature) {
+ mAidlResponseHandler.onFeatureSet(feature);
+ }
+
+ void onAuthenticatorIdRetrieved(long authenticatorId) {
+ mAidlResponseHandler.onAuthenticatorIdRetrieved(authenticatorId);
+ }
+}
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
new file mode 100644
index 000000000000..4a019436cf6f
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java
@@ -0,0 +1,274 @@
+/*
+ * 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.aidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.biometrics.face.Error;
+import android.hardware.biometrics.fingerprint.ISessionCallback;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.keymaster.HardwareAuthToken;
+import android.util.Slog;
+
+import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.sensors.AcquisitionClient;
+import com.android.server.biometrics.sensors.AuthSessionCoordinator;
+import com.android.server.biometrics.sensors.AuthenticationConsumer;
+import com.android.server.biometrics.sensors.BaseClientMonitor;
+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.RemovalConsumer;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
+
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+
+/**
+ * Response handler for the {@link ISessionCallback} HAL AIDL interface.
+ */
+public class AidlResponseHandler extends ISessionCallback.Stub {
+
+ /**
+ * Interface to send results to the AidlResponseHandler's owner.
+ */
+ public interface HardwareUnavailableCallback {
+ /**
+ * Invoked when the HAL sends ERROR_HW_UNAVAILABLE.
+ */
+ void onHardwareUnavailable();
+ }
+
+ private static final String TAG = "AidlResponseHandler";
+
+ @NonNull
+ private final Context mContext;
+ @NonNull
+ private final BiometricScheduler mScheduler;
+ private final int mSensorId;
+ private final int mUserId;
+ @NonNull
+ private final LockoutCache mLockoutCache;
+ @NonNull
+ private final LockoutResetDispatcher mLockoutResetDispatcher;
+ @NonNull
+ private final AuthSessionCoordinator mAuthSessionCoordinator;
+ @NonNull
+ private final HardwareUnavailableCallback mHardwareUnavailableCallback;
+
+ public AidlResponseHandler(@NonNull Context context,
+ @NonNull BiometricScheduler scheduler, int sensorId, int userId,
+ @NonNull LockoutCache lockoutTracker,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull AuthSessionCoordinator authSessionCoordinator,
+ @NonNull HardwareUnavailableCallback hardwareUnavailableCallback) {
+ mContext = context;
+ mScheduler = scheduler;
+ mSensorId = sensorId;
+ mUserId = userId;
+ mLockoutCache = lockoutTracker;
+ mLockoutResetDispatcher = lockoutResetDispatcher;
+ mAuthSessionCoordinator = authSessionCoordinator;
+ mHardwareUnavailableCallback = hardwareUnavailableCallback;
+ }
+
+ @Override
+ public int getInterfaceVersion() {
+ return this.VERSION;
+ }
+
+ @Override
+ public String getInterfaceHash() {
+ return this.HASH;
+ }
+
+ @Override
+ public void onChallengeGenerated(long challenge) {
+ handleResponse(FingerprintGenerateChallengeClient.class, (c) -> c.onChallengeGenerated(
+ mSensorId, mUserId, challenge), null);
+ }
+
+ @Override
+ public void onChallengeRevoked(long challenge) {
+ handleResponse(FingerprintRevokeChallengeClient.class, (c) -> c.onChallengeRevoked(
+ challenge), null);
+ }
+
+ /**
+ * 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);
+ }
+
+ @Override
+ public void onAcquired(byte info, int vendorCode) {
+ handleResponse(AcquisitionClient.class, (c) -> c.onAcquired(
+ AidlConversionUtils.toFrameworkAcquiredInfo(info), vendorCode), null);
+ }
+
+ /**
+ * Handle error messages from the HAL.
+ */
+ public void onError(int error, int vendorCode) {
+ handleResponse(ErrorConsumer.class, (c) -> {
+ c.onError(error, vendorCode);
+ if (error == Error.HW_UNAVAILABLE) {
+ mHardwareUnavailableCallback.onHardwareUnavailable();
+ }
+ }, null);
+ }
+
+ @Override
+ public void onError(byte error, int vendorCode) {
+ onError(AidlConversionUtils.toFrameworkError(error), vendorCode);
+ }
+
+ @Override
+ public void onEnrollmentProgress(int enrollmentId, int remaining) {
+ BaseClientMonitor client = mScheduler.getCurrentClient();
+ final int currentUserId;
+ if (client == null) {
+ return;
+ } else {
+ currentUserId = client.getTargetUserId();
+ }
+ final CharSequence name = FingerprintUtils.getInstance(mSensorId)
+ .getUniqueName(mContext, currentUserId);
+ final Fingerprint fingerprint = new Fingerprint(name, currentUserId,
+ enrollmentId, mSensorId);
+ handleResponse(FingerprintEnrollClient.class, (c) -> c.onEnrollResult(fingerprint,
+ remaining), null);
+ }
+
+ @Override
+ public void onAuthenticationSucceeded(int enrollmentId, HardwareAuthToken hat) {
+ final Fingerprint fp = new Fingerprint("", enrollmentId, mSensorId);
+ final byte[] byteArray = HardwareAuthTokenUtils.toByteArray(hat);
+ final ArrayList<Byte> byteList = new ArrayList<>();
+ for (byte b : byteArray) {
+ byteList.add(b);
+ }
+ handleResponse(AuthenticationConsumer.class, (c) -> c.onAuthenticated(fp,
+ true /* authenticated */, byteList), (c) -> onInteractionDetected());
+ }
+
+ @Override
+ public void onAuthenticationFailed() {
+ final Fingerprint fp = new Fingerprint("", 0 /* enrollmentId */, mSensorId);
+ handleResponse(AuthenticationConsumer.class, (c) -> c.onAuthenticated(fp,
+ false /* authenticated */, null /* hardwareAuthToken */),
+ (c) -> onInteractionDetected());
+ }
+
+ @Override
+ public void onLockoutTimed(long durationMillis) {
+ handleResponse(LockoutConsumer.class, (c) -> c.onLockoutTimed(durationMillis),
+ null);
+ }
+
+ @Override
+ public void onLockoutPermanent() {
+ handleResponse(LockoutConsumer.class, LockoutConsumer::onLockoutPermanent, null);
+ }
+
+ @Override
+ public void onLockoutCleared() {
+ handleResponse(FingerprintResetLockoutClient.class,
+ FingerprintResetLockoutClient::onLockoutCleared,
+ (c) -> FingerprintResetLockoutClient.resetLocalLockoutStateToNone(
+ mSensorId, mUserId, mLockoutCache, mLockoutResetDispatcher,
+ mAuthSessionCoordinator, Utils.getCurrentStrength(mSensorId),
+ -1 /* requestId */));
+ }
+
+ @Override
+ public void onInteractionDetected() {
+ handleResponse(FingerprintDetectClient.class,
+ FingerprintDetectClient::onInteractionDetected, null);
+ }
+
+ @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);
+ }
+ } else {
+ handleResponse(EnumerateConsumer.class, (c) -> c.onEnumerationResult(null,
+ 0), null);
+ }
+ }
+
+ @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);
+ }
+ } else {
+ handleResponse(RemovalConsumer.class, (c) -> c.onRemoved(null, 0),
+ null);
+ }
+ }
+
+ @Override
+ public void onAuthenticatorIdRetrieved(long authenticatorId) {
+ handleResponse(FingerprintGetAuthenticatorIdClient.class,
+ (c) -> c.onAuthenticatorIdRetrieved(authenticatorId), null);
+ }
+
+ @Override
+ public void onAuthenticatorIdInvalidated(long newAuthenticatorId) {
+ handleResponse(FingerprintInvalidationClient.class, (c) -> c.onAuthenticatorIdInvalidated(
+ newAuthenticatorId), null);
+ }
+
+ private <T> void handleResponse(@NonNull Class<T> className,
+ @NonNull Consumer<T> action,
+ @Nullable Consumer<BaseClientMonitor> alternateAction) {
+ mScheduler.getHandler().post(() -> {
+ final BaseClientMonitor client = mScheduler.getCurrentClient();
+ if (className.isInstance(client)) {
+ action.accept((T) client);
+ } else {
+ Slog.e(TAG, "Client monitor is not an instance of " + className.getName());
+ if (alternateAction != null) {
+ alternateAction.accept(client);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onSessionClosed() {
+ mScheduler.getHandler().post(mScheduler::onUserStopped);
+ }
+}
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 55861bb4cc6f..299a310caee9 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
@@ -16,10 +16,13 @@
package com.android.server.biometrics.sensors.fingerprint.aidl;
-import static com.android.server.biometrics.sensors.fingerprint.aidl.Sensor.HalSessionCallback;
-
import android.annotation.NonNull;
import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+
+import com.android.server.biometrics.sensors.fingerprint.hidl.AidlToHidlAdapter;
+
+import java.util.function.Supplier;
/**
* A holder for an AIDL {@link ISession} with additional metadata about the current user
@@ -30,14 +33,22 @@ public class AidlSession {
private final int mHalInterfaceVersion;
@NonNull private final ISession mSession;
private final int mUserId;
- @NonNull private final HalSessionCallback mHalSessionCallback;
+ @NonNull private final AidlResponseHandler mAidlResponseHandler;
public AidlSession(int halInterfaceVersion, @NonNull ISession session, int userId,
- HalSessionCallback halSessionCallback) {
+ AidlResponseHandler aidlResponseHandler) {
mHalInterfaceVersion = halInterfaceVersion;
mSession = session;
mUserId = userId;
- mHalSessionCallback = halSessionCallback;
+ mAidlResponseHandler = aidlResponseHandler;
+ }
+
+ public AidlSession(@NonNull Supplier<IBiometricsFingerprint> session,
+ int userId, AidlResponseHandler aidlResponseHandler) {
+ mSession = new AidlToHidlAdapter(session, userId, aidlResponseHandler);
+ mHalInterfaceVersion = 0;
+ mUserId = userId;
+ mAidlResponseHandler = aidlResponseHandler;
}
/** The underlying {@link ISession}. */
@@ -51,8 +62,8 @@ public class AidlSession {
}
/** The HAL callback, which should only be used in tests {@See BiometricTestSessionImpl}. */
- HalSessionCallback getHalSessionCallback() {
- return mHalSessionCallback;
+ AidlResponseHandler getHalSessionCallback() {
+ return mAidlResponseHandler;
}
/**
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 54d1faa39be0..337c3c299e54 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.app.TaskStackListener;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricFingerprintConstants.FingerprintAcquired;
import android.hardware.biometrics.BiometricManager.Authenticators;
@@ -51,8 +52,8 @@ import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
-import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutConsumer;
+import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.PerformanceTracker;
import com.android.server.biometrics.sensors.SensorOverlays;
import com.android.server.biometrics.sensors.fingerprint.PowerPressHandler;
@@ -66,7 +67,7 @@ import java.util.function.Supplier;
* Fingerprint-specific authentication client supporting the {@link
* android.hardware.biometrics.fingerprint.IFingerprint} AIDL interface.
*/
-class FingerprintAuthenticationClient
+public class FingerprintAuthenticationClient
extends AuthenticationClient<AidlSession, FingerprintAuthenticateOptions>
implements Udfps, LockoutConsumer, PowerPressHandler {
private static final String TAG = "FingerprintAuthenticationClient";
@@ -93,7 +94,7 @@ class FingerprintAuthenticationClient
private Runnable mAuthSuccessRunnable;
private final Clock mClock;
- FingerprintAuthenticationClient(
+ public FingerprintAuthenticationClient(
@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon,
@NonNull IBinder token,
@@ -108,14 +109,14 @@ class FingerprintAuthenticationClient
@NonNull BiometricContext biometricContext,
boolean isStrongBiometric,
@Nullable TaskStackListener taskStackListener,
- @NonNull LockoutCache lockoutCache,
@Nullable IUdfpsOverlayController udfpsOverlayController,
@Nullable ISidefpsController sidefpsController,
boolean allowBackgroundAuthentication,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
@NonNull Handler handler,
@Authenticators.Types int biometricStrength,
- @NonNull Clock clock) {
+ @NonNull Clock clock,
+ @Nullable LockoutTracker lockoutTracker) {
super(
context,
lazyDaemon,
@@ -130,7 +131,7 @@ class FingerprintAuthenticationClient
biometricContext,
isStrongBiometric,
taskStackListener,
- null /* lockoutCache */,
+ lockoutTracker,
allowBackgroundAuthentication,
false /* shouldVibrate */,
biometricStrength);
@@ -211,6 +212,7 @@ class FingerprintAuthenticationClient
boolean authenticated,
ArrayList<Byte> token) {
super.onAuthenticated(identifier, authenticated, token);
+ handleLockout(authenticated);
if (authenticated) {
mState = STATE_STOPPED;
mSensorOverlays.hide(getSensorId());
@@ -219,6 +221,32 @@ class FingerprintAuthenticationClient
}
}
+ private void handleLockout(boolean authenticated) {
+ if (getLockoutTracker() == null) {
+ Slog.d(TAG, "Lockout is implemented by the HAL");
+ return;
+ }
+ if (authenticated) {
+ getLockoutTracker().resetFailedAttemptsForUser(true /* clearAttemptCounter */,
+ getTargetUserId());
+ } else {
+ @LockoutTracker.LockoutMode final int lockoutMode =
+ getLockoutTracker().getLockoutModeForUser(getTargetUserId());
+ if (lockoutMode != LockoutTracker.LOCKOUT_NONE) {
+ Slog.w(TAG, "Fingerprint locked out, lockoutMode(" + lockoutMode + ")");
+ final int errorCode = lockoutMode == LockoutTracker.LOCKOUT_TIMED
+ ? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
+ : BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
+ // Send the error, but do not invoke the FinishCallback yet. Since lockout is not
+ // controlled by the HAL, the framework must stop the sensor before finishing the
+ // client.
+ mSensorOverlays.hide(getSensorId());
+ onErrorInternal(errorCode, 0 /* vendorCode */, false /* finish */);
+ cancel();
+ }
+ }
+ }
+
@Override
public void onAcquired(@FingerprintAcquired int acquiredInfo, int vendorCode) {
// For UDFPS, notify SysUI with acquiredInfo, so that the illumination can be turned off
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index 4502e5d0c4b6..e2413ee1c016 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -43,7 +43,8 @@ import java.util.function.Supplier;
* Performs fingerprint detection without exposing any matching information (e.g. accept/reject
* have the same haptic, lockout counter is not increased).
*/
-class FingerprintDetectClient extends AcquisitionClient<AidlSession> implements DetectionConsumer {
+public class FingerprintDetectClient extends AcquisitionClient<AidlSession>
+ implements DetectionConsumer {
private static final String TAG = "FingerprintDetectClient";
@@ -52,7 +53,8 @@ class FingerprintDetectClient extends AcquisitionClient<AidlSession> implements
@NonNull private final SensorOverlays mSensorOverlays;
@Nullable private ICancellationSignal mCancellationSignal;
- FingerprintDetectClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
+ public FingerprintDetectClient(@NonNull Context context,
+ @NonNull Supplier<AidlSession> lazyDaemon,
@NonNull IBinder token, long requestId,
@NonNull ClientMonitorCallbackConverter listener,
@NonNull FingerprintAuthenticateOptions options,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index 46ff6b4fab1a..06550d8b4fce 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -49,14 +49,13 @@ import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
import com.android.server.biometrics.sensors.EnrollClient;
import com.android.server.biometrics.sensors.SensorOverlays;
-import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
import com.android.server.biometrics.sensors.fingerprint.PowerPressHandler;
import com.android.server.biometrics.sensors.fingerprint.Udfps;
import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
import java.util.function.Supplier;
-class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps,
+public class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps,
PowerPressHandler {
private static final String TAG = "FingerprintEnrollClient";
@@ -72,12 +71,16 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps
private static boolean shouldVibrateFor(Context context,
FingerprintSensorPropertiesInternal sensorProps) {
- final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);
- final boolean isAccessbilityEnabled = am.isTouchExplorationEnabled();
- return !sensorProps.isAnyUdfpsType() || isAccessbilityEnabled;
+ if (sensorProps != null) {
+ final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);
+ final boolean isAccessbilityEnabled = am.isTouchExplorationEnabled();
+ return !sensorProps.isAnyUdfpsType() || isAccessbilityEnabled;
+ } else {
+ return true;
+ }
}
- FingerprintEnrollClient(@NonNull Context context,
+ public FingerprintEnrollClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token, long requestId,
@NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String owner,
@@ -89,8 +92,8 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps
int maxTemplatesPerUser, @FingerprintManager.EnrollReason int enrollReason) {
// UDFPS haptics occur when an image is acquired (instead of when the result is known)
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
- 0 /* timeoutSec */, sensorId, shouldVibrateFor(context, sensorProps), logger,
- biometricContext);
+ 0 /* timeoutSec */, sensorId, shouldVibrateFor(context, sensorProps),
+ logger, biometricContext);
setRequestId(requestId);
mSensorProps = sensorProps;
mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
@@ -136,7 +139,7 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps
acquiredInfo == BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD;
// For UDFPS, notify SysUI that the illumination can be turned off.
// See AcquiredInfo#GOOD and AcquiredInfo#RETRYING_CAPTURE
- if (mSensorProps.isAnyUdfpsType()) {
+ if (mSensorProps != null && mSensorProps.isAnyUdfpsType()) {
if (acquiredGood && mShouldVibrate) {
vibrateSuccess();
}
@@ -162,8 +165,7 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps
@Override
protected boolean hasReachedEnrollmentLimit() {
- return FingerprintUtils.getInstance(getSensorId())
- .getBiometricsForUser(getContext(), getTargetUserId()).size()
+ return mBiometricUtils.getBiometricsForUser(getContext(), getTargetUserId()).size()
>= mMaxTemplatesPerUser;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
index ddae8bedf7c1..ce693ff58e19 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
@@ -33,10 +33,10 @@ import java.util.function.Supplier;
/**
* Fingerprint-specific generateChallenge client for the {@link IFingerprint} AIDL HAL interface.
*/
-class FingerprintGenerateChallengeClient extends GenerateChallengeClient<AidlSession> {
+public class FingerprintGenerateChallengeClient extends GenerateChallengeClient<AidlSession> {
private static final String TAG = "FingerprintGenerateChallengeClient";
- FingerprintGenerateChallengeClient(@NonNull Context context,
+ public FingerprintGenerateChallengeClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon,
@NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
index ff9127f516af..5edc2ca080ad 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
@@ -39,9 +39,10 @@ import java.util.function.Supplier;
* Fingerprint-specific internal cleanup client supporting the
* {@link android.hardware.biometrics.fingerprint.IFingerprint} AIDL interface.
*/
-class FingerprintInternalCleanupClient extends InternalCleanupClient<Fingerprint, AidlSession> {
+public class FingerprintInternalCleanupClient
+ extends InternalCleanupClient<Fingerprint, AidlSession> {
- FingerprintInternalCleanupClient(@NonNull Context context,
+ public FingerprintInternalCleanupClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon,
int userId, @NonNull String owner, int sensorId,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
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 e42b66472cbe..9985b06833c9 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
@@ -428,8 +428,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext,
mFingerprintSensors.get(sensorId).getLazySession(), token, id,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
- opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
- createLogger(BiometricsProtoEnums.ACTION_ENROLL,
+ opPackageName, FingerprintUtils.getInstance(sensorId),
+ sensorId, createLogger(BiometricsProtoEnums.ACTION_ENROLL,
BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext,
mFingerprintSensors.get(sensorId).getSensorProperties(),
@@ -496,12 +496,13 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
mAuthenticationStatsCollector),
mBiometricContext, isStrongBiometric,
- mTaskStackListener, mFingerprintSensors.get(sensorId).getLockoutCache(),
+ mTaskStackListener,
mUdfpsOverlayController, mSidefpsController,
allowBackgroundAuthentication,
mFingerprintSensors.get(sensorId).getSensorProperties(), mHandler,
Utils.getCurrentStrength(sensorId),
- SystemClock.elapsedRealtimeClock());
+ SystemClock.elapsedRealtimeClock(),
+ null /* lockoutTracker */);
scheduleForSensor(sensorId, client, new ClientMonitorCallback() {
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
index d559bb1d72f1..4f08f6fbde54 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
@@ -37,12 +37,12 @@ import java.util.function.Supplier;
* Fingerprint-specific removal client supporting the
* {@link android.hardware.biometrics.fingerprint.IFingerprint} interface.
*/
-class FingerprintRemovalClient extends RemovalClient<Fingerprint, AidlSession> {
+public class FingerprintRemovalClient extends RemovalClient<Fingerprint, AidlSession> {
private static final String TAG = "FingerprintRemovalClient";
private final int[] mBiometricIds;
- FingerprintRemovalClient(@NonNull Context context,
+ public FingerprintRemovalClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token,
@Nullable ClientMonitorCallbackConverter listener, int[] biometricIds, int userId,
@NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
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 7a620349075c..ec225a60d54b 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
@@ -32,7 +32,6 @@ import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ErrorConsumer;
import com.android.server.biometrics.sensors.HalClientMonitor;
-import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
@@ -43,19 +42,20 @@ import java.util.function.Supplier;
* Updates the framework's lockout cache and notifies clients such as Keyguard when lockout is
* cleared.
*/
-class FingerprintResetLockoutClient extends HalClientMonitor<AidlSession> implements ErrorConsumer {
+public class FingerprintResetLockoutClient extends HalClientMonitor<AidlSession>
+ implements ErrorConsumer {
private static final String TAG = "FingerprintResetLockoutClient";
private final HardwareAuthToken mHardwareAuthToken;
- private final LockoutCache mLockoutCache;
+ private final LockoutTracker mLockoutCache;
private final LockoutResetDispatcher mLockoutResetDispatcher;
private final int mBiometricStrength;
- FingerprintResetLockoutClient(@NonNull Context context,
+ public FingerprintResetLockoutClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, int userId, String owner, int sensorId,
@NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
- @NonNull byte[] hardwareAuthToken, @NonNull LockoutCache lockoutTracker,
+ @NonNull byte[] hardwareAuthToken, @NonNull LockoutTracker lockoutTracker,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@Authenticators.Types int biometricStrength) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
@@ -107,10 +107,11 @@ class FingerprintResetLockoutClient extends HalClientMonitor<AidlSession> implem
* be used instead.
*/
static void resetLocalLockoutStateToNone(int sensorId, int userId,
- @NonNull LockoutCache lockoutTracker,
+ @NonNull LockoutTracker lockoutTracker,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull AuthSessionCoordinator authSessionCoordinator,
@Authenticators.Types int biometricStrength, long requestId) {
+ lockoutTracker.resetFailedAttemptsForUser(true /* clearAttemptCounter */, userId);
lockoutTracker.setLockoutModeForUser(userId, LockoutTracker.LOCKOUT_NONE);
lockoutResetDispatcher.notifyLockoutResetCallbacks(sensorId);
authSessionCoordinator.resetLockoutFor(userId, biometricStrength, requestId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java
index afa62e2e3173..23d8e1e63f63 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java
@@ -32,13 +32,13 @@ import java.util.function.Supplier;
/**
* Fingerprint-specific revokeChallenge client for the {@link IFingerprint} AIDL HAL interface.
*/
-class FingerprintRevokeChallengeClient extends RevokeChallengeClient<AidlSession> {
+public class FingerprintRevokeChallengeClient extends RevokeChallengeClient<AidlSession> {
- private static final String TAG = "FingerpirntRevokeChallengeClient";
+ private static final String TAG = "FingerprintRevokeChallengeClient";
private final long mChallenge;
- FingerprintRevokeChallengeClient(@NonNull Context context,
+ public FingerprintRevokeChallengeClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token,
int userId, @NonNull String owner, int sensorId,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@@ -57,7 +57,7 @@ class FingerprintRevokeChallengeClient extends RevokeChallengeClient<AidlSession
}
}
- void onChallengeRevoked(int sensorId, int userId, long challenge) {
+ void onChallengeRevoked(long challenge) {
final boolean success = challenge == mChallenge;
mCallback.onClientFinished(FingerprintRevokeChallengeClient.this, success);
}
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 56b85ceb8e6b..893cb8f9b4fc 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
@@ -23,13 +23,9 @@ import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
-import android.hardware.biometrics.fingerprint.Error;
import android.hardware.biometrics.fingerprint.ISession;
-import android.hardware.biometrics.fingerprint.ISessionCallback;
-import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
-import android.hardware.keymaster.HardwareAuthToken;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -39,34 +35,25 @@ import android.os.UserManager;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
-import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.SensorServiceStateProto;
import com.android.server.biometrics.SensorStateProto;
import com.android.server.biometrics.UserStateProto;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
-import com.android.server.biometrics.sensors.AcquisitionClient;
-import com.android.server.biometrics.sensors.AuthSessionCoordinator;
-import com.android.server.biometrics.sensors.AuthenticationConsumer;
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.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.RemovalConsumer;
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.HashMap;
import java.util.Map;
import java.util.function.Supplier;
@@ -93,348 +80,6 @@ public class Sensor {
@Nullable AidlSession mCurrentSession;
@NonNull private final Supplier<AidlSession> mLazySession;
- @VisibleForTesting
- public static class HalSessionCallback extends ISessionCallback.Stub {
-
- /**
- * Interface to sends results to the HalSessionCallback's owner.
- */
- public interface Callback {
- /**
- * Invoked when the HAL sends ERROR_HW_UNAVAILABLE.
- */
- void onHardwareUnavailable();
- }
-
- @NonNull
- private final Context mContext;
- @NonNull
- private final Handler mHandler;
- @NonNull
- private final String mTag;
- @NonNull
- private final UserAwareBiometricScheduler mScheduler;
- private final int mSensorId;
- private final int mUserId;
- @NonNull
- private final LockoutCache mLockoutCache;
- @NonNull
- private final LockoutResetDispatcher mLockoutResetDispatcher;
- @NonNull
- private AuthSessionCoordinator mAuthSessionCoordinator;
- @NonNull
- private final Callback mCallback;
-
- HalSessionCallback(@NonNull Context context, @NonNull Handler handler, @NonNull String tag,
- @NonNull UserAwareBiometricScheduler scheduler, int sensorId, int userId,
- @NonNull LockoutCache lockoutTracker,
- @NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull AuthSessionCoordinator authSessionCoordinator,
- @NonNull Callback callback) {
- mContext = context;
- mHandler = handler;
- mTag = tag;
- mScheduler = scheduler;
- mSensorId = sensorId;
- mUserId = userId;
- mLockoutCache = lockoutTracker;
- mLockoutResetDispatcher = lockoutResetDispatcher;
- mAuthSessionCoordinator = authSessionCoordinator;
- mCallback = callback;
- }
-
- @Override
- public int getInterfaceVersion() {
- return this.VERSION;
- }
-
- @Override
- public String getInterfaceHash() {
- return this.HASH;
- }
-
- @Override
- public void onChallengeGenerated(long challenge) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FingerprintGenerateChallengeClient)) {
- Slog.e(mTag, "onChallengeGenerated for wrong client: "
- + Utils.getClientName(client));
- return;
- }
-
- final FingerprintGenerateChallengeClient generateChallengeClient =
- (FingerprintGenerateChallengeClient) client;
- generateChallengeClient.onChallengeGenerated(mSensorId, mUserId, challenge);
- });
- }
-
- @Override
- public void onChallengeRevoked(long challenge) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FingerprintRevokeChallengeClient)) {
- Slog.e(mTag, "onChallengeRevoked for wrong client: "
- + Utils.getClientName(client));
- return;
- }
-
- final FingerprintRevokeChallengeClient revokeChallengeClient =
- (FingerprintRevokeChallengeClient) client;
- revokeChallengeClient.onChallengeRevoked(mSensorId, mUserId, challenge);
- });
- }
-
- @Override
- public void onAcquired(byte info, int vendorCode) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof AcquisitionClient)) {
- Slog.e(mTag, "onAcquired for non-acquisition client: "
- + Utils.getClientName(client));
- return;
- }
-
- final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client;
- acquisitionClient.onAcquired(AidlConversionUtils.toFrameworkAcquiredInfo(info),
- vendorCode);
- });
- }
-
- @Override
- public void onError(byte error, int vendorCode) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- Slog.d(mTag, "onError"
- + ", client: " + Utils.getClientName(client)
- + ", error: " + error
- + ", vendorCode: " + vendorCode);
- if (!(client instanceof ErrorConsumer)) {
- Slog.e(mTag, "onError for non-error consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final ErrorConsumer errorConsumer = (ErrorConsumer) client;
- errorConsumer.onError(AidlConversionUtils.toFrameworkError(error), vendorCode);
-
- if (error == Error.HW_UNAVAILABLE) {
- mCallback.onHardwareUnavailable();
- }
- });
- }
-
- @Override
- public void onEnrollmentProgress(int enrollmentId, int remaining) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FingerprintEnrollClient)) {
- Slog.e(mTag, "onEnrollmentProgress for non-enroll client: "
- + Utils.getClientName(client));
- return;
- }
-
- final int currentUserId = client.getTargetUserId();
- final CharSequence name = FingerprintUtils.getInstance(mSensorId)
- .getUniqueName(mContext, currentUserId);
- final Fingerprint fingerprint = new Fingerprint(name, enrollmentId, mSensorId);
-
- final FingerprintEnrollClient enrollClient = (FingerprintEnrollClient) client;
- enrollClient.onEnrollResult(fingerprint, remaining);
- });
- }
-
- @Override
- public void onAuthenticationSucceeded(int enrollmentId, HardwareAuthToken hat) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof AuthenticationConsumer)) {
- Slog.e(mTag, "onAuthenticationSucceeded for non-authentication consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final AuthenticationConsumer authenticationConsumer =
- (AuthenticationConsumer) client;
- final Fingerprint fp = new Fingerprint("", enrollmentId, mSensorId);
- final byte[] byteArray = HardwareAuthTokenUtils.toByteArray(hat);
- final ArrayList<Byte> byteList = new ArrayList<>();
- for (byte b : byteArray) {
- byteList.add(b);
- }
-
- authenticationConsumer.onAuthenticated(fp, true /* authenticated */, byteList);
- });
- }
-
- @Override
- public void onAuthenticationFailed() {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof AuthenticationConsumer)) {
- Slog.e(mTag, "onAuthenticationFailed for non-authentication consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final AuthenticationConsumer authenticationConsumer =
- (AuthenticationConsumer) client;
- final Fingerprint fp = new Fingerprint("", 0 /* enrollmentId */, mSensorId);
- authenticationConsumer
- .onAuthenticated(fp, false /* authenticated */, null /* hat */);
- });
- }
-
- @Override
- public void onLockoutTimed(long durationMillis) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof LockoutConsumer)) {
- Slog.e(mTag, "onLockoutTimed for non-lockout consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final LockoutConsumer lockoutConsumer = (LockoutConsumer) client;
- lockoutConsumer.onLockoutTimed(durationMillis);
- });
- }
-
- @Override
- public void onLockoutPermanent() {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof LockoutConsumer)) {
- Slog.e(mTag, "onLockoutPermanent for non-lockout consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final LockoutConsumer lockoutConsumer = (LockoutConsumer) client;
- lockoutConsumer.onLockoutPermanent();
- });
- }
-
- @Override
- public void onLockoutCleared() {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FingerprintResetLockoutClient)) {
- Slog.d(mTag, "onLockoutCleared outside of resetLockout by HAL");
- // Given that onLockoutCleared() can happen at any time, and is not necessarily
- // coming from a specific client, set this to -1 to indicate it wasn't for a
- // specific request.
- FingerprintResetLockoutClient.resetLocalLockoutStateToNone(mSensorId, mUserId,
- mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
- Utils.getCurrentStrength(mSensorId), -1 /* requestId */);
- } else {
- Slog.d(mTag, "onLockoutCleared after resetLockout");
- final FingerprintResetLockoutClient resetLockoutClient =
- (FingerprintResetLockoutClient) client;
- resetLockoutClient.onLockoutCleared();
- }
- });
- }
-
- @Override
- public void onInteractionDetected() {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FingerprintDetectClient)) {
- Slog.e(mTag, "onInteractionDetected for non-detect client: "
- + Utils.getClientName(client));
- return;
- }
-
- final FingerprintDetectClient fingerprintDetectClient =
- (FingerprintDetectClient) client;
- fingerprintDetectClient.onInteractionDetected();
- });
- }
-
- @Override
- public void onEnrollmentsEnumerated(int[] enrollmentIds) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof EnumerateConsumer)) {
- Slog.e(mTag, "onEnrollmentsEnumerated for non-enumerate consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final EnumerateConsumer enumerateConsumer =
- (EnumerateConsumer) client;
- if (enrollmentIds.length > 0) {
- for (int i = 0; i < enrollmentIds.length; i++) {
- final Fingerprint fp = new Fingerprint("", enrollmentIds[i], mSensorId);
- enumerateConsumer.onEnumerationResult(fp, enrollmentIds.length - i - 1);
- }
- } else {
- enumerateConsumer.onEnumerationResult(null /* identifier */, 0);
- }
- });
- }
-
- @Override
- public void onEnrollmentsRemoved(int[] enrollmentIds) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof RemovalConsumer)) {
- Slog.e(mTag, "onRemoved for non-removal consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final RemovalConsumer removalConsumer = (RemovalConsumer) client;
- if (enrollmentIds.length > 0) {
- for (int i = 0; i < enrollmentIds.length; i++) {
- final Fingerprint fp = new Fingerprint("", enrollmentIds[i], mSensorId);
- removalConsumer.onRemoved(fp, enrollmentIds.length - i - 1);
- }
- } else {
- removalConsumer.onRemoved(null, 0);
- }
- });
- }
-
- @Override
- public void onAuthenticatorIdRetrieved(long authenticatorId) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FingerprintGetAuthenticatorIdClient)) {
- Slog.e(mTag, "onAuthenticatorIdRetrieved for wrong consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final FingerprintGetAuthenticatorIdClient getAuthenticatorIdClient =
- (FingerprintGetAuthenticatorIdClient) client;
- getAuthenticatorIdClient.onAuthenticatorIdRetrieved(authenticatorId);
- });
- }
-
- @Override
- public void onAuthenticatorIdInvalidated(long newAuthenticatorId) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FingerprintInvalidationClient)) {
- Slog.e(mTag, "onAuthenticatorIdInvalidated for wrong consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final FingerprintInvalidationClient invalidationClient =
- (FingerprintInvalidationClient) client;
- invalidationClient.onAuthenticatorIdInvalidated(newAuthenticatorId);
- });
- }
-
- @Override
- public void onSessionClosed() {
- mHandler.post(mScheduler::onUserStopped);
- }
- }
-
Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, @NonNull Context context,
@NonNull Handler handler, @NonNull FingerprintSensorPropertiesInternal sensorProperties,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@@ -466,9 +111,9 @@ public class Sensor {
public StartUserClient<?, ?> getStartUserClient(int newUserId) {
final int sensorId = mSensorProperties.sensorId;
- final HalSessionCallback resultController = new HalSessionCallback(mContext,
- mHandler, mTag, mScheduler, sensorId, newUserId, mLockoutCache,
- lockoutResetDispatcher,
+ final AidlResponseHandler resultController = new AidlResponseHandler(
+ mContext, mScheduler, sensorId, newUserId,
+ mLockoutCache, lockoutResetDispatcher,
biometricContext.getAuthSessionCoordinator(), () -> {
Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE");
mCurrentSession = null;
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/AidlToHidlAdapter.java
new file mode 100644
index 000000000000..b48d232e65af
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/AidlToHidlAdapter.java
@@ -0,0 +1,231 @@
+/*
+ * 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.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.common.OperationContext;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.biometrics.fingerprint.PointerContext;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.keymaster.HardwareAuthToken;
+import android.os.IBinder;
+import android.os.RemoteException;
+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 java.util.function.Supplier;
+
+/**
+ * Adapter to convert AIDL-specific interface {@link ISession} methods to HIDL implementation.
+ */
+public class AidlToHidlAdapter implements ISession {
+ private final String TAG = "AidlToHidlAdapter";
+ @VisibleForTesting
+ static final int ENROLL_TIMEOUT_SEC = 60;
+ @NonNull
+ private final Supplier<IBiometricsFingerprint> mSession;
+ private final int mUserId;
+ private HidlToAidlCallbackConverter mHidlToAidlCallbackConverter;
+
+ public AidlToHidlAdapter(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;
+ }
+
+ @Override
+ public void generateChallenge() throws RemoteException {
+ long challenge = mSession.get().preEnroll();
+ mHidlToAidlCallbackConverter.onChallengeGenerated(challenge);
+ }
+
+ @Override
+ public void revokeChallenge(long challenge) throws RemoteException {
+ mSession.get().postEnroll();
+ mHidlToAidlCallbackConverter.onChallengeRevoked(0L);
+ }
+
+ @Override
+ public ICancellationSignal enroll(HardwareAuthToken hat) throws RemoteException {
+ mSession.get().enroll(HardwareAuthTokenUtils.toByteArray(hat), mUserId,
+ ENROLL_TIMEOUT_SEC);
+ return new Cancellation();
+ }
+
+ @Override
+ public ICancellationSignal authenticate(long operationId) throws RemoteException {
+ mSession.get().authenticate(operationId, mUserId);
+ return new Cancellation();
+ }
+
+ @Override
+ public ICancellationSignal detectInteraction() throws RemoteException {
+ mSession.get().authenticate(0, mUserId);
+ return new Cancellation();
+ }
+
+ @Override
+ public void enumerateEnrollments() throws RemoteException {
+ mSession.get().enumerate();
+ }
+
+ @Override
+ public void removeEnrollments(int[] enrollmentIds) throws RemoteException {
+ if (enrollmentIds.length > 1) {
+ mSession.get().remove(mUserId, 0);
+ } else {
+ mSession.get().remove(mUserId, enrollmentIds[0]);
+ }
+ }
+
+ @Override
+ public void onPointerDown(int pointerId, int x, int y, float minor, float major)
+ throws RemoteException {
+ UdfpsHelper.onFingerDown(mSession.get(), x, y, minor, major);
+ }
+
+ @Override
+ public void onPointerUp(int pointerId) throws RemoteException {
+ UdfpsHelper.onFingerUp(mSession.get());
+ }
+
+ @Override
+ public void getAuthenticatorId() throws RemoteException {
+ //Unsupported in HIDL
+ }
+
+ @Override
+ public void invalidateAuthenticatorId() throws RemoteException {
+ //Unsupported in HIDL
+ }
+
+ @Override
+ public void resetLockout(HardwareAuthToken hat) throws RemoteException {
+ mHidlToAidlCallbackConverter.onResetLockout();
+ }
+
+ @Override
+ public void close() throws RemoteException {
+ //Unsupported in HIDL
+ }
+
+ @Override
+ public void onUiReady() throws RemoteException {
+ //Unsupported in HIDL
+ }
+
+ @Override
+ public ICancellationSignal authenticateWithContext(long operationId, OperationContext context)
+ throws RemoteException {
+ //Unsupported in HIDL
+ return null;
+ }
+
+ @Override
+ public ICancellationSignal enrollWithContext(HardwareAuthToken hat, OperationContext context)
+ throws RemoteException {
+ //Unsupported in HIDL
+ return null;
+ }
+
+ @Override
+ public ICancellationSignal detectInteractionWithContext(OperationContext context)
+ throws RemoteException {
+ //Unsupported in HIDL
+ return null;
+ }
+
+ @Override
+ public void onPointerDownWithContext(PointerContext context) throws RemoteException {
+ //Unsupported in HIDL
+ }
+
+ @Override
+ public void onPointerUpWithContext(PointerContext context) throws RemoteException {
+ //Unsupported in HIDL
+ }
+
+ @Override
+ public void onContextChanged(OperationContext context) throws RemoteException {
+ //Unsupported in HIDL
+ }
+
+ @Override
+ public void onPointerCancelWithContext(PointerContext context) throws RemoteException {
+ //Unsupported in HIDL
+ }
+
+ @Override
+ public void setIgnoreDisplayTouches(boolean shouldIgnore) throws RemoteException {
+ //Unsupported in HIDL
+ }
+
+ @Override
+ public int getInterfaceVersion() throws RemoteException {
+ //Unsupported in HIDL
+ return 0;
+ }
+
+ @Override
+ public String getInterfaceHash() throws RemoteException {
+ //Unsupported in HIDL
+ return null;
+ }
+
+ private class Cancellation extends ICancellationSignal.Stub {
+
+ Cancellation() {}
+ @Override
+ public void cancel() throws RemoteException {
+ try {
+ mSession.get().cancel();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when requesting cancel", e);
+ }
+ }
+
+ @Override
+ public int getInterfaceVersion() throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public String getInterfaceHash() throws RemoteException {
+ return null;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index a655f3601a07..8bfa560b8031 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -54,6 +54,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.AuthenticationStatsBroadcastReceiver;
import com.android.server.biometrics.AuthenticationStatsCollector;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.SensorServiceStateProto;
import com.android.server.biometrics.SensorStateProto;
import com.android.server.biometrics.UserStateProto;
@@ -64,6 +65,7 @@ import com.android.server.biometrics.fingerprint.PerformanceStatsProto;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AcquisitionClient;
+import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BaseClientMonitor;
@@ -74,6 +76,7 @@ import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
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.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.PerformanceTracker;
@@ -82,6 +85,8 @@ import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
import com.android.server.biometrics.sensors.fingerprint.ServiceProvider;
import com.android.server.biometrics.sensors.fingerprint.Udfps;
+import com.android.server.biometrics.sensors.fingerprint.aidl.AidlResponseHandler;
+import com.android.server.biometrics.sensors.fingerprint.aidl.AidlSession;
import org.json.JSONArray;
import org.json.JSONException;
@@ -132,6 +137,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
private final boolean mIsUdfps;
private final int mSensorId;
private final boolean mIsPowerbuttonFps;
+ private AidlSession mSession;
private final class BiometricTaskStackListener extends TaskStackListener {
@Override
@@ -413,6 +419,20 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
});
}
+ synchronized AidlSession getSession() {
+ if (mDaemon != null && mSession != null) {
+ return mSession;
+ } else {
+ return mSession = new AidlSession(this::getDaemon,
+ mCurrentUserId, new AidlResponseHandler(mContext,
+ mScheduler, mSensorId, mCurrentUserId, new LockoutCache(),
+ mLockoutResetDispatcher, new AuthSessionCoordinator(), () -> {
+ mDaemon = null;
+ mCurrentUserId = UserHandle.USER_NULL;
+ }));
+ }
+ }
+
@VisibleForTesting
synchronized IBiometricsFingerprint getDaemon() {
if (mTestHalEnabled) {
@@ -518,6 +538,10 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
boolean success) {
if (success) {
+ if (mCurrentUserId != targetUserId) {
+ // Create new session with updated user ID
+ mSession = null;
+ }
mCurrentUserId = targetUserId;
} else {
Slog.w(TAG, "Failed to change user, still: " + mCurrentUserId);
@@ -554,47 +578,116 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
// Fingerprint2.1 keeps track of lockout in the framework. Let's just do it on the handler
// thread.
mHandler.post(() -> {
- final FingerprintResetLockoutClient client = new FingerprintResetLockoutClient(mContext,
- userId, mContext.getOpPackageName(), sensorId,
- createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN,
- mAuthenticationStatsCollector),
- mBiometricContext, mLockoutTracker);
- mScheduler.scheduleClientMonitor(client);
+ if (Flags.deHidl()) {
+ scheduleResetLockoutAidl(sensorId, userId, hardwareAuthToken);
+ } else {
+ scheduleResetLockoutHidl(sensorId, userId);
+ }
});
}
+ private void scheduleResetLockoutAidl(int sensorId, int userId,
+ @Nullable byte[] hardwareAuthToken) {
+ final com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintResetLockoutClient client =
+ new com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintResetLockoutClient(
+ mContext, this::getSession, userId, mContext.getOpPackageName(),
+ sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
+ mBiometricContext, hardwareAuthToken, mLockoutTracker,
+ mLockoutResetDispatcher,
+ Utils.getCurrentStrength(sensorId));
+ mScheduler.scheduleClientMonitor(client);
+ }
+
+ private void scheduleResetLockoutHidl(int sensorId, int userId) {
+ final FingerprintResetLockoutClient client = new FingerprintResetLockoutClient(mContext,
+ userId, mContext.getOpPackageName(), sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
+ mBiometricContext, mLockoutTracker);
+ mScheduler.scheduleClientMonitor(client);
+ }
+
@Override
public void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName) {
mHandler.post(() -> {
- final FingerprintGenerateChallengeClient client =
- new FingerprintGenerateChallengeClient(mContext, mLazyDaemon, token,
- new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
- mSensorProperties.sensorId,
- createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN,
- mAuthenticationStatsCollector),
- mBiometricContext);
- mScheduler.scheduleClientMonitor(client);
+ if (Flags.deHidl()) {
+ scheduleGenerateChallengeAidl(userId, token, receiver, opPackageName);
+ } else {
+ scheduleGenerateChallengeHidl(userId, token, receiver, opPackageName);
+ }
});
}
+ private void scheduleGenerateChallengeAidl(int userId, @NonNull IBinder token,
+ @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName) {
+ final com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintGenerateChallengeClient client =
+ new com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintGenerateChallengeClient(
+ mContext, this::getSession, token,
+ new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
+ mBiometricContext);
+ mScheduler.scheduleClientMonitor(client);
+ }
+
+ private void scheduleGenerateChallengeHidl(int userId, @NonNull IBinder token,
+ @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName) {
+ final FingerprintGenerateChallengeClient client =
+ new FingerprintGenerateChallengeClient(mContext, mLazyDaemon, token,
+ new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
+ mBiometricContext);
+ mScheduler.scheduleClientMonitor(client);
+ }
+
@Override
public void scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token,
@NonNull String opPackageName, long challenge) {
mHandler.post(() -> {
- final FingerprintRevokeChallengeClient client = new FingerprintRevokeChallengeClient(
- mContext, mLazyDaemon, token, userId, opPackageName,
- mSensorProperties.sensorId,
- createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN,
- mAuthenticationStatsCollector),
- mBiometricContext);
- mScheduler.scheduleClientMonitor(client);
+ if (Flags.deHidl()) {
+ scheduleRevokeChallengeAidl(userId, token, opPackageName);
+ } else {
+ scheduleRevokeChallengeHidl(userId, token, opPackageName);
+ }
});
}
+ private void scheduleRevokeChallengeAidl(int userId, @NonNull IBinder token,
+ @NonNull String opPackageName) {
+ final com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintRevokeChallengeClient client =
+ new com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintRevokeChallengeClient(
+ mContext, this::getSession,
+ token, userId, opPackageName,
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
+ mBiometricContext, 0L);
+ mScheduler.scheduleClientMonitor(client);
+ }
+
+ private void scheduleRevokeChallengeHidl(int userId, @NonNull IBinder token,
+ @NonNull String opPackageName) {
+ final FingerprintRevokeChallengeClient client = new FingerprintRevokeChallengeClient(
+ mContext, mLazyDaemon, token, userId, opPackageName,
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
+ mBiometricContext);
+ mScheduler.scheduleClientMonitor(client);
+ }
+
@Override
public long scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId,
@@ -604,38 +697,96 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
- final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext,
- mLazyDaemon, token, id, new ClientMonitorCallbackConverter(receiver),
- userId, hardwareAuthToken, opPackageName,
- FingerprintUtils.getLegacyInstance(mSensorId), ENROLL_TIMEOUT_SEC,
- mSensorProperties.sensorId,
- createLogger(BiometricsProtoEnums.ACTION_ENROLL,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext, mUdfpsOverlayController, mSidefpsController, enrollReason);
- mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
- @Override
- public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
- mBiometricStateCallback.onClientStarted(clientMonitor);
- }
+ if (Flags.deHidl()) {
+ scheduleEnrollAidl(token, hardwareAuthToken, userId, receiver,
+ opPackageName, enrollReason, id);
+ } else {
+ scheduleEnrollHidl(token, hardwareAuthToken, userId, receiver,
+ opPackageName, enrollReason, id);
+ }
+ });
+ return id;
+ }
- @Override
- public void onBiometricAction(int action) {
- mBiometricStateCallback.onBiometricAction(action);
+ private void scheduleEnrollHidl(@NonNull IBinder token,
+ @NonNull byte[] hardwareAuthToken, int userId,
+ @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
+ @FingerprintManager.EnrollReason int enrollReason, long id) {
+ final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext,
+ mLazyDaemon, token, id, new ClientMonitorCallbackConverter(receiver),
+ userId, hardwareAuthToken, opPackageName,
+ FingerprintUtils.getLegacyInstance(mSensorId), ENROLL_TIMEOUT_SEC,
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_ENROLL,
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
+ mBiometricContext, mUdfpsOverlayController, mSidefpsController, enrollReason);
+ mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
+ @Override
+ public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
+ mBiometricStateCallback.onClientStarted(clientMonitor);
+ }
+
+ @Override
+ public void onBiometricAction(int action) {
+ mBiometricStateCallback.onBiometricAction(action);
+ }
+
+ @Override
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
+ boolean success) {
+ mBiometricStateCallback.onClientFinished(clientMonitor, success);
+ if (success) {
+ // Update authenticatorIds
+ scheduleUpdateActiveUserWithoutHandler(clientMonitor.getTargetUserId(),
+ true /* force */);
}
+ }
+ });
+ }
- @Override
- public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
- boolean success) {
- mBiometricStateCallback.onClientFinished(clientMonitor, success);
- if (success) {
- // Update authenticatorIds
- scheduleUpdateActiveUserWithoutHandler(clientMonitor.getTargetUserId(),
- true /* force */);
- }
+ private void scheduleEnrollAidl(@NonNull IBinder token,
+ @NonNull byte[] hardwareAuthToken, int userId,
+ @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
+ @FingerprintManager.EnrollReason int enrollReason, long id) {
+ final com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintEnrollClient
+ client =
+ new com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintEnrollClient(
+ mContext,
+ this::getSession, token, id,
+ new ClientMonitorCallbackConverter(receiver),
+ userId, hardwareAuthToken, opPackageName,
+ FingerprintUtils.getLegacyInstance(mSensorId),
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_ENROLL,
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
+ mBiometricContext, null /* sensorProps */,
+ mUdfpsOverlayController, mSidefpsController,
+ mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser),
+ enrollReason);
+ mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
+ @Override
+ public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
+ mBiometricStateCallback.onClientStarted(clientMonitor);
+ }
+
+ @Override
+ public void onBiometricAction(int action) {
+ mBiometricStateCallback.onBiometricAction(action);
+ }
+
+ @Override
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
+ boolean success) {
+ mBiometricStateCallback.onClientFinished(clientMonitor, success);
+ if (success) {
+ // Update authenticatorIds
+ scheduleUpdateActiveUserWithoutHandler(clientMonitor.getTargetUserId(),
+ true /* force */);
}
- });
+ }
});
- return id;
}
@Override
@@ -653,17 +804,46 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
scheduleUpdateActiveUserWithoutHandler(options.getUserId());
final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorProperties.sensorId);
- final FingerprintDetectClient client = new FingerprintDetectClient(mContext,
- mLazyDaemon, token, id, listener, options,
- createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
- mAuthenticationStatsCollector),
- mBiometricContext, mUdfpsOverlayController, isStrongBiometric);
- mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
+
+ if (Flags.deHidl()) {
+ scheduleFingerDetectAidl(token, listener, options, statsClient, id,
+ isStrongBiometric);
+ } else {
+ scheduleFingerDetectHidl(token, listener, options, statsClient, id,
+ isStrongBiometric);
+ }
});
return id;
}
+ private void scheduleFingerDetectHidl(@NonNull IBinder token,
+ @NonNull ClientMonitorCallbackConverter listener,
+ @NonNull FingerprintAuthenticateOptions options,
+ int statsClient, long id, boolean isStrongBiometric) {
+ final FingerprintDetectClient client = new FingerprintDetectClient(mContext,
+ mLazyDaemon, token, id, listener, options,
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
+ mAuthenticationStatsCollector),
+ mBiometricContext, mUdfpsOverlayController, isStrongBiometric);
+ mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
+ }
+
+ private void scheduleFingerDetectAidl(@NonNull IBinder token,
+ @NonNull ClientMonitorCallbackConverter listener,
+ @NonNull FingerprintAuthenticateOptions options,
+ int statsClient, long id, boolean isStrongBiometric) {
+ final com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintDetectClient
+ client =
+ new com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintDetectClient(
+ mContext,
+ this::getSession, token, id, listener, options,
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
+ mAuthenticationStatsCollector),
+ mBiometricContext, mUdfpsOverlayController, isStrongBiometric);
+ mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
+ }
+
@Override
public void scheduleAuthenticate(@NonNull IBinder token, long operationId,
int cookie, @NonNull ClientMonitorCallbackConverter listener,
@@ -674,20 +854,55 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
scheduleUpdateActiveUserWithoutHandler(options.getUserId());
final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorProperties.sensorId);
- final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
- mContext, mLazyDaemon, token, requestId, listener, operationId,
- restricted, options, cookie, false /* requireConfirmation */,
- createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
- mAuthenticationStatsCollector),
- mBiometricContext, isStrongBiometric,
- mTaskStackListener, mLockoutTracker,
- mUdfpsOverlayController, mSidefpsController,
- allowBackgroundAuthentication, mSensorProperties,
- Utils.getCurrentStrength(mSensorId));
- mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
+
+ if (Flags.deHidl()) {
+ scheduleAuthenticateAidl(token, operationId, cookie, listener, options, requestId,
+ restricted, statsClient, allowBackgroundAuthentication, isStrongBiometric);
+ } else {
+ scheduleAuthenticateHidl(token, operationId, cookie, listener, options, requestId,
+ restricted, statsClient, allowBackgroundAuthentication, isStrongBiometric);
+ }
});
}
+ private void scheduleAuthenticateAidl(@NonNull IBinder token, long operationId,
+ int cookie, @NonNull ClientMonitorCallbackConverter listener,
+ @NonNull FingerprintAuthenticateOptions options,
+ long requestId, boolean restricted, int statsClient,
+ boolean allowBackgroundAuthentication, boolean isStrongBiometric) {
+ final com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintAuthenticationClient
+ client =
+ new com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintAuthenticationClient(
+ mContext, this::getSession, token, requestId, listener, operationId,
+ restricted, options, cookie, false /* requireConfirmation */,
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
+ mAuthenticationStatsCollector),
+ mBiometricContext, isStrongBiometric,
+ mTaskStackListener,
+ mUdfpsOverlayController, mSidefpsController,
+ allowBackgroundAuthentication, mSensorProperties, mHandler,
+ Utils.getCurrentStrength(mSensorId), null /* clock */, mLockoutTracker);
+ mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
+ }
+
+ private void scheduleAuthenticateHidl(@NonNull IBinder token, long operationId,
+ int cookie, @NonNull ClientMonitorCallbackConverter listener,
+ @NonNull FingerprintAuthenticateOptions options,
+ long requestId, boolean restricted, int statsClient,
+ boolean allowBackgroundAuthentication, boolean isStrongBiometric) {
+ final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
+ mContext, mLazyDaemon, token, requestId, listener, operationId,
+ restricted, options, cookie, false /* requireConfirmation */,
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
+ mAuthenticationStatsCollector),
+ mBiometricContext, isStrongBiometric,
+ mTaskStackListener, mLockoutTracker,
+ mUdfpsOverlayController, mSidefpsController,
+ allowBackgroundAuthentication, mSensorProperties,
+ Utils.getCurrentStrength(mSensorId));
+ mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
+ }
+
@Override
public long scheduleAuthenticate(@NonNull IBinder token, long operationId,
int cookie, @NonNull ClientMonitorCallbackConverter listener,
@@ -719,17 +934,41 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
- final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext,
- mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), fingerId,
- userId, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId),
- mSensorProperties.sensorId,
- createLogger(BiometricsProtoEnums.ACTION_REMOVE,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext, mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
+ if (Flags.deHidl()) {
+ scheduleRemoveAidl(token, receiver, fingerId, userId, opPackageName);
+ } else {
+ scheduleRemoveHidl(token, receiver, fingerId, userId, opPackageName);
+ }
});
}
+ private void scheduleRemoveHidl(@NonNull IBinder token,
+ @NonNull IFingerprintServiceReceiver receiver, int fingerId, int userId,
+ @NonNull String opPackageName) {
+ final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext,
+ mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), fingerId,
+ userId, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId),
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_REMOVE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
+ mBiometricContext, mAuthenticatorIds);
+ mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
+ }
+
+ private void scheduleRemoveAidl(@NonNull IBinder token,
+ @NonNull IFingerprintServiceReceiver receiver, int fingerId, int userId,
+ @NonNull String opPackageName) {
+ final com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintRemovalClient client =
+ new com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintRemovalClient(
+ mContext, this::getSession, token,
+ new ClientMonitorCallbackConverter(receiver), new int[]{fingerId}, userId,
+ opPackageName, FingerprintUtils.getLegacyInstance(mSensorId), mSensorId,
+ createLogger(BiometricsProtoEnums.ACTION_REMOVE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
+ mBiometricContext, mAuthenticatorIds);
+ mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
+ }
+
@Override
public void scheduleRemoveAll(int sensorId, @NonNull IBinder token,
@NonNull IFingerprintServiceReceiver receiver, int userId,
@@ -738,15 +977,11 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
scheduleUpdateActiveUserWithoutHandler(userId);
// For IBiometricsFingerprint@2.1, remove(0) means remove all enrollments
- final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext,
- mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver),
- 0 /* fingerprintId */, userId, opPackageName,
- FingerprintUtils.getLegacyInstance(mSensorId),
- mSensorProperties.sensorId,
- createLogger(BiometricsProtoEnums.ACTION_REMOVE,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext, mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
+ if (Flags.deHidl()) {
+ scheduleRemoveAidl(token, receiver, 0 /* fingerId */, userId, opPackageName);
+ } else {
+ scheduleRemoveHidl(token, receiver, 0 /* fingerId */, userId, opPackageName);
+ }
});
}
@@ -755,17 +990,41 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
- final FingerprintInternalCleanupClient client = new FingerprintInternalCleanupClient(
- mContext, mLazyDaemon, userId, mContext.getOpPackageName(),
- mSensorProperties.sensorId,
- createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext,
- FingerprintUtils.getLegacyInstance(mSensorId), mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client, callback);
+ if (Flags.deHidl()) {
+ scheduleInternalCleanupAidl(userId, callback);
+ } else {
+ scheduleInternalCleanupHidl(userId, callback);
+ }
});
}
+ private void scheduleInternalCleanupHidl(int userId,
+ @Nullable ClientMonitorCallback callback) {
+ final FingerprintInternalCleanupClient client = new FingerprintInternalCleanupClient(
+ mContext, mLazyDaemon, userId, mContext.getOpPackageName(),
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
+ mBiometricContext,
+ FingerprintUtils.getLegacyInstance(mSensorId), mAuthenticatorIds);
+ mScheduler.scheduleClientMonitor(client, callback);
+ }
+
+ private void scheduleInternalCleanupAidl(int userId,
+ @Nullable ClientMonitorCallback callback) {
+ final com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintInternalCleanupClient
+ client =
+ new com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintInternalCleanupClient(
+ mContext, this::getSession, userId, mContext.getOpPackageName(),
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
+ mBiometricContext,
+ FingerprintUtils.getLegacyInstance(mSensorId), mAuthenticatorIds);
+ mScheduler.scheduleClientMonitor(client, callback);
+ }
+
@Override
public void scheduleInternalCleanup(int sensorId, int userId,
@Nullable ClientMonitorCallback callback) {
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
new file mode 100644
index 000000000000..c3e5cbe7daf4
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlCallbackConverter.java
@@ -0,0 +1,95 @@
+/*
+ * 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.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprintClientCallback;
+
+import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.sensors.fingerprint.aidl.AidlResponseHandler;
+
+import java.util.ArrayList;
+
+/**
+ * Convert HIDL-specific callback interface {@link IBiometricsFingerprintClientCallback} to AIDL
+ * response handler.
+ */
+public class HidlToAidlCallbackConverter extends IBiometricsFingerprintClientCallback.Stub {
+
+ final AidlResponseHandler mAidlResponseHandler;
+
+ public HidlToAidlCallbackConverter(@NonNull AidlResponseHandler aidlResponseHandler) {
+ mAidlResponseHandler = aidlResponseHandler;
+ }
+
+ @Override
+ public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
+ mAidlResponseHandler.onEnrollmentProgress(fingerId, remaining);
+ }
+
+ @Override
+ public void onAcquired(long deviceId, int acquiredInfo, int vendorCode) {
+ onAcquired_2_2(deviceId, acquiredInfo, vendorCode);
+ }
+
+ @Override
+ public void onAcquired_2_2(long deviceId, int acquiredInfo, int vendorCode) {
+ mAidlResponseHandler.onAcquired(acquiredInfo, vendorCode);
+ }
+
+ @Override
+ public void onAuthenticated(long deviceId, int fingerId, int groupId,
+ ArrayList<Byte> token) {
+ if (fingerId != 0) {
+ byte[] hardwareAuthToken = new byte[token.size()];
+ for (int i = 0; i < token.size(); i++) {
+ hardwareAuthToken[i] = token.get(i);
+ }
+ mAidlResponseHandler.onAuthenticationSucceeded(fingerId,
+ HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken));
+ } else {
+ mAidlResponseHandler.onAuthenticationFailed();
+ }
+ }
+
+ @Override
+ public void onError(long deviceId, int error, int vendorCode) {
+ mAidlResponseHandler.onError(error, vendorCode);
+ }
+
+ @Override
+ public void onRemoved(long deviceId, int fingerId, int groupId, int remaining) {
+ mAidlResponseHandler.onEnrollmentsRemoved(new int[]{fingerId});
+ }
+
+ @Override
+ public void onEnumerate(long deviceId, int fingerId, int groupId, int remaining) {
+ mAidlResponseHandler.onEnrollmentsEnumerated(new int[]{fingerId});
+ }
+
+ void onChallengeGenerated(long challenge) {
+ mAidlResponseHandler.onChallengeGenerated(challenge);
+ }
+
+ void onChallengeRevoked(long challenge) {
+ mAidlResponseHandler.onChallengeRevoked(challenge);
+ }
+
+ void onResetLockout() {
+ mAidlResponseHandler.onLockoutCleared();
+ }
+}
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 36d56c8a1544..0730c672acd9 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
@@ -89,7 +89,8 @@ public class LockoutFrameworkImpl implements LockoutTracker {
// Attempt counter should only be cleared when Keyguard goes away or when
// a biometric is successfully authenticated. Lockout should eventually be done below the HAL.
// See AuthenticationClient#shouldFrameworkHandleLockout().
- void resetFailedAttemptsForUser(boolean clearAttemptCounter, int userId) {
+ @Override
+ public void resetFailedAttemptsForUser(boolean clearAttemptCounter, int userId) {
if (getLockoutModeForUser(userId) != LOCKOUT_NONE) {
Slog.v(TAG, "Reset biometric lockout for user: " + userId
+ ", clearAttemptCounter: " + clearAttemptCounter);
@@ -104,7 +105,8 @@ public class LockoutFrameworkImpl implements LockoutTracker {
mLockoutResetCallback.onLockoutReset(userId);
}
- void addFailedAttemptForUser(int userId) {
+ @Override
+ public void addFailedAttemptForUser(int userId) {
mFailedAttempts.put(userId, mFailedAttempts.get(userId, 0) + 1);
mTimedLockoutCleared.put(userId, false);
@@ -114,7 +116,8 @@ public class LockoutFrameworkImpl implements LockoutTracker {
}
@Override
- public @LockoutMode int getLockoutModeForUser(int userId) {
+ @LockoutMode
+ public int getLockoutModeForUser(int userId) {
final int failedAttempts = mFailedAttempts.get(userId, 0);
if (failedAttempts >= MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT) {
return LOCKOUT_PERMANENT;
@@ -126,6 +129,19 @@ public class LockoutFrameworkImpl implements LockoutTracker {
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);
+ }
+
private void cancelLockoutResetForUser(int userId) {
mAlarmManager.cancel(getLockoutResetIntentForUser(userId));
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
index 9e5a0479ea70..3a3dd6ea2746 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
@@ -36,6 +36,7 @@ import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.content.ComponentName;
+import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.common.AuthenticateReason;
import android.hardware.biometrics.common.ICancellationSignal;
@@ -59,6 +60,7 @@ import com.android.server.biometrics.log.OperationContextExt;
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.face.UsageStats;
import org.junit.Before;
@@ -103,7 +105,7 @@ public class FaceAuthenticationClientTest {
@Mock
private ClientMonitorCallback mCallback;
@Mock
- private Sensor.HalSessionCallback mHalSessionCallback;
+ private AidlResponseHandler mAidlResponseHandler;
@Mock
private ActivityTaskManager mActivityTaskManager;
@Mock
@@ -112,6 +114,8 @@ public class FaceAuthenticationClientTest {
private AuthSessionCoordinator mAuthSessionCoordinator;
@Mock
private BiometricManager mBiometricManager;
+ @Mock
+ private LockoutTracker mLockoutTracker;
@Captor
private ArgumentCaptor<OperationContextExt> mOperationContextCaptor;
@Captor
@@ -236,27 +240,63 @@ public class FaceAuthenticationClientTest {
verify(mCallback).onClientFinished(client, true);
}
+ @Test
+ public void authWithNoLockout() throws RemoteException {
+ when(mLockoutTracker.getLockoutModeForUser(anyInt())).thenReturn(
+ LockoutTracker.LOCKOUT_NONE);
+
+ final FaceAuthenticationClient client = createClientWithLockoutTracker(mLockoutTracker);
+ client.start(mCallback);
+
+ verify(mHal).authenticate(OP_ID);
+ }
+
+ @Test
+ public void authWithLockout() throws RemoteException {
+ when(mLockoutTracker.getLockoutModeForUser(anyInt())).thenReturn(
+ LockoutTracker.LOCKOUT_PERMANENT);
+
+ final FaceAuthenticationClient client = createClientWithLockoutTracker(mLockoutTracker);
+ client.start(mCallback);
+
+ verify(mClientMonitorCallbackConverter).onError(anyInt(), anyInt(),
+ eq(BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT), anyInt());
+ verify(mHal, never()).authenticate(anyInt());
+ }
+
private FaceAuthenticationClient createClient() throws RemoteException {
return createClient(2 /* version */, mClientMonitorCallbackConverter,
- false /* allowBackgroundAuthentication */);
+ false /* allowBackgroundAuthentication */,
+ null /* lockoutTracker */);
}
private FaceAuthenticationClient createClientWithNullListener() throws RemoteException {
return createClient(2 /* version */, null /* listener */,
- true /* allowBackgroundAuthentication */);
+ true /* allowBackgroundAuthentication */,
+ null /* lockoutTracker */);
}
private FaceAuthenticationClient createClient(int version) throws RemoteException {
return createClient(version, mClientMonitorCallbackConverter,
- false /* allowBackgroundAuthentication */);
+ false /* allowBackgroundAuthentication */,
+ null /* lockoutTracker */);
+ }
+
+ private FaceAuthenticationClient createClientWithLockoutTracker(LockoutTracker lockoutTracker)
+ throws RemoteException {
+ return createClient(0 /* version */,
+ mClientMonitorCallbackConverter,
+ true /* allowBackgroundAuthentication */,
+ lockoutTracker);
}
private FaceAuthenticationClient createClient(int version,
ClientMonitorCallbackConverter listener,
- boolean allowBackgroundAuthentication) throws RemoteException {
+ boolean allowBackgroundAuthentication,
+ LockoutTracker lockoutTracker) throws RemoteException {
when(mHal.getInterfaceVersion()).thenReturn(version);
- final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+ final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mAidlResponseHandler);
final FaceAuthenticateOptions options = new FaceAuthenticateOptions.Builder()
.setOpPackageName("test-owner")
.setUserId(USER_ID)
@@ -270,7 +310,7 @@ public class FaceAuthenticationClientTest {
false /* restricted */, options, 4 /* cookie */,
false /* requireConfirmation */,
mBiometricLogger, mBiometricContext, true /* isStrongBiometric */,
- mUsageStats, null /* mLockoutCache */, allowBackgroundAuthentication,
+ mUsageStats, lockoutTracker, allowBackgroundAuthentication,
null /* sensorPrivacyManager */, 0 /* biometricStrength */) {
@Override
protected ActivityTaskManager getActivityTaskManager() {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
index ade3e8275157..fbf0e13c2ac9 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
@@ -87,7 +87,7 @@ public class FaceDetectClientTest {
@Mock
private ClientMonitorCallback mCallback;
@Mock
- private Sensor.HalSessionCallback mHalSessionCallback;
+ private AidlResponseHandler mAidlResponseHandler;
@Captor
private ArgumentCaptor<OperationContextExt> mOperationContextCaptor;
@Captor
@@ -170,7 +170,7 @@ public class FaceDetectClientTest {
private FaceDetectClient createClient(int version) throws RemoteException {
when(mHal.getInterfaceVersion()).thenReturn(version);
- final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+ final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mAidlResponseHandler);
return new FaceDetectClient(mContext, () -> aidl, mToken,
99 /* requestId */, mClientMonitorCallbackConverter,
new FaceAuthenticateOptions.Builder()
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
index 54d116f07805..128f3149e6d4 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
@@ -83,7 +83,7 @@ public class FaceEnrollClientTest {
@Mock
private ClientMonitorCallback mCallback;
@Mock
- private Sensor.HalSessionCallback mHalSessionCallback;
+ private AidlResponseHandler mAidlResponseHandler;
@Captor
private ArgumentCaptor<OperationContextExt> mOperationContextCaptor;
@Captor
@@ -150,7 +150,7 @@ public class FaceEnrollClientTest {
private FaceEnrollClient createClient(int version) throws RemoteException {
when(mHal.getInterfaceVersion()).thenReturn(version);
- final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+ final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mAidlResponseHandler);
return new FaceEnrollClient(mContext, () -> aidl, mToken, mClientMonitorCallbackConverter,
USER_ID, HAT, "com.foo.bar", 44 /* requestId */,
mUtils, new int[0] /* disabledFeatures */, 6 /* timeoutSec */,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClientTest.java
new file mode 100644
index 000000000000..c8bfaa90d863
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClientTest.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 com.android.server.biometrics.sensors.face.aidl;
+
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.biometrics.face.ISession;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.test.suitebuilder.annotation.SmallTest;
+
+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.ClientMonitorCallbackConverter;
+
+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 FaceGenerateChallengeClientTest {
+ private static final String TAG = "FaceGenerateChallengeClientTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+ private static final long CHALLENGE = 200;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private AidlSession mAidlSession;
+ @Mock
+ private ISession mSession;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallbackConverter mListener;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ private Context mContext;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+
+ private FaceGenerateChallengeClient mClient;
+
+ @Before
+ public void setUp() throws RemoteException {
+ when(mAidlSession.getSession()).thenReturn(mSession);
+ doAnswer(invocation -> {
+ mClient.onChallengeGenerated(SENSOR_ID, USER_ID, CHALLENGE);
+ return null;
+ }).when(mSession).generateChallenge();
+ }
+
+ @Test
+ public void generateChallenge() throws RemoteException {
+ createClient(mListener);
+ mClient.start(mCallback);
+
+ verify(mListener).onChallengeGenerated(SENSOR_ID, USER_ID, CHALLENGE);
+ verify(mCallback).onClientFinished(mClient, true);
+ }
+
+ @Test
+ public void generateChallenge_nullListener() {
+ createClient(null);
+ mClient.start(mCallback);
+
+ verify(mCallback).onClientFinished(mClient, false);
+ }
+
+ private void createClient(ClientMonitorCallbackConverter listener) {
+ mClient = new FaceGenerateChallengeClient(mContext, () -> mAidlSession, mToken, listener,
+ USER_ID, TAG, SENSOR_ID, mBiometricLogger, mBiometricContext);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClientTest.java
new file mode 100644
index 000000000000..9d0c84edb366
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClientTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.aidl;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.BiometricFaceConstants;
+import android.hardware.biometrics.face.ISession;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.TestableContext;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+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.ClientMonitorCallbackConverter;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.List;
+
+@Presubmit
+@SmallTest
+public class FaceGetFeatureClientTest {
+ private static final String TAG = "FaceGetFeatureClientTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private AidlSession mAidlSession;
+ @Mock
+ private ISession mSession;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallbackConverter mListener;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getContext());
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+
+ private final int mFeature = BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION;
+ private FaceGetFeatureClient mClient;
+
+ @Before
+ public void setUp() throws RemoteException {
+ mClient = new FaceGetFeatureClient(mContext, () -> mAidlSession, mToken, mListener,
+ USER_ID, TAG, SENSOR_ID, mBiometricLogger, mBiometricContext, mFeature);
+
+ when(mAidlSession.getSession()).thenReturn(mSession);
+ doAnswer(invocation -> {
+ mClient.onFeatureGet(true, new byte[]{
+ AidlConversionUtils.convertFrameworkToAidlFeature(mFeature)});
+ return null;
+ }).when(mSession).getFeatures();
+ }
+
+ @Test
+ public void getFeature() throws RemoteException {
+ ArgumentCaptor<int[]> featuresToSend = ArgumentCaptor.forClass(int[].class);
+ ArgumentCaptor<boolean[]> featureState = ArgumentCaptor.forClass(boolean[].class);
+ mClient.start(mCallback);
+
+ verify(mListener).onFeatureGet(eq(true), featuresToSend.capture(),
+ featureState.capture());
+ assertThat(featuresToSend.getValue()).asList().containsExactlyElementsIn(List.of(mFeature));
+ assertThat(featureState.getValue()).asList().containsExactlyElementsIn(List.of(true));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClientTest.java
new file mode 100644
index 000000000000..1b4c01723027
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClientTest.java
@@ -0,0 +1,187 @@
+/*
+ * 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.aidl;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.face.ISession;
+import android.hardware.face.Face;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.annotation.NonNull;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Presubmit
+@SmallTest
+public class FaceInternalCleanupClientTest {
+ private static final String TAG = "FaceInternalCleanupClientTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private AidlSession mAidlSession;
+ @Mock
+ private ISession mSession;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ Context mContext;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ @Mock
+ private BiometricUtils<Face> mBiometricUtils;
+ @Mock
+ private Map<Integer, Long> mAuthenticatorIds;
+
+ private final List<Face> mEnrolledList = new ArrayList<>();
+ private final int mBiometricId = 1;
+ private final Face mFace = new Face("face", mBiometricId, 1 /* deviceId */);
+ private FaceInternalCleanupClient mClient;
+ private List<Integer> mAddedIds;
+
+ @Before
+ public void setUp() throws RemoteException {
+ when(mAidlSession.getSession()).thenReturn(mSession);
+
+ mEnrolledList.add(mFace);
+ mAddedIds = new ArrayList<>();
+ mClient = new FaceInternalCleanupClient(mContext, () -> mAidlSession, USER_ID, TAG,
+ SENSOR_ID, mBiometricLogger, mBiometricContext, mBiometricUtils,
+ mAuthenticatorIds) {
+ @Override
+ protected void onAddUnknownTemplate(int userId,
+ @NonNull BiometricAuthenticator.Identifier identifier) {
+ mAddedIds.add(identifier.getBiometricId());
+ }
+ };
+ }
+
+ @Test
+ public void removesUnknownTemplate() throws Exception {
+ final List<Face> templates = List.of(
+ new Face("one", 1, 1),
+ new Face("two", 2, 1)
+ );
+ mClient.start(mCallback);
+ for (int i = templates.size() - 1; i >= 0; i--) {
+ mClient.getCurrentEnumerateClient().onEnumerationResult(templates.get(i), i);
+ }
+ for (int i = templates.size() - 1; i >= 0; i--) {
+ mClient.getCurrentRemoveClient().onRemoved(templates.get(i), 0);
+ }
+
+ assertThat(mAddedIds).isEmpty();
+ final ArgumentCaptor<int[]> captor = ArgumentCaptor.forClass(int[].class);
+
+ verify(mSession, times(2)).removeEnrollments(captor.capture());
+ assertThat(captor.getAllValues().stream()
+ .flatMap(x -> Arrays.stream(x).boxed())
+ .collect(Collectors.toList()))
+ .containsExactly(1, 2);
+ verify(mCallback).onClientFinished(eq(mClient), eq(true));
+ }
+
+ @Test
+ public void addsUnknownTemplateWhenVirtualIsEnabled() throws Exception {
+ mClient.setFavorHalEnrollments();
+ final List<Face> templates = List.of(
+ new Face("one", 1, 1),
+ new Face("two", 2, 1)
+ );
+ mClient.start(mCallback);
+ for (int i = templates.size() - 1; i >= 0; i--) {
+ mClient.getCurrentEnumerateClient().onEnumerationResult(templates.get(i), i);
+ }
+
+ assertThat(mAddedIds).containsExactly(1, 2);
+ verify(mSession, never()).removeEnrollments(any());
+ verify(mCallback).onClientFinished(eq(mClient), eq(true));
+ }
+
+ @Test
+ public void cleanupUnknownHalTemplatesAfterEnumerationWhenVirtualIsDisabled() {
+ final List<Face> templates = List.of(
+ new Face("one", 1, 1),
+ new Face("two", 2, 1),
+ new Face("three", 3, 1)
+ );
+ mClient.start(mCallback);
+ for (int i = templates.size() - 1; i >= 0; i--) {
+ mClient.getCurrentEnumerateClient().onEnumerationResult(templates.get(i), i);
+ }
+
+ // The first template is removed after enumeration
+ assertThat(mClient.getUnknownHALTemplates().size()).isEqualTo(2);
+
+ // Simulate finishing the removal of the first template.
+ // |remaining| is 0 because one FaceRemovalClient is associated with only one
+ // biometrics ID.
+ mClient.getCurrentRemoveClient().onRemoved(templates.get(0), 0);
+
+ assertThat(mClient.getUnknownHALTemplates().size()).isEqualTo(1);
+
+ // Simulate finishing the removal of the second template.
+ mClient.getCurrentRemoveClient().onRemoved(templates.get(1), 0);
+
+ assertThat(mClient.getUnknownHALTemplates()).isEmpty();
+ }
+
+ @Test
+ public void noUnknownTemplates() throws RemoteException {
+ mClient.start(mCallback);
+ mClient.getCurrentEnumerateClient().onEnumerationResult(null, 0);
+
+ verify(mSession).enumerateEnrollments();
+ assertThat(mClient.getUnknownHALTemplates().size()).isEqualTo(0);
+ verify(mSession, never()).removeEnrollments(any());
+ verify(mCallback).onClientFinished(mClient, true);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClientTest.java
new file mode 100644
index 000000000000..8d74fd1d56be
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClientTest.java
@@ -0,0 +1,169 @@
+/*
+ * 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.aidl;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+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.content.Context;
+import android.hardware.biometrics.face.ISession;
+import android.hardware.face.Face;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+
+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.ArrayList;
+import java.util.List;
+
+@Presubmit
+@SmallTest
+public class FaceInternalEnumerateClientTest {
+ private static final String TAG = "FaceInternalEnumerateClientTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private AidlSession mAidlSession;
+ @Mock
+ private ISession mSession;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ Context mContext;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ @Mock
+ private BiometricUtils<Face> mBiometricUtils;
+
+ private final int mBiometricId = 1;
+ private final Face mFace = new Face("face", mBiometricId, 1 /* deviceId */);
+ private FaceInternalEnumerateClient mClient;
+
+ @Before
+ public void setUp() {
+ when(mAidlSession.getSession()).thenReturn(mSession);
+
+ final List<Face> enrolled = new ArrayList<>();
+ enrolled.add(mFace);
+ mClient = new FaceInternalEnumerateClient(mContext, () -> mAidlSession, mToken, USER_ID,
+ TAG, enrolled, mBiometricUtils, SENSOR_ID, mBiometricLogger, mBiometricContext);
+ }
+
+ @Test
+ public void internalCleanupClient_noTemplatesRemaining() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onEnumerationResult(mFace, 0);
+ return null;
+ }).when(mSession).enumerateEnrollments();
+
+ mClient.start(mCallback);
+
+ verify(mSession).enumerateEnrollments();
+ assertThat(mClient.getUnknownHALTemplates().size()).isEqualTo(0);
+ verify(mBiometricUtils, never()).removeBiometricForUser(any(), anyInt(), anyInt());
+ verify(mCallback).onClientFinished(mClient, true);
+ }
+
+ @Test
+ public void internalCleanupClient_nullIdentifier_remainingOne() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onEnumerationResult(null, 1);
+ return null;
+ }).when(mSession).enumerateEnrollments();
+
+ mClient.start(mCallback);
+
+ verify(mSession).enumerateEnrollments();
+ assertThat(mClient.getUnknownHALTemplates().size()).isEqualTo(0);
+ verify(mBiometricUtils, never()).removeBiometricForUser(any(), anyInt(), anyInt());
+ verify(mCallback, never()).onClientFinished(mClient, true);
+ }
+
+ @Test
+ public void internalCleanupClient_nullIdentifier_noTemplatesRemaining() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onEnumerationResult(null, 0);
+ return null;
+ }).when(mSession).enumerateEnrollments();
+
+ mClient.start(mCallback);
+
+ verify(mSession).enumerateEnrollments();
+ assertThat(mClient.getUnknownHALTemplates().size()).isEqualTo(0);
+ verify(mBiometricUtils).removeBiometricForUser(mContext, USER_ID, mBiometricId);
+ verify(mCallback).onClientFinished(mClient, true);
+ }
+
+ @Test
+ public void internalCleanupClient_templatesRemaining() throws RemoteException {
+ final Face identifier = new Face("face", 2, 1);
+ doAnswer(invocation -> {
+ mClient.onEnumerationResult(identifier, 1);
+ return null;
+ }).when(mSession).enumerateEnrollments();
+
+ mClient.start(mCallback);
+
+ verify(mSession).enumerateEnrollments();
+ assertThat(mClient.getUnknownHALTemplates().size()).isEqualTo(1);
+ verify(mBiometricUtils, never()).removeBiometricForUser(any(), anyInt(), anyInt());
+ verify(mCallback, never()).onClientFinished(mClient, true);
+ }
+
+ @Test
+ public void internalCleanupClient_differentIdentifier_noTemplatesRemaining()
+ throws RemoteException {
+ final Face identifier = new Face("face", 2, 1);
+ doAnswer(invocation -> {
+ mClient.onEnumerationResult(identifier, 0);
+ return null;
+ }).when(mSession).enumerateEnrollments();
+
+ mClient.start(mCallback);
+
+ verify(mSession).enumerateEnrollments();
+ assertThat(mClient.getUnknownHALTemplates().size()).isEqualTo(1);
+ verify(mBiometricUtils).removeBiometricForUser(mContext, USER_ID, mBiometricId);
+ verify(mCallback).onClientFinished(mClient, true);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClientTest.java
index 76a5accc5fe9..1d9e9334f043 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClientTest.java
@@ -76,7 +76,7 @@ public class FaceRemovalClientTest {
@Mock
private ClientMonitorCallback mCallback;
@Mock
- private Sensor.HalSessionCallback mHalSessionCallback;
+ private AidlResponseHandler mAidlResponseHandler;
@Mock
private BiometricUtils<Face> mUtils;
@Mock
@@ -115,7 +115,7 @@ public class FaceRemovalClientTest {
private FaceRemovalClient createClient(int version, int[] biometricIds) throws RemoteException {
when(mHal.getInterfaceVersion()).thenReturn(version);
- final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+ final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mAidlResponseHandler);
return new FaceRemovalClient(mContext, () -> aidl, mToken,
mClientMonitorCallbackConverter, biometricIds, USER_ID,
"own-it", mUtils /* utils */, 5 /* sensorId */, mBiometricLogger, mBiometricContext,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClientTest.java
new file mode 100644
index 000000000000..dbbd69bdd3b5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClientTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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.aidl;
+
+import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG;
+
+import static org.mockito.ArgumentMatchers.any;
+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.content.Context;
+import android.hardware.biometrics.face.ISession;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.test.suitebuilder.annotation.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.ClientMonitorCallback;
+import com.android.server.biometrics.sensors.LockoutCache;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
+
+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 FaceResetLockoutClientTest {
+ private static final String TAG = "FaceResetLockoutClientTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private AidlSession mAidlSession;
+ @Mock
+ private ISession mSession;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ Context mContext;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ private final byte[] mHardwareAuthToken = new byte[69];
+ @Mock
+ private LockoutCache mLockoutTracker;
+ @Mock
+ private LockoutResetDispatcher mLockoutResetDispatcher;
+ @Mock
+ private AuthSessionCoordinator mAuthSessionCoordinator;
+
+ private FaceResetLockoutClient mClient;
+
+ @Before
+ public void setUp() {
+ mClient = new FaceResetLockoutClient(mContext, () -> mAidlSession, USER_ID, TAG, SENSOR_ID,
+ mBiometricLogger, mBiometricContext, mHardwareAuthToken, mLockoutTracker,
+ mLockoutResetDispatcher, BIOMETRIC_STRONG);
+
+ when(mAidlSession.getSession()).thenReturn(mSession);
+ when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator);
+ }
+
+ @Test
+ public void testResetLockout_onLockoutCleared() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onLockoutCleared();
+ return null;
+ }).when(mSession).resetLockout(any());
+ mClient.start(mCallback);
+
+ verify(mSession).resetLockout(any());
+ verify(mAuthSessionCoordinator).resetLockoutFor(USER_ID, BIOMETRIC_STRONG, -1);
+ verify(mLockoutTracker).setLockoutModeForUser(USER_ID, LockoutTracker.LOCKOUT_NONE);
+ verify(mLockoutResetDispatcher).notifyLockoutResetCallbacks(SENSOR_ID);
+ verify(mCallback).onClientFinished(mClient, true);
+ }
+
+ @Test
+ public void testResetLockout_onError() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onError(0, 0);
+ return null;
+ }).when(mSession).resetLockout(any());
+ mClient.start(mCallback);
+
+ verify(mSession).resetLockout(any());
+ verify(mAuthSessionCoordinator, never()).resetLockoutFor(USER_ID,
+ BIOMETRIC_STRONG, -1);
+ verify(mLockoutTracker, never()).setLockoutModeForUser(USER_ID,
+ LockoutTracker.LOCKOUT_NONE);
+ verify(mLockoutResetDispatcher, never()).notifyLockoutResetCallbacks(SENSOR_ID);
+ verify(mCallback).onClientFinished(mClient, false);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClientTest.java
new file mode 100644
index 000000000000..fb5502a1795d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClientTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.aidl;
+
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.biometrics.face.ISession;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+
+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 FaceRevokeChallengeClientTest {
+ private static final String TAG = "FaceRevokeChallengeClientTest";
+ private static final long CHALLENGE = 200L;
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private AidlSession mAidlSession;
+ @Mock
+ private ISession mSession;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ Context mContext;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+
+ private FaceRevokeChallengeClient mClient;
+
+ @Before
+ public void setUp() {
+ when(mAidlSession.getSession()).thenReturn(mSession);
+
+ mClient = new FaceRevokeChallengeClient(mContext, () -> mAidlSession, mToken, USER_ID, TAG,
+ SENSOR_ID, mBiometricLogger, mBiometricContext, CHALLENGE);
+ }
+
+ @Test
+ public void revokeChallenge() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onChallengeRevoked(SENSOR_ID, USER_ID, CHALLENGE);
+ return null;
+ }).when(mSession).revokeChallenge(CHALLENGE);
+ mClient.start(mCallback);
+
+ verify(mSession).revokeChallenge(CHALLENGE);
+ verify(mCallback).onClientFinished(mClient, true);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClientTest.java
new file mode 100644
index 000000000000..eb8cc9c63e75
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClientTest.java
@@ -0,0 +1,120 @@
+/*
+ * 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.aidl;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.BiometricFaceConstants;
+import android.hardware.biometrics.face.ISession;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.TestableContext;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+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.ClientMonitorCallbackConverter;
+
+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 FaceSetFeatureClientTest {
+ private static final String TAG = "FaceSetFeatureClientTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private AidlSession mAidlSession;
+ @Mock
+ private ISession mSession;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallbackConverter mListener;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+
+ private final int mFeature = BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION;
+ private final boolean mEnabled = true;
+ private final byte[] mHardwareAuthToken = new byte[69];
+ private FaceSetFeatureClient mClient;
+ TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getContext());
+
+ @Before
+ public void setUp() {
+ when(mAidlSession.getSession()).thenReturn(mSession);
+
+ mClient = new FaceSetFeatureClient(mContext, () -> mAidlSession, mToken, mListener, USER_ID,
+ TAG, SENSOR_ID, mBiometricLogger, mBiometricContext, mFeature, mEnabled,
+ mHardwareAuthToken);
+ }
+
+ @Test
+ public void setFeature_onFeatureSet() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onFeatureSet(true);
+ return null;
+ }).when(mSession).setFeature(any(),
+ eq(AidlConversionUtils.convertFrameworkToAidlFeature(mFeature)), eq(mEnabled));
+ mClient.start(mCallback);
+
+ verify(mSession).setFeature(any(),
+ eq(AidlConversionUtils.convertFrameworkToAidlFeature(mFeature)),
+ eq(mEnabled));
+ verify(mListener).onFeatureSet(true, mFeature);
+ verify(mCallback).onClientFinished(mClient, true);
+ }
+
+ @Test
+ public void setFeature_onError() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onError(0, 0);
+ return null;
+ }).when(mSession).setFeature(any(),
+ eq(AidlConversionUtils.convertFrameworkToAidlFeature(mFeature)),
+ eq(mEnabled));
+ mClient.start(mCallback);
+
+ verify(mSession).setFeature(any(),
+ eq(AidlConversionUtils.convertFrameworkToAidlFeature(mFeature)),
+ eq(mEnabled));
+ verify(mListener).onFeatureSet(false, mFeature);
+ verify(mCallback).onClientFinished(mClient, false);
+ }
+}
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 be9f52e00b16..7a293e80c183 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
@@ -74,7 +74,7 @@ public class SensorTest {
@Mock
private UserAwareBiometricScheduler.UserSwitchCallback mUserSwitchCallback;
@Mock
- private Sensor.HalSessionCallback.Callback mHalSessionCallback;
+ private AidlResponseHandler.HardwareUnavailableCallback mHardwareUnavailableCallback;
@Mock
private LockoutResetDispatcher mLockoutResetDispatcher;
@Mock
@@ -94,7 +94,7 @@ public class SensorTest {
private final LockoutCache mLockoutCache = new LockoutCache();
private UserAwareBiometricScheduler mScheduler;
- private Sensor.HalSessionCallback mHalCallback;
+ private AidlResponseHandler mHalCallback;
@Before
public void setUp() {
@@ -111,10 +111,9 @@ public class SensorTest {
mBiometricService,
() -> USER_ID,
mUserSwitchCallback);
- mHalCallback = new Sensor.HalSessionCallback(mContext, new Handler(mLooper.getLooper()),
- TAG, mScheduler, SENSOR_ID,
- USER_ID, mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
- mHalSessionCallback);
+ mHalCallback = new AidlResponseHandler(mContext, mScheduler, SENSOR_ID, USER_ID,
+ mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
+ mHardwareUnavailableCallback);
}
@Test
@@ -153,11 +152,11 @@ public class SensorTest {
sensorProps.commonProps.sensorId = 1;
final FaceSensorPropertiesInternal internalProp = new FaceSensorPropertiesInternal(
sensorProps.commonProps.sensorId, sensorProps.commonProps.sensorStrength,
- sensorProps.commonProps.maxEnrollmentsPerUser, null,
+ sensorProps.commonProps.maxEnrollmentsPerUser, null /* componentInfo */,
sensorProps.sensorType, sensorProps.supportsDetectInteraction,
sensorProps.halControlsPreview, false /* resetLockoutRequiresChallenge */);
- final Sensor sensor = new Sensor("SensorTest", mFaceProvider, mContext, null,
- internalProp, mLockoutResetDispatcher, mBiometricContext);
+ final Sensor sensor = new Sensor("SensorTest", mFaceProvider, mContext,
+ null /* handler */, internalProp, mLockoutResetDispatcher, mBiometricContext);
mScheduler.reset();
@@ -181,7 +180,7 @@ public class SensorTest {
sensorProps.commonProps.sensorId = 1;
final FaceSensorPropertiesInternal internalProp = new FaceSensorPropertiesInternal(
sensorProps.commonProps.sensorId, sensorProps.commonProps.sensorStrength,
- sensorProps.commonProps.maxEnrollmentsPerUser, null,
+ sensorProps.commonProps.maxEnrollmentsPerUser, null /* componentInfo */,
sensorProps.sensorType, sensorProps.supportsDetectInteraction,
sensorProps.halControlsPreview, false /* resetLockoutRequiresChallenge */);
final Sensor sensor = new Sensor("SensorTest", mFaceProvider, mContext, null,
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/AidlToHidlAdapterTest.java
new file mode 100644
index 000000000000..9a40e8a7201a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapterTest.java
@@ -0,0 +1,342 @@
+/*
+ * 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.AidlToHidlAdapter.ENROLL_TIMEOUT_SEC;
+import static com.android.server.biometrics.sensors.face.hidl.FaceGenerateChallengeClient.CHALLENGE_TIMEOUT_SEC;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.BiometricFaceConstants;
+import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.face.EnrollmentType;
+import android.hardware.biometrics.face.Feature;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.biometrics.face.V1_0.OptionalBool;
+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.FaceManager;
+import android.hardware.keymaster.HardwareAuthToken;
+import android.hardware.keymaster.Timestamp;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.TestableContext;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.sensors.face.aidl.AidlConversionUtils;
+import com.android.server.biometrics.sensors.face.aidl.AidlResponseHandler;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.time.Clock;
+import java.util.ArrayList;
+import java.util.List;
+
+@Presubmit
+@SmallTest
+public class AidlToHidlAdapterTest {
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private IBiometricsFace mSession;
+ @Mock
+ FaceManager mFaceManager;
+ @Mock
+ private AidlResponseHandler mAidlResponseHandler;
+ @Mock
+ private HardwareAuthToken mHardwareAuthToken;
+ @Mock
+ private Clock mClock;
+
+ private final long mChallenge = 100L;
+ private AidlToHidlAdapter mAidlToHidlAdapter;
+ 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 {
+ TestableContext testableContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getContext());
+ testableContext.addMockSystemService(FaceManager.class, mFaceManager);
+ mAidlToHidlAdapter = new AidlToHidlAdapter(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);
+ when(mFaceManager.getEnrolledFaces(anyInt())).thenReturn(List.of(mFace));
+ }
+
+ @Test
+ public void testGenerateChallengeCache() throws RemoteException {
+ verify(mSession).setCallback(any());
+
+ final ArgumentCaptor<Long> challengeCaptor = ArgumentCaptor.forClass(Long.class);
+
+ mAidlToHidlAdapter.generateChallenge();
+
+ verify(mSession).generateChallenge(CHALLENGE_TIMEOUT_SEC);
+ verify(mAidlResponseHandler).onChallengeGenerated(challengeCaptor.capture());
+ assertThat(challengeCaptor.getValue()).isEqualTo(mChallenge);
+
+ forwardTime(10 /* seconds */);
+ mAidlToHidlAdapter.generateChallenge();
+ forwardTime(20 /* seconds */);
+ mAidlToHidlAdapter.generateChallenge();
+
+ //Confirms that the challenge is cached and the hal method is not called again
+ verifyNoMoreInteractions(mSession);
+ verify(mAidlResponseHandler, times(3))
+ .onChallengeGenerated(mChallenge);
+
+ forwardTime(60 /* seconds */);
+ mAidlToHidlAdapter.generateChallenge();
+
+ //HAL method called after challenge has timed out
+ verify(mSession, times(2)).generateChallenge(CHALLENGE_TIMEOUT_SEC);
+ }
+
+ @Test
+ public void testRevokeChallenge_waitsUntilEmpty() throws RemoteException {
+ for (int i = 0; i < 3; i++) {
+ mAidlToHidlAdapter.generateChallenge();
+ forwardTime(10 /* seconds */);
+ }
+ for (int i = 0; i < 3; i++) {
+ mAidlToHidlAdapter.revokeChallenge(0);
+ forwardTime((i + 1) * 10 /* seconds */);
+ }
+
+ verify(mSession).revokeChallenge();
+ }
+
+ @Test
+ public void testRevokeChallenge_timeout() throws RemoteException {
+ mAidlToHidlAdapter.generateChallenge();
+ mAidlToHidlAdapter.generateChallenge();
+ forwardTime(700);
+ mAidlToHidlAdapter.generateChallenge();
+ mAidlToHidlAdapter.revokeChallenge(0);
+
+ verify(mSession).revokeChallenge();
+ }
+
+ @Test
+ public void testEnroll() throws RemoteException {
+ ICancellationSignal cancellationSignal = mAidlToHidlAdapter.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());
+
+ ArrayList<Integer> features = featureCaptor.getValue();
+
+ assertThat(features).containsExactly(
+ AidlConversionUtils.convertAidlToFrameworkFeature(mFeatures[0]));
+
+ cancellationSignal.cancel();
+
+ verify(mSession).cancel();
+ }
+
+ @Test
+ public void testAuthenticate() throws RemoteException {
+ final int operationId = 2;
+ ICancellationSignal cancellationSignal = mAidlToHidlAdapter.authenticate(operationId);
+
+ verify(mSession).authenticate(operationId);
+
+ cancellationSignal.cancel();
+
+ verify(mSession).cancel();
+ }
+
+ @Test
+ public void testDetectInteraction() throws RemoteException {
+ ICancellationSignal cancellationSignal = mAidlToHidlAdapter.detectInteraction();
+
+ verify(mSession).authenticate(0);
+
+ cancellationSignal.cancel();
+
+ verify(mSession).cancel();
+ }
+
+ @Test
+ public void testEnumerateEnrollments() throws RemoteException {
+ mAidlToHidlAdapter.enumerateEnrollments();
+
+ verify(mSession).enumerate();
+ }
+
+ @Test
+ public void testRemoveEnrollment() throws RemoteException {
+ final int[] enrollments = new int[]{1};
+ mAidlToHidlAdapter.removeEnrollments(enrollments);
+
+ verify(mSession).remove(enrollments[0]);
+ }
+
+ @Test
+ public void testGetFeatures_onResultSuccess() throws RemoteException {
+ final OptionalBool result = new OptionalBool();
+ result.status = Status.OK;
+ result.value = true;
+ ArgumentCaptor<byte[]> featureRetrieved = ArgumentCaptor.forClass(byte[].class);
+
+ when(mSession.getFeature(eq(mFeature), anyInt())).thenReturn(result);
+
+ mAidlToHidlAdapter.setFeature(mFeature);
+ mAidlToHidlAdapter.getFeatures();
+
+ verify(mSession).getFeature(eq(mFeature), anyInt());
+ verify(mAidlResponseHandler).onFeaturesRetrieved(featureRetrieved.capture());
+ assertThat(featureRetrieved.getValue()[0]).isEqualTo(
+ AidlConversionUtils.convertFrameworkToAidlFeature(mFeature));
+ }
+
+ @Test
+ public void testGetFeatures_onResultFailed() throws RemoteException {
+ final OptionalBool result = new OptionalBool();
+ result.status = Status.OK;
+ result.value = false;
+ ArgumentCaptor<byte[]> featureRetrieved = ArgumentCaptor.forClass(byte[].class);
+
+ when(mSession.getFeature(eq(mFeature), anyInt())).thenReturn(result);
+
+ mAidlToHidlAdapter.setFeature(mFeature);
+ mAidlToHidlAdapter.getFeatures();
+
+ verify(mSession).getFeature(eq(mFeature), anyInt());
+ verify(mAidlResponseHandler).onFeaturesRetrieved(featureRetrieved.capture());
+ assertThat(featureRetrieved.getValue().length).isEqualTo(0);
+ }
+
+ @Test
+ public void testGetFeatures_onStatusFailed() throws RemoteException {
+ final OptionalBool result = new OptionalBool();
+ result.status = Status.INTERNAL_ERROR;
+ result.value = false;
+
+ when(mSession.getFeature(eq(mFeature), anyInt())).thenReturn(result);
+
+ mAidlToHidlAdapter.setFeature(mFeature);
+ mAidlToHidlAdapter.getFeatures();
+
+ verify(mSession).getFeature(eq(mFeature), anyInt());
+ verify(mAidlResponseHandler, never()).onFeaturesRetrieved(any());
+ verify(mAidlResponseHandler).onError(BiometricFaceConstants.FACE_ERROR_UNKNOWN, 0);
+ }
+
+ @Test
+ public void testGetFeatures_featureNotSet() throws RemoteException {
+ mAidlToHidlAdapter.getFeatures();
+
+ verify(mSession, never()).getFeature(eq(mFeature), anyInt());
+ verify(mAidlResponseHandler, never()).onFeaturesRetrieved(any());
+ }
+
+ @Test
+ public void testSetFeatureSuccessful() throws RemoteException {
+ byte feature = Feature.REQUIRE_ATTENTION;
+ boolean enabled = true;
+
+ when(mSession.setFeature(anyInt(), anyBoolean(), any(), anyInt())).thenReturn(Status.OK);
+
+ mAidlToHidlAdapter.setFeature(mHardwareAuthToken, feature, enabled);
+
+ verify(mAidlResponseHandler).onFeatureSet(feature);
+ }
+
+ @Test
+ public void testSetFeatureFailed() throws RemoteException {
+ byte feature = Feature.REQUIRE_ATTENTION;
+ boolean enabled = true;
+
+ when(mSession.setFeature(anyInt(), anyBoolean(), any(), anyInt()))
+ .thenReturn(Status.INTERNAL_ERROR);
+
+ mAidlToHidlAdapter.setFeature(mHardwareAuthToken, feature, enabled);
+
+ verify(mAidlResponseHandler).onError(BiometricFaceConstants.FACE_ERROR_UNKNOWN,
+ 0 /* vendorCode */);
+ }
+
+ @Test
+ public void testGetAuthenticatorId() throws RemoteException {
+ final long authenticatorId = 2L;
+ final OptionalUint64 result = new OptionalUint64();
+ result.status = Status.OK;
+ result.value = authenticatorId;
+
+ when(mSession.getAuthenticatorId()).thenReturn(result);
+
+ mAidlToHidlAdapter.getAuthenticatorId();
+
+ verify(mSession).getAuthenticatorId();
+ verify(mAidlResponseHandler).onAuthenticatorIdRetrieved(authenticatorId);
+ }
+
+ @Test
+ public void testResetLockout() throws RemoteException {
+ mAidlToHidlAdapter.resetLockout(mHardwareAuthToken);
+
+ ArgumentCaptor<ArrayList> hatCaptor = ArgumentCaptor.forClass(ArrayList.class);
+
+ verify(mSession).resetLockout(hatCaptor.capture());
+
+ assertThat(hatCaptor.getValue()).containsExactlyElementsIn(processHAT(mHardwareAuthToken));
+ }
+
+ private ArrayList<Byte> processHAT(HardwareAuthToken hat) {
+ ArrayList<Byte> hardwareAuthToken = new ArrayList<>();
+ for (byte b : HardwareAuthTokenUtils.toByteArray(hat)) {
+ hardwareAuthToken.add(b);
+ }
+ return hardwareAuthToken;
+ }
+
+ private void forwardTime(long seconds) {
+ when(mClock.millis()).thenReturn(seconds * 1000);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
index 8a11e31014d5..79a528c59f49 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
@@ -53,11 +53,15 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.test.TestLooper;
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.testing.TestableContext;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.log.CallbackWithProbe;
@@ -66,6 +70,7 @@ import com.android.server.biometrics.log.Probe;
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.LockoutTracker;
import org.junit.Before;
import org.junit.Rule;
@@ -102,6 +107,9 @@ public class FingerprintAuthenticationClientTest {
InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
@Mock
private ISession mHal;
@@ -124,7 +132,7 @@ public class FingerprintAuthenticationClientTest {
@Mock
private ClientMonitorCallback mCallback;
@Mock
- private Sensor.HalSessionCallback mHalSessionCallback;
+ private AidlResponseHandler mAidlResponseHandler;
@Mock
private ActivityTaskManager mActivityTaskManager;
@Mock
@@ -135,6 +143,8 @@ public class FingerprintAuthenticationClientTest {
private AuthSessionCoordinator mAuthSessionCoordinator;
@Mock
private Clock mClock;
+ @Mock
+ private LockoutTracker mLockoutTracker;
@Captor
private ArgumentCaptor<OperationContextExt> mOperationContextCaptor;
@Captor
@@ -425,37 +435,65 @@ public class FingerprintAuthenticationClientTest {
verify(mCallback).onClientFinished(client, true);
}
+ @Test
+ public void testLockoutTracker_authSuccess() throws RemoteException {
+ final FingerprintAuthenticationClient client = createClient(1 /* version */,
+ true /* allowBackgroundAuthentication */, mClientMonitorCallbackConverter,
+ mLockoutTracker);
+ client.start(mCallback);
+ client.onAuthenticated(new Fingerprint("friendly", 1 /* fingerId */,
+ 2 /* deviceId */), true /* authenticated */, new ArrayList<>());
+
+ verify(mLockoutTracker).resetFailedAttemptsForUser(true, USER_ID);
+ verify(mLockoutTracker, never()).addFailedAttemptForUser(anyInt());
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
+ public void testLockoutTracker_authFailed() throws RemoteException {
+ final FingerprintAuthenticationClient client = createClient(1 /* version */,
+ true /* allowBackgroundAuthentication */, mClientMonitorCallbackConverter,
+ mLockoutTracker);
+ client.start(mCallback);
+ client.onAuthenticated(new Fingerprint("friendly", 1 /* fingerId */,
+ 2 /* deviceId */), false /* authenticated */, new ArrayList<>());
+
+ verify(mLockoutTracker, never()).resetFailedAttemptsForUser(anyBoolean(), anyInt());
+ verify(mLockoutTracker).addFailedAttemptForUser(USER_ID);
+ }
+
private FingerprintAuthenticationClient createClient() throws RemoteException {
return createClient(100 /* version */, true /* allowBackgroundAuthentication */,
- mClientMonitorCallbackConverter);
+ mClientMonitorCallbackConverter, null);
}
private FingerprintAuthenticationClient createClientWithoutBackgroundAuth()
throws RemoteException {
return createClient(100 /* version */, false /* allowBackgroundAuthentication */,
- mClientMonitorCallbackConverter);
+ mClientMonitorCallbackConverter, null);
}
private FingerprintAuthenticationClient createClient(int version) throws RemoteException {
return createClient(version, true /* allowBackgroundAuthentication */,
- mClientMonitorCallbackConverter);
+ mClientMonitorCallbackConverter, null);
}
private FingerprintAuthenticationClient createClientWithNullListener() throws RemoteException {
return createClient(100 /* version */, true /* allowBackgroundAuthentication */,
- null /* listener */);
+ null, /* listener */null);
}
private FingerprintAuthenticationClient createClient(int version,
- boolean allowBackgroundAuthentication, ClientMonitorCallbackConverter listener)
+ boolean allowBackgroundAuthentication, ClientMonitorCallbackConverter listener,
+ LockoutTracker lockoutTracker)
throws RemoteException {
when(mHal.getInterfaceVersion()).thenReturn(version);
- final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+ final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mAidlResponseHandler);
final FingerprintAuthenticateOptions options = new FingerprintAuthenticateOptions.Builder()
.setOpPackageName("test-owner")
- .setUserId(5)
- .setSensorId(9)
+ .setUserId(USER_ID)
+ .setSensorId(SENSOR_ID)
.build();
return new FingerprintAuthenticationClient(mContext, () -> aidl, mToken,
REQUEST_ID, listener, OP_ID,
@@ -463,10 +501,11 @@ public class FingerprintAuthenticationClientTest {
false /* requireConfirmation */,
mBiometricLogger, mBiometricContext,
true /* isStrongBiometric */,
- null /* taskStackListener */, null /* lockoutCache */,
+ null /* taskStackListener */,
mUdfpsOverlayController, mSideFpsController, allowBackgroundAuthentication,
mSensorProps,
- new Handler(mLooper.getLooper()), 0 /* biometricStrength */, mClock) {
+ new Handler(mLooper.getLooper()), 0 /* biometricStrength */, mClock,
+ lockoutTracker) {
@Override
protected ActivityTaskManager getActivityTaskManager() {
return mActivityTaskManager;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
index 78d3a9dd9f9e..a467c84845cd 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
@@ -83,7 +83,7 @@ public class FingerprintDetectClientTest {
@Mock
private ClientMonitorCallback mCallback;
@Mock
- private Sensor.HalSessionCallback mHalSessionCallback;
+ private AidlResponseHandler mAidlResponseHandler;
@Captor
private ArgumentCaptor<OperationContextExt> mOperationContextCaptor;
@Captor
@@ -150,7 +150,7 @@ public class FingerprintDetectClientTest {
@Test
public void testWhenListenerIsNull() {
- final AidlSession aidl = new AidlSession(0, mHal, USER_ID, mHalSessionCallback);
+ final AidlSession aidl = new AidlSession(0, mHal, USER_ID, mAidlResponseHandler);
final FingerprintDetectClient client = new FingerprintDetectClient(mContext, () -> aidl,
mToken, 6 /* requestId */, null /* listener */,
new FingerprintAuthenticateOptions.Builder()
@@ -173,7 +173,7 @@ public class FingerprintDetectClientTest {
private FingerprintDetectClient createClient(int version) throws RemoteException {
when(mHal.getInterfaceVersion()).thenReturn(version);
- final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+ final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mAidlResponseHandler);
return new FingerprintDetectClient(mContext, () -> aidl, mToken,
6 /* requestId */, mClientMonitorCallbackConverter,
new FingerprintAuthenticateOptions.Builder()
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
index ef253801b118..c7eb1db3e74b 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
@@ -102,7 +102,7 @@ public class FingerprintEnrollClientTest {
@Mock
private ClientMonitorCallback mCallback;
@Mock
- private Sensor.HalSessionCallback mHalSessionCallback;
+ private AidlResponseHandler mAidlResponseHandler;
@Mock
private Probe mLuxProbe;
@Captor
@@ -291,7 +291,7 @@ public class FingerprintEnrollClientTest {
private FingerprintEnrollClient createClient(int version) throws RemoteException {
when(mHal.getInterfaceVersion()).thenReturn(version);
- final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+ final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mAidlResponseHandler);
return new FingerprintEnrollClient(mContext, () -> aidl, mToken, REQUEST_ID,
mClientMonitorCallbackConverter, 0 /* userId */,
HAT, "owner", mBiometricUtils, 8 /* sensorId */,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClientTest.java
new file mode 100644
index 000000000000..840961938cb2
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClientTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.aidl;
+
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.test.suitebuilder.annotation.SmallTest;
+
+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.ClientMonitorCallbackConverter;
+
+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 FingerprintGenerateChallengeClientTest {
+ private static final String TAG = "FingerprintGenerateChallengeClientTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+ private static final long CHALLENGE = 200;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private AidlSession mAidlSession;
+ @Mock
+ private ISession mSession;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallbackConverter mListener;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ private Context mContext;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+
+ private FingerprintGenerateChallengeClient mClient;
+
+ @Before
+ public void setUp() {
+ when(mAidlSession.getSession()).thenReturn(mSession);
+
+ mClient = new FingerprintGenerateChallengeClient(mContext, () -> mAidlSession, mToken,
+ mListener, USER_ID, TAG, SENSOR_ID, mBiometricLogger, mBiometricContext);
+ }
+
+ @Test
+ public void generateChallenge() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onChallengeGenerated(SENSOR_ID, USER_ID, CHALLENGE);
+ return null;
+ }).when(mSession).generateChallenge();
+ mClient.start(mCallback);
+
+ verify(mSession).generateChallenge();
+ verify(mListener).onChallengeGenerated(SENSOR_ID, USER_ID, CHALLENGE);
+ verify(mCallback).onClientFinished(mClient, true);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java
index 580644347c84..c9482ceb00f5 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java
@@ -28,6 +28,7 @@ import static org.mockito.Mockito.when;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.fingerprint.ISession;
import android.hardware.fingerprint.Fingerprint;
+import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.testing.TestableContext;
@@ -41,7 +42,6 @@ import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
@@ -70,9 +70,9 @@ public class FingerprintInternalCleanupClientTest {
InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
@Mock
- private AidlSession mAidlSession;
+ ISession mSession;
@Mock
- private ISession mSession;
+ private AidlSession mAidlSession;
@Mock
private BiometricLogger mLogger;
@Mock
@@ -87,15 +87,16 @@ public class FingerprintInternalCleanupClientTest {
@Before
public void setup() {
- when(mAidlSession.getSession()).thenReturn(mSession);
mAddedIds = new ArrayList<>();
+
+ when(mAidlSession.getSession()).thenReturn(mSession);
}
- @Ignore("TODO(b/229015801): verify cleanup behavior")
@Test
public void removesUnknownTemplate() throws Exception {
mClient = createClient();
+ final ArgumentCaptor<int[]> captor = ArgumentCaptor.forClass(int[].class);
final List<Fingerprint> templates = List.of(
new Fingerprint("one", 1, 1),
new Fingerprint("two", 2, 1)
@@ -108,8 +109,8 @@ public class FingerprintInternalCleanupClientTest {
mClient.getCurrentRemoveClient().onRemoved(templates.get(i), 0);
}
+ verify(mSession).enumerateEnrollments();
assertThat(mAddedIds).isEmpty();
- final ArgumentCaptor<int[]> captor = ArgumentCaptor.forClass(int[].class);
verify(mSession, times(2)).removeEnrollments(captor.capture());
assertThat(captor.getAllValues().stream()
.flatMap(x -> Arrays.stream(x).boxed())
@@ -132,13 +133,15 @@ public class FingerprintInternalCleanupClientTest {
mClient.getCurrentEnumerateClient().onEnumerationResult(templates.get(i), i);
}
+ verify(mSession).enumerateEnrollments();
assertThat(mAddedIds).containsExactly(1, 2);
verify(mSession, never()).removeEnrollments(any());
verify(mCallback).onClientFinished(eq(mClient), eq(true));
}
@Test
- public void cleanupUnknownHalTemplatesAfterEnumerationWhenVirtualIsDisabled() {
+ public void cleanupUnknownHalTemplatesAfterEnumerationWhenVirtualIsDisabled()
+ throws RemoteException {
mClient = createClient();
final List<Fingerprint> templates = List.of(
@@ -150,6 +153,8 @@ public class FingerprintInternalCleanupClientTest {
for (int i = templates.size() - 1; i >= 0; i--) {
mClient.getCurrentEnumerateClient().onEnumerationResult(templates.get(i), i);
}
+
+ verify(mSession).enumerateEnrollments();
// The first template is removed after enumeration
assertThat(mClient.getUnknownHALTemplates().size()).isEqualTo(2);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClientTest.java
new file mode 100644
index 000000000000..723f916f99c8
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClientTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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.aidl;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+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.content.Context;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.fingerprint.Fingerprint;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+
+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.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+@Presubmit
+@SmallTest
+public class FingerprintInternalEnumerateClientTest {
+ private static final String TAG = "FingerprintInternalEnumerateClientTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private AidlSession mAidlSession;
+ @Mock
+ private ISession mSession;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ private Context mContext;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ @Mock
+ private BiometricUtils<Fingerprint> mBiometricUtils;
+
+ private FingerprintInternalEnumerateClient mClient;
+
+ @Before
+ public void setUp() {
+ when(mAidlSession.getSession()).thenReturn(mSession);
+
+ List<Fingerprint> enrolled = new ArrayList<>();
+ enrolled.add(new Fingerprint("one", 1, 1));
+ mClient = new FingerprintInternalEnumerateClient(mContext, () -> mAidlSession, mToken,
+ USER_ID, TAG, enrolled, mBiometricUtils, SENSOR_ID, mBiometricLogger,
+ mBiometricContext);
+ }
+
+ @Test
+ public void internalEnumerate_unknownTemplates() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onEnumerationResult(new Fingerprint("two", 2, 1), 1);
+ mClient.onEnumerationResult(new Fingerprint("three", 3, 1), 0);
+ return null;
+ }).when(mSession).enumerateEnrollments();
+ mClient.start(mCallback);
+
+ verify(mSession).enumerateEnrollments();
+ assertThat(mClient.getUnknownHALTemplates().stream()
+ .flatMap(x -> Stream.of(x.getBiometricId()))
+ .collect(Collectors.toList())).containsExactly(2, 3);
+ verify(mBiometricUtils).removeBiometricForUser(mContext, USER_ID, 1);
+ verify(mCallback).onClientFinished(mClient, true);
+ }
+
+ @Test
+ public void internalEnumerate_noUnknownTemplates() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onEnumerationResult(new Fingerprint("one", 1, 1), 0);
+ return null;
+ }).when(mSession).enumerateEnrollments();
+ mClient.start(mCallback);
+
+ verify(mSession).enumerateEnrollments();
+ assertThat(mClient.getUnknownHALTemplates().size()).isEqualTo(0);
+ verify(mBiometricUtils, never()).removeBiometricForUser(any(), anyInt(), anyInt());
+ verify(mCallback).onClientFinished(mClient, true);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClientTest.java
new file mode 100644
index 000000000000..64f07e20e958
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClientTest.java
@@ -0,0 +1,147 @@
+/*
+ * 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.aidl;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.fingerprint.Fingerprint;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+
+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.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+
+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.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@Presubmit
+@SmallTest
+public class FingerprintRemovalClientTest {
+ private static final String TAG = "FingerprintRemovalClientTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private AidlSession mAidlSession;
+ @Mock
+ private ISession mSession;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallbackConverter mListener;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ private Context mContext;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ @Mock
+ private BiometricUtils<Fingerprint> mBiometricUtils;
+ @Mock
+ private Map<Integer, Long> mAuthenticatorIds;
+
+ private FingerprintRemovalClient mClient;
+ private int[] mBiometricIds = new int[]{1, 2};
+
+ @Before
+ public void setUp() {
+ when(mAidlSession.getSession()).thenReturn(mSession);
+
+ mClient = new FingerprintRemovalClient(mContext, () -> mAidlSession, mToken, mListener,
+ mBiometricIds, USER_ID, TAG, mBiometricUtils, SENSOR_ID,
+ mBiometricLogger, mBiometricContext, mAuthenticatorIds);
+ }
+
+ @Test
+ public void removalMultipleFingerprints() throws RemoteException {
+ when(mBiometricUtils.getBiometricsForUser(any(), anyInt())).thenReturn(
+ List.of(new Fingerprint("three", 3, 1)));
+ doAnswer(invocation -> {
+ mClient.onRemoved(new Fingerprint("one", 1, 1), 1);
+ mClient.onRemoved(new Fingerprint("two", 2, 1), 0);
+ return null;
+ }).when(mSession).removeEnrollments(mBiometricIds);
+ mClient.start(mCallback);
+
+ verify(mSession).removeEnrollments(mBiometricIds);
+ verify(mBiometricUtils, times(2)).removeBiometricForUser(eq(mContext),
+ eq(USER_ID), anyInt());
+ verifyNoMoreInteractions(mAuthenticatorIds);
+ verify(mListener, times(2)).onRemoved(any(), anyInt());
+ verify(mCallback).onClientFinished(mClient, true);
+ }
+
+ @Test
+ public void removeFingerprint_nullIdentifier() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onRemoved(null, 0);
+ return null;
+ }).when(mSession).removeEnrollments(mBiometricIds);
+ mClient.start(mCallback);
+
+ verify(mSession).removeEnrollments(mBiometricIds);
+ verify(mListener).onError(anyInt(), anyInt(), anyInt(), anyInt());
+ verify(mCallback).onClientFinished(mClient, false);
+ }
+
+ @Test
+ public void removeFingerprints_noFingerprintEnrolled() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onRemoved(new Fingerprint("one", 1, 1), 1);
+ mClient.onRemoved(new Fingerprint("two", 2, 1), 0);
+ return null;
+ }).when(mSession).removeEnrollments(mBiometricIds);
+ when(mBiometricUtils.getBiometricsForUser(any(), anyInt())).thenReturn(new ArrayList<>());
+
+ mClient.start(mCallback);
+
+ verify(mSession).removeEnrollments(mBiometricIds);
+ verify(mBiometricUtils, times(2)).removeBiometricForUser(eq(mContext),
+ eq(USER_ID), anyInt());
+ verify(mAuthenticatorIds).put(USER_ID, 0L);
+ verify(mListener, times(2)).onRemoved(any(), anyInt());
+ verify(mCallback).onClientFinished(mClient, true);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClientTest.java
new file mode 100644
index 000000000000..a4746dea7ac5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClientTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.aidl;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+
+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.ClientMonitorCallback;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
+
+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 FingerprintResetLockoutClientTest {
+ private static final String TAG = "FingerprintResetLockoutClientTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private AidlSession mAidlSession;
+ @Mock
+ private ISession mSession;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ private Context mContext;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ @Mock
+ private LockoutTracker mLockoutTracker;
+ @Mock
+ private LockoutResetDispatcher mLockoutResetDispatcher;
+ @Mock
+ private AuthSessionCoordinator mAuthSessionCoordinator;
+
+ private FingerprintResetLockoutClient mClient;
+
+ @Before
+ public void setUp() {
+ when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator);
+ when(mAidlSession.getSession()).thenReturn(mSession);
+
+ mClient = new FingerprintResetLockoutClient(mContext, () -> mAidlSession, USER_ID, TAG,
+ SENSOR_ID, mBiometricLogger, mBiometricContext, new byte[69],
+ mLockoutTracker, mLockoutResetDispatcher,
+ BiometricManager.Authenticators.BIOMETRIC_STRONG);
+ }
+
+ @Test
+ public void resetLockout_onLockoutCleared() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onLockoutCleared();
+ return null;
+ }).when(mSession).resetLockout(any());
+ mClient.start(mCallback);
+
+ verify(mSession).resetLockout(any());
+ verify(mLockoutTracker).setLockoutModeForUser(USER_ID, LockoutTracker.LOCKOUT_NONE);
+ verify(mLockoutTracker).resetFailedAttemptsForUser(true, USER_ID);
+ verify(mLockoutResetDispatcher).notifyLockoutResetCallbacks(SENSOR_ID);
+ verify(mAuthSessionCoordinator).resetLockoutFor(eq(USER_ID),
+ eq(BiometricManager.Authenticators.BIOMETRIC_STRONG), anyLong());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClientTest.java
new file mode 100644
index 000000000000..f19b2f7da8a5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClientTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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.aidl;
+
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+
+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.ClientMonitorCallback;
+
+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 FingerprintRevokeChallengeClientTest {
+ private static final String TAG = "FingerprintRevokeChallengeClientTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+ private static final long CHALLENGE = 200;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private AidlSession mAidlSession;
+ @Mock
+ private ISession mSession;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ private Context mContext;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+
+ private FingerprintRevokeChallengeClient mClient;
+
+ @Before
+ public void setUp() {
+ when(mAidlSession.getSession()).thenReturn(mSession);
+
+ mClient = new FingerprintRevokeChallengeClient(mContext, () -> mAidlSession, mToken,
+ USER_ID, TAG, SENSOR_ID, mBiometricLogger, mBiometricContext, CHALLENGE);
+ }
+
+ @Test
+ public void revokeChallenge_sameChallenge() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onChallengeRevoked(CHALLENGE);
+ return null;
+ }).when(mSession).revokeChallenge(CHALLENGE);
+ mClient.start(mCallback);
+
+ verify(mSession).revokeChallenge(CHALLENGE);
+ verify(mCallback).onClientFinished(mClient, true);
+ }
+
+ @Test
+ public void revokeChallenge_differentChallenge() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onChallengeRevoked(CHALLENGE + 1);
+ return null;
+ }).when(mSession).revokeChallenge(CHALLENGE);
+ mClient.start(mCallback);
+
+ verify(mSession).revokeChallenge(CHALLENGE);
+ verify(mCallback).onClientFinished(mClient, false);
+ }
+}
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 15d7601dde34..410260074fbd 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
@@ -75,7 +75,7 @@ public class SensorTest {
@Mock
private UserAwareBiometricScheduler.UserSwitchCallback mUserSwitchCallback;
@Mock
- private Sensor.HalSessionCallback.Callback mHalSessionCallback;
+ private AidlResponseHandler.HardwareUnavailableCallback mHardwareUnavailableCallback;
@Mock
private LockoutResetDispatcher mLockoutResetDispatcher;
@Mock
@@ -97,7 +97,7 @@ public class SensorTest {
private final LockoutCache mLockoutCache = new LockoutCache();
private UserAwareBiometricScheduler mScheduler;
- private Sensor.HalSessionCallback mHalCallback;
+ private AidlResponseHandler mHalCallback;
@Before
public void setUp() {
@@ -113,10 +113,9 @@ public class SensorTest {
mBiometricService,
() -> USER_ID,
mUserSwitchCallback);
- mHalCallback = new Sensor.HalSessionCallback(mContext, new Handler(mLooper.getLooper()),
- TAG, mScheduler, SENSOR_ID,
- USER_ID, mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
- mHalSessionCallback);
+ mHalCallback = new AidlResponseHandler(mContext, mScheduler, SENSOR_ID, USER_ID,
+ mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
+ mHardwareUnavailableCallback);
}
@Test
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/AidlToHidlAdapterTest.java
new file mode 100644
index 000000000000..b78ba82bd8fe
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/AidlToHidlAdapterTest.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint.hidl;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.keymaster.HardwareAuthToken;
+import android.hardware.keymaster.Timestamp;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.biometrics.sensors.fingerprint.aidl.AidlResponseHandler;
+
+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 AidlToHidlAdapterTest {
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private IBiometricsFingerprint mSession;
+ @Mock
+ private AidlResponseHandler mAidlResponseHandler;
+ @Mock
+ private HardwareAuthToken mHardwareAuthToken;
+
+ private final long mChallenge = 100L;
+ private final int mUserId = 0;
+ private AidlToHidlAdapter mAidlToHidlAdapter;
+
+ @Before
+ public void setUp() {
+ mAidlToHidlAdapter = new AidlToHidlAdapter(() -> mSession, mUserId,
+ mAidlResponseHandler);
+ mHardwareAuthToken.timestamp = new Timestamp();
+ mHardwareAuthToken.mac = new byte[10];
+ }
+
+ @Test
+ public void testGenerateChallenge() throws RemoteException {
+ when(mSession.preEnroll()).thenReturn(mChallenge);
+ mAidlToHidlAdapter.generateChallenge();
+
+ verify(mSession).preEnroll();
+ verify(mAidlResponseHandler).onChallengeGenerated(mChallenge);
+ }
+
+ @Test
+ public void testRevokeChallenge() throws RemoteException {
+ mAidlToHidlAdapter.revokeChallenge(mChallenge);
+
+ verify(mSession).postEnroll();
+ verify(mAidlResponseHandler).onChallengeRevoked(0L);
+ }
+
+ @Test
+ public void testEnroll() throws RemoteException {
+ final ICancellationSignal cancellationSignal =
+ mAidlToHidlAdapter.enroll(mHardwareAuthToken);
+
+ verify(mSession).enroll(any(), anyInt(), eq(AidlToHidlAdapter.ENROLL_TIMEOUT_SEC));
+
+ cancellationSignal.cancel();
+
+ verify(mSession).cancel();
+ }
+
+ @Test
+ public void testAuthenticate() throws RemoteException {
+ final int operationId = 2;
+ final ICancellationSignal cancellationSignal = mAidlToHidlAdapter.authenticate(operationId);
+
+ verify(mSession).authenticate(operationId, mUserId);
+
+ cancellationSignal.cancel();
+
+ verify(mSession).cancel();
+ }
+
+ @Test
+ public void testDetectInteraction() throws RemoteException {
+ final ICancellationSignal cancellationSignal = mAidlToHidlAdapter.detectInteraction();
+
+ verify(mSession).authenticate(0 /* operationId */, mUserId);
+
+ cancellationSignal.cancel();
+
+ verify(mSession).cancel();
+ }
+
+ @Test
+ public void testEnumerateEnrollments() throws RemoteException {
+ mAidlToHidlAdapter.enumerateEnrollments();
+
+ verify(mSession).enumerate();
+ }
+
+ @Test
+ public void testRemoveEnrollment() throws RemoteException {
+ final int[] enrollmentIds = new int[]{1};
+ mAidlToHidlAdapter.removeEnrollments(enrollmentIds);
+
+ verify(mSession).remove(mUserId, enrollmentIds[0]);
+ }
+
+ @Test
+ public void testRemoveMultipleEnrollments() throws RemoteException {
+ final int[] enrollmentIds = new int[]{1, 2};
+ mAidlToHidlAdapter.removeEnrollments(enrollmentIds);
+
+ verify(mSession).remove(mUserId, 0);
+ }
+
+ @Test
+ public void testResetLockout() throws RemoteException {
+ mAidlToHidlAdapter.resetLockout(mHardwareAuthToken);
+
+ verify(mAidlResponseHandler).onLockoutCleared();
+ }
+}