/* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.hardware.fingerprint; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR_BASE; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_VENDOR; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_VENDOR_BASE; import static android.hardware.fingerprint.FingerprintManager.getAcquiredString; import static android.hardware.fingerprint.FingerprintManager.getErrorString; import android.annotation.IntDef; import android.content.Context; import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback; import android.hardware.fingerprint.FingerprintManager.AuthenticationResult; import android.hardware.fingerprint.FingerprintManager.CryptoObject; import android.hardware.fingerprint.FingerprintManager.EnrollmentCallback; import android.hardware.fingerprint.FingerprintManager.FingerprintDetectionCallback; import android.hardware.fingerprint.FingerprintManager.GenerateChallengeCallback; import android.hardware.fingerprint.FingerprintManager.RemovalCallback; import android.util.Slog; import androidx.annotation.NonNull; import androidx.annotation.Nullable; /** * Encapsulates callbacks and client specific information for each fingerprint related request. * @hide */ public class FingerprintCallback { private static final String TAG = "FingerprintCallback"; public static final int REMOVE_SINGLE = 1; public static final int REMOVE_ALL = 2; @IntDef({REMOVE_SINGLE, REMOVE_ALL}) public @interface RemoveRequest {} @Nullable private AuthenticationCallback mAuthenticationCallback; @Nullable private EnrollmentCallback mEnrollmentCallback; @Nullable private RemovalCallback mRemovalCallback; @Nullable private GenerateChallengeCallback mGenerateChallengeCallback; @Nullable private FingerprintDetectionCallback mFingerprintDetectionCallback; @Nullable private CryptoObject mCryptoObject; @Nullable private @RemoveRequest int mRemoveRequest; @Nullable private Fingerprint mRemoveFingerprint; /** * Construction for fingerprint authentication client callback. */ FingerprintCallback(@NonNull AuthenticationCallback authenticationCallback, @Nullable CryptoObject cryptoObject) { mAuthenticationCallback = authenticationCallback; mCryptoObject = cryptoObject; } /** * Construction for fingerprint detect client callback. */ FingerprintCallback(@NonNull FingerprintDetectionCallback fingerprintDetectionCallback) { mFingerprintDetectionCallback = fingerprintDetectionCallback; } /** * Construction for fingerprint enroll client callback. */ FingerprintCallback(@NonNull EnrollmentCallback enrollmentCallback) { mEnrollmentCallback = enrollmentCallback; } /** * Construction for fingerprint generate challenge client callback. */ FingerprintCallback(@NonNull GenerateChallengeCallback generateChallengeCallback) { mGenerateChallengeCallback = generateChallengeCallback; } /** * Construction for fingerprint removal client callback. */ FingerprintCallback(@NonNull RemovalCallback removalCallback, @RemoveRequest int removeRequest, @Nullable Fingerprint removeFingerprint) { mRemovalCallback = removalCallback; mRemoveRequest = removeRequest; mRemoveFingerprint = removeFingerprint; } /** * Propagate enroll progress via the callback. * @param remaining number of enrollment steps remaining */ public void sendEnrollResult(int remaining) { if (mEnrollmentCallback != null) { mEnrollmentCallback.onEnrollmentProgress(remaining); } } /** * Propagate remove face completed via the callback. * @param fingerprint removed identifier * @param remaining number of face enrollments remaining */ public void sendRemovedResult(@Nullable Fingerprint fingerprint, int remaining) { if (mRemovalCallback == null) { return; } if (mRemoveRequest == REMOVE_SINGLE) { if (fingerprint == null) { Slog.e(TAG, "Received MSG_REMOVED, but fingerprint is null"); return; } if (mRemoveFingerprint == null) { Slog.e(TAG, "Missing fingerprint"); return; } final int fingerId = fingerprint.getBiometricId(); int reqFingerId = mRemoveFingerprint.getBiometricId(); if (reqFingerId != 0 && fingerId != 0 && fingerId != reqFingerId) { Slog.w(TAG, "Finger id didn't match: " + fingerId + " != " + reqFingerId); return; } } mRemovalCallback.onRemovalSucceeded(fingerprint, remaining); } /** * Propagate authentication succeeded via the callback. * @param fingerprint matched identifier * @param userId id of the corresponding user * @param isStrongBiometric if the sensor is strong or not */ public void sendAuthenticatedSucceeded(@NonNull Fingerprint fingerprint, int userId, boolean isStrongBiometric) { if (mAuthenticationCallback == null) { Slog.e(TAG, "Authentication succeeded but callback is null."); return; } final AuthenticationResult result = new AuthenticationResult(mCryptoObject, fingerprint, userId, isStrongBiometric); mAuthenticationCallback.onAuthenticationSucceeded(result); } /** * Propagate authentication failed via the callback. */ public void sendAuthenticatedFailed() { if (mAuthenticationCallback != null) { mAuthenticationCallback.onAuthenticationFailed(); } } /** * Propagate acquired result via the callback. * @param context corresponding context * @param acquireInfo represents the framework acquired id * @param vendorCode represents the vendor acquired code */ public void sendAcquiredResult(@NonNull Context context, int acquireInfo, int vendorCode) { if (mAuthenticationCallback != null) { mAuthenticationCallback.onAuthenticationAcquired(acquireInfo); } if (mEnrollmentCallback != null && acquireInfo != FINGERPRINT_ACQUIRED_START) { mEnrollmentCallback.onAcquired(acquireInfo == FINGERPRINT_ACQUIRED_GOOD); } final String msg = getAcquiredString(context, acquireInfo, vendorCode); if (msg == null) { return; } // emulate HAL 2.1 behavior and send real acquiredInfo final int clientInfo = acquireInfo == FINGERPRINT_ACQUIRED_VENDOR ? (vendorCode + FINGERPRINT_ACQUIRED_VENDOR_BASE) : acquireInfo; if (mEnrollmentCallback != null) { mEnrollmentCallback.onEnrollmentHelp(clientInfo, msg); } else if (mAuthenticationCallback != null) { if (acquireInfo != FINGERPRINT_ACQUIRED_START) { mAuthenticationCallback.onAuthenticationHelp(clientInfo, msg); } } } /** * Propagate errors via the callback. * @param context corresponding context * @param errMsgId represents the framework error id * @param vendorCode represents the vendor error code */ public void sendErrorResult(@NonNull Context context, int errMsgId, int vendorCode) { // emulate HAL 2.1 behavior and send real errMsgId final int clientErrMsgId = errMsgId == FINGERPRINT_ERROR_VENDOR ? (vendorCode + FINGERPRINT_ERROR_VENDOR_BASE) : errMsgId; if (mEnrollmentCallback != null) { mEnrollmentCallback.onEnrollmentError(clientErrMsgId, getErrorString(context, errMsgId, vendorCode)); } else if (mAuthenticationCallback != null) { mAuthenticationCallback.onAuthenticationError(clientErrMsgId, getErrorString(context, errMsgId, vendorCode)); } else if (mRemovalCallback != null) { mRemovalCallback.onRemovalError(mRemoveFingerprint, clientErrMsgId, getErrorString(context, errMsgId, vendorCode)); } else if (mFingerprintDetectionCallback != null) { mFingerprintDetectionCallback.onDetectionError(errMsgId); mFingerprintDetectionCallback = null; } } /** * Propagate challenge generated completed via the callback. * @param sensorId id of the corresponding sensor * @param userId id of the corresponding sensor * @param challenge value of the challenge generated */ public void sendChallengeGenerated(long challenge, int sensorId, int userId) { if (mGenerateChallengeCallback == null) { Slog.e(TAG, "sendChallengeGenerated, callback null"); return; } mGenerateChallengeCallback.onChallengeGenerated(sensorId, userId, challenge); } /** * Propagate fingerprint detected completed via the callback. * @param sensorId id of the corresponding sensor * @param userId id of the corresponding user * @param isStrongBiometric if the sensor is strong or not */ public void sendFingerprintDetected(int sensorId, int userId, boolean isStrongBiometric) { if (mFingerprintDetectionCallback == null) { Slog.e(TAG, "sendFingerprintDetected, callback null"); return; } mFingerprintDetectionCallback.onFingerprintDetected(sensorId, userId, isStrongBiometric); } /** * Propagate udfps pointer down via the callback. * @param sensorId id of the corresponding sensor */ public void sendUdfpsPointerDown(int sensorId) { if (mAuthenticationCallback == null) { Slog.e(TAG, "sendUdfpsPointerDown, callback null"); } else { mAuthenticationCallback.onUdfpsPointerDown(sensorId); } if (mEnrollmentCallback != null) { mEnrollmentCallback.onUdfpsPointerDown(sensorId); } } /** * Propagate udfps pointer up via the callback. * @param sensorId id of the corresponding sensor */ public void sendUdfpsPointerUp(int sensorId) { if (mAuthenticationCallback == null) { Slog.e(TAG, "sendUdfpsPointerUp, callback null"); } else { mAuthenticationCallback.onUdfpsPointerUp(sensorId); } if (mEnrollmentCallback != null) { mEnrollmentCallback.onUdfpsPointerUp(sensorId); } } /** * Propagate udfps overlay shown via the callback. */ public void sendUdfpsOverlayShown() { if (mEnrollmentCallback != null) { mEnrollmentCallback.onUdfpsOverlayShown(); } } }