diff options
| author | 2024-04-16 13:00:40 -0700 | |
|---|---|---|
| committer | 2024-04-17 09:39:56 -0700 | |
| commit | bbd8a6a61cec52f7c96f81beef98c23c042f8562 (patch) | |
| tree | 1eb127aebd0074a34aaf0e03cd60a57d680913b2 | |
| parent | 3582d53076f99e2a46ad7a697af277319a016b6c (diff) | |
Distinct callback for each request
1. Created a callback class for face and fingerprint
2. Each biometric request has a new receiver
3. This ensures that the accurate callback is used, instead of the most
recent one
Test: atest FingerprintManagerTest FaceManagerTest
Bug: 314165741
Change-Id: I37bac28544c422d5cdaa179e464927811f0bfc49
6 files changed, 822 insertions, 556 deletions
diff --git a/core/java/android/hardware/face/FaceCallback.java b/core/java/android/hardware/face/FaceCallback.java new file mode 100644 index 000000000000..b69024fd05fa --- /dev/null +++ b/core/java/android/hardware/face/FaceCallback.java @@ -0,0 +1,321 @@ +/* + * 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.face; + +import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_VENDOR; +import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_VENDOR_BASE; +import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_VENDOR; +import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_VENDOR_BASE; +import static android.hardware.face.FaceManager.getAuthHelpMessage; +import static android.hardware.face.FaceManager.getEnrollHelpMessage; +import static android.hardware.face.FaceManager.getErrorString; + +import android.content.Context; +import android.hardware.biometrics.CryptoObject; +import android.hardware.face.FaceManager.AuthenticationCallback; +import android.hardware.face.FaceManager.EnrollmentCallback; +import android.hardware.face.FaceManager.FaceDetectionCallback; +import android.hardware.face.FaceManager.GenerateChallengeCallback; +import android.hardware.face.FaceManager.GetFeatureCallback; +import android.hardware.face.FaceManager.RemovalCallback; +import android.hardware.face.FaceManager.SetFeatureCallback; +import android.util.Slog; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +/** + * Encapsulates callbacks and client specific information for each face related request. + * @hide + */ +public class FaceCallback { + private static final String TAG = " FaceCallback"; + + @Nullable + private AuthenticationCallback mAuthenticationCallback; + @Nullable + private EnrollmentCallback mEnrollmentCallback; + @Nullable + private RemovalCallback mRemovalCallback; + @Nullable + private GenerateChallengeCallback mGenerateChallengeCallback; + @Nullable + private FaceDetectionCallback mFaceDetectionCallback; + @Nullable + private SetFeatureCallback mSetFeatureCallback; + @Nullable + private GetFeatureCallback mGetFeatureCallback; + @Nullable + private Face mRemovalFace; + @Nullable + private CryptoObject mCryptoObject; + + /** + * Construction for face authentication client callback. + */ + FaceCallback(AuthenticationCallback authenticationCallback, CryptoObject cryptoObject) { + mAuthenticationCallback = authenticationCallback; + mCryptoObject = cryptoObject; + } + + /** + * Construction for face detect client callback. + */ + FaceCallback(FaceDetectionCallback faceDetectionCallback) { + mFaceDetectionCallback = faceDetectionCallback; + } + + /** + * Construction for face enroll client callback. + */ + FaceCallback(EnrollmentCallback enrollmentCallback) { + mEnrollmentCallback = enrollmentCallback; + } + + /** + * Construction for face generate challenge client callback. + */ + FaceCallback(GenerateChallengeCallback generateChallengeCallback) { + mGenerateChallengeCallback = generateChallengeCallback; + } + + /** + * Construction for face set feature client callback. + */ + FaceCallback(SetFeatureCallback setFeatureCallback) { + mSetFeatureCallback = setFeatureCallback; + } + + /** + * Construction for face get feature client callback. + */ + FaceCallback(GetFeatureCallback getFeatureCallback) { + mGetFeatureCallback = getFeatureCallback; + } + + /** + * Construction for single face removal client callback. + */ + FaceCallback(RemovalCallback removalCallback, Face removalFace) { + mRemovalCallback = removalCallback; + mRemovalFace = removalFace; + } + + /** + * Construction for all face removal client callback. + */ + FaceCallback(RemovalCallback removalCallback) { + mRemovalCallback = removalCallback; + } + + /** + * Propagate set feature completed via the callback. + * @param success if the operation was completed successfully + * @param feature the feature that was set + */ + public void sendSetFeatureCompleted(boolean success, int feature) { + if (mSetFeatureCallback == null) { + return; + } + mSetFeatureCallback.onCompleted(success, feature); + } + + /** + * Propagate get feature completed via the callback. + * @param success if the operation was completed successfully + * @param features list of features available + * @param featureState status of the features corresponding to the previous parameter + */ + public void sendGetFeatureCompleted(boolean success, int[] features, boolean[] featureState) { + if (mGetFeatureCallback == null) { + return; + } + mGetFeatureCallback.onCompleted(success, features, featureState); + } + + /** + * 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(int sensorId, int userId, long challenge) { + if (mGenerateChallengeCallback == null) { + return; + } + mGenerateChallengeCallback.onGenerateChallengeResult(sensorId, userId, challenge); + } + + /** + * Propagate face 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 sendFaceDetected(int sensorId, int userId, boolean isStrongBiometric) { + if (mFaceDetectionCallback == null) { + Slog.e(TAG, "sendFaceDetected, callback null"); + return; + } + mFaceDetectionCallback.onFaceDetected(sensorId, userId, isStrongBiometric); + } + + /** + * Propagate remove face completed via the callback. + * @param face removed identifier + * @param remaining number of face enrollments remaining + */ + public void sendRemovedResult(Face face, int remaining) { + if (mRemovalCallback == null) { + return; + } + mRemovalCallback.onRemovalSucceeded(face, remaining); + } + + /** + * 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(Context context, int errMsgId, int vendorCode) { + // emulate HAL 2.1 behavior and send real errMsgId + final int clientErrMsgId = errMsgId == FACE_ERROR_VENDOR + ? (vendorCode + FACE_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(mRemovalFace, clientErrMsgId, + getErrorString(context, errMsgId, vendorCode)); + } else if (mFaceDetectionCallback != null) { + mFaceDetectionCallback.onDetectionError(errMsgId); + mFaceDetectionCallback = null; + } + } + + /** + * 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 authentication succeeded via the callback. + * @param face matched identifier + * @param userId id of the corresponding user + * @param isStrongBiometric if the sensor is strong or not + */ + public void sendAuthenticatedSucceeded(Face face, int userId, boolean isStrongBiometric) { + if (mAuthenticationCallback != null) { + final FaceManager.AuthenticationResult result = new FaceManager.AuthenticationResult( + mCryptoObject, face, 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(Context context, int acquireInfo, int vendorCode) { + if (mAuthenticationCallback != null) { + final FaceAuthenticationFrame frame = new FaceAuthenticationFrame( + new FaceDataFrame(acquireInfo, vendorCode)); + sendAuthenticationFrame(context, frame); + } else if (mEnrollmentCallback != null) { + final FaceEnrollFrame frame = new FaceEnrollFrame( + null /* cell */, + FaceEnrollStages.UNKNOWN, + new FaceDataFrame(acquireInfo, vendorCode)); + sendEnrollmentFrame(context, frame); + } + } + + /** + * Propagate authentication frame via the callback. + * @param context corresponding context + * @param frame authentication frame to be sent + */ + public void sendAuthenticationFrame(@NonNull Context context, + @Nullable FaceAuthenticationFrame frame) { + if (frame == null) { + Slog.w(TAG, "Received null authentication frame"); + } else if (mAuthenticationCallback != null) { + // TODO(b/178414967): Send additional frame data to callback + final int acquireInfo = frame.getData().getAcquiredInfo(); + final int vendorCode = frame.getData().getVendorCode(); + final int helpCode = getHelpCode(acquireInfo, vendorCode); + final String helpMessage = getAuthHelpMessage(context, acquireInfo, vendorCode); + mAuthenticationCallback.onAuthenticationAcquired(acquireInfo); + + // Ensure that only non-null help messages are sent. + if (helpMessage != null) { + mAuthenticationCallback.onAuthenticationHelp(helpCode, helpMessage); + } + } + } + + /** + * Propagate enrollment via the callback. + * @param context corresponding context + * @param frame enrollment frame to be sent + */ + public void sendEnrollmentFrame(Context context, @Nullable FaceEnrollFrame frame) { + if (frame == null) { + Slog.w(TAG, "Received null enrollment frame"); + } else if (mEnrollmentCallback != null) { + final FaceDataFrame data = frame.getData(); + final int acquireInfo = data.getAcquiredInfo(); + final int vendorCode = data.getVendorCode(); + final int helpCode = getHelpCode(acquireInfo, vendorCode); + final String helpMessage = getEnrollHelpMessage(context, acquireInfo, vendorCode); + mEnrollmentCallback.onEnrollmentFrame( + helpCode, + helpMessage, + frame.getCell(), + frame.getStage(), + data.getPan(), + data.getTilt(), + data.getDistance()); + } + } + + private static int getHelpCode(int acquireInfo, int vendorCode) { + return acquireInfo == FACE_ACQUIRED_VENDOR + ? vendorCode + FACE_ACQUIRED_VENDOR_BASE + : acquireInfo; + } +} diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index 210ce2b78fca..2592630c80d2 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -37,9 +37,9 @@ import android.os.Binder; import android.os.CancellationSignal; import android.os.CancellationSignal.OnCancelListener; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.IBinder; import android.os.IRemoteCallback; -import android.os.Looper; import android.os.PowerManager; import android.os.RemoteException; import android.os.Trace; @@ -49,7 +49,6 @@ import android.util.Slog; import android.view.Surface; import com.android.internal.R; -import com.android.internal.os.SomeArgs; import java.util.ArrayList; import java.util.List; @@ -63,71 +62,56 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan private static final String TAG = "FaceManager"; - private static final int MSG_ENROLL_RESULT = 100; - private static final int MSG_ACQUIRED = 101; - private static final int MSG_AUTHENTICATION_SUCCEEDED = 102; - private static final int MSG_AUTHENTICATION_FAILED = 103; - private static final int MSG_ERROR = 104; - private static final int MSG_REMOVED = 105; - private static final int MSG_GET_FEATURE_COMPLETED = 106; - private static final int MSG_SET_FEATURE_COMPLETED = 107; - private static final int MSG_CHALLENGE_GENERATED = 108; - private static final int MSG_FACE_DETECTED = 109; - private static final int MSG_AUTHENTICATION_FRAME = 112; - private static final int MSG_ENROLLMENT_FRAME = 113; - private final IFaceService mService; private final Context mContext; private final IBinder mToken = new Binder(); - @Nullable private AuthenticationCallback mAuthenticationCallback; - @Nullable private FaceDetectionCallback mFaceDetectionCallback; - @Nullable private EnrollmentCallback mEnrollmentCallback; - @Nullable private RemovalCallback mRemovalCallback; - @Nullable private SetFeatureCallback mSetFeatureCallback; - @Nullable private GetFeatureCallback mGetFeatureCallback; - @Nullable private GenerateChallengeCallback mGenerateChallengeCallback; - private CryptoObject mCryptoObject; - private Face mRemovalFace; private Handler mHandler; private List<FaceSensorPropertiesInternal> mProps = new ArrayList<>(); + private HandlerExecutor mExecutor; + + private class FaceServiceReceiver extends IFaceServiceReceiver.Stub { + private final FaceCallback mFaceCallback; - private final IFaceServiceReceiver mServiceReceiver = new IFaceServiceReceiver.Stub() { + FaceServiceReceiver(FaceCallback faceCallback) { + mFaceCallback = faceCallback; + } @Override // binder call public void onEnrollResult(Face face, int remaining) { - mHandler.obtainMessage(MSG_ENROLL_RESULT, remaining, 0, face).sendToTarget(); + mExecutor.execute(() -> mFaceCallback.sendEnrollResult(remaining)); } @Override // binder call public void onAcquired(int acquireInfo, int vendorCode) { - mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, vendorCode).sendToTarget(); + mExecutor.execute(() -> mFaceCallback.sendAcquiredResult(mContext, acquireInfo, + vendorCode)); } @Override // binder call public void onAuthenticationSucceeded(Face face, int userId, boolean isStrongBiometric) { - mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, - isStrongBiometric ? 1 : 0, face).sendToTarget(); + mExecutor.execute(() -> mFaceCallback.sendAuthenticatedSucceeded(face, userId, + isStrongBiometric)); } @Override // binder call public void onFaceDetected(int sensorId, int userId, boolean isStrongBiometric) { - mHandler.obtainMessage(MSG_FACE_DETECTED, sensorId, userId, isStrongBiometric) - .sendToTarget(); + mExecutor.execute(() -> mFaceCallback.sendFaceDetected(sensorId, userId, + isStrongBiometric)); } @Override // binder call public void onAuthenticationFailed() { - mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget(); + mExecutor.execute(mFaceCallback::sendAuthenticatedFailed); } @Override // binder call public void onError(int error, int vendorCode) { - mHandler.obtainMessage(MSG_ERROR, error, vendorCode).sendToTarget(); + mExecutor.execute(() -> mFaceCallback.sendErrorResult(mContext, error, vendorCode)); } @Override // binder call public void onRemoved(Face face, int remaining) { - mHandler.obtainMessage(MSG_REMOVED, remaining, 0, face).sendToTarget(); + mExecutor.execute(() -> mFaceCallback.sendRemovedResult(face, remaining)); if (remaining == 0) { Settings.Secure.putIntForUser(mContext.getContentResolver(), Settings.Secure.FACE_UNLOCK_RE_ENROLL, 0, @@ -137,34 +121,31 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan @Override public void onFeatureSet(boolean success, int feature) { - mHandler.obtainMessage(MSG_SET_FEATURE_COMPLETED, feature, 0, success).sendToTarget(); + mExecutor.execute(() -> mFaceCallback.sendSetFeatureCompleted(success, feature)); } @Override public void onFeatureGet(boolean success, int[] features, boolean[] featureState) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = success; - args.arg2 = features; - args.arg3 = featureState; - mHandler.obtainMessage(MSG_GET_FEATURE_COMPLETED, args).sendToTarget(); + mExecutor.execute(() -> mFaceCallback.sendGetFeatureCompleted(success, features, + featureState)); } @Override public void onChallengeGenerated(int sensorId, int userId, long challenge) { - mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, sensorId, userId, challenge) - .sendToTarget(); + mExecutor.execute(() -> mFaceCallback.sendChallengeGenerated(sensorId, userId, + challenge)); } @Override public void onAuthenticationFrame(FaceAuthenticationFrame frame) { - mHandler.obtainMessage(MSG_AUTHENTICATION_FRAME, frame).sendToTarget(); + mExecutor.execute(() -> mFaceCallback.sendAuthenticationFrame(mContext, frame)); } @Override public void onEnrollmentFrame(FaceEnrollFrame frame) { - mHandler.obtainMessage(MSG_ENROLLMENT_FRAME, frame).sendToTarget(); + mExecutor.execute(() -> mFaceCallback.sendEnrollmentFrame(mContext, frame)); } - }; + } /** * @hide @@ -175,7 +156,8 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan if (mService == null) { Slog.v(TAG, "FaceAuthenticationManagerService was null"); } - mHandler = new MyHandler(context); + mHandler = context.getMainThreadHandler(); + mExecutor = new HandlerExecutor(mHandler); if (context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL) == PackageManager.PERMISSION_GRANTED) { addAuthenticatorsRegisteredCallback(new IFaceAuthenticatorsRegisteredCallback.Stub() { @@ -193,9 +175,11 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan */ private void useHandler(Handler handler) { if (handler != null) { - mHandler = new MyHandler(handler.getLooper()); - } else if (mHandler.getLooper() != mContext.getMainLooper()) { - mHandler = new MyHandler(mContext.getMainLooper()); + mHandler = handler; + mExecutor = new HandlerExecutor(mHandler); + } else if (mHandler != mContext.getMainThreadHandler()) { + mHandler = mContext.getMainThreadHandler(); + mExecutor = new HandlerExecutor(mHandler); } } @@ -249,13 +233,12 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan if (mService != null) { try { + final FaceCallback faceCallback = new FaceCallback(callback, crypto); useHandler(handler); - mAuthenticationCallback = callback; - mCryptoObject = crypto; final long operationId = crypto != null ? crypto.getOpId() : 0; Trace.beginSection("FaceManager#authenticate"); final long authId = mService.authenticate( - mToken, operationId, mServiceReceiver, options); + mToken, operationId, new FaceServiceReceiver(faceCallback), options); if (cancel != null) { cancel.setOnCancelListener(new OnAuthenticationCancelListener(authId)); } @@ -292,10 +275,11 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan options.setOpPackageName(mContext.getOpPackageName()); options.setAttributionTag(mContext.getAttributionTag()); - mFaceDetectionCallback = callback; + final FaceCallback faceCallback = new FaceCallback(callback); try { - final long authId = mService.detectFace(mToken, mServiceReceiver, options); + final long authId = mService.detectFace(mToken, + new FaceServiceReceiver(faceCallback), options); cancel.setOnCancelListener(new OnFaceDetectionCancelListener(authId)); } catch (RemoteException e) { Slog.w(TAG, "Remote exception when requesting finger detect", e); @@ -367,11 +351,11 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan if (mService != null) { try { - mEnrollmentCallback = callback; + final FaceCallback faceCallback = new FaceCallback(callback); Trace.beginSection("FaceManager#enroll"); final long enrollId = mService.enroll(userId, mToken, hardwareAuthToken, - mServiceReceiver, mContext.getOpPackageName(), disabledFeatures, - previewSurface, debugConsent, options); + new FaceServiceReceiver(faceCallback), mContext.getOpPackageName(), + disabledFeatures, previewSurface, debugConsent, options); if (cancel != null) { cancel.setOnCancelListener(new OnEnrollCancelListener(enrollId)); } @@ -419,10 +403,11 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan if (mService != null) { try { - mEnrollmentCallback = callback; + final FaceCallback faceCallback = new FaceCallback(callback); Trace.beginSection("FaceManager#enrollRemotely"); final long enrolId = mService.enrollRemotely(userId, mToken, hardwareAuthToken, - mServiceReceiver, mContext.getOpPackageName(), disabledFeatures); + new FaceServiceReceiver(faceCallback), mContext.getOpPackageName(), + disabledFeatures); if (cancel != null) { cancel.setOnCancelListener(new OnEnrollCancelListener(enrolId)); } @@ -455,9 +440,9 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan public void generateChallenge(int sensorId, int userId, GenerateChallengeCallback callback) { if (mService != null) { try { - mGenerateChallengeCallback = callback; - mService.generateChallenge(mToken, sensorId, userId, mServiceReceiver, - mContext.getOpPackageName()); + final FaceCallback faceCallback = new FaceCallback(callback); + mService.generateChallenge(mToken, sensorId, userId, + new FaceServiceReceiver(faceCallback), mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -528,9 +513,9 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan SetFeatureCallback callback) { if (mService != null) { try { - mSetFeatureCallback = callback; + final FaceCallback faceCallback = new FaceCallback(callback); mService.setFeature(mToken, userId, feature, enabled, hardwareAuthToken, - mServiceReceiver, mContext.getOpPackageName()); + new FaceServiceReceiver(faceCallback), mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -544,8 +529,8 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan public void getFeature(int userId, int feature, GetFeatureCallback callback) { if (mService != null) { try { - mGetFeatureCallback = callback; - mService.getFeature(mToken, userId, feature, mServiceReceiver, + final FaceCallback faceCallback = new FaceCallback(callback); + mService.getFeature(mToken, userId, feature, new FaceServiceReceiver(faceCallback), mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -566,10 +551,9 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan public void remove(Face face, int userId, RemovalCallback callback) { if (mService != null) { try { - mRemovalCallback = callback; - mRemovalFace = face; - mService.remove(mToken, face.getBiometricId(), userId, mServiceReceiver, - mContext.getOpPackageName()); + final FaceCallback faceCallback = new FaceCallback(callback, face); + mService.remove(mToken, face.getBiometricId(), userId, + new FaceServiceReceiver(faceCallback), mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -584,8 +568,9 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan public void removeAll(int userId, @NonNull RemovalCallback callback) { if (mService != null) { try { - mRemovalCallback = callback; - mService.removeAll(mToken, userId, mServiceReceiver, mContext.getOpPackageName()); + final FaceCallback faceCallback = new FaceCallback(callback); + mService.removeAll(mToken, userId, new FaceServiceReceiver(faceCallback), + mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1270,203 +1255,6 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan } } - private class MyHandler extends Handler { - private MyHandler(Context context) { - super(context.getMainLooper()); - } - - private MyHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(android.os.Message msg) { - Trace.beginSection("FaceManager#handleMessage: " + Integer.toString(msg.what)); - switch (msg.what) { - case MSG_ENROLL_RESULT: - sendEnrollResult((Face) msg.obj, msg.arg1 /* remaining */); - break; - case MSG_ACQUIRED: - sendAcquiredResult(msg.arg1 /* acquire info */, msg.arg2 /* vendorCode */); - break; - case MSG_AUTHENTICATION_SUCCEEDED: - sendAuthenticatedSucceeded((Face) msg.obj, msg.arg1 /* userId */, - msg.arg2 == 1 /* isStrongBiometric */); - break; - case MSG_AUTHENTICATION_FAILED: - sendAuthenticatedFailed(); - break; - case MSG_ERROR: - sendErrorResult(msg.arg1 /* errMsgId */, msg.arg2 /* vendorCode */); - break; - case MSG_REMOVED: - sendRemovedResult((Face) msg.obj, msg.arg1 /* remaining */); - break; - case MSG_SET_FEATURE_COMPLETED: - sendSetFeatureCompleted((boolean) msg.obj /* success */, - msg.arg1 /* feature */); - break; - case MSG_GET_FEATURE_COMPLETED: - SomeArgs args = (SomeArgs) msg.obj; - sendGetFeatureCompleted((boolean) args.arg1 /* success */, - (int[]) args.arg2 /* features */, - (boolean[]) args.arg3 /* featureState */); - args.recycle(); - break; - case MSG_CHALLENGE_GENERATED: - sendChallengeGenerated(msg.arg1 /* sensorId */, msg.arg2 /* userId */, - (long) msg.obj /* challenge */); - break; - case MSG_FACE_DETECTED: - sendFaceDetected(msg.arg1 /* sensorId */, msg.arg2 /* userId */, - (boolean) msg.obj /* isStrongBiometric */); - break; - case MSG_AUTHENTICATION_FRAME: - sendAuthenticationFrame((FaceAuthenticationFrame) msg.obj /* frame */); - break; - case MSG_ENROLLMENT_FRAME: - sendEnrollmentFrame((FaceEnrollFrame) msg.obj /* frame */); - break; - default: - Slog.w(TAG, "Unknown message: " + msg.what); - } - Trace.endSection(); - } - } - - private void sendSetFeatureCompleted(boolean success, int feature) { - if (mSetFeatureCallback == null) { - return; - } - mSetFeatureCallback.onCompleted(success, feature); - } - - private void sendGetFeatureCompleted(boolean success, int[] features, boolean[] featureState) { - if (mGetFeatureCallback == null) { - return; - } - mGetFeatureCallback.onCompleted(success, features, featureState); - } - - private void sendChallengeGenerated(int sensorId, int userId, long challenge) { - if (mGenerateChallengeCallback == null) { - return; - } - mGenerateChallengeCallback.onGenerateChallengeResult(sensorId, userId, challenge); - } - - private void sendFaceDetected(int sensorId, int userId, boolean isStrongBiometric) { - if (mFaceDetectionCallback == null) { - Slog.e(TAG, "sendFaceDetected, callback null"); - return; - } - mFaceDetectionCallback.onFaceDetected(sensorId, userId, isStrongBiometric); - } - - private void sendRemovedResult(Face face, int remaining) { - if (mRemovalCallback == null) { - return; - } - mRemovalCallback.onRemovalSucceeded(face, remaining); - } - - private void sendErrorResult(int errMsgId, int vendorCode) { - // emulate HAL 2.1 behavior and send real errMsgId - final int clientErrMsgId = errMsgId == FACE_ERROR_VENDOR - ? (vendorCode + FACE_ERROR_VENDOR_BASE) : errMsgId; - if (mEnrollmentCallback != null) { - mEnrollmentCallback.onEnrollmentError(clientErrMsgId, - getErrorString(mContext, errMsgId, vendorCode)); - } else if (mAuthenticationCallback != null) { - mAuthenticationCallback.onAuthenticationError(clientErrMsgId, - getErrorString(mContext, errMsgId, vendorCode)); - } else if (mRemovalCallback != null) { - mRemovalCallback.onRemovalError(mRemovalFace, clientErrMsgId, - getErrorString(mContext, errMsgId, vendorCode)); - } else if (mFaceDetectionCallback != null) { - mFaceDetectionCallback.onDetectionError(errMsgId); - mFaceDetectionCallback = null; - } - } - - private void sendEnrollResult(Face face, int remaining) { - if (mEnrollmentCallback != null) { - mEnrollmentCallback.onEnrollmentProgress(remaining); - } - } - - private void sendAuthenticatedSucceeded(Face face, int userId, boolean isStrongBiometric) { - if (mAuthenticationCallback != null) { - final AuthenticationResult result = - new AuthenticationResult(mCryptoObject, face, userId, isStrongBiometric); - mAuthenticationCallback.onAuthenticationSucceeded(result); - } - } - - private void sendAuthenticatedFailed() { - if (mAuthenticationCallback != null) { - mAuthenticationCallback.onAuthenticationFailed(); - } - } - - private void sendAcquiredResult(int acquireInfo, int vendorCode) { - if (mAuthenticationCallback != null) { - final FaceAuthenticationFrame frame = new FaceAuthenticationFrame( - new FaceDataFrame(acquireInfo, vendorCode)); - sendAuthenticationFrame(frame); - } else if (mEnrollmentCallback != null) { - final FaceEnrollFrame frame = new FaceEnrollFrame( - null /* cell */, - FaceEnrollStages.UNKNOWN, - new FaceDataFrame(acquireInfo, vendorCode)); - sendEnrollmentFrame(frame); - } - } - - private void sendAuthenticationFrame(@Nullable FaceAuthenticationFrame frame) { - if (frame == null) { - Slog.w(TAG, "Received null authentication frame"); - } else if (mAuthenticationCallback != null) { - // TODO(b/178414967): Send additional frame data to callback - final int acquireInfo = frame.getData().getAcquiredInfo(); - final int vendorCode = frame.getData().getVendorCode(); - final int helpCode = getHelpCode(acquireInfo, vendorCode); - final String helpMessage = getAuthHelpMessage(mContext, acquireInfo, vendorCode); - mAuthenticationCallback.onAuthenticationAcquired(acquireInfo); - - // Ensure that only non-null help messages are sent. - if (helpMessage != null) { - mAuthenticationCallback.onAuthenticationHelp(helpCode, helpMessage); - } - } - } - - private void sendEnrollmentFrame(@Nullable FaceEnrollFrame frame) { - if (frame == null) { - Slog.w(TAG, "Received null enrollment frame"); - } else if (mEnrollmentCallback != null) { - final FaceDataFrame data = frame.getData(); - final int acquireInfo = data.getAcquiredInfo(); - final int vendorCode = data.getVendorCode(); - final int helpCode = getHelpCode(acquireInfo, vendorCode); - final String helpMessage = getEnrollHelpMessage(mContext, acquireInfo, vendorCode); - mEnrollmentCallback.onEnrollmentFrame( - helpCode, - helpMessage, - frame.getCell(), - frame.getStage(), - data.getPan(), - data.getTilt(), - data.getDistance()); - } - } - - private static int getHelpCode(int acquireInfo, int vendorCode) { - return acquireInfo == FACE_ACQUIRED_VENDOR - ? vendorCode + FACE_ACQUIRED_VENDOR_BASE - : acquireInfo; - } - /** * @hide */ diff --git a/core/java/android/hardware/fingerprint/FingerprintCallback.java b/core/java/android/hardware/fingerprint/FingerprintCallback.java new file mode 100644 index 000000000000..e4fbe6e09709 --- /dev/null +++ b/core/java/android/hardware/fingerprint/FingerprintCallback.java @@ -0,0 +1,299 @@ +/* + * 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(); + } + } +} diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 81e321d96aa6..37f2fb2a538a 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -25,6 +25,8 @@ import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; import static android.Manifest.permission.USE_FINGERPRINT; import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_NONE; import static android.hardware.biometrics.Flags.FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT; +import static android.hardware.fingerprint.FingerprintCallback.REMOVE_ALL; +import static android.hardware.fingerprint.FingerprintCallback.REMOVE_SINGLE; import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON; import static com.android.internal.util.FrameworkStatsLog.AUTH_DEPRECATED_APIUSED__DEPRECATED_API__API_FINGERPRINT_MANAGER_AUTHENTICATE; @@ -57,9 +59,9 @@ import android.os.Build; import android.os.CancellationSignal; import android.os.CancellationSignal.OnCancelListener; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.IBinder; import android.os.IRemoteCallback; -import android.os.Looper; import android.os.PowerManager; import android.os.RemoteException; import android.os.UserHandle; @@ -94,19 +96,6 @@ import javax.crypto.Mac; @RequiresFeature(PackageManager.FEATURE_FINGERPRINT) public class FingerprintManager implements BiometricAuthenticator, BiometricFingerprintConstants { private static final String TAG = "FingerprintManager"; - private static final boolean DEBUG = true; - private static final int MSG_ENROLL_RESULT = 100; - private static final int MSG_ACQUIRED = 101; - private static final int MSG_AUTHENTICATION_SUCCEEDED = 102; - private static final int MSG_AUTHENTICATION_FAILED = 103; - private static final int MSG_ERROR = 104; - private static final int MSG_REMOVED = 105; - private static final int MSG_CHALLENGE_GENERATED = 106; - private static final int MSG_FINGERPRINT_DETECTED = 107; - private static final int MSG_UDFPS_POINTER_DOWN = 108; - private static final int MSG_UDFPS_POINTER_UP = 109; - private static final int MSG_POWER_BUTTON_PRESSED = 110; - private static final int MSG_UDFPS_OVERLAY_SHOWN = 111; /** * @hide @@ -148,34 +137,14 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing */ public static final int SENSOR_ID_ANY = -1; - private static class RemoveTracker { - static final int REMOVE_SINGLE = 1; - static final int REMOVE_ALL = 2; - @IntDef({REMOVE_SINGLE, REMOVE_ALL}) - @interface RemoveRequest {} + private final IFingerprintService mService; + private final Context mContext; + private final IBinder mToken = new Binder(); - final @RemoveRequest int mRemoveRequest; - @Nullable final Fingerprint mSingleFingerprint; - - RemoveTracker(@RemoveRequest int request, @Nullable Fingerprint fingerprint) { - mRemoveRequest = request; - mSingleFingerprint = fingerprint; - } - } - - private IFingerprintService mService; - private Context mContext; - private IBinder mToken = new Binder(); - private AuthenticationCallback mAuthenticationCallback; - private FingerprintDetectionCallback mFingerprintDetectionCallback; - private EnrollmentCallback mEnrollmentCallback; - private RemovalCallback mRemovalCallback; - private GenerateChallengeCallback mGenerateChallengeCallback; - private CryptoObject mCryptoObject; - @Nullable private RemoveTracker mRemoveTracker; private Handler mHandler; @Nullable private float[] mEnrollStageThresholds; private List<FingerprintSensorPropertiesInternal> mProps = new ArrayList<>(); + private HandlerExecutor mExecutor; /** * Retrieves a list of properties for all fingerprint sensors on the device. @@ -395,7 +364,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing * @deprecated See {@link android.hardware.biometrics.BiometricPrompt.AuthenticationCallback} */ @Deprecated - public static abstract class AuthenticationCallback + public abstract static class AuthenticationCallback extends BiometricAuthenticator.AuthenticationCallback { /** * Called when an unrecoverable error has been encountered and the operation is complete. @@ -479,7 +448,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing * * @hide */ - public static abstract class EnrollmentCallback { + public abstract static class EnrollmentCallback { /** * Called when an unrecoverable error has been encountered and the operation is complete. * No further callbacks will be made on this object. @@ -536,7 +505,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing * * @hide */ - public static abstract class RemovalCallback { + public abstract static class RemovalCallback { /** * Called when the given fingerprint can't be removed. * @param fp The fingerprint that the call attempted to remove @@ -559,7 +528,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing /** * @hide */ - public static abstract class LockoutResetCallback { + public abstract static class LockoutResetCallback { /** * Called when lockout period expired and clients are allowed to listen for fingerprint @@ -584,9 +553,11 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing */ private void useHandler(Handler handler) { if (handler != null) { - mHandler = new MyHandler(handler.getLooper()); - } else if (mHandler.getLooper() != mContext.getMainLooper()) { - mHandler = new MyHandler(mContext.getMainLooper()); + mHandler = handler; + mExecutor = new HandlerExecutor(mHandler); + } else if (mHandler != mContext.getMainThreadHandler()) { + mHandler = mContext.getMainThreadHandler(); + mExecutor = new HandlerExecutor(mHandler); } } @@ -676,11 +647,12 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing if (mService != null) { try { + final FingerprintCallback fingerprintCallback = new FingerprintCallback(callback, + crypto); useHandler(handler); - mAuthenticationCallback = callback; - mCryptoObject = crypto; final long operationId = crypto != null ? crypto.getOpId() : 0; - final long authId = mService.authenticate(mToken, operationId, mServiceReceiver, options); + final long authId = mService.authenticate(mToken, operationId, + new FingerprintServiceReceiver(fingerprintCallback), options); if (cancel != null) { cancel.setOnCancelListener(new OnAuthenticationCancelListener(authId)); } @@ -715,10 +687,11 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing options.setOpPackageName(mContext.getOpPackageName()); options.setAttributionTag(mContext.getAttributionTag()); - mFingerprintDetectionCallback = callback; + final FingerprintCallback fingerprintCallback = new FingerprintCallback(callback); try { - final long authId = mService.detectFingerprint(mToken, mServiceReceiver, options); + final long authId = mService.detectFingerprint(mToken, + new FingerprintServiceReceiver(fingerprintCallback), options); cancel.setOnCancelListener(new OnFingerprintDetectionCancelListener(authId)); } catch (RemoteException e) { Slog.w(TAG, "Remote exception when requesting finger detect", e); @@ -767,9 +740,10 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing if (mService != null) { try { - mEnrollmentCallback = callback; + final FingerprintCallback fingerprintCallback = new FingerprintCallback(callback); final long enrollId = mService.enroll(mToken, hardwareAuthToken, userId, - mServiceReceiver, mContext.getOpPackageName(), enrollReason, options); + new FingerprintServiceReceiver(fingerprintCallback), + mContext.getOpPackageName(), enrollReason, options); if (cancel != null) { cancel.setOnCancelListener(new OnEnrollCancelListener(enrollId)); } @@ -799,12 +773,13 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing @RequiresPermission(MANAGE_FINGERPRINT) public void generateChallenge(int sensorId, int userId, GenerateChallengeCallback callback) { if (mService != null) try { - mGenerateChallengeCallback = callback; - mService.generateChallenge(mToken, sensorId, userId, mServiceReceiver, - mContext.getOpPackageName()); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + final FingerprintCallback fingerprintCallback = new FingerprintCallback(callback); + mService.generateChallenge(mToken, sensorId, userId, + new FingerprintServiceReceiver(fingerprintCallback), + mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** @@ -875,13 +850,14 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing @RequiresPermission(MANAGE_FINGERPRINT) public void remove(Fingerprint fp, int userId, RemovalCallback callback) { if (mService != null) try { - mRemovalCallback = callback; - mRemoveTracker = new RemoveTracker(RemoveTracker.REMOVE_SINGLE, fp); - mService.remove(mToken, fp.getBiometricId(), userId, mServiceReceiver, - mContext.getOpPackageName()); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + final FingerprintCallback fingerprintCallback = new FingerprintCallback(callback, + REMOVE_SINGLE, fp); + mService.remove(mToken, fp.getBiometricId(), userId, + new FingerprintServiceReceiver(fingerprintCallback), + mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** @@ -892,9 +868,11 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing public void removeAll(int userId, @NonNull RemovalCallback callback) { if (mService != null) { try { - mRemovalCallback = callback; - mRemoveTracker = new RemoveTracker(RemoveTracker.REMOVE_ALL, null /* fp */); - mService.removeAll(mToken, userId, mServiceReceiver, mContext.getOpPackageName()); + final FingerprintCallback fingerprintCallback = new FingerprintCallback(callback, + REMOVE_ALL, null); + mService.removeAll(mToken, userId, + new FingerprintServiceReceiver(fingerprintCallback), + mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1162,7 +1140,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing @RequiresPermission(USE_BIOMETRIC_INTERNAL) public void onPowerPressed() { Slog.i(TAG, "onPowerPressed"); - mHandler.obtainMessage(MSG_POWER_BUTTON_PRESSED).sendToTarget(); + mExecutor.execute(() -> sendPowerPressed()); } /** @@ -1346,199 +1324,6 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } } - private class MyHandler extends Handler { - private MyHandler(Context context) { - super(context.getMainLooper()); - } - - private MyHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(android.os.Message msg) { - switch (msg.what) { - case MSG_ENROLL_RESULT: - sendEnrollResult((Fingerprint) msg.obj, msg.arg1 /* remaining */); - break; - case MSG_ACQUIRED: - sendAcquiredResult(msg.arg1 /* acquire info */, - msg.arg2 /* vendorCode */); - break; - case MSG_AUTHENTICATION_SUCCEEDED: - sendAuthenticatedSucceeded((Fingerprint) msg.obj, msg.arg1 /* userId */, - msg.arg2 == 1 /* isStrongBiometric */); - break; - case MSG_AUTHENTICATION_FAILED: - sendAuthenticatedFailed(); - break; - case MSG_ERROR: - sendErrorResult(msg.arg1 /* errMsgId */, msg.arg2 /* vendorCode */); - break; - case MSG_REMOVED: - sendRemovedResult((Fingerprint) msg.obj, msg.arg1 /* remaining */); - break; - case MSG_CHALLENGE_GENERATED: - sendChallengeGenerated(msg.arg1 /* sensorId */, msg.arg2 /* userId */, - (long) msg.obj /* challenge */); - break; - case MSG_FINGERPRINT_DETECTED: - sendFingerprintDetected(msg.arg1 /* sensorId */, msg.arg2 /* userId */, - (boolean) msg.obj /* isStrongBiometric */); - break; - case MSG_UDFPS_POINTER_DOWN: - sendUdfpsPointerDown(msg.arg1 /* sensorId */); - break; - case MSG_UDFPS_POINTER_UP: - sendUdfpsPointerUp(msg.arg1 /* sensorId */); - break; - case MSG_POWER_BUTTON_PRESSED: - sendPowerPressed(); - break; - case MSG_UDFPS_OVERLAY_SHOWN: - sendUdfpsOverlayShown(); - default: - Slog.w(TAG, "Unknown message: " + msg.what); - - } - } - } - - private void sendRemovedResult(Fingerprint fingerprint, int remaining) { - if (mRemovalCallback == null) { - return; - } - - if (mRemoveTracker == null) { - Slog.w(TAG, "Removal tracker is null"); - return; - } - - if (mRemoveTracker.mRemoveRequest == RemoveTracker.REMOVE_SINGLE) { - if (fingerprint == null) { - Slog.e(TAG, "Received MSG_REMOVED, but fingerprint is null"); - return; - } - - if (mRemoveTracker.mSingleFingerprint == null) { - Slog.e(TAG, "Missing fingerprint"); - return; - } - - final int fingerId = fingerprint.getBiometricId(); - int reqFingerId = mRemoveTracker.mSingleFingerprint.getBiometricId(); - if (reqFingerId != 0 && fingerId != 0 && fingerId != reqFingerId) { - Slog.w(TAG, "Finger id didn't match: " + fingerId + " != " + reqFingerId); - return; - } - } - - mRemovalCallback.onRemovalSucceeded(fingerprint, remaining); - } - - private void sendEnrollResult(Fingerprint fp, int remaining) { - if (mEnrollmentCallback != null) { - mEnrollmentCallback.onEnrollmentProgress(remaining); - } - } - - private void sendAuthenticatedSucceeded(Fingerprint fp, int userId, boolean isStrongBiometric) { - if (mAuthenticationCallback != null) { - final AuthenticationResult result = - new AuthenticationResult(mCryptoObject, fp, userId, isStrongBiometric); - mAuthenticationCallback.onAuthenticationSucceeded(result); - } - } - - private void sendAuthenticatedFailed() { - if (mAuthenticationCallback != null) { - mAuthenticationCallback.onAuthenticationFailed(); - } - } - - private void sendAcquiredResult(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(mContext, 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 != BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START) { - mAuthenticationCallback.onAuthenticationHelp(clientInfo, msg); - } - } - } - - private void sendErrorResult(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(mContext, errMsgId, vendorCode)); - } else if (mAuthenticationCallback != null) { - mAuthenticationCallback.onAuthenticationError(clientErrMsgId, - getErrorString(mContext, errMsgId, vendorCode)); - } else if (mRemovalCallback != null) { - final Fingerprint fp = mRemoveTracker != null - ? mRemoveTracker.mSingleFingerprint : null; - mRemovalCallback.onRemovalError(fp, clientErrMsgId, - getErrorString(mContext, errMsgId, vendorCode)); - } else if (mFingerprintDetectionCallback != null) { - mFingerprintDetectionCallback.onDetectionError(errMsgId); - mFingerprintDetectionCallback = null; - } - } - - private void sendChallengeGenerated(int sensorId, int userId, long challenge) { - if (mGenerateChallengeCallback == null) { - Slog.e(TAG, "sendChallengeGenerated, callback null"); - return; - } - mGenerateChallengeCallback.onChallengeGenerated(sensorId, userId, challenge); - } - - private void sendFingerprintDetected(int sensorId, int userId, boolean isStrongBiometric) { - if (mFingerprintDetectionCallback == null) { - Slog.e(TAG, "sendFingerprintDetected, callback null"); - return; - } - mFingerprintDetectionCallback.onFingerprintDetected(sensorId, userId, isStrongBiometric); - } - - private void sendUdfpsPointerDown(int sensorId) { - if (mAuthenticationCallback == null) { - Slog.e(TAG, "sendUdfpsPointerDown, callback null"); - } else { - mAuthenticationCallback.onUdfpsPointerDown(sensorId); - } - - if (mEnrollmentCallback != null) { - mEnrollmentCallback.onUdfpsPointerDown(sensorId); - } - } - - private void sendUdfpsPointerUp(int sensorId) { - if (mAuthenticationCallback == null) { - Slog.e(TAG, "sendUdfpsPointerUp, callback null"); - } else { - mAuthenticationCallback.onUdfpsPointerUp(sensorId); - } - if (mEnrollmentCallback != null) { - mEnrollmentCallback.onUdfpsPointerUp(sensorId); - } - } - private void sendPowerPressed() { try { mService.onPowerPressed(); @@ -1547,12 +1332,6 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } } - private void sendUdfpsOverlayShown() { - if (mEnrollmentCallback != null) { - mEnrollmentCallback.onUdfpsOverlayShown(); - } - } - /** * @hide */ @@ -1562,7 +1341,6 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing if (mService == null) { Slog.v(TAG, "FingerprintService was null"); } - mHandler = new MyHandler(context); if (context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL) == PackageManager.PERMISSION_GRANTED) { addAuthenticatorsRegisteredCallback( @@ -1574,6 +1352,8 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } }); } + mHandler = context.getMainThreadHandler(); + mExecutor = new HandlerExecutor(mHandler); } private int getCurrentUserId() { @@ -1773,66 +1553,72 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing return null; } - private IFingerprintServiceReceiver mServiceReceiver = new IFingerprintServiceReceiver.Stub() { + class FingerprintServiceReceiver extends IFingerprintServiceReceiver.Stub { + private final FingerprintCallback mFingerprintCallback; + + FingerprintServiceReceiver(FingerprintCallback fingerprintCallback) { + mFingerprintCallback = fingerprintCallback; + } @Override // binder call public void onEnrollResult(Fingerprint fp, int remaining) { - mHandler.obtainMessage(MSG_ENROLL_RESULT, remaining, 0, fp).sendToTarget(); + mExecutor.execute(() -> mFingerprintCallback.sendEnrollResult(remaining)); } @Override // binder call public void onAcquired(int acquireInfo, int vendorCode) { - mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, vendorCode).sendToTarget(); + mExecutor.execute(() -> mFingerprintCallback.sendAcquiredResult(mContext, acquireInfo, + vendorCode)); } @Override // binder call public void onAuthenticationSucceeded(Fingerprint fp, int userId, boolean isStrongBiometric) { - mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, isStrongBiometric ? 1 : 0, - fp).sendToTarget(); + mExecutor.execute(() -> mFingerprintCallback.sendAuthenticatedSucceeded(fp, userId, + isStrongBiometric)); } @Override public void onFingerprintDetected(int sensorId, int userId, boolean isStrongBiometric) { - mHandler.obtainMessage(MSG_FINGERPRINT_DETECTED, sensorId, userId, isStrongBiometric) - .sendToTarget(); + mExecutor.execute(() -> mFingerprintCallback.sendFingerprintDetected(sensorId, userId, + isStrongBiometric)); } @Override // binder call public void onAuthenticationFailed() { - mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget(); + mExecutor.execute(mFingerprintCallback::sendAuthenticatedFailed); } @Override // binder call public void onError(int error, int vendorCode) { - mHandler.obtainMessage(MSG_ERROR, error, vendorCode).sendToTarget(); + mExecutor.execute(() -> mFingerprintCallback.sendErrorResult(mContext, error, + vendorCode)); } @Override // binder call public void onRemoved(Fingerprint fp, int remaining) { - mHandler.obtainMessage(MSG_REMOVED, remaining, 0, fp).sendToTarget(); + mExecutor.execute(() -> mFingerprintCallback.sendRemovedResult(fp, remaining)); } @Override // binder call public void onChallengeGenerated(int sensorId, int userId, long challenge) { - mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, sensorId, userId, challenge) - .sendToTarget(); + mExecutor.execute(() -> mFingerprintCallback.sendChallengeGenerated(challenge, sensorId, + userId)); } @Override // binder call public void onUdfpsPointerDown(int sensorId) { - mHandler.obtainMessage(MSG_UDFPS_POINTER_DOWN, sensorId, 0).sendToTarget(); + mExecutor.execute(() -> mFingerprintCallback.sendUdfpsPointerDown(sensorId)); } @Override // binder call public void onUdfpsPointerUp(int sensorId) { - mHandler.obtainMessage(MSG_UDFPS_POINTER_UP, sensorId, 0).sendToTarget(); + mExecutor.execute(() -> mFingerprintCallback.sendUdfpsPointerUp(sensorId)); } @Override public void onUdfpsOverlayShown() { - mHandler.obtainMessage(MSG_UDFPS_OVERLAY_SHOWN).sendToTarget(); + mExecutor.execute(mFingerprintCallback::sendUdfpsOverlayShown); } - }; - + } } diff --git a/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java b/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java index 3a872b50af75..5bf88da1b3bb 100644 --- a/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java +++ b/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java @@ -28,7 +28,9 @@ import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.atMost; import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -97,7 +99,7 @@ public class FaceManagerTest { mLooper = new TestLooper(); mHandler = new Handler(mLooper.getLooper()); - when(mContext.getMainLooper()).thenReturn(mLooper.getLooper()); + when(mContext.getMainThreadHandler()).thenReturn(mHandler); when(mContext.getOpPackageName()).thenReturn(PACKAGE_NAME); when(mContext.getAttributionTag()).thenReturn(ATTRIBUTION_TAG); when(mContext.getApplicationInfo()).thenReturn(new ApplicationInfo()); @@ -210,6 +212,39 @@ public class FaceManagerTest { verify(mFaceDetectionCallback).onDetectionError(anyInt()); } + @Test + public void authenticate_onErrorCanceled() throws RemoteException { + final FaceManager.AuthenticationCallback authenticationCallback1 = mock( + FaceManager.AuthenticationCallback.class); + final FaceManager.AuthenticationCallback authenticationCallback2 = mock( + FaceManager.AuthenticationCallback.class); + + final ArgumentCaptor<IFaceServiceReceiver> faceServiceReceiverArgumentCaptor = + ArgumentCaptor.forClass(IFaceServiceReceiver.class); + + mFaceManager.authenticate(null, new CancellationSignal(), + authenticationCallback1, mHandler, + new FaceAuthenticateOptions.Builder().build()); + mFaceManager.authenticate(null, new CancellationSignal(), + authenticationCallback2, mHandler, + new FaceAuthenticateOptions.Builder().build()); + + verify(mService, times(2)).authenticate(any(IBinder.class), eq(0L), + faceServiceReceiverArgumentCaptor.capture(), any()); + + final List<IFaceServiceReceiver> faceServiceReceivers = + faceServiceReceiverArgumentCaptor.getAllValues(); + faceServiceReceivers.get(0).onError(5 /* error */, 0 /* vendorCode */); + mLooper.dispatchAll(); + + verify(authenticationCallback1).onAuthenticationError(eq(5), anyString()); + verify(authenticationCallback2, never()).onAuthenticationError(anyInt(), anyString()); + + faceServiceReceivers.get(1).onError(5 /* error */, 0 /* vendorCode */); + mLooper.dispatchAll(); + verify(authenticationCallback2).onAuthenticationError(eq(5), anyString()); + } + private void initializeProperties() throws RemoteException { verify(mService).addAuthenticatorsRegisteredCallback(mCaptor.capture()); diff --git a/core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java b/core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java index ce7d6a95c2f4..c3ea7d38e2f1 100644 --- a/core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java +++ b/core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java @@ -26,7 +26,9 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -93,6 +95,7 @@ public class FingerprintManagerTest { mHandler = new Handler(mLooper.getLooper()); when(mContext.getMainLooper()).thenReturn(mLooper.getLooper()); + when(mContext.getMainThreadHandler()).thenReturn(mHandler); when(mContext.getOpPackageName()).thenReturn(PACKAGE_NAME); when(mContext.getAttributionTag()).thenReturn(ATTRIBUTION_TAG); when(mContext.getApplicationInfo()).thenReturn(new ApplicationInfo()); @@ -187,4 +190,38 @@ public class FingerprintManagerTest { verify(mFingerprintDetectionCallback).onDetectionError(anyInt()); } + + @Test + public void authenticate_onErrorCanceled() throws RemoteException { + final FingerprintManager.AuthenticationCallback authenticationCallback1 = mock( + FingerprintManager.AuthenticationCallback.class); + final FingerprintManager.AuthenticationCallback authenticationCallback2 = mock( + FingerprintManager.AuthenticationCallback.class); + + final ArgumentCaptor<IFingerprintServiceReceiver> fingerprintServiceReceiverArgumentCaptor = + ArgumentCaptor.forClass(IFingerprintServiceReceiver.class); + + mFingerprintManager.authenticate(null, new CancellationSignal(), + authenticationCallback1, mHandler, + new FingerprintAuthenticateOptions.Builder().build()); + mFingerprintManager.authenticate(null, new CancellationSignal(), + authenticationCallback2, mHandler, + new FingerprintAuthenticateOptions.Builder().build()); + + verify(mService, times(2)).authenticate(any(IBinder.class), eq(0L), + fingerprintServiceReceiverArgumentCaptor.capture(), any()); + + final List<IFingerprintServiceReceiver> fingerprintServiceReceivers = + fingerprintServiceReceiverArgumentCaptor.getAllValues(); + fingerprintServiceReceivers.get(0).onError(5 /* error */, 0 /* vendorCode */); + mLooper.dispatchAll(); + + verify(authenticationCallback1).onAuthenticationError(eq(5), anyString()); + verify(authenticationCallback2, never()).onAuthenticationError(anyInt(), anyString()); + + fingerprintServiceReceivers.get(1).onError(5 /* error */, 0 /* vendorCode */); + mLooper.dispatchAll(); + + verify(authenticationCallback2).onAuthenticationError(eq(5), anyString()); + } } |