diff options
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(); + } +} |