From dc4989389186c85c5ce113e3d3316245cd803814 Mon Sep 17 00:00:00 2001 From: Joe Bolinger Date: Thu, 19 Aug 2021 14:06:53 -0700 Subject: [PATCH 1/2] Check cancel status whenever cookies are received. Bug: 194405579 Bug: 189451155 Test: atest AuthSessionTest Change-Id: I8073b8b22d06770ef0f2838d61ac3f27893a26e9 --- .../server/biometrics/AuthSession.java | 10 +++++++ .../server/biometrics/AuthSessionTest.java | 26 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java index bdde980f5cae3..4c616062918bf 100644 --- a/services/core/java/com/android/server/biometrics/AuthSession.java +++ b/services/core/java/com/android/server/biometrics/AuthSession.java @@ -142,6 +142,7 @@ public final class AuthSession implements IBinder.DeathRecipient { private @BiometricMultiSensorMode int mMultiSensorMode; private @MultiSensorState int mMultiSensorState; private int[] mSensors; + private boolean mCancelled; // For explicit confirmation, do not send to keystore until the user has confirmed // the authentication. private byte[] mTokenEscrow; @@ -187,6 +188,7 @@ public final class AuthSession implements IBinder.DeathRecipient { mPromptInfo = promptInfo; mDebugEnabled = debugEnabled; mFingerprintSensorProperties = fingerprintSensorProperties; + mCancelled = false; try { mClientReceiver.asBinder().linkToDeath(this, 0 /* flags */); @@ -270,6 +272,11 @@ public final class AuthSession implements IBinder.DeathRecipient { } void onCookieReceived(int cookie) { + if (mCancelled) { + Slog.w(TAG, "Received cookie but already cancelled (ignoring): " + cookie); + return; + } + for (BiometricSensor sensor : mPreAuthInfo.eligibleSensors) { sensor.goToStateCookieReturnedIfCookieMatches(cookie); } @@ -775,6 +782,8 @@ public final class AuthSession implements IBinder.DeathRecipient { * @return true if this AuthSession is finished, e.g. should be set to null */ boolean onCancelAuthSession(boolean force) { + mCancelled = true; + final boolean authStarted = mState == STATE_AUTH_CALLED || mState == STATE_AUTH_STARTED || mState == STATE_AUTH_STARTED_UI_SHOWING; @@ -901,6 +910,7 @@ public final class AuthSession implements IBinder.DeathRecipient { @Override public String toString() { return "State: " + mState + + ", cancelled: " + mCancelled + ", isCrypto: " + isCrypto() + ", PreAuthInfo: " + mPreAuthInfo; } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java index 2892bf58a7d2a..1903bc88db357 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java @@ -184,6 +184,32 @@ public class AuthSessionTest { } } + @Test + public void testCancelReducesAppetiteForCookies() throws Exception { + setupFace(0 /* id */, false /* confirmationAlwaysRequired */, + mock(IBiometricAuthenticator.class)); + setupFingerprint(1 /* id */, FingerprintSensorProperties.TYPE_UDFPS_OPTICAL); + + final AuthSession session = createAuthSession(mSensors, + false /* checkDevicePolicyManager */, + Authenticators.BIOMETRIC_STRONG, + 44 /* operationId */, + 2 /* userId */); + + session.goToInitialState(); + + for (BiometricSensor sensor : session.mPreAuthInfo.eligibleSensors) { + assertEquals(BiometricSensor.STATE_WAITING_FOR_COOKIE, sensor.getSensorState()); + } + + session.onCancelAuthSession(false /* force */); + + for (BiometricSensor sensor : session.mPreAuthInfo.eligibleSensors) { + session.onCookieReceived(sensor.getCookie()); + assertEquals(BiometricSensor.STATE_CANCELING, sensor.getSensorState()); + } + } + @Test public void testMultiAuth_singleSensor_fingerprintSensorStartsAfterDialogAnimationCompletes() throws Exception { From bdd477a5d1db803cbd2f1b54087d45a7065794be Mon Sep 17 00:00:00 2001 From: Joe Bolinger Date: Thu, 22 Jul 2021 16:08:23 -0700 Subject: [PATCH 2/2] Add requestId to authentication entry points. The CancellationSignal passed into the authentication methods is not associated with the request and can be used to cancel the current operation, even if it is more recent. The new id prevents outdated signals from being using. Note that there are still issues with the Callbacks that are not addressed (see bug for details). Bug: 194405579 Bug: 189451155 Bug: 191716671 Test: atest com.android.systemui.biometrics com.android.server.biometrics Change-Id: Id71be912cc88ae90df8087eb8f1a6fc3e110f883 --- .../hardware/biometrics/BiometricPrompt.java | 34 ++++-- .../hardware/biometrics/IAuthService.aidl | 11 +- .../biometrics/IBiometricAuthenticator.aidl | 6 +- .../biometrics/IBiometricService.aidl | 11 +- .../android/hardware/face/FaceManager.java | 63 +++++----- .../android/hardware/face/IFaceService.aidl | 26 ++-- .../fingerprint/FingerprintManager.java | 48 ++++---- .../fingerprint/IFingerprintService.aidl | 27 +++-- .../internal/statusbar/IStatusBar.aidl | 2 +- .../internal/statusbar/IStatusBarService.aidl | 3 +- .../biometrics/AuthContainerView.java | 7 ++ .../systemui/biometrics/AuthController.java | 10 +- .../systemui/statusbar/CommandQueue.java | 10 +- .../biometrics/AuthControllerTest.java | 5 +- .../systemui/statusbar/CommandQueueTest.java | 9 +- .../server/biometrics/AuthService.java | 14 +-- .../server/biometrics/AuthSession.java | 25 +++- .../server/biometrics/BiometricSensor.java | 9 +- .../server/biometrics/BiometricService.java | 59 ++++++--- .../biometrics/sensors/BaseClientMonitor.java | 26 ++++ .../sensors/BiometricScheduler.java | 47 +++++--- .../sensors/face/FaceAuthenticator.java | 9 +- .../biometrics/sensors/face/FaceService.java | 40 +++--- .../sensors/face/ServiceProvider.java | 13 +- .../face/aidl/FaceAuthenticationClient.java | 4 +- .../sensors/face/aidl/FaceDetectClient.java | 4 +- .../sensors/face/aidl/FaceProvider.java | 44 +++++-- .../biometrics/sensors/face/hidl/Face10.java | 37 ++++-- .../face/hidl/FaceAuthenticationClient.java | 4 +- .../fingerprint/FingerprintAuthenticator.java | 9 +- .../fingerprint/FingerprintService.java | 53 ++++---- .../sensors/fingerprint/ServiceProvider.java | 10 +- .../aidl/FingerprintAuthenticationClient.java | 4 +- .../aidl/FingerprintDetectClient.java | 4 +- .../fingerprint/aidl/FingerprintProvider.java | 40 ++++-- .../fingerprint/hidl/Fingerprint21.java | 39 ++++-- .../hidl/FingerprintAuthenticationClient.java | 4 +- .../hidl/FingerprintDetectClient.java | 4 +- .../sensors/iris/IrisAuthenticator.java | 4 +- .../statusbar/StatusBarManagerService.java | 5 +- .../server/biometrics/AuthSessionTest.java | 16 ++- .../biometrics/BiometricServiceTest.java | 73 ++++++----- .../sensors/BiometricSchedulerTest.java | 114 +++++++++++++++++- 43 files changed, 677 insertions(+), 309 deletions(-) diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index 3f3db29ed0fd7..c8c122da4ab84 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -438,9 +438,16 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan } private class OnAuthenticationCancelListener implements CancellationSignal.OnCancelListener { + private final long mAuthRequestId; + + OnAuthenticationCancelListener(long id) { + mAuthRequestId = id; + } + @Override public void onCancel() { - cancelAuthentication(); + Log.d(TAG, "Cancel BP authentication requested for: " + mAuthRequestId); + cancelAuthentication(mAuthRequestId); } } @@ -853,10 +860,12 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan * @param userId The user to authenticate * @param operationId The keystore operation associated with authentication * + * @return A requestId that can be used to cancel this operation. + * * @hide */ @RequiresPermission(USE_BIOMETRIC_INTERNAL) - public void authenticateUserForOperation( + public long authenticateUserForOperation( @NonNull CancellationSignal cancel, @NonNull @CallbackExecutor Executor executor, @NonNull AuthenticationCallback callback, @@ -871,7 +880,8 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan if (callback == null) { throw new IllegalArgumentException("Must supply a callback"); } - authenticateInternal(operationId, cancel, executor, callback, userId); + + return authenticateInternal(operationId, cancel, executor, callback, userId); } /** @@ -1002,10 +1012,10 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan authenticateInternal(null /* crypto */, cancel, executor, callback, mContext.getUserId()); } - private void cancelAuthentication() { + private void cancelAuthentication(long requestId) { if (mService != null) { try { - mService.cancelAuthentication(mToken, mContext.getOpPackageName()); + mService.cancelAuthentication(mToken, mContext.getOpPackageName(), requestId); } catch (RemoteException e) { Log.e(TAG, "Unable to cancel authentication", e); } @@ -1024,7 +1034,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan authenticateInternal(operationId, cancel, executor, callback, userId); } - private void authenticateInternal( + private long authenticateInternal( long operationId, @NonNull CancellationSignal cancel, @NonNull @CallbackExecutor Executor executor, @@ -1040,9 +1050,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan try { if (cancel.isCanceled()) { Log.w(TAG, "Authentication already canceled"); - return; - } else { - cancel.setOnCancelListener(new OnAuthenticationCancelListener()); + return -1; } mExecutor = executor; @@ -1065,14 +1073,16 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan promptInfo = mPromptInfo; } - mService.authenticate(mToken, operationId, userId, mBiometricServiceReceiver, - mContext.getOpPackageName(), promptInfo); - + final long authId = mService.authenticate(mToken, operationId, userId, + mBiometricServiceReceiver, mContext.getOpPackageName(), promptInfo); + cancel.setOnCancelListener(new OnAuthenticationCancelListener(authId)); + return authId; } catch (RemoteException e) { Log.e(TAG, "Remote exception while authenticating", e); mExecutor.execute(() -> callback.onAuthenticationError( BiometricPrompt.BIOMETRIC_ERROR_HW_UNAVAILABLE, mContext.getString(R.string.biometric_error_hw_unavailable))); + return -1; } } diff --git a/core/java/android/hardware/biometrics/IAuthService.aidl b/core/java/android/hardware/biometrics/IAuthService.aidl index 4c2a9ae10cbdf..91f794c161bf6 100644 --- a/core/java/android/hardware/biometrics/IAuthService.aidl +++ b/core/java/android/hardware/biometrics/IAuthService.aidl @@ -41,13 +41,14 @@ interface IAuthService { // Retrieve the package where BIometricOrompt's UI is implemented String getUiPackage(); - // Requests authentication. The service choose the appropriate biometric to use, and show - // the corresponding BiometricDialog. - void authenticate(IBinder token, long sessionId, int userId, + // Requests authentication. The service chooses the appropriate biometric to use, and shows + // the corresponding BiometricDialog. A requestId is returned that can be used to cancel + // this operation. + long authenticate(IBinder token, long sessionId, int userId, IBiometricServiceReceiver receiver, String opPackageName, in PromptInfo promptInfo); - // Cancel authentication for the given sessionId - void cancelAuthentication(IBinder token, String opPackageName); + // Cancel authentication for the given requestId. + void cancelAuthentication(IBinder token, String opPackageName, long requestId); // TODO(b/141025588): Make userId the first arg to be consistent with hasEnrolledBiometrics. // Checks if biometrics can be used. diff --git a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl index 876513f266e82..addd622eef356 100644 --- a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl +++ b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl @@ -48,13 +48,13 @@ interface IBiometricAuthenticator { // startPreparedClient(). void prepareForAuthentication(boolean requireConfirmation, IBinder token, long operationId, int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName, - int cookie, boolean allowBackgroundAuthentication); + long requestId, int cookie, boolean allowBackgroundAuthentication); // Starts authentication with the previously prepared client. void startPreparedClient(int cookie); - // Cancels authentication. - void cancelAuthenticationFromService(IBinder token, String opPackageName); + // Cancels authentication for the given requestId. + void cancelAuthenticationFromService(IBinder token, String opPackageName, long requestId); // Determine if HAL is loaded and ready boolean isHardwareDetected(String opPackageName); diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl index 64b51183a170b..2c3c8c353e8c0 100644 --- a/core/java/android/hardware/biometrics/IBiometricService.aidl +++ b/core/java/android/hardware/biometrics/IBiometricService.aidl @@ -36,13 +36,14 @@ interface IBiometricService { // Retrieve static sensor properties for all biometric sensors List getSensorProperties(String opPackageName); - // Requests authentication. The service choose the appropriate biometric to use, and show - // the corresponding BiometricDialog. - void authenticate(IBinder token, long operationId, int userId, + // Requests authentication. The service chooses the appropriate biometric to use, and shows + // the corresponding BiometricDialog. A requestId is returned that can be used to cancel + // this operation. + long authenticate(IBinder token, long operationId, int userId, IBiometricServiceReceiver receiver, String opPackageName, in PromptInfo promptInfo); - // Cancel authentication for the given session. - void cancelAuthentication(IBinder token, String opPackageName); + // Cancel authentication for the given requestId. + void cancelAuthentication(IBinder token, String opPackageName, long requestId); // Checks if biometrics can be used. int canAuthenticate(String opPackageName, int userId, int callingUserId, int authenticators); diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index 385ad2d3577fe..8a7d891324c05 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -58,7 +58,7 @@ import java.util.List; public class FaceManager implements BiometricAuthenticator, BiometricFaceConstants { private static final String TAG = "FaceManager"; - 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; @@ -207,13 +207,9 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan throw new IllegalArgumentException("Must supply an authentication callback"); } - if (cancel != null) { - if (cancel.isCanceled()) { - Slog.w(TAG, "authentication already canceled"); - return; - } else { - cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto)); - } + if (cancel != null && cancel.isCanceled()) { + Slog.w(TAG, "authentication already canceled"); + return; } if (mService != null) { @@ -223,17 +219,18 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan mCryptoObject = crypto; final long operationId = crypto != null ? crypto.getOpId() : 0; Trace.beginSection("FaceManager#authenticate"); - mService.authenticate(mToken, operationId, userId, mServiceReceiver, - mContext.getOpPackageName(), isKeyguardBypassEnabled); + final long authId = mService.authenticate(mToken, operationId, userId, + mServiceReceiver, mContext.getOpPackageName(), isKeyguardBypassEnabled); + if (cancel != null) { + cancel.setOnCancelListener(new OnAuthenticationCancelListener(authId)); + } } catch (RemoteException e) { Slog.w(TAG, "Remote exception while authenticating: ", e); - if (callback != null) { - // Though this may not be a hardware issue, it will cause apps to give up or - // try again later. - callback.onAuthenticationError(FACE_ERROR_HW_UNAVAILABLE, - getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE, - 0 /* vendorCode */)); - } + // Though this may not be a hardware issue, it will cause apps to give up or + // try again later. + callback.onAuthenticationError(FACE_ERROR_HW_UNAVAILABLE, + getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE, + 0 /* vendorCode */)); } finally { Trace.endSection(); } @@ -255,14 +252,14 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan if (cancel.isCanceled()) { Slog.w(TAG, "Detection already cancelled"); return; - } else { - cancel.setOnCancelListener(new OnFaceDetectionCancelListener()); } mFaceDetectionCallback = callback; try { - mService.detectFace(mToken, userId, mServiceReceiver, mContext.getOpPackageName()); + final long authId = mService.detectFace( + mToken, userId, mServiceReceiver, mContext.getOpPackageName()); + cancel.setOnCancelListener(new OnFaceDetectionCancelListener(authId)); } catch (RemoteException e) { Slog.w(TAG, "Remote exception when requesting finger detect", e); } @@ -726,23 +723,23 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan } } - private void cancelAuthentication(CryptoObject cryptoObject) { + private void cancelAuthentication(long requestId) { if (mService != null) { try { - mService.cancelAuthentication(mToken, mContext.getOpPackageName()); + mService.cancelAuthentication(mToken, mContext.getOpPackageName(), requestId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } } - private void cancelFaceDetect() { + private void cancelFaceDetect(long requestId) { if (mService == null) { return; } try { - mService.cancelFaceDetect(mToken, mContext.getOpPackageName()); + mService.cancelFaceDetect(mToken, mContext.getOpPackageName(), requestId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1110,22 +1107,30 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan } private class OnAuthenticationCancelListener implements OnCancelListener { - private final CryptoObject mCrypto; + private final long mAuthRequestId; - OnAuthenticationCancelListener(CryptoObject crypto) { - mCrypto = crypto; + OnAuthenticationCancelListener(long id) { + mAuthRequestId = id; } @Override public void onCancel() { - cancelAuthentication(mCrypto); + Slog.d(TAG, "Cancel face authentication requested for: " + mAuthRequestId); + cancelAuthentication(mAuthRequestId); } } private class OnFaceDetectionCancelListener implements OnCancelListener { + private final long mAuthRequestId; + + OnFaceDetectionCancelListener(long id) { + mAuthRequestId = id; + } + @Override public void onCancel() { - cancelFaceDetect(); + Slog.d(TAG, "Cancel face detect requested for: " + mAuthRequestId); + cancelFaceDetect(mAuthRequestId); } } diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl index db02a0ef2a10c..e9198246dee3f 100644 --- a/core/java/android/hardware/face/IFaceService.aidl +++ b/core/java/android/hardware/face/IFaceService.aidl @@ -44,34 +44,36 @@ interface IFaceService { // Retrieve static sensor properties for the specified sensor FaceSensorPropertiesInternal getSensorProperties(int sensorId, String opPackageName); - // Authenticate the given sessionId with a face - void authenticate(IBinder token, long operationId, int userId, IFaceServiceReceiver receiver, + // Authenticate with a face. A requestId is returned that can be used to cancel this operation. + long authenticate(IBinder token, long operationId, int userId, IFaceServiceReceiver receiver, String opPackageName, boolean isKeyguardBypassEnabled); // Uses the face hardware to detect for the presence of a face, without giving details - // about accept/reject/lockout. - void detectFace(IBinder token, int userId, IFaceServiceReceiver receiver, String opPackageName); + // about accept/reject/lockout. A requestId is returned that can be used to cancel this + // operation. + long detectFace(IBinder token, int userId, IFaceServiceReceiver receiver, String opPackageName); // This method prepares the service to start authenticating, but doesn't start authentication. // This is protected by the MANAGE_BIOMETRIC signatuer permission. This method should only be // called from BiometricService. The additional uid, pid, userId arguments should be determined // by BiometricService. To start authentication after the clients are ready, use // startPreparedClient(). - void prepareForAuthentication(int sensorId, boolean requireConfirmation, IBinder token, long operationId, - int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName, - int cookie, boolean allowBackgroundAuthentication); + void prepareForAuthentication(int sensorId, boolean requireConfirmation, IBinder token, + long operationId, int userId, IBiometricSensorReceiver sensorReceiver, + String opPackageName, long requestId, int cookie, + boolean allowBackgroundAuthentication); // Starts authentication with the previously prepared client. void startPreparedClient(int sensorId, int cookie); - // Cancel authentication for the given sessionId - void cancelAuthentication(IBinder token, String opPackageName); + // Cancel authentication for the given requestId. + void cancelAuthentication(IBinder token, String opPackageName, long requestId); - // Cancel face detection - void cancelFaceDetect(IBinder token, String opPackageName); + // Cancel face detection for the given requestId. + void cancelFaceDetect(IBinder token, String opPackageName, long requestId); // Same as above, with extra arguments. - void cancelAuthenticationFromService(int sensorId, IBinder token, String opPackageName); + void cancelAuthenticationFromService(int sensorId, IBinder token, String opPackageName, long requestId); // Start face enrollment void enroll(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver, diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 87d45b9de745b..fd2d765e62e0a 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -189,22 +189,30 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } private class OnAuthenticationCancelListener implements OnCancelListener { - private android.hardware.biometrics.CryptoObject mCrypto; + private final long mAuthRequestId; - public OnAuthenticationCancelListener(android.hardware.biometrics.CryptoObject crypto) { - mCrypto = crypto; + OnAuthenticationCancelListener(long id) { + mAuthRequestId = id; } @Override public void onCancel() { - cancelAuthentication(mCrypto); + Slog.d(TAG, "Cancel fingerprint authentication requested for: " + mAuthRequestId); + cancelAuthentication(mAuthRequestId); } } private class OnFingerprintDetectionCancelListener implements OnCancelListener { + private final long mAuthRequestId; + + OnFingerprintDetectionCancelListener(long id) { + mAuthRequestId = id; + } + @Override public void onCancel() { - cancelFingerprintDetect(); + Slog.d(TAG, "Cancel fingerprint detect requested for: " + mAuthRequestId); + cancelFingerprintDetect(mAuthRequestId); } } @@ -552,13 +560,9 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing throw new IllegalArgumentException("Must supply an authentication callback"); } - if (cancel != null) { - if (cancel.isCanceled()) { - Slog.w(TAG, "authentication already canceled"); - return; - } else { - cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto)); - } + if (cancel != null && cancel.isCanceled()) { + Slog.w(TAG, "authentication already canceled"); + return; } if (mService != null) { @@ -567,8 +571,11 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing mAuthenticationCallback = callback; mCryptoObject = crypto; final long operationId = crypto != null ? crypto.getOpId() : 0; - mService.authenticate(mToken, operationId, sensorId, userId, mServiceReceiver, - mContext.getOpPackageName()); + final long authId = mService.authenticate(mToken, operationId, sensorId, userId, + mServiceReceiver, mContext.getOpPackageName()); + if (cancel != null) { + cancel.setOnCancelListener(new OnAuthenticationCancelListener(authId)); + } } catch (RemoteException e) { Slog.w(TAG, "Remote exception while authenticating: ", e); // Though this may not be a hardware issue, it will cause apps to give up or try @@ -595,15 +602,14 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing if (cancel.isCanceled()) { Slog.w(TAG, "Detection already cancelled"); return; - } else { - cancel.setOnCancelListener(new OnFingerprintDetectionCancelListener()); } mFingerprintDetectionCallback = callback; try { - mService.detectFingerprint(mToken, userId, mServiceReceiver, + final long authId = mService.detectFingerprint(mToken, userId, mServiceReceiver, mContext.getOpPackageName()); + cancel.setOnCancelListener(new OnFingerprintDetectionCancelListener(authId)); } catch (RemoteException e) { Slog.w(TAG, "Remote exception when requesting finger detect", e); } @@ -1320,21 +1326,21 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } } - private void cancelAuthentication(android.hardware.biometrics.CryptoObject cryptoObject) { + private void cancelAuthentication(long requestId) { if (mService != null) try { - mService.cancelAuthentication(mToken, mContext.getOpPackageName()); + mService.cancelAuthentication(mToken, mContext.getOpPackageName(), requestId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } - private void cancelFingerprintDetect() { + private void cancelFingerprintDetect(long requestId) { if (mService == null) { return; } try { - mService.cancelFingerprintDetect(mToken, mContext.getOpPackageName()); + mService.cancelFingerprintDetect(mToken, mContext.getOpPackageName(), requestId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index 3979afe80d171..4774827576994 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -48,15 +48,16 @@ interface IFingerprintService { // Retrieve static sensor properties for the specified sensor FingerprintSensorPropertiesInternal getSensorProperties(int sensorId, String opPackageName); - // Authenticate the given sessionId with a fingerprint. This is protected by - // USE_FINGERPRINT/USE_BIOMETRIC permission. This is effectively deprecated, since it only comes - // through FingerprintManager now. - void authenticate(IBinder token, long operationId, int sensorId, int userId, + // Authenticate with a fingerprint. This is protected by USE_FINGERPRINT/USE_BIOMETRIC + // permission. This is effectively deprecated, since it only comes through FingerprintManager + // now. A requestId is returned that can be used to cancel this operation. + long authenticate(IBinder token, long operationId, int sensorId, int userId, IFingerprintServiceReceiver receiver, String opPackageName); // Uses the fingerprint hardware to detect for the presence of a finger, without giving details - // about accept/reject/lockout. - void detectFingerprint(IBinder token, int userId, IFingerprintServiceReceiver receiver, + // about accept/reject/lockout. A requestId is returned that can be used to cancel this + // operation. + long detectFingerprint(IBinder token, int userId, IFingerprintServiceReceiver receiver, String opPackageName); // This method prepares the service to start authenticating, but doesn't start authentication. @@ -65,21 +66,21 @@ interface IFingerprintService { // by BiometricService. To start authentication after the clients are ready, use // startPreparedClient(). void prepareForAuthentication(int sensorId, IBinder token, long operationId, int userId, - IBiometricSensorReceiver sensorReceiver, String opPackageName, int cookie, - boolean allowBackgroundAuthentication); + IBiometricSensorReceiver sensorReceiver, String opPackageName, long requestId, + int cookie, boolean allowBackgroundAuthentication); // Starts authentication with the previously prepared client. void startPreparedClient(int sensorId, int cookie); - // Cancel authentication for the given sessionId - void cancelAuthentication(IBinder token, String opPackageName); + // Cancel authentication for the given requestId. + void cancelAuthentication(IBinder token, String opPackageName, long requestId); - // Cancel finger detection - void cancelFingerprintDetect(IBinder token, String opPackageName); + // Cancel finger detection for the given requestId. + void cancelFingerprintDetect(IBinder token, String opPackageName, long requestId); // Same as above, except this is protected by the MANAGE_BIOMETRIC signature permission. Takes // an additional uid, pid, userid. - void cancelAuthenticationFromService(int sensorId, IBinder token, String opPackageName); + void cancelAuthenticationFromService(int sensorId, IBinder token, String opPackageName, long requestId); // Start fingerprint enrollment void enroll(IBinder token, in byte [] hardwareAuthToken, int userId, IFingerprintServiceReceiver receiver, diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index 10f14b42ae427..ed6415d749a39 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -148,7 +148,7 @@ oneway interface IStatusBar */ void showAuthenticationDialog(in PromptInfo promptInfo, IBiometricSysuiReceiver sysuiReceiver, in int[] sensorIds, boolean credentialAllowed, boolean requireConfirmation, int userId, - String opPackageName, long operationId, int multiSensorConfig); + long operationId, String opPackageName, long requestId, int multiSensorConfig); /** * Used to notify the authentication dialog that a biometric has been authenticated. */ diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index e7d6d6cf89366..b3499db94c883 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -110,7 +110,8 @@ interface IStatusBarService // Used to show the authentication dialog (Biometrics, Device Credential) void showAuthenticationDialog(in PromptInfo promptInfo, IBiometricSysuiReceiver sysuiReceiver, in int[] sensorIds, boolean credentialAllowed, boolean requireConfirmation, - int userId, String opPackageName, long operationId, int multiSensorConfig); + int userId, long operationId, String opPackageName, long requestId, + int multiSensorConfig); // Used to notify the authentication dialog that a biometric has been authenticated void onBiometricAuthenticated(); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index 3f61d3c6af9a4..fd37b3509a4ed 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -126,6 +126,7 @@ public class AuthContainerView extends LinearLayout boolean mCredentialAllowed; boolean mSkipIntro; long mOperationId; + long mRequestId; @BiometricMultiSensorMode int mMultiSensorConfig; } @@ -172,6 +173,12 @@ public class AuthContainerView extends LinearLayout return this; } + /** Unique id for this request. */ + public Builder setRequestId(long requestId) { + mConfig.mRequestId = requestId; + return this; + } + /** The multi-sensor mode. */ public Builder setMultiSensorConfig(@BiometricMultiSensorMode int multiSensorConfig) { mConfig.mMultiSensorConfig = multiSensorConfig; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index ab5f2b8289bed..bcc0530687122 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -501,7 +501,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, @Override public void showAuthenticationDialog(PromptInfo promptInfo, IBiometricSysuiReceiver receiver, int[] sensorIds, boolean credentialAllowed, boolean requireConfirmation, - int userId, String opPackageName, long operationId, + int userId, long operationId, String opPackageName, long requestId, @BiometricMultiSensorMode int multiSensorConfig) { @Authenticators.Types final int authenticators = promptInfo.getAuthenticators(); @@ -515,6 +515,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, + ", credentialAllowed: " + credentialAllowed + ", requireConfirmation: " + requireConfirmation + ", operationId: " + operationId + + ", requestId: " + requestId + ", multiSensorConfig: " + multiSensorConfig); } SomeArgs args = SomeArgs.obtain(); @@ -526,6 +527,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, args.argi1 = userId; args.arg6 = opPackageName; args.arg7 = operationId; + args.arg8 = requestId; args.argi2 = multiSensorConfig; boolean skipAnimation = false; @@ -629,6 +631,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, if (mCurrentDialog == null) { // Could be possible if the caller canceled authentication after credential success // but before the client was notified. + if (DEBUG) Log.d(TAG, "dialog already gone"); return; } @@ -683,6 +686,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, final int userId = args.argi1; final String opPackageName = (String) args.arg6; final long operationId = (long) args.arg7; + final long requestId = (long) args.arg8; final @BiometricMultiSensorMode int multiSensorConfig = args.argi2; // Create a new dialog but do not replace the current one yet. @@ -695,6 +699,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, opPackageName, skipAnimation, operationId, + requestId, multiSensorConfig); if (newDialog == null) { @@ -772,7 +777,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, protected AuthDialog buildDialog(PromptInfo promptInfo, boolean requireConfirmation, int userId, int[] sensorIds, boolean credentialAllowed, String opPackageName, - boolean skipIntro, long operationId, + boolean skipIntro, long operationId, long requestId, @BiometricMultiSensorMode int multiSensorConfig) { return new AuthContainerView.Builder(mContext) .setCallback(this) @@ -782,6 +787,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, .setOpPackageName(opPackageName) .setSkipIntro(skipIntro) .setOperationId(operationId) + .setRequestId(requestId) .setMultiSensorConfig(multiSensorConfig) .build(sensorIds, credentialAllowed, mFpProps, mFaceProps); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index 8e52b0da54ef1..50911d162113d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -290,8 +290,8 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< default void showAuthenticationDialog(PromptInfo promptInfo, IBiometricSysuiReceiver receiver, int[] sensorIds, boolean credentialAllowed, - boolean requireConfirmation, int userId, String opPackageName, - long operationId, @BiometricMultiSensorMode int multiSensorConfig) { + boolean requireConfirmation, int userId, long operationId, String opPackageName, + long requestId, @BiometricMultiSensorMode int multiSensorConfig) { } /** @see IStatusBar#onBiometricAuthenticated() */ @@ -843,7 +843,7 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< @Override public void showAuthenticationDialog(PromptInfo promptInfo, IBiometricSysuiReceiver receiver, int[] sensorIds, boolean credentialAllowed, boolean requireConfirmation, - int userId, String opPackageName, long operationId, + int userId, long operationId, String opPackageName, long requestId, @BiometricMultiSensorMode int multiSensorConfig) { synchronized (mLock) { SomeArgs args = SomeArgs.obtain(); @@ -855,6 +855,7 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< args.argi1 = userId; args.arg6 = opPackageName; args.arg7 = operationId; + args.arg8 = requestId; args.argi2 = multiSensorConfig; mHandler.obtainMessage(MSG_BIOMETRIC_SHOW, args) .sendToTarget(); @@ -1312,8 +1313,9 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< (boolean) someArgs.arg4 /* credentialAllowed */, (boolean) someArgs.arg5 /* requireConfirmation */, someArgs.argi1 /* userId */, - (String) someArgs.arg6 /* opPackageName */, (long) someArgs.arg7 /* operationId */, + (String) someArgs.arg6 /* opPackageName */, + (long) someArgs.arg8 /* requestId */, someArgs.argi2 /* multiSensorConfig */); } someArgs.recycle(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index 39d5314107ee3..8dd5d6c01394c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -565,8 +565,9 @@ public class AuthControllerTest extends SysuiTestCase { credentialAllowed, true /* requireConfirmation */, 0 /* userId */, - "testPackage", 0 /* operationId */, + "testPackage", + 1 /* requestId */, BIOMETRIC_MULTI_SENSOR_FACE_THEN_FINGERPRINT); } @@ -612,7 +613,7 @@ public class AuthControllerTest extends SysuiTestCase { @Override protected AuthDialog buildDialog(PromptInfo promptInfo, boolean requireConfirmation, int userId, int[] sensorIds, boolean credentialAllowed, - String opPackageName, boolean skipIntro, long operationId, + String opPackageName, boolean skipIntro, long operationId, long requestId, @BiometricManager.BiometricMultiSensorMode int multiSensorConfig) { mLastBiometricPromptInfo = promptInfo; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java index 21c6292c151fc..f3762c566731b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java @@ -423,17 +423,18 @@ public class CommandQueueTest extends SysuiTestCase { final boolean credentialAllowed = true; final boolean requireConfirmation = true; final int userId = 10; - final String packageName = "test"; final long operationId = 1; + final String packageName = "test"; + final long requestId = 10; final int multiSensorConfig = BiometricManager.BIOMETRIC_MULTI_SENSOR_DEFAULT; mCommandQueue.showAuthenticationDialog(promptInfo, receiver, sensorIds, - credentialAllowed, requireConfirmation , userId, packageName, operationId, + credentialAllowed, requireConfirmation, userId, operationId, packageName, requestId, multiSensorConfig); waitForIdleSync(); verify(mCallbacks).showAuthenticationDialog(eq(promptInfo), eq(receiver), eq(sensorIds), - eq(credentialAllowed), eq(requireConfirmation), eq(userId), eq(packageName), - eq(operationId), eq(multiSensorConfig)); + eq(credentialAllowed), eq(requireConfirmation), eq(userId), eq(operationId), + eq(packageName), eq(requestId), eq(multiSensorConfig)); } @Test diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java index 0cd2e3d0ff593..b42f8980d1c0c 100644 --- a/services/core/java/com/android/server/biometrics/AuthService.java +++ b/services/core/java/com/android/server/biometrics/AuthService.java @@ -206,7 +206,7 @@ public class AuthService extends SystemService { } @Override - public void authenticate(IBinder token, long sessionId, int userId, + public long authenticate(IBinder token, long sessionId, int userId, IBiometricServiceReceiver receiver, String opPackageName, PromptInfo promptInfo) throws RemoteException { // Only allow internal clients to authenticate with a different userId. @@ -223,18 +223,18 @@ public class AuthService extends SystemService { if (!checkAppOps(callingUid, opPackageName, "authenticate()")) { authenticateFastFail("Denied by app ops: " + opPackageName, receiver); - return; + return -1; } if (token == null || receiver == null || opPackageName == null || promptInfo == null) { authenticateFastFail( "Unable to authenticate, one or more null arguments", receiver); - return; + return -1; } if (!Utils.isForeground(callingUid, callingPid)) { authenticateFastFail("Caller is not foreground: " + opPackageName, receiver); - return; + return -1; } if (promptInfo.containsTestConfigurations()) { @@ -251,7 +251,7 @@ public class AuthService extends SystemService { final long identity = Binder.clearCallingIdentity(); try { - mBiometricService.authenticate( + return mBiometricService.authenticate( token, sessionId, userId, receiver, opPackageName, promptInfo); } finally { Binder.restoreCallingIdentity(identity); @@ -270,7 +270,7 @@ public class AuthService extends SystemService { } @Override - public void cancelAuthentication(IBinder token, String opPackageName) + public void cancelAuthentication(IBinder token, String opPackageName, long requestId) throws RemoteException { checkPermission(); @@ -281,7 +281,7 @@ public class AuthService extends SystemService { final long identity = Binder.clearCallingIdentity(); try { - mBiometricService.cancelAuthentication(token, opPackageName); + mBiometricService.cancelAuthentication(token, opPackageName, requestId); } finally { Binder.restoreCallingIdentity(identity); } diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java index 4c616062918bf..0da6a1ba3109d 100644 --- a/services/core/java/com/android/server/biometrics/AuthSession.java +++ b/services/core/java/com/android/server/biometrics/AuthSession.java @@ -128,6 +128,7 @@ public final class AuthSession implements IBinder.DeathRecipient { @VisibleForTesting final IBinder mToken; // Info to be shown on BiometricDialog when all cookies are returned. @VisibleForTesting final PromptInfo mPromptInfo; + private final long mRequestId; private final long mOperationId; private final int mUserId; private final IBiometricSensorReceiver mSensorReceiver; @@ -142,6 +143,7 @@ public final class AuthSession implements IBinder.DeathRecipient { private @BiometricMultiSensorMode int mMultiSensorMode; private @MultiSensorState int mMultiSensorState; private int[] mSensors; + // TODO(b/197265902): merge into state private boolean mCancelled; // For explicit confirmation, do not send to keystore until the user has confirmed // the authentication. @@ -163,6 +165,7 @@ public final class AuthSession implements IBinder.DeathRecipient { @NonNull ClientDeathReceiver clientDeathReceiver, @NonNull PreAuthInfo preAuthInfo, @NonNull IBinder token, + long requestId, long operationId, int userId, @NonNull IBiometricSensorReceiver sensorReceiver, @@ -180,6 +183,7 @@ public final class AuthSession implements IBinder.DeathRecipient { mClientDeathReceiver = clientDeathReceiver; mPreAuthInfo = preAuthInfo; mToken = token; + mRequestId = requestId; mOperationId = operationId; mUserId = userId; mSensorReceiver = sensorReceiver; @@ -235,7 +239,7 @@ public final class AuthSession implements IBinder.DeathRecipient { Slog.v(TAG, "waiting for cooking for sensor: " + sensor.id); } sensor.goToStateWaitingForCookie(requireConfirmation, mToken, mOperationId, - mUserId, mSensorReceiver, mOpPackageName, cookie, + mUserId, mSensorReceiver, mOpPackageName, mRequestId, cookie, mPromptInfo.isAllowBackgroundAuthentication()); } } @@ -257,8 +261,9 @@ public final class AuthSession implements IBinder.DeathRecipient { true /* credentialAllowed */, false /* requireConfirmation */, mUserId, - mOpPackageName, mOperationId, + mOpPackageName, + mRequestId, mMultiSensorMode); } else if (!mPreAuthInfo.eligibleSensors.isEmpty()) { // Some combination of biometric or biometric|credential is requested @@ -308,8 +313,9 @@ public final class AuthSession implements IBinder.DeathRecipient { mPreAuthInfo.shouldShowCredential(), requireConfirmation, mUserId, - mOpPackageName, mOperationId, + mOpPackageName, + mRequestId, mMultiSensorMode); mState = STATE_AUTH_STARTED; } catch (RemoteException e) { @@ -376,7 +382,7 @@ public final class AuthSession implements IBinder.DeathRecipient { final boolean shouldCancel = filter.apply(sensor); Slog.d(TAG, "sensorId: " + sensor.id + ", shouldCancel: " + shouldCancel); if (shouldCancel) { - sensor.goToStateCancelling(mToken, mOpPackageName); + sensor.goToStateCancelling(mToken, mOpPackageName, mRequestId); } } catch (RemoteException e) { Slog.e(TAG, "Unable to cancel authentication"); @@ -432,8 +438,9 @@ public final class AuthSession implements IBinder.DeathRecipient { true /* credentialAllowed */, false /* requireConfirmation */, mUserId, - mOpPackageName, mOperationId, + mOpPackageName, + mRequestId, mMultiSensorMode); } else { mClientReceiver.onError(modality, error, vendorCode); @@ -829,6 +836,7 @@ public final class AuthSession implements IBinder.DeathRecipient { return Utils.isCredentialRequested(mPromptInfo); } + @VisibleForTesting boolean allCookiesReceived() { final int remainingCookies = mPreAuthInfo.numSensorsWaitingForCookie(); Slog.d(TAG, "Remaining cookies: " + remainingCookies); @@ -848,6 +856,10 @@ public final class AuthSession implements IBinder.DeathRecipient { return mState; } + long getRequestId() { + return mRequestId; + } + private int statsModality() { int modality = 0; @@ -912,6 +924,7 @@ public final class AuthSession implements IBinder.DeathRecipient { return "State: " + mState + ", cancelled: " + mCancelled + ", isCrypto: " + isCrypto() - + ", PreAuthInfo: " + mPreAuthInfo; + + ", PreAuthInfo: " + mPreAuthInfo + + ", requestId: " + mRequestId; } } diff --git a/services/core/java/com/android/server/biometrics/BiometricSensor.java b/services/core/java/com/android/server/biometrics/BiometricSensor.java index 8a842b53d8e8d..0333c3e247c0a 100644 --- a/services/core/java/com/android/server/biometrics/BiometricSensor.java +++ b/services/core/java/com/android/server/biometrics/BiometricSensor.java @@ -108,11 +108,11 @@ public abstract class BiometricSensor { void goToStateWaitingForCookie(boolean requireConfirmation, IBinder token, long sessionId, int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName, - int cookie, boolean allowBackgroundAuthentication) + long requestId, int cookie, boolean allowBackgroundAuthentication) throws RemoteException { mCookie = cookie; impl.prepareForAuthentication(requireConfirmation, token, - sessionId, userId, sensorReceiver, opPackageName, mCookie, + sessionId, userId, sensorReceiver, opPackageName, requestId, mCookie, allowBackgroundAuthentication); mSensorState = STATE_WAITING_FOR_COOKIE; } @@ -129,8 +129,9 @@ public abstract class BiometricSensor { mSensorState = STATE_AUTHENTICATING; } - void goToStateCancelling(IBinder token, String opPackageName) throws RemoteException { - impl.cancelAuthenticationFromService(token, opPackageName); + void goToStateCancelling(IBinder token, String opPackageName, long requestId) + throws RemoteException { + impl.cancelAuthenticationFromService(token, opPackageName, requestId); mSensorState = STATE_CANCELING; } diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index b1d300cd838e4..e0775d48b42fd 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -83,6 +83,7 @@ import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; +import java.util.concurrent.atomic.AtomicLong; /** * System service that arbitrates the modality for BiometricPrompt to use. @@ -115,6 +116,7 @@ public class BiometricService extends SystemService { final SettingObserver mSettingObserver; private final List mEnabledOnKeyguardCallbacks; private final Random mRandom = new Random(); + @NonNull private final AtomicLong mRequestCounter; @VisibleForTesting IStatusBarService mStatusBarService; @@ -194,6 +196,7 @@ public class BiometricService extends SystemService { SomeArgs args = (SomeArgs) msg.obj; handleAuthenticate( (IBinder) args.arg1 /* token */, + (long) args.arg6 /* requestId */, (long) args.arg2 /* operationId */, args.argi1 /* userid */, (IBiometricServiceReceiver) args.arg3 /* receiver */, @@ -204,7 +207,9 @@ public class BiometricService extends SystemService { } case MSG_CANCEL_AUTHENTICATION: { - handleCancelAuthentication(); + SomeArgs args = (SomeArgs) msg.obj; + handleCancelAuthentication((long) args.arg3 /* requestId */); + args.recycle(); break; } @@ -683,13 +688,13 @@ public class BiometricService extends SystemService { } @Override // Binder call - public void authenticate(IBinder token, long operationId, int userId, + public long authenticate(IBinder token, long operationId, int userId, IBiometricServiceReceiver receiver, String opPackageName, PromptInfo promptInfo) { checkInternalPermission(); if (token == null || receiver == null || opPackageName == null || promptInfo == null) { Slog.e(TAG, "Unable to authenticate, one or more null arguments"); - return; + return -1; } if (!Utils.isValidAuthenticatorConfig(promptInfo)) { @@ -706,6 +711,8 @@ public class BiometricService extends SystemService { } } + final long requestId = mRequestCounter.incrementAndGet(); + SomeArgs args = SomeArgs.obtain(); args.arg1 = token; args.arg2 = operationId; @@ -713,15 +720,23 @@ public class BiometricService extends SystemService { args.arg3 = receiver; args.arg4 = opPackageName; args.arg5 = promptInfo; + args.arg6 = requestId; mHandler.obtainMessage(MSG_AUTHENTICATE, args).sendToTarget(); + + return requestId; } @Override // Binder call - public void cancelAuthentication(IBinder token, String opPackageName) { + public void cancelAuthentication(IBinder token, String opPackageName, long requestId) { checkInternalPermission(); - mHandler.obtainMessage(MSG_CANCEL_AUTHENTICATION).sendToTarget(); + SomeArgs args = SomeArgs.obtain(); + args.arg1 = token; + args.arg2 = opPackageName; + args.arg3 = requestId; + + mHandler.obtainMessage(MSG_CANCEL_AUTHENTICATION, args).sendToTarget(); } @Override // Binder call @@ -1111,6 +1126,10 @@ public class BiometricService extends SystemService { return Settings.Secure.getInt(context.getContentResolver(), CoexCoordinator.FACE_HAPTIC_DISABLE, 1) != 0; } + + public AtomicLong getRequestGenerator() { + return new AtomicLong(0); + } } /** @@ -1136,6 +1155,7 @@ public class BiometricService extends SystemService { mEnabledOnKeyguardCallbacks = new ArrayList<>(); mSettingObserver = mInjector.getSettingObserver(context, mHandler, mEnabledOnKeyguardCallbacks); + mRequestCounter = mInjector.getRequestGenerator(); // TODO(b/193089985) This logic lives here (outside of CoexCoordinator) so that it doesn't // need to depend on context. We can remove this code once the advanced logic is enabled @@ -1349,7 +1369,7 @@ public class BiometricService extends SystemService { mCurrentAuthSession.onCookieReceived(cookie); } - private void handleAuthenticate(IBinder token, long operationId, int userId, + private void handleAuthenticate(IBinder token, long requestId, long operationId, int userId, IBiometricServiceReceiver receiver, String opPackageName, PromptInfo promptInfo) { mHandler.post(() -> { try { @@ -1360,7 +1380,8 @@ public class BiometricService extends SystemService { final Pair preAuthStatus = preAuthInfo.getPreAuthenticateStatus(); Slog.d(TAG, "handleAuthenticate: modality(" + preAuthStatus.first - + "), status(" + preAuthStatus.second + "), preAuthInfo: " + preAuthInfo); + + "), status(" + preAuthStatus.second + "), preAuthInfo: " + preAuthInfo + + " requestId: " + requestId); if (preAuthStatus.second == BiometricConstants.BIOMETRIC_SUCCESS) { // If BIOMETRIC_WEAK or BIOMETRIC_STRONG are allowed, but not enrolled, but @@ -1372,8 +1393,8 @@ public class BiometricService extends SystemService { promptInfo.setAuthenticators(Authenticators.DEVICE_CREDENTIAL); } - authenticateInternal(token, operationId, userId, receiver, opPackageName, - promptInfo, preAuthInfo); + authenticateInternal(token, requestId, operationId, userId, receiver, + opPackageName, promptInfo, preAuthInfo); } else { receiver.onError(preAuthStatus.first /* modality */, preAuthStatus.second /* errorCode */, @@ -1394,7 +1415,7 @@ public class BiometricService extends SystemService { * Note that this path is NOT invoked when the BiometricPrompt "Try again" button is pressed. * In that case, see {@link #handleOnTryAgainPressed()}. */ - private void authenticateInternal(IBinder token, long operationId, int userId, + private void authenticateInternal(IBinder token, long requestId, long operationId, int userId, IBiometricServiceReceiver receiver, String opPackageName, PromptInfo promptInfo, PreAuthInfo preAuthInfo) { Slog.d(TAG, "Creating authSession with authRequest: " + preAuthInfo); @@ -1412,9 +1433,9 @@ public class BiometricService extends SystemService { final boolean debugEnabled = mInjector.isDebugEnabled(getContext(), userId); mCurrentAuthSession = new AuthSession(getContext(), mStatusBarService, mSysuiReceiver, - mKeyStore, mRandom, mClientDeathReceiver, preAuthInfo, token, operationId, userId, - mBiometricSensorReceiver, receiver, opPackageName, promptInfo, debugEnabled, - mInjector.getFingerprintSensorProperties(getContext())); + mKeyStore, mRandom, mClientDeathReceiver, preAuthInfo, token, requestId, + operationId, userId, mBiometricSensorReceiver, receiver, opPackageName, promptInfo, + debugEnabled, mInjector.getFingerprintSensorProperties(getContext())); try { mCurrentAuthSession.goToInitialState(); } catch (RemoteException e) { @@ -1422,11 +1443,21 @@ public class BiometricService extends SystemService { } } - private void handleCancelAuthentication() { + private void handleCancelAuthentication(long requestId) { if (mCurrentAuthSession == null) { Slog.e(TAG, "handleCancelAuthentication: AuthSession is null"); return; } + if (mCurrentAuthSession.getRequestId() != requestId) { + // TODO: actually cancel the operation + // This can happen if the operation has been queued, but is cancelled before + // it reaches the head of the scheduler. Consider it a programming error for now + // and ignore it. + Slog.e(TAG, "handleCancelAuthentication: AuthSession mismatch current requestId: " + + mCurrentAuthSession.getRequestId() + " cancel for: " + requestId + + " (ignoring cancellation)"); + return; + } final boolean finished = mCurrentAuthSession.onCancelAuthSession(false /* force */); if (finished) { diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java index 3eb6f4ae48a2b..0a1c77bf10a26 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java +++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java @@ -101,6 +101,7 @@ public abstract class BaseClientMonitor extends LoggableMonitor private final int mSensorId; // sensorId as configured by the framework @Nullable private IBinder mToken; + private long mRequestId; @Nullable private ClientMonitorCallbackConverter mListener; // Currently only used for authentication client. The cookie generated by BiometricService // is never 0. @@ -154,6 +155,7 @@ public abstract class BaseClientMonitor extends LoggableMonitor mSequentialId = sCount++; mContext = context; mToken = token; + mRequestId = -1; mListener = listener; mTargetUserId = userId; mOwner = owner; @@ -258,6 +260,29 @@ public abstract class BaseClientMonitor extends LoggableMonitor return mSensorId; } + /** Unique request id. */ + public final long getRequestId() { + return mRequestId; + } + + /** If a unique id has been set via {@link #setRequestId(long)} */ + public final boolean hasRequestId() { + return mRequestId > 0; + } + + /** + * A unique identifier used to tie this operation to a request (i.e an API invocation). + * + * Subclasses should not call this method if this operation does not have a direct + * correspondence to a request and {@link #hasRequestId()} will return false. + */ + protected final void setRequestId(long id) { + if (id <= 0) { + throw new IllegalArgumentException("request id must be positive"); + } + mRequestId = id; + } + @VisibleForTesting public Callback getCallback() { return mCallback; @@ -270,6 +295,7 @@ public abstract class BaseClientMonitor extends LoggableMonitor + ", proto=" + getProtoEnum() + ", owner=" + getOwnerString() + ", cookie=" + getCookie() + + ", requestId=" + getRequestId() + ", userId=" + getTargetUserId() + "}"; } } diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java index feb9e2a5b03f3..361ec40f28779 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java @@ -643,22 +643,18 @@ public class BiometricScheduler { /** * Requests to cancel authentication or detection. * @param token from the caller, should match the token passed in when requesting authentication + * @param requestId the id returned when requesting authentication */ - public void cancelAuthenticationOrDetection(IBinder token) { - if (mCurrentOperation == null) { - Slog.e(getTag(), "Unable to cancel authentication, null operation"); - return; - } - final boolean isCorrectClient = isAuthenticationOrDetectionOperation(mCurrentOperation); - final boolean tokenMatches = mCurrentOperation.mClientMonitor.getToken() == token; + public void cancelAuthenticationOrDetection(IBinder token, long requestId) { + Slog.d(getTag(), "cancelAuthenticationOrDetection, requestId: " + requestId + + " current: " + mCurrentOperation + + " stack size: " + mPendingOperations.size()); - Slog.d(getTag(), "cancelAuthenticationOrDetection, isCorrectClient: " + isCorrectClient - + ", tokenMatches: " + tokenMatches); - - if (isCorrectClient && tokenMatches) { + if (mCurrentOperation != null + && canCancelAuthOperation(mCurrentOperation, token, requestId)) { Slog.d(getTag(), "Cancelling: " + mCurrentOperation); cancelInternal(mCurrentOperation); - } else if (!isCorrectClient) { + } else { // Look through the current queue for all authentication clients for the specified // token, and mark them as STATE_WAITING_IN_QUEUE_CANCELING. Note that we're marking // all of them, instead of just the first one, since the API surface currently doesn't @@ -666,8 +662,7 @@ public class BiometricScheduler { // process. However, this generally does not happen anyway, and would be a class of // bugs on its own. for (Operation operation : mPendingOperations) { - if (isAuthenticationOrDetectionOperation(operation) - && operation.mClientMonitor.getToken() == token) { + if (canCancelAuthOperation(operation, token, requestId)) { Slog.d(getTag(), "Marking " + operation + " as STATE_WAITING_IN_QUEUE_CANCELING"); operation.mState = Operation.STATE_WAITING_IN_QUEUE_CANCELING; @@ -676,10 +671,26 @@ public class BiometricScheduler { } } - private boolean isAuthenticationOrDetectionOperation(@NonNull Operation operation) { - final boolean isAuthentication = operation.mClientMonitor - instanceof AuthenticationConsumer; - final boolean isDetection = operation.mClientMonitor instanceof DetectionConsumer; + private static boolean canCancelAuthOperation(Operation operation, IBinder token, + long requestId) { + // TODO: restrict callers that can cancel without requestId (negative value)? + return isAuthenticationOrDetectionOperation(operation) + && operation.mClientMonitor.getToken() == token + && isMatchingRequestId(operation, requestId); + } + + // By default, monitors are not associated with a request id to retain the original + // behavior (i.e. if no requestId is explicitly set then assume it matches) + private static boolean isMatchingRequestId(Operation operation, long requestId) { + return !operation.mClientMonitor.hasRequestId() + || operation.mClientMonitor.getRequestId() == requestId; + } + + private static boolean isAuthenticationOrDetectionOperation(@NonNull Operation operation) { + final boolean isAuthentication = + operation.mClientMonitor instanceof AuthenticationConsumer; + final boolean isDetection = + operation.mClientMonitor instanceof DetectionConsumer; return isAuthentication || isDetection; } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java index 0bc4f1b54d592..b2fd46d1475d2 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java @@ -61,10 +61,11 @@ public final class FaceAuthenticator extends IBiometricAuthenticator.Stub { @Override public void prepareForAuthentication(boolean requireConfirmation, IBinder token, long operationId, int userId, IBiometricSensorReceiver sensorReceiver, - String opPackageName, int cookie, boolean allowBackgroundAuthentication) + String opPackageName, long requestId, int cookie, boolean allowBackgroundAuthentication) throws RemoteException { mFaceService.prepareForAuthentication(mSensorId, requireConfirmation, token, operationId, - userId, sensorReceiver, opPackageName, cookie, allowBackgroundAuthentication); + userId, sensorReceiver, opPackageName, requestId, cookie, + allowBackgroundAuthentication); } @Override @@ -73,9 +74,9 @@ public final class FaceAuthenticator extends IBiometricAuthenticator.Stub { } @Override - public void cancelAuthenticationFromService(IBinder token, String opPackageName) + public void cancelAuthenticationFromService(IBinder token, String opPackageName, long requestId) throws RemoteException { - mFaceService.cancelAuthenticationFromService(mSensorId, token, opPackageName); + mFaceService.cancelAuthenticationFromService(mSensorId, token, opPackageName, requestId); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java index 12d6b08b8bcf6..675ee545a14f5 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java @@ -250,7 +250,7 @@ public class FaceService extends SystemService { } @Override // Binder call - public void authenticate(final IBinder token, final long operationId, int userId, + public long authenticate(final IBinder token, final long operationId, int userId, final IFaceServiceReceiver receiver, final String opPackageName, boolean isKeyguardBypassEnabled) { Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); @@ -270,38 +270,38 @@ public class FaceService extends SystemService { final Pair provider = getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for authenticate"); - return; + return -1; } - provider.second.scheduleAuthenticate(provider.first, token, operationId, userId, + return provider.second.scheduleAuthenticate(provider.first, token, operationId, userId, 0 /* cookie */, new ClientMonitorCallbackConverter(receiver), opPackageName, restricted, statsClient, isKeyguard, isKeyguardBypassEnabled); } @Override // Binder call - public void detectFace(final IBinder token, final int userId, + public long detectFace(final IBinder token, final int userId, final IFaceServiceReceiver receiver, final String opPackageName) { Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); if (!Utils.isKeyguard(getContext(), opPackageName)) { Slog.w(TAG, "detectFace called from non-sysui package: " + opPackageName); - return; + return -1; } if (!Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)) { // If this happens, something in KeyguardUpdateMonitor is wrong. This should only // ever be invoked when the user is encrypted or lockdown. Slog.e(TAG, "detectFace invoked when user is not encrypted or lockdown"); - return; + return -1; } final Pair provider = getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for detectFace"); - return; + return -1; } - provider.second.scheduleFaceDetect(provider.first, token, userId, + return provider.second.scheduleFaceDetect(provider.first, token, userId, new ClientMonitorCallbackConverter(receiver), opPackageName, BiometricsProtoEnums.CLIENT_KEYGUARD); } @@ -309,8 +309,8 @@ public class FaceService extends SystemService { @Override // Binder call public void prepareForAuthentication(int sensorId, boolean requireConfirmation, IBinder token, long operationId, int userId, - IBiometricSensorReceiver sensorReceiver, String opPackageName, int cookie, - boolean allowBackgroundAuthentication) { + IBiometricSensorReceiver sensorReceiver, String opPackageName, long requestId, + int cookie, boolean allowBackgroundAuthentication) { Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); final ServiceProvider provider = getProviderForSensor(sensorId); @@ -322,9 +322,9 @@ public class FaceService extends SystemService { final boolean isKeyguardBypassEnabled = false; // only valid for keyguard clients final boolean restricted = true; // BiometricPrompt is always restricted provider.scheduleAuthenticate(sensorId, token, operationId, userId, cookie, - new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, restricted, - BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, allowBackgroundAuthentication, - isKeyguardBypassEnabled); + new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, requestId, + restricted, BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, + allowBackgroundAuthentication, isKeyguardBypassEnabled); } @Override // Binder call @@ -341,7 +341,8 @@ public class FaceService extends SystemService { } @Override // Binder call - public void cancelAuthentication(final IBinder token, final String opPackageName) { + public void cancelAuthentication(final IBinder token, final String opPackageName, + final long requestId) { Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); final Pair provider = getSingleProvider(); @@ -350,11 +351,12 @@ public class FaceService extends SystemService { return; } - provider.second.cancelAuthentication(provider.first, token); + provider.second.cancelAuthentication(provider.first, token, requestId); } @Override // Binder call - public void cancelFaceDetect(final IBinder token, final String opPackageName) { + public void cancelFaceDetect(final IBinder token, final String opPackageName, + final long requestId) { Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); if (!Utils.isKeyguard(getContext(), opPackageName)) { Slog.w(TAG, "cancelFaceDetect called from non-sysui package: " @@ -368,12 +370,12 @@ public class FaceService extends SystemService { return; } - provider.second.cancelFaceDetect(provider.first, token); + provider.second.cancelFaceDetect(provider.first, token, requestId); } @Override // Binder call public void cancelAuthenticationFromService(int sensorId, final IBinder token, - final String opPackageName) { + final String opPackageName, final long requestId) { Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); final ServiceProvider provider = getProviderForSensor(sensorId); @@ -382,7 +384,7 @@ public class FaceService extends SystemService { return; } - provider.cancelAuthentication(sensorId, token); + provider.cancelAuthentication(sensorId, token, requestId); } @Override // Binder call diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java index 93ab1b65edffe..e099ba372b058 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java @@ -101,18 +101,23 @@ public interface ServiceProvider { void cancelEnrollment(int sensorId, @NonNull IBinder token); - void scheduleFaceDetect(int sensorId, @NonNull IBinder token, int userId, + long scheduleFaceDetect(int sensorId, @NonNull IBinder token, int userId, @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName, int statsClient); - void cancelFaceDetect(int sensorId, @NonNull IBinder token); + void cancelFaceDetect(int sensorId, @NonNull IBinder token, long requestId); - void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId, + long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName, boolean restricted, int statsClient, boolean allowBackgroundAuthentication, boolean isKeyguardBypassEnabled); - void cancelAuthentication(int sensorId, @NonNull IBinder token); + void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId, + int cookie, @NonNull ClientMonitorCallbackConverter callback, + @NonNull String opPackageName, long requestId, boolean restricted, int statsClient, + boolean allowBackgroundAuthentication, boolean isKeyguardBypassEnabled); + + void cancelAuthentication(int sensorId, @NonNull IBinder token, long requestId); void scheduleRemove(int sensorId, @NonNull IBinder token, int faceId, int userId, @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java index d66a27920f49c..cbceba6cc959e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java @@ -65,7 +65,8 @@ class FaceAuthenticationClient extends AuthenticationClient implements @FaceManager.FaceAcquired private int mLastAcquire = FaceManager.FACE_ACQUIRED_UNKNOWN; FaceAuthenticationClient(@NonNull Context context, - @NonNull LazyDaemon lazyDaemon, @NonNull IBinder token, + @NonNull LazyDaemon lazyDaemon, + @NonNull IBinder token, long requestId, @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId, boolean restricted, String owner, int cookie, boolean requireConfirmation, int sensorId, boolean isStrongBiometric, int statsClient, @NonNull UsageStats usageStats, @@ -76,6 +77,7 @@ class FaceAuthenticationClient extends AuthenticationClient implements BiometricsProtoEnums.MODALITY_FACE, statsClient, null /* taskStackListener */, lockoutCache, allowBackgroundAuthentication, true /* shouldVibrate */, isKeyguardBypassEnabled); + setRequestId(requestId); mUsageStats = usageStats; mLockoutCache = lockoutCache; mNotificationManager = context.getSystemService(NotificationManager.class); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java index 1e73ac528f088..2ef0911658b1b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java @@ -43,11 +43,13 @@ public class FaceDetectClient extends AcquisitionClient implements Det @Nullable private ICancellationSignal mCancellationSignal; public FaceDetectClient(@NonNull Context context, @NonNull LazyDaemon lazyDaemon, - @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, + @NonNull IBinder token, long requestId, + @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner, int sensorId, boolean isStrongBiometric, int statsClient) { super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, true /* shouldVibrate */, BiometricsProtoEnums.MODALITY_FACE, BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient); + setRequestId(requestId); mIsStrongBiometric = isStrongBiometric; } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java index 718b9da968f52..4bae7756abe00 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java @@ -65,6 +65,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicLong; /** * Provider for a single instance of the {@link IFace} HAL. @@ -83,6 +84,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { @NonNull private final UsageStats mUsageStats; @NonNull private final ActivityTaskManager mActivityTaskManager; @NonNull private final BiometricTaskStackListener mTaskStackListener; + // for requests that do not use biometric prompt + @NonNull private final AtomicLong mRequestCounter = new AtomicLong(0); @Nullable private IFace mDaemon; @@ -110,8 +113,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { && !client.isAlreadyDone()) { Slog.e(getTag(), "Stopping background authentication, top: " + topPackage + " currentClient: " + client); - mSensors.valueAt(i).getScheduler() - .cancelAuthenticationOrDetection(client.getToken()); + mSensors.valueAt(i).getScheduler().cancelAuthenticationOrDetection( + client.getToken(), client.getRequestId()); } } } @@ -356,34 +359,39 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { } @Override - public void scheduleFaceDetect(int sensorId, @NonNull IBinder token, + public long scheduleFaceDetect(int sensorId, @NonNull IBinder token, int userId, @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName, int statsClient) { + final long id = mRequestCounter.incrementAndGet(); + mHandler.post(() -> { final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId); final FaceDetectClient client = new FaceDetectClient(mContext, - mSensors.get(sensorId).getLazySession(), token, callback, userId, opPackageName, + mSensors.get(sensorId).getLazySession(), + token, id, callback, userId, opPackageName, sensorId, isStrongBiometric, statsClient); scheduleForSensor(sensorId, client); }); + + return id; } @Override - public void cancelFaceDetect(int sensorId, @NonNull IBinder token) { + public void cancelFaceDetect(int sensorId, @NonNull IBinder token, long requestId) { mHandler.post(() -> mSensors.get(sensorId).getScheduler() - .cancelAuthenticationOrDetection(token)); + .cancelAuthenticationOrDetection(token, requestId)); } @Override public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback, - @NonNull String opPackageName, boolean restricted, int statsClient, + @NonNull String opPackageName, long requestId, boolean restricted, int statsClient, boolean allowBackgroundAuthentication, boolean isKeyguardBypassEnabled) { mHandler.post(() -> { final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId); final FaceAuthenticationClient client = new FaceAuthenticationClient( - mContext, mSensors.get(sensorId).getLazySession(), token, callback, userId, - operationId, restricted, opPackageName, cookie, + mContext, mSensors.get(sensorId).getLazySession(), token, requestId, callback, + userId, operationId, restricted, opPackageName, cookie, false /* requireConfirmation */, sensorId, isStrongBiometric, statsClient, mUsageStats, mSensors.get(sensorId).getLockoutCache(), allowBackgroundAuthentication, isKeyguardBypassEnabled); @@ -392,9 +400,23 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { } @Override - public void cancelAuthentication(int sensorId, @NonNull IBinder token) { + public long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, + int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback, + @NonNull String opPackageName, boolean restricted, int statsClient, + boolean allowBackgroundAuthentication, boolean isKeyguardBypassEnabled) { + final long id = mRequestCounter.incrementAndGet(); + + scheduleAuthenticate(sensorId, token, operationId, userId, cookie, callback, + opPackageName, id, restricted, statsClient, + allowBackgroundAuthentication, isKeyguardBypassEnabled); + + return id; + } + + @Override + public void cancelAuthentication(int sensorId, @NonNull IBinder token, long requestId) { mHandler.post(() -> mSensors.get(sensorId).getScheduler() - .cancelAuthenticationOrDetection(token)); + .cancelAuthenticationOrDetection(token, requestId)); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java index d05333db75326..f4dcbbba21d73 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java @@ -87,6 +87,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; /** * Supports a single instance of the {@link android.hardware.biometrics.face.V1_0} or its extended @@ -115,6 +116,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { @NonNull private final Map mAuthenticatorIds; @Nullable private IBiometricsFace mDaemon; @NonNull private final HalResultController mHalResultController; + // for requests that do not use biometric prompt + @NonNull private final AtomicLong mRequestCounter = new AtomicLong(0); private int mCurrentUserId = UserHandle.USER_NULL; private final int mSensorId; private final List mGeneratedChallengeCount = new ArrayList<>(); @@ -605,7 +608,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { } @Override - public void scheduleFaceDetect(int sensorId, @NonNull IBinder token, + public long scheduleFaceDetect(int sensorId, @NonNull IBinder token, int userId, @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName, int statsClient) { throw new IllegalStateException("Face detect not supported by IBiometricsFace@1.0. Did you" @@ -613,7 +616,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { } @Override - public void cancelFaceDetect(int sensorId, @NonNull IBinder token) { + public void cancelFaceDetect(int sensorId, @NonNull IBinder token, long requestId) { throw new IllegalStateException("Face detect not supported by IBiometricsFace@1.0. Did you" + "forget to check the supportsFaceDetection flag?"); } @@ -621,26 +624,38 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { @Override public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId, int cookie, @NonNull ClientMonitorCallbackConverter receiver, - @NonNull String opPackageName, boolean restricted, int statsClient, + @NonNull String opPackageName, long requestId, boolean restricted, int statsClient, boolean allowBackgroundAuthentication, boolean isKeyguardBypassEnabled) { mHandler.post(() -> { scheduleUpdateActiveUserWithoutHandler(userId); final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorId); final FaceAuthenticationClient client = new FaceAuthenticationClient(mContext, - mLazyDaemon, token, receiver, userId, operationId, restricted, opPackageName, - cookie, false /* requireConfirmation */, mSensorId, isStrongBiometric, - statsClient, mLockoutTracker, mUsageStats, allowBackgroundAuthentication, - isKeyguardBypassEnabled); + mLazyDaemon, token, requestId, receiver, userId, operationId, restricted, + opPackageName, cookie, false /* requireConfirmation */, mSensorId, + isStrongBiometric, statsClient, mLockoutTracker, mUsageStats, + allowBackgroundAuthentication, isKeyguardBypassEnabled); mScheduler.scheduleClientMonitor(client); }); } @Override - public void cancelAuthentication(int sensorId, @NonNull IBinder token) { - mHandler.post(() -> { - mScheduler.cancelAuthenticationOrDetection(token); - }); + public long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, + int userId, int cookie, @NonNull ClientMonitorCallbackConverter receiver, + @NonNull String opPackageName, boolean restricted, int statsClient, + boolean allowBackgroundAuthentication, boolean isKeyguardBypassEnabled) { + final long id = mRequestCounter.incrementAndGet(); + + scheduleAuthenticate(sensorId, token, operationId, userId, cookie, receiver, + opPackageName, id, restricted, statsClient, + allowBackgroundAuthentication, isKeyguardBypassEnabled); + + return id; + } + + @Override + public void cancelAuthentication(int sensorId, @NonNull IBinder token, long requestId) { + mHandler.post(() -> mScheduler.cancelAuthenticationOrDetection(token, requestId)); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java index 33950af2216ff..40f2801541d36 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java @@ -57,7 +57,8 @@ class FaceAuthenticationClient extends AuthenticationClient { private int mLastAcquire; FaceAuthenticationClient(@NonNull Context context, - @NonNull LazyDaemon lazyDaemon, @NonNull IBinder token, + @NonNull LazyDaemon lazyDaemon, + @NonNull IBinder token, long requestId, @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId, boolean restricted, String owner, int cookie, boolean requireConfirmation, int sensorId, boolean isStrongBiometric, int statsClient, @NonNull LockoutTracker lockoutTracker, @@ -68,6 +69,7 @@ class FaceAuthenticationClient extends AuthenticationClient { BiometricsProtoEnums.MODALITY_FACE, statsClient, null /* taskStackListener */, lockoutTracker, allowBackgroundAuthentication, true /* shouldVibrate */, isKeyguardBypassEnabled); + setRequestId(requestId); mUsageStats = usageStats; final Resources resources = getContext().getResources(); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java index 1e5942930b018..52d887a752169 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java @@ -61,10 +61,10 @@ public final class FingerprintAuthenticator extends IBiometricAuthenticator.Stub @Override public void prepareForAuthentication(boolean requireConfirmation, IBinder token, long operationId, int userId, IBiometricSensorReceiver sensorReceiver, - String opPackageName, int cookie, boolean allowBackgroundAuthentication) + String opPackageName, long requestId, int cookie, boolean allowBackgroundAuthentication) throws RemoteException { mFingerprintService.prepareForAuthentication(mSensorId, token, operationId, userId, - sensorReceiver, opPackageName, cookie, allowBackgroundAuthentication); + sensorReceiver, opPackageName, requestId, cookie, allowBackgroundAuthentication); } @Override @@ -73,9 +73,10 @@ public final class FingerprintAuthenticator extends IBiometricAuthenticator.Stub } @Override - public void cancelAuthenticationFromService(IBinder token, String opPackageName) + public void cancelAuthenticationFromService(IBinder token, String opPackageName, long requestId) throws RemoteException { - mFingerprintService.cancelAuthenticationFromService(mSensorId, token, opPackageName); + mFingerprintService.cancelAuthenticationFromService( + mSensorId, token, opPackageName, requestId); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index 183fabdc2a7b0..f0a8b9ca31464 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -245,7 +245,7 @@ public class FingerprintService extends SystemService { @SuppressWarnings("deprecation") @Override // Binder call - public void authenticate(final IBinder token, final long operationId, + public long authenticate(final IBinder token, final long operationId, final int sensorId, final int userId, final IFingerprintServiceReceiver receiver, final String opPackageName) { final int callingUid = Binder.getCallingUid(); @@ -255,7 +255,7 @@ public class FingerprintService extends SystemService { if (!canUseFingerprint(opPackageName, true /* requireForeground */, callingUid, callingPid, callingUserId)) { Slog.w(TAG, "Authenticate rejecting package: " + opPackageName); - return; + return -1; } // Keyguard check must be done on the caller's binder identity, since it also checks @@ -270,7 +270,7 @@ public class FingerprintService extends SystemService { // SafetyNet for b/79776455 EventLog.writeEvent(0x534e4554, "79776455"); Slog.e(TAG, "Authenticate invoked when user is encrypted or lockdown"); - return; + return -1; } } finally { Binder.restoreCallingIdentity(identity); @@ -290,7 +290,7 @@ public class FingerprintService extends SystemService { } if (provider == null) { Slog.w(TAG, "Null provider for authenticate"); - return; + return -1; } final FingerprintSensorPropertiesInternal sensorProps = @@ -299,18 +299,17 @@ public class FingerprintService extends SystemService { && sensorProps != null && sensorProps.isAnyUdfpsType()) { identity = Binder.clearCallingIdentity(); try { - authenticateWithPrompt(operationId, sensorProps, userId, receiver); + return authenticateWithPrompt(operationId, sensorProps, userId, receiver); } finally { Binder.restoreCallingIdentity(identity); } - } else { - provider.second.scheduleAuthenticate(provider.first, token, operationId, userId, - 0 /* cookie */, new ClientMonitorCallbackConverter(receiver), opPackageName, - restricted, statsClient, isKeyguard, mFingerprintStateCallback); } + return provider.second.scheduleAuthenticate(provider.first, token, operationId, userId, + 0 /* cookie */, new ClientMonitorCallbackConverter(receiver), opPackageName, + restricted, statsClient, isKeyguard, mFingerprintStateCallback); } - private void authenticateWithPrompt( + private long authenticateWithPrompt( final long operationId, @NonNull final FingerprintSensorPropertiesInternal props, final int userId, @@ -387,33 +386,33 @@ public class FingerprintService extends SystemService { } }; - biometricPrompt.authenticateUserForOperation( + return biometricPrompt.authenticateUserForOperation( new CancellationSignal(), executor, promptCallback, userId, operationId); } @Override - public void detectFingerprint(final IBinder token, final int userId, + public long detectFingerprint(final IBinder token, final int userId, final IFingerprintServiceReceiver receiver, final String opPackageName) { Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); if (!Utils.isKeyguard(getContext(), opPackageName)) { Slog.w(TAG, "detectFingerprint called from non-sysui package: " + opPackageName); - return; + return -1; } if (!Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)) { // If this happens, something in KeyguardUpdateMonitor is wrong. This should only // ever be invoked when the user is encrypted or lockdown. Slog.e(TAG, "detectFingerprint invoked when user is not encrypted or lockdown"); - return; + return -1; } final Pair provider = getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for detectFingerprint"); - return; + return -1; } - provider.second.scheduleFingerDetect(provider.first, token, userId, + return provider.second.scheduleFingerDetect(provider.first, token, userId, new ClientMonitorCallbackConverter(receiver), opPackageName, BiometricsProtoEnums.CLIENT_KEYGUARD, mFingerprintStateCallback); } @@ -421,7 +420,7 @@ public class FingerprintService extends SystemService { @Override // Binder call public void prepareForAuthentication(int sensorId, IBinder token, long operationId, int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName, - int cookie, boolean allowBackgroundAuthentication) { + long requestId, int cookie, boolean allowBackgroundAuthentication) { Utils.checkPermission(getContext(), MANAGE_BIOMETRIC); final ServiceProvider provider = getProviderForSensor(sensorId); @@ -432,9 +431,9 @@ public class FingerprintService extends SystemService { final boolean restricted = true; // BiometricPrompt is always restricted provider.scheduleAuthenticate(sensorId, token, operationId, userId, cookie, - new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, restricted, - BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, allowBackgroundAuthentication, - mFingerprintStateCallback); + new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, requestId, + restricted, BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, + allowBackgroundAuthentication, mFingerprintStateCallback); } @Override // Binder call @@ -452,7 +451,8 @@ public class FingerprintService extends SystemService { @Override // Binder call - public void cancelAuthentication(final IBinder token, final String opPackageName) { + public void cancelAuthentication(final IBinder token, final String opPackageName, + long requestId) { final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); final int callingUserId = UserHandle.getCallingUserId(); @@ -469,11 +469,12 @@ public class FingerprintService extends SystemService { return; } - provider.second.cancelAuthentication(provider.first, token); + provider.second.cancelAuthentication(provider.first, token, requestId); } @Override // Binder call - public void cancelFingerprintDetect(final IBinder token, final String opPackageName) { + public void cancelFingerprintDetect(final IBinder token, final String opPackageName, + final long requestId) { Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); if (!Utils.isKeyguard(getContext(), opPackageName)) { Slog.w(TAG, "cancelFingerprintDetect called from non-sysui package: " @@ -489,12 +490,12 @@ public class FingerprintService extends SystemService { return; } - provider.second.cancelAuthentication(provider.first, token); + provider.second.cancelAuthentication(provider.first, token, requestId); } @Override // Binder call public void cancelAuthenticationFromService(final int sensorId, final IBinder token, - final String opPackageName) { + final String opPackageName, final long requestId) { Utils.checkPermission(getContext(), MANAGE_BIOMETRIC); @@ -506,7 +507,7 @@ public class FingerprintService extends SystemService { return; } - provider.cancelAuthentication(sensorId, token); + provider.cancelAuthentication(sensorId, token, requestId); } @Override // Binder call diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java index 706ac10137467..b9fcd8ef4e424 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java @@ -95,12 +95,18 @@ public interface ServiceProvider { void cancelEnrollment(int sensorId, @NonNull IBinder token); - void scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId, + long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId, @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName, int statsClient, @NonNull FingerprintStateCallback fingerprintStateCallback); void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId, + int cookie, @NonNull ClientMonitorCallbackConverter callback, + @NonNull String opPackageName, long requestId, boolean restricted, int statsClient, + boolean allowBackgroundAuthentication, + @NonNull FingerprintStateCallback fingerprintStateCallback); + + long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName, boolean restricted, int statsClient, boolean allowBackgroundAuthentication, @@ -108,7 +114,7 @@ public interface ServiceProvider { void startPreparedClient(int sensorId, int cookie); - void cancelAuthentication(int sensorId, @NonNull IBinder token); + void cancelAuthentication(int sensorId, @NonNull IBinder token, long requestId); void scheduleRemove(int sensorId, @NonNull IBinder token, @NonNull IFingerprintServiceReceiver receiver, int fingerId, int userId, diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java index 37ee76adeecef..9d911e0a320bd 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java @@ -61,7 +61,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient imp private boolean mIsPointerDown; FingerprintAuthenticationClient(@NonNull Context context, - @NonNull LazyDaemon lazyDaemon, @NonNull IBinder token, + @NonNull LazyDaemon lazyDaemon, + @NonNull IBinder token, long requestId, @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId, boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation, int sensorId, boolean isStrongBiometric, int statsClient, @@ -74,6 +75,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient imp BiometricsProtoEnums.MODALITY_FINGERPRINT, statsClient, taskStackListener, lockoutCache, allowBackgroundAuthentication, true /* shouldVibrate */, false /* isKeyguardBypassEnabled */); + setRequestId(requestId); mLockoutCache = lockoutCache; mUdfpsOverlayController = udfpsOverlayController; mSensorProps = sensorProps; diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java index c5dc44988612e..da91cdd981b9d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java @@ -47,13 +47,15 @@ class FingerprintDetectClient extends AcquisitionClient implements Det @Nullable private ICancellationSignal mCancellationSignal; FingerprintDetectClient(@NonNull Context context, @NonNull LazyDaemon lazyDaemon, - @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, + @NonNull IBinder token, long requestId, + @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner, int sensorId, @Nullable IUdfpsOverlayController udfpsOverlayController, boolean isStrongBiometric, int statsClient) { super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, true /* shouldVibrate */, BiometricsProtoEnums.MODALITY_FINGERPRINT, BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient); + setRequestId(requestId); mIsStrongBiometric = isStrongBiometric; mUdfpsOverlayController = udfpsOverlayController; } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index 102b074931342..377fecac61177 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -71,6 +71,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicLong; /** * Provider for a single instance of the {@link IFingerprint} HAL. @@ -88,6 +89,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher; @NonNull private final ActivityTaskManager mActivityTaskManager; @NonNull private final BiometricTaskStackListener mTaskStackListener; + // for requests that do not use biometric prompt + @NonNull private final AtomicLong mRequestCounter = new AtomicLong(0); @Nullable private IFingerprint mDaemon; @Nullable private IUdfpsOverlayController mUdfpsOverlayController; @@ -118,8 +121,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi && !client.isAlreadyDone()) { Slog.e(getTag(), "Stopping background authentication, top: " + topPackage + " currentClient: " + client); - mSensors.valueAt(i).getScheduler() - .cancelAuthenticationOrDetection(client.getToken()); + mSensors.valueAt(i).getScheduler().cancelAuthenticationOrDetection( + client.getToken(), client.getRequestId()); } } } @@ -369,31 +372,35 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi } @Override - public void scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId, + public long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId, @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName, int statsClient, @NonNull FingerprintStateCallback fingerprintStateCallback) { + final long id = mRequestCounter.incrementAndGet(); + mHandler.post(() -> { final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId); final FingerprintDetectClient client = new FingerprintDetectClient(mContext, - mSensors.get(sensorId).getLazySession(), token, callback, userId, + mSensors.get(sensorId).getLazySession(), token, id, callback, userId, opPackageName, sensorId, mUdfpsOverlayController, isStrongBiometric, statsClient); scheduleForSensor(sensorId, client, fingerprintStateCallback); }); + + return id; } @Override public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback, - @NonNull String opPackageName, boolean restricted, int statsClient, + @NonNull String opPackageName, long requestId, boolean restricted, int statsClient, boolean allowBackgroundAuthentication, @NonNull FingerprintStateCallback fingerprintStateCallback) { mHandler.post(() -> { final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId); final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient( - mContext, mSensors.get(sensorId).getLazySession(), token, callback, userId, - operationId, restricted, opPackageName, cookie, + mContext, mSensors.get(sensorId).getLazySession(), token, requestId, callback, + userId, operationId, restricted, opPackageName, cookie, false /* requireConfirmation */, sensorId, isStrongBiometric, statsClient, mTaskStackListener, mSensors.get(sensorId).getLockoutCache(), mUdfpsOverlayController, allowBackgroundAuthentication, @@ -402,15 +409,30 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi }); } + @Override + public long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, + int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback, + @NonNull String opPackageName, boolean restricted, int statsClient, + boolean allowBackgroundAuthentication, + @NonNull FingerprintStateCallback fingerprintStateCallback) { + final long id = mRequestCounter.incrementAndGet(); + + scheduleAuthenticate(sensorId, token, operationId, userId, cookie, callback, + opPackageName, id, restricted, statsClient, allowBackgroundAuthentication, + fingerprintStateCallback); + + return id; + } + @Override public void startPreparedClient(int sensorId, int cookie) { mHandler.post(() -> mSensors.get(sensorId).getScheduler().startPreparedClient(cookie)); } @Override - public void cancelAuthentication(int sensorId, @NonNull IBinder token) { + public void cancelAuthentication(int sensorId, @NonNull IBinder token, long requestId) { mHandler.post(() -> mSensors.get(sensorId).getScheduler() - .cancelAuthenticationOrDetection(token)); + .cancelAuthenticationOrDetection(token, requestId)); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index 2f5b5c7b97270..f17bcc8c63546 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -88,6 +88,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; /** * Supports a single instance of the {@link android.hardware.biometrics.fingerprint.V2_1} or @@ -115,6 +116,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @NonNull private final HalResultController mHalResultController; @Nullable private IUdfpsOverlayController mUdfpsOverlayController; @Nullable private ISidefpsController mSidefpsController; + // for requests that do not use biometric prompt + @NonNull private final AtomicLong mRequestCounter = new AtomicLong(0); private int mCurrentUserId = UserHandle.USER_NULL; private final boolean mIsUdfps; private final int mSensorId; @@ -142,7 +145,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider && !client.isAlreadyDone()) { Slog.e(TAG, "Stopping background authentication, top: " + topPackage + " currentClient: " + client); - mScheduler.cancelAuthenticationOrDetection(client.getToken()); + mScheduler.cancelAuthenticationOrDetection( + client.getToken(), client.getRequestId()); } } }); @@ -591,26 +595,30 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider } @Override - public void scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId, + public long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId, @NonNull ClientMonitorCallbackConverter listener, @NonNull String opPackageName, int statsClient, @NonNull FingerprintStateCallback fingerprintStateCallback) { + final long id = mRequestCounter.incrementAndGet(); + mHandler.post(() -> { scheduleUpdateActiveUserWithoutHandler(userId); final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorProperties.sensorId); final FingerprintDetectClient client = new FingerprintDetectClient(mContext, - mLazyDaemon, token, listener, userId, opPackageName, + mLazyDaemon, token, id, listener, userId, opPackageName, mSensorProperties.sensorId, mUdfpsOverlayController, isStrongBiometric, statsClient); mScheduler.scheduleClientMonitor(client, fingerprintStateCallback); }); + + return id; } @Override public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId, int cookie, @NonNull ClientMonitorCallbackConverter listener, - @NonNull String opPackageName, boolean restricted, int statsClient, + @NonNull String opPackageName, long requestId, boolean restricted, int statsClient, boolean allowBackgroundAuthentication, @NonNull FingerprintStateCallback fingerprintStateCallback) { mHandler.post(() -> { @@ -618,8 +626,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorProperties.sensorId); final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient( - mContext, mLazyDaemon, token, listener, userId, operationId, restricted, - opPackageName, cookie, false /* requireConfirmation */, + mContext, mLazyDaemon, token, requestId, listener, userId, operationId, + restricted, opPackageName, cookie, false /* requireConfirmation */, mSensorProperties.sensorId, isStrongBiometric, statsClient, mTaskStackListener, mLockoutTracker, mUdfpsOverlayController, allowBackgroundAuthentication, mSensorProperties); @@ -627,15 +635,30 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider }); } + @Override + public long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, + int userId, int cookie, @NonNull ClientMonitorCallbackConverter listener, + @NonNull String opPackageName, boolean restricted, int statsClient, + boolean allowBackgroundAuthentication, + @NonNull FingerprintStateCallback fingerprintStateCallback) { + final long id = mRequestCounter.incrementAndGet(); + + scheduleAuthenticate(sensorId, token, operationId, userId, cookie, listener, + opPackageName, id, restricted, statsClient, allowBackgroundAuthentication, + fingerprintStateCallback); + + return id; + } + @Override public void startPreparedClient(int sensorId, int cookie) { mHandler.post(() -> mScheduler.startPreparedClient(cookie)); } @Override - public void cancelAuthentication(int sensorId, @NonNull IBinder token) { + public void cancelAuthentication(int sensorId, @NonNull IBinder token, long requestId) { Slog.d(TAG, "cancelAuthentication, sensorId: " + sensorId); - mHandler.post(() -> mScheduler.cancelAuthenticationOrDetection(token)); + mHandler.post(() -> mScheduler.cancelAuthenticationOrDetection(token, requestId)); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java index 5060744bb33e1..7d95ec098fee5 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java @@ -59,7 +59,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient lazyDaemon, @NonNull IBinder token, + @NonNull LazyDaemon lazyDaemon, + @NonNull IBinder token, long requestId, @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId, boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation, int sensorId, boolean isStrongBiometric, int statsClient, @@ -73,6 +74,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient private boolean mIsPointerDown; public FingerprintDetectClient(@NonNull Context context, - @NonNull LazyDaemon lazyDaemon, @NonNull IBinder token, + @NonNull LazyDaemon lazyDaemon, + @NonNull IBinder token, long requestId, @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner, int sensorId, @Nullable IUdfpsOverlayController udfpsOverlayController, boolean isStrongBiometric, int statsClient) { super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, true /* shouldVibrate */, BiometricsProtoEnums.MODALITY_FINGERPRINT, BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient); + setRequestId(requestId); mUdfpsOverlayController = udfpsOverlayController; mIsStrongBiometric = isStrongBiometric; } diff --git a/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java index 491818520e3cc..5c0c3626037a0 100644 --- a/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java +++ b/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java @@ -59,7 +59,7 @@ public final class IrisAuthenticator extends IBiometricAuthenticator.Stub { @Override public void prepareForAuthentication(boolean requireConfirmation, IBinder token, long sessionId, int userId, IBiometricSensorReceiver sensorReceiver, - String opPackageName, int cookie, boolean allowBackgroundAuthentication) + String opPackageName, long requestId, int cookie, boolean allowBackgroundAuthentication) throws RemoteException { } @@ -68,7 +68,7 @@ public final class IrisAuthenticator extends IBiometricAuthenticator.Stub { } @Override - public void cancelAuthenticationFromService(IBinder token, String opPackageName) + public void cancelAuthenticationFromService(IBinder token, String opPackageName, long requestId) throws RemoteException { } diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 3a7e13b8d8720..abe81e139b431 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -783,13 +783,14 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D @Override public void showAuthenticationDialog(PromptInfo promptInfo, IBiometricSysuiReceiver receiver, int[] sensorIds, boolean credentialAllowed, boolean requireConfirmation, - int userId, String opPackageName, long operationId, + int userId, long operationId, String opPackageName, long requestId, @BiometricMultiSensorMode int multiSensorConfig) { enforceBiometricDialog(); if (mBar != null) { try { mBar.showAuthenticationDialog(promptInfo, receiver, sensorIds, credentialAllowed, - requireConfirmation, userId, opPackageName, operationId, multiSensorConfig); + requireConfirmation, userId, operationId, opPackageName, requestId, + multiSensorConfig); } catch (RemoteException ex) { } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java index 1903bc88db357..b3f7587df6124 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java @@ -74,6 +74,7 @@ import java.util.function.Consumer; public class AuthSessionTest { private static final String TEST_PACKAGE = "test_package"; + private static final long TEST_REQUEST_ID = 22; @Mock private Context mContext; @Mock private ITrustManager mTrustManager; @@ -112,6 +113,7 @@ public class AuthSessionTest { final AuthSession session = createAuthSession(mSensors, false /* checkDevicePolicyManager */, Authenticators.BIOMETRIC_STRONG, + TEST_REQUEST_ID, 0 /* operationId */, 0 /* userId */); @@ -133,6 +135,7 @@ public class AuthSessionTest { final AuthSession session = createAuthSession(mSensors, false /* checkDevicePolicyManager */, Authenticators.BIOMETRIC_STRONG, + TEST_REQUEST_ID, operationId, userId); assertEquals(mSensors.size(), session.mPreAuthInfo.eligibleSensors.size()); @@ -153,6 +156,7 @@ public class AuthSessionTest { eq(userId), eq(mSensorReceiver), eq(TEST_PACKAGE), + eq(TEST_REQUEST_ID), eq(sensor.getCookie()), anyBoolean() /* allowBackgroundAuthentication */); } @@ -193,6 +197,7 @@ public class AuthSessionTest { final AuthSession session = createAuthSession(mSensors, false /* checkDevicePolicyManager */, Authenticators.BIOMETRIC_STRONG, + TEST_REQUEST_ID, 44 /* operationId */, 2 /* userId */); @@ -238,6 +243,7 @@ public class AuthSessionTest { final AuthSession session = createAuthSession(mSensors, false /* checkDevicePolicyManager */, Authenticators.BIOMETRIC_STRONG, + TEST_REQUEST_ID, operationId, userId); assertEquals(mSensors.size(), session.mPreAuthInfo.eligibleSensors.size()); @@ -264,7 +270,7 @@ public class AuthSessionTest { // fingerprint sensor does not start even if all cookies are received assertEquals(STATE_AUTH_STARTED, session.getState()); verify(mStatusBarService).showAuthenticationDialog(any(), any(), any(), - anyBoolean(), anyBoolean(), anyInt(), any(), anyLong(), anyInt()); + anyBoolean(), anyBoolean(), anyInt(), anyLong(), any(), anyLong(), anyInt()); // Notify AuthSession that the UI is shown. Then, fingerprint sensor should be started. session.onDialogAnimatedIn(); @@ -303,6 +309,7 @@ public class AuthSessionTest { final AuthSession session = createAuthSession(mSensors, false /* checkDevicePolicyManager */, Authenticators.BIOMETRIC_STRONG, + TEST_REQUEST_ID, 0 /* operationId */, 0 /* userId */); @@ -311,7 +318,8 @@ public class AuthSessionTest { sessionConsumer.accept(session); - verify(faceAuthenticator).cancelAuthenticationFromService(eq(mToken), eq(TEST_PACKAGE)); + verify(faceAuthenticator).cancelAuthenticationFromService( + eq(mToken), eq(TEST_PACKAGE), eq(TEST_REQUEST_ID)); } private PreAuthInfo createPreAuthInfo(List sensors, int userId, @@ -328,14 +336,14 @@ public class AuthSessionTest { private AuthSession createAuthSession(List sensors, boolean checkDevicePolicyManager, @Authenticators.Types int authenticators, - long operationId, int userId) throws RemoteException { + long requestId, long operationId, int userId) throws RemoteException { final PromptInfo promptInfo = createPromptInfo(authenticators); final PreAuthInfo preAuthInfo = createPreAuthInfo(sensors, userId, promptInfo, checkDevicePolicyManager); return new AuthSession(mContext, mStatusBarService, mSysuiReceiver, mKeyStore, - mRandom, mClientDeathReceiver, preAuthInfo, mToken, operationId, userId, + mRandom, mClientDeathReceiver, preAuthInfo, mToken, requestId, operationId, userId, mSensorReceiver, mClientReceiver, TEST_PACKAGE, promptInfo, false /* debugEnabled */, mFingerprintSensorProps); } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java index 7c7afb7dde36d..69d8e89ac3b37 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java @@ -85,6 +85,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.Random; +import java.util.concurrent.atomic.AtomicLong; @Presubmit @SmallTest @@ -93,6 +94,7 @@ public class BiometricServiceTest { private static final String TAG = "BiometricServiceTest"; private static final String TEST_PACKAGE_NAME = "test_package"; + private static final long TEST_REQUEST_ID = 44; private static final String ERROR_HW_UNAVAILABLE = "hw_unavailable"; private static final String ERROR_NOT_RECOGNIZED = "not_recognized"; @@ -151,6 +153,7 @@ public class BiometricServiceTest { .thenReturn(mock(BiometricStrengthController.class)); when(mInjector.getTrustManager()).thenReturn(mTrustManager); when(mInjector.getDevicePolicyManager(any())).thenReturn(mDevicePolicyManager); + when(mInjector.getRequestGenerator()).thenReturn(new AtomicLong(TEST_REQUEST_ID - 1)); when(mResources.getString(R.string.biometric_error_hw_unavailable)) .thenReturn(ERROR_HW_UNAVAILABLE); @@ -215,8 +218,7 @@ public class BiometricServiceTest { mBiometricService.mCurrentAuthSession.getState()); verify(mBiometricService.mCurrentAuthSession.mPreAuthInfo.eligibleSensors.get(0).impl) - .cancelAuthenticationFromService(any(), - any()); + .cancelAuthenticationFromService(any(), any(), anyLong()); // Simulate ERROR_CANCELED received from HAL mBiometricService.mBiometricSensorReceiver.onError( @@ -272,8 +274,9 @@ public class BiometricServiceTest { eq(true) /* credentialAllowed */, anyBoolean() /* requireConfirmation */, anyInt() /* userId */, + anyLong() /* operationId */, eq(TEST_PACKAGE_NAME), - anyLong() /* sessionId */, + eq(TEST_REQUEST_ID), eq(BIOMETRIC_MULTI_SENSOR_DEFAULT)); } @@ -357,8 +360,9 @@ public class BiometricServiceTest { eq(false) /* credentialAllowed */, eq(false) /* requireConfirmation */, anyInt() /* userId */, + anyLong() /* operationId */, eq(TEST_PACKAGE_NAME), - anyLong() /* sessionId */, + eq(TEST_REQUEST_ID), eq(BIOMETRIC_MULTI_SENSOR_DEFAULT)); } @@ -467,6 +471,7 @@ public class BiometricServiceTest { anyInt() /* userId */, any(IBiometricSensorReceiver.class), anyString() /* opPackageName */, + eq(TEST_REQUEST_ID), cookieCaptor.capture() /* cookie */, anyBoolean() /* allowBackgroundAuthentication */); @@ -488,8 +493,9 @@ public class BiometricServiceTest { eq(false) /* credentialAllowed */, anyBoolean() /* requireConfirmation */, anyInt() /* userId */, + anyLong() /* operationId */, eq(TEST_PACKAGE_NAME), - anyLong() /* sessionId */, + eq(TEST_REQUEST_ID), eq(BIOMETRIC_MULTI_SENSOR_DEFAULT)); // Hardware authenticated @@ -543,8 +549,9 @@ public class BiometricServiceTest { eq(true) /* credentialAllowed */, anyBoolean() /* requireConfirmation */, anyInt() /* userId */, + anyLong() /* operationId */, eq(TEST_PACKAGE_NAME), - anyLong() /* sessionId */, + eq(TEST_REQUEST_ID), eq(BIOMETRIC_MULTI_SENSOR_DEFAULT)); } @@ -705,8 +712,9 @@ public class BiometricServiceTest { anyBoolean() /* credentialAllowed */, anyBoolean() /* requireConfirmation */, anyInt() /* userId */, + anyLong() /* operationId */, anyString(), - anyLong() /* sessionId */, + anyLong() /* requestId */, eq(BIOMETRIC_MULTI_SENSOR_DEFAULT)); } @@ -805,8 +813,9 @@ public class BiometricServiceTest { eq(true) /* credentialAllowed */, anyBoolean() /* requireConfirmation */, anyInt() /* userId */, + anyLong() /* operationId */, eq(TEST_PACKAGE_NAME), - anyLong() /* sessionId */, + eq(TEST_REQUEST_ID), eq(BIOMETRIC_MULTI_SENSOR_DEFAULT)); } @@ -885,8 +894,9 @@ public class BiometricServiceTest { eq(true) /* credentialAllowed */, anyBoolean() /* requireConfirmation */, anyInt() /* userId */, + anyLong() /* operationId */, eq(TEST_PACKAGE_NAME), - anyLong() /* sessionId */, + eq(TEST_REQUEST_ID), eq(BIOMETRIC_MULTI_SENSOR_DEFAULT)); } @@ -1030,8 +1040,7 @@ public class BiometricServiceTest { eq(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED), eq(0 /* vendorCode */)); verify(mBiometricService.mSensors.get(0).impl).cancelAuthenticationFromService( - any(), - any()); + any(), any(), anyLong()); assertNull(mBiometricService.mCurrentAuthSession); } @@ -1051,7 +1060,7 @@ public class BiometricServiceTest { waitForIdle(); verify(mBiometricService.mSensors.get(0).impl) - .cancelAuthenticationFromService(any(), any()); + .cancelAuthenticationFromService(any(), any(), anyLong()); } @Test @@ -1071,7 +1080,7 @@ public class BiometricServiceTest { waitForIdle(); verify(mBiometricService.mSensors.get(0).impl) - .cancelAuthenticationFromService(any(), any()); + .cancelAuthenticationFromService(any(), any(), anyLong()); } @Test @@ -1088,7 +1097,7 @@ public class BiometricServiceTest { waitForIdle(); verify(mBiometricService.mSensors.get(0).impl) - .cancelAuthenticationFromService(any(), any()); + .cancelAuthenticationFromService(any(), any(), anyLong()); verify(mReceiver1).onError( eq(BiometricAuthenticator.TYPE_FACE), eq(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED), @@ -1126,7 +1135,7 @@ public class BiometricServiceTest { false /* requireConfirmation */, null /* authenticators */); mBiometricService.mImpl.cancelAuthentication(mBiometricService.mCurrentAuthSession.mToken, - TEST_PACKAGE_NAME); + TEST_PACKAGE_NAME, TEST_REQUEST_ID); waitForIdle(); // Pretend that the HAL has responded to cancel with ERROR_CANCELED @@ -1353,8 +1362,8 @@ public class BiometricServiceTest { int authenticators = Authenticators.BIOMETRIC_STRONG; assertEquals(BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED, invokeCanAuthenticate(mBiometricService, authenticators)); - invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, - authenticators); + long requestId = invokeAuthenticate(mBiometricService.mImpl, mReceiver1, + false /* requireConfirmation */, authenticators); waitForIdle(); verify(mReceiver1).onError( eq(BiometricAuthenticator.TYPE_FINGERPRINT), @@ -1366,7 +1375,7 @@ public class BiometricServiceTest { authenticators = Authenticators.BIOMETRIC_WEAK; assertEquals(BiometricManager.BIOMETRIC_SUCCESS, invokeCanAuthenticate(mBiometricService, authenticators)); - invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1, + requestId = invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, authenticators); waitForIdle(); @@ -1377,8 +1386,9 @@ public class BiometricServiceTest { eq(false) /* credentialAllowed */, anyBoolean() /* requireConfirmation */, anyInt() /* userId */, + anyLong() /* operationId */, eq(TEST_PACKAGE_NAME), - anyLong() /* sessionId */, + eq(requestId), eq(BIOMETRIC_MULTI_SENSOR_DEFAULT)); // Requesting strong and credential, when credential is setup @@ -1387,7 +1397,7 @@ public class BiometricServiceTest { when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true); assertEquals(BiometricManager.BIOMETRIC_SUCCESS, invokeCanAuthenticate(mBiometricService, authenticators)); - invokeAuthenticate(mBiometricService.mImpl, mReceiver1, + requestId = invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, authenticators); waitForIdle(); @@ -1399,8 +1409,9 @@ public class BiometricServiceTest { eq(true) /* credentialAllowed */, anyBoolean() /* requireConfirmation */, anyInt() /* userId */, + anyLong() /* operationId */, eq(TEST_PACKAGE_NAME), - anyLong() /* sessionId */, + eq(requestId), eq(BIOMETRIC_MULTI_SENSOR_DEFAULT)); // Un-downgrading the authenticator allows successful strong auth @@ -1414,7 +1425,7 @@ public class BiometricServiceTest { authenticators = Authenticators.BIOMETRIC_STRONG; assertEquals(BiometricManager.BIOMETRIC_SUCCESS, invokeCanAuthenticate(mBiometricService, authenticators)); - invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1, + requestId = invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, authenticators); waitForIdle(); verify(mBiometricService.mStatusBarService).showAuthenticationDialog( @@ -1424,8 +1435,9 @@ public class BiometricServiceTest { eq(false) /* credentialAllowed */, anyBoolean() /* requireConfirmation */, anyInt() /* userId */, + anyLong() /* operationId */, eq(TEST_PACKAGE_NAME), - anyLong() /* sessionId */, + eq(requestId), eq(BIOMETRIC_MULTI_SENSOR_DEFAULT)); } @@ -1617,11 +1629,12 @@ public class BiometricServiceTest { mBiometricService.mStatusBarService = mock(IStatusBarService.class); } - private void invokeAuthenticateAndStart(IBiometricService.Stub service, + private long invokeAuthenticateAndStart(IBiometricService.Stub service, IBiometricServiceReceiver receiver, boolean requireConfirmation, Integer authenticators) throws Exception { // Request auth, creates a pending session - invokeAuthenticate(service, receiver, requireConfirmation, authenticators); + final long requestId = invokeAuthenticate( + service, receiver, requireConfirmation, authenticators); waitForIdle(); startPendingAuthSession(mBiometricService); @@ -1629,6 +1642,8 @@ public class BiometricServiceTest { assertNotNull(mBiometricService.mCurrentAuthSession); assertEquals(STATE_AUTH_STARTED, mBiometricService.mCurrentAuthSession.getState()); + + return requestId; } private static void startPendingAuthSession(BiometricService service) throws Exception { @@ -1644,10 +1659,10 @@ public class BiometricServiceTest { service.mImpl.onReadyForAuthentication(cookie); } - private static void invokeAuthenticate(IBiometricService.Stub service, + private static long invokeAuthenticate(IBiometricService.Stub service, IBiometricServiceReceiver receiver, boolean requireConfirmation, Integer authenticators) throws Exception { - service.authenticate( + return service.authenticate( new Binder() /* token */, 0 /* operationId */, 0 /* userId */, @@ -1657,9 +1672,9 @@ public class BiometricServiceTest { false /* checkDevicePolicy */)); } - private static void invokeAuthenticateForWorkApp(IBiometricService.Stub service, + private static long invokeAuthenticateForWorkApp(IBiometricService.Stub service, IBiometricServiceReceiver receiver, Integer authenticators) throws Exception { - service.authenticate( + return service.authenticate( new Binder() /* token */, 0 /* operationId */, 0 /* userId */, diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java index a41f79e8f6821..e3e3900c47e08 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java @@ -40,6 +40,7 @@ import android.platform.test.annotations.Presubmit; import android.testing.TestableContext; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -193,7 +194,7 @@ public class BiometricSchedulerTest { // Request it to be canceled. The operation can be canceled immediately, and the scheduler // should go back to idle, since in this case the framework has not even requested the HAL // to authenticate yet. - mScheduler.cancelAuthenticationOrDetection(mToken); + mScheduler.cancelAuthenticationOrDetection(mToken, 1 /* requestId */); assertNull(mScheduler.mCurrentOperation); } @@ -303,7 +304,7 @@ public class BiometricSchedulerTest { mScheduler.mPendingOperations.getFirst().mState); // Request cancel before the authentication client has started - mScheduler.cancelAuthenticationOrDetection(mToken); + mScheduler.cancelAuthenticationOrDetection(mToken, 1 /* requestId */); waitForIdle(); assertEquals(Operation.STATE_WAITING_IN_QUEUE_CANCELING, mScheduler.mPendingOperations.getFirst().mState); @@ -317,6 +318,107 @@ public class BiometricSchedulerTest { assertNull(mScheduler.getCurrentClient()); } + @Test + public void testCancels_whenAuthRequestIdNotSet() { + testCancelsWhenRequestId(null /* requestId */, 2, true /* started */); + } + + @Test + public void testCancels_whenAuthRequestIdNotSet_notStarted() { + testCancelsWhenRequestId(null /* requestId */, 2, false /* started */); + } + + @Test + public void testCancels_whenAuthRequestIdMatches() { + testCancelsWhenRequestId(200L, 200, true /* started */); + } + + @Test + public void testCancels_whenAuthRequestIdMatches_noStarted() { + testCancelsWhenRequestId(200L, 200, false /* started */); + } + + @Test + public void testDoesNotCancel_whenAuthRequestIdMismatched() { + testCancelsWhenRequestId(10L, 20, true /* started */); + } + + @Test + public void testDoesNotCancel_whenAuthRequestIdMismatched_notStarted() { + testCancelsWhenRequestId(10L, 20, false /* started */); + } + + private void testCancelsWhenRequestId(@Nullable Long requestId, long cancelRequestId, + boolean started) { + final boolean matches = requestId == null || requestId == cancelRequestId; + final HalClientMonitor.LazyDaemon lazyDaemon = () -> mock(Object.class); + final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class); + final TestAuthenticationClient client = new TestAuthenticationClient( + mContext, lazyDaemon, mToken, callback); + if (requestId != null) { + client.setRequestId(requestId); + } + + mScheduler.scheduleClientMonitor(client); + if (started) { + mScheduler.startPreparedClient(client.getCookie()); + } + waitForIdle(); + mScheduler.cancelAuthenticationOrDetection(mToken, cancelRequestId); + waitForIdle(); + + assertEquals(matches && started ? 1 : 0, client.mNumCancels); + + if (matches) { + if (started) { + assertEquals(Operation.STATE_STARTED_CANCELING, + mScheduler.mCurrentOperation.mState); + } + } else { + if (started) { + assertEquals(Operation.STATE_STARTED, + mScheduler.mCurrentOperation.mState); + } else { + assertEquals(Operation.STATE_WAITING_FOR_COOKIE, + mScheduler.mCurrentOperation.mState); + } + } + } + + @Test + public void testCancelsPending_whenAuthRequestIdsSet() { + final long requestId1 = 10; + final long requestId2 = 20; + final HalClientMonitor.LazyDaemon lazyDaemon = () -> mock(Object.class); + final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class); + final TestAuthenticationClient client1 = new TestAuthenticationClient( + mContext, lazyDaemon, mToken, callback); + client1.setRequestId(requestId1); + final TestAuthenticationClient client2 = new TestAuthenticationClient( + mContext, lazyDaemon, mToken, callback); + client2.setRequestId(requestId2); + + mScheduler.scheduleClientMonitor(client1); + mScheduler.scheduleClientMonitor(client2); + mScheduler.startPreparedClient(client1.getCookie()); + waitForIdle(); + mScheduler.cancelAuthenticationOrDetection(mToken, 9999); + waitForIdle(); + + assertEquals(Operation.STATE_STARTED, + mScheduler.mCurrentOperation.mState); + assertEquals(Operation.STATE_WAITING_IN_QUEUE, + mScheduler.mPendingOperations.getFirst().mState); + + mScheduler.cancelAuthenticationOrDetection(mToken, requestId2); + waitForIdle(); + + assertEquals(Operation.STATE_STARTED, + mScheduler.mCurrentOperation.mState); + assertEquals(Operation.STATE_WAITING_IN_QUEUE_CANCELING, + mScheduler.mPendingOperations.getFirst().mState); + } + @Test public void testInterruptPrecedingClients_whenExpected() { final BaseClientMonitor interruptableMonitor = mock(BaseClientMonitor.class, @@ -377,12 +479,10 @@ public class BiometricSchedulerTest { @Override protected void stopHalOperation() { - } @Override protected void startHalOperation() { - } @Override @@ -397,6 +497,7 @@ public class BiometricSchedulerTest { } private static class TestAuthenticationClient extends AuthenticationClient { + int mNumCancels = 0; public TestAuthenticationClient(@NonNull Context context, @NonNull LazyDaemon lazyDaemon, @NonNull IBinder token, @@ -428,6 +529,11 @@ public class BiometricSchedulerTest { public boolean wasUserDetected() { return false; } + + public void cancel() { + mNumCancels++; + super.cancel(); + } } private static class TestClientMonitor2 extends TestClientMonitor {