summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Diya Bera <diyab@google.com> 2024-04-16 13:00:40 -0700
committer Diya Bera <diyab@google.com> 2024-04-17 09:39:56 -0700
commitbbd8a6a61cec52f7c96f81beef98c23c042f8562 (patch)
tree1eb127aebd0074a34aaf0e03cd60a57d680913b2
parent3582d53076f99e2a46ad7a697af277319a016b6c (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
-rw-r--r--core/java/android/hardware/face/FaceCallback.java321
-rw-r--r--core/java/android/hardware/face/FaceManager.java326
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintCallback.java299
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java358
-rw-r--r--core/tests/coretests/src/android/hardware/face/FaceManagerTest.java37
-rw-r--r--core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java37
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());
+ }
}