diff options
6 files changed, 296 insertions, 58 deletions
diff --git a/Android.bp b/Android.bp index 5b8e6e1b8d32..cbb8bbbbe172 100644 --- a/Android.bp +++ b/Android.bp @@ -161,6 +161,7 @@ java_defaults { ":libcamera_client_framework_aidl", "core/java/android/hardware/IConsumerIrService.aidl", "core/java/android/hardware/ISerialManager.aidl", + "core/java/android/hardware/biometrics/IBiometricConfirmDeviceCredentialCallback.aidl", "core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl", "core/java/android/hardware/biometrics/IBiometricService.aidl", "core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl", diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java index a696eeb6bcc7..6c497d47c645 100644 --- a/core/java/android/hardware/biometrics/BiometricManager.java +++ b/core/java/android/hardware/biometrics/BiometricManager.java @@ -207,5 +207,22 @@ public class BiometricManager { Slog.w(TAG, "onConfirmDeviceCredentialError(): Service not connected"); } } + + /** + * TODO(b/123378871): Remove when moved. + * @hide + */ + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + public void registerCancellationCallback(IBiometricConfirmDeviceCredentialCallback callback) { + if (mService != null) { + try { + mService.registerCancellationCallback(callback); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } else { + Slog.w(TAG, "registerCancellationCallback(): Service not connected"); + } + } } diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index 08035972a0db..1142a07bc66c 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -82,6 +82,11 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan * @hide */ public static final String KEY_ALLOW_DEVICE_CREDENTIAL = "allow_device_credential"; + /** + * @hide + */ + public static final String KEY_FROM_CONFIRM_DEVICE_CREDENTIAL + = "from_confirm_device_credential"; /** * Error/help message will show for this amount of time. @@ -271,6 +276,17 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan } /** + * TODO(123378871): Remove when moved. + * @return + * @hide + */ + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + @NonNull public Builder setFromConfirmDeviceCredential() { + mBundle.putBoolean(KEY_FROM_CONFIRM_DEVICE_CREDENTIAL, true); + return this; + } + + /** * Creates a {@link BiometricPrompt}. * @return a {@link BiometricPrompt} * @throws IllegalArgumentException if any of the required fields are not set. @@ -494,7 +510,8 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan public void authenticateUser(@NonNull CancellationSignal cancel, @NonNull @CallbackExecutor Executor executor, @NonNull AuthenticationCallback callback, - int userId) { + int userId, + IBiometricConfirmDeviceCredentialCallback confirmDeviceCredentialCallback) { if (cancel == null) { throw new IllegalArgumentException("Must supply a cancellation signal"); } @@ -504,7 +521,8 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan if (callback == null) { throw new IllegalArgumentException("Must supply a callback"); } - authenticateInternal(null /* crypto */, cancel, executor, callback, userId); + authenticateInternal(null /* crypto */, cancel, executor, callback, userId, + confirmDeviceCredentialCallback); } /** @@ -555,7 +573,8 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan if (mBundle.getBoolean(KEY_ALLOW_DEVICE_CREDENTIAL)) { throw new IllegalArgumentException("Device credential not supported with crypto"); } - authenticateInternal(crypto, cancel, executor, callback, mContext.getUserId()); + authenticateInternal(crypto, cancel, executor, callback, mContext.getUserId(), + null /* confirmDeviceCredentialCallback */); } /** @@ -597,7 +616,8 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan if (callback == null) { throw new IllegalArgumentException("Must supply a callback"); } - authenticateInternal(null /* crypto */, cancel, executor, callback, mContext.getUserId()); + authenticateInternal(null /* crypto */, cancel, executor, callback, mContext.getUserId(), + null /* confirmDeviceCredentialCallback */); } private void cancelAuthentication() { @@ -614,7 +634,8 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan @NonNull CancellationSignal cancel, @NonNull @CallbackExecutor Executor executor, @NonNull AuthenticationCallback callback, - int userId) { + int userId, + IBiometricConfirmDeviceCredentialCallback confirmDeviceCredentialCallback) { try { if (cancel.isCanceled()) { Log.w(TAG, "Authentication already canceled"); @@ -629,7 +650,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan final long sessionId = crypto != null ? crypto.getOpId() : 0; if (BiometricManager.hasBiometrics(mContext)) { mService.authenticate(mToken, sessionId, userId, mBiometricServiceReceiver, - mContext.getOpPackageName(), mBundle); + mContext.getOpPackageName(), mBundle, confirmDeviceCredentialCallback); } else { mExecutor.execute(() -> { callback.onAuthenticationError(BiometricPrompt.BIOMETRIC_ERROR_HW_NOT_PRESENT, diff --git a/core/java/android/hardware/biometrics/IBiometricConfirmDeviceCredentialCallback.aidl b/core/java/android/hardware/biometrics/IBiometricConfirmDeviceCredentialCallback.aidl new file mode 100644 index 000000000000..8b35852efd31 --- /dev/null +++ b/core/java/android/hardware/biometrics/IBiometricConfirmDeviceCredentialCallback.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.biometrics; + +/** + * Communication channel between ConfirmDeviceCredential / ConfirmLock* and BiometricService. + * @hide + */ +interface IBiometricConfirmDeviceCredentialCallback { + // Invoked when authentication should be canceled. + oneway void cancel(); +}
\ No newline at end of file diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl index 4971911eb87c..90d4921c3c18 100644 --- a/core/java/android/hardware/biometrics/IBiometricService.aidl +++ b/core/java/android/hardware/biometrics/IBiometricService.aidl @@ -17,6 +17,7 @@ package android.hardware.biometrics; import android.os.Bundle; +import android.hardware.biometrics.IBiometricConfirmDeviceCredentialCallback; import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback; import android.hardware.biometrics.IBiometricServiceReceiver; @@ -30,8 +31,10 @@ import android.hardware.biometrics.IBiometricServiceReceiver; interface IBiometricService { // Requests authentication. The service choose the appropriate biometric to use, and show // the corresponding BiometricDialog. + // TODO(b/123378871): Remove callback when moved. void authenticate(IBinder token, long sessionId, int userId, - IBiometricServiceReceiver receiver, String opPackageName, in Bundle bundle); + IBiometricServiceReceiver receiver, String opPackageName, in Bundle bundle, + IBiometricConfirmDeviceCredentialCallback callback); // Cancel authentication for the given sessionId void cancelAuthentication(IBinder token, String opPackageName); @@ -59,4 +62,8 @@ interface IBiometricService { void onConfirmDeviceCredentialSuccess(); // TODO(b/123378871): Remove when moved. void onConfirmDeviceCredentialError(int error, String message); + // TODO(b/123378871): Remove when moved. + // When ConfirmLock* is invoked from BiometricPrompt, it needs to register a callback so that + // it can receive the cancellation signal. + void registerCancellationCallback(IBiometricConfirmDeviceCredentialCallback callback); } diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index 516844d85484..c558b5fd64ae 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -42,6 +42,7 @@ import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricPrompt; import android.hardware.biometrics.BiometricSourceType; import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.IBiometricConfirmDeviceCredentialCallback; import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback; import android.hardware.biometrics.IBiometricService; import android.hardware.biometrics.IBiometricServiceReceiver; @@ -86,6 +87,7 @@ import java.util.Random; public class BiometricService extends SystemService { private static final String TAG = "BiometricService"; + private static final boolean DEBUG = true; private static final int MSG_ON_TASK_STACK_CHANGED = 1; private static final int MSG_ON_AUTHENTICATION_SUCCEEDED = 2; @@ -97,6 +99,9 @@ public class BiometricService extends SystemService { private static final int MSG_ON_READY_FOR_AUTHENTICATION = 8; private static final int MSG_AUTHENTICATE = 9; private static final int MSG_CANCEL_AUTHENTICATION = 10; + private static final int MSG_ON_CONFIRM_DEVICE_CREDENTIAL_SUCCESS = 11; + private static final int MSG_ON_CONFIRM_DEVICE_CREDENTIAL_ERROR = 12; + private static final int MSG_REGISTER_CANCELLATION_CALLBACK = 13; private static final int[] FEATURE_ID = { TYPE_FINGERPRINT, @@ -129,8 +134,12 @@ public class BiometricService extends SystemService { * Authentication is successful, but we're waiting for the user to press "confirm" button. */ private static final int STATE_AUTH_PENDING_CONFIRM = 5; + /** + * Biometric authentication was canceled, but the device is now showing ConfirmDeviceCredential + */ + private static final int STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC = 6; - private final class AuthSession { + private final class AuthSession implements IBinder.DeathRecipient { // Map of Authenticator/Cookie pairs. We expect to receive the cookies back from // <Biometric>Services before we can start authenticating. Pairs that have been returned // are moved to mModalitiesMatched. @@ -165,10 +174,14 @@ public class BiometricService extends SystemService { // Timestamp when hardware authentication occurred private long mAuthenticatedTimeMs; + // TODO(b/123378871): Remove when moved. + private IBiometricConfirmDeviceCredentialCallback mConfirmDeviceCredentialCallback; + AuthSession(HashMap<Integer, Integer> modalities, IBinder token, long sessionId, int userId, IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle, int callingUid, int callingPid, int callingUserId, - int modality, boolean requireConfirmation) { + int modality, boolean requireConfirmation, + IBiometricConfirmDeviceCredentialCallback callback) { mModalitiesWaiting = modalities; mToken = token; mSessionId = sessionId; @@ -181,12 +194,25 @@ public class BiometricService extends SystemService { mCallingUserId = callingUserId; mModality = modality; mRequireConfirmation = requireConfirmation; + mConfirmDeviceCredentialCallback = callback; + + if (isFromConfirmDeviceCredential()) { + try { + token.linkToDeath(this, 0 /* flags */); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to link to death", e); + } + } } boolean isCrypto() { return mSessionId != 0; } + boolean isFromConfirmDeviceCredential() { + return mBundle.getBoolean(BiometricPrompt.KEY_FROM_CONFIRM_DEVICE_CREDENTIAL, false); + } + boolean containsCookie(int cookie) { if (mModalitiesWaiting != null && mModalitiesWaiting.containsValue(cookie)) { return true; @@ -196,6 +222,25 @@ public class BiometricService extends SystemService { } return false; } + + // TODO(b/123378871): Remove when moved. + @Override + public void binderDied() { + mHandler.post(() -> { + Slog.e(TAG, "Binder died, killing ConfirmDeviceCredential"); + if (mConfirmDeviceCredentialCallback == null) { + Slog.e(TAG, "Callback is null"); + return; + } + + try { + mConfirmDeviceCredentialCallback.cancel(); + mConfirmDeviceCredentialCallback = null; + } catch (RemoteException e) { + Slog.e(TAG, "Unable to send cancel", e); + } + }); + } } private final class BiometricTaskStackListener extends TaskStackListener { @@ -235,6 +280,14 @@ public class BiometricService extends SystemService { private AuthSession mCurrentAuthSession; private AuthSession mPendingAuthSession; + // TODO(b/123378871): Remove when moved. + // When BiometricPrompt#setAllowDeviceCredentials is set to true, we need to store the + // client (app) receiver. BiometricService internally launches CDCA which invokes + // BiometricService to start authentication (normal path). When auth is success/rejected, + // CDCA will use an aidl method to poke BiometricService - the result will then be forwarded + // to this receiver. + private IBiometricServiceReceiver mConfirmDeviceCredentialReceiver; + private final Handler mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { @@ -312,7 +365,8 @@ public class BiometricService extends SystemService { (Bundle) args.arg5 /* bundle */, args.argi2 /* callingUid */, args.argi3 /* callingPid */, - args.argi4 /* callingUserId */); + args.argi4 /* callingUserId */, + (IBiometricConfirmDeviceCredentialCallback) args.arg6 /* callback */); args.recycle(); break; } @@ -326,7 +380,28 @@ public class BiometricService extends SystemService { break; } + case MSG_ON_CONFIRM_DEVICE_CREDENTIAL_SUCCESS: { + handleOnConfirmDeviceCredentialSuccess(); + break; + } + + case MSG_ON_CONFIRM_DEVICE_CREDENTIAL_ERROR: { + SomeArgs args = (SomeArgs) msg.obj; + handleOnConfirmDeviceCredentialError( + args.argi1 /* error */, + (String) args.arg1 /* errorMsg */); + args.recycle(); + break; + } + + case MSG_REGISTER_CANCELLATION_CALLBACK: { + handleRegisterCancellationCallback( + (IBiometricConfirmDeviceCredentialCallback) msg.obj /* callback */); + break; + } + default: + Slog.e(TAG, "Unknown message: " + msg); break; } } @@ -526,14 +601,6 @@ public class BiometricService extends SystemService { * cancelAuthentication() can go to the right place. */ private final class BiometricServiceWrapper extends IBiometricService.Stub { - // TODO(b/123378871): Remove when moved. - // When BiometricPrompt#setAllowDeviceCredentials is set to true, we need to store the - // client (app) receiver. BiometricService internally launches CDCA which invokes - // BiometricService to start authentication (normal path). When auth is success/rejected, - // CDCA will use an aidl method to poke BiometricService - the result will then be forwarded - // to this receiver. - private IBiometricServiceReceiver mConfirmDeviceCredentialReceiver; - @Override // Binder call public void onReadyForAuthentication(int cookie, boolean requireConfirmation, int userId) { checkInternalPermission(); @@ -547,12 +614,18 @@ public class BiometricService extends SystemService { @Override // Binder call public void authenticate(IBinder token, long sessionId, int userId, - IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle) + IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle, + IBiometricConfirmDeviceCredentialCallback callback) throws RemoteException { final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); final int callingUserId = UserHandle.getCallingUserId(); + // TODO(b/123378871): Remove when moved. + if (callback != null) { + checkInternalPermission(); + } + // In the BiometricServiceBase, check do the AppOps and foreground check. if (userId == callingUserId) { // Check the USE_BIOMETRIC permission here. @@ -569,6 +642,12 @@ public class BiometricService extends SystemService { return; } + final boolean isFromConfirmDeviceCredential = + bundle.getBoolean(BiometricPrompt.KEY_FROM_CONFIRM_DEVICE_CREDENTIAL, false); + if (isFromConfirmDeviceCredential) { + checkInternalPermission(); + } + // Check the usage of this in system server. Need to remove this check if it becomes // a public API. final boolean useDefaultTitle = @@ -646,6 +725,7 @@ public class BiometricService extends SystemService { args.argi2 = callingUid; args.argi3 = callingPid; args.argi4 = callingUserId; + args.arg6 = callback; mHandler.obtainMessage(MSG_AUTHENTICATE, args).sendToTarget(); } @@ -653,35 +733,30 @@ public class BiometricService extends SystemService { @Override // Binder call public void onConfirmDeviceCredentialSuccess() { checkInternalPermission(); - mHandler.post(() -> { - if (mConfirmDeviceCredentialReceiver == null) { - Slog.w(TAG, "onCDCASuccess null!"); - return; - } - try { - mConfirmDeviceCredentialReceiver.onAuthenticationSucceeded(); - } catch (RemoteException e) { - Slog.e(TAG, "RemoteException", e); - } - mConfirmDeviceCredentialReceiver = null; - }); + + mHandler.sendEmptyMessage(MSG_ON_CONFIRM_DEVICE_CREDENTIAL_SUCCESS); } @Override // Binder call public void onConfirmDeviceCredentialError(int error, String message) { checkInternalPermission(); - mHandler.post(() -> { - if (mConfirmDeviceCredentialReceiver == null) { - Slog.w(TAG, "onCDCAError null! Error: " + error + " " + message); - return; - } - try { - mConfirmDeviceCredentialReceiver.onError(error, message); - } catch (RemoteException e) { - Slog.e(TAG, "RemoteException", e); - } - mConfirmDeviceCredentialReceiver = null; - }); + + SomeArgs args = SomeArgs.obtain(); + args.argi1 = error; + args.arg1 = message; + mHandler.obtainMessage(MSG_ON_CONFIRM_DEVICE_CREDENTIAL_ERROR, args).sendToTarget(); + } + + @Override // Binder call + public void registerCancellationCallback( + IBiometricConfirmDeviceCredentialCallback callback) { + // TODO(b/123378871): Remove when moved. + // This callback replaces the one stored in the current session. If the session is null + // we can ignore this, since it means ConfirmDeviceCredential was launched by something + // else (not BiometricPrompt) + checkInternalPermission(); + + mHandler.obtainMessage(MSG_REGISTER_CANCELLATION_CALLBACK, callback).sendToTarget(); } @Override // Binder call @@ -1117,6 +1192,52 @@ public class BiometricService extends SystemService { } } + private void handleOnConfirmDeviceCredentialSuccess() { + if (mConfirmDeviceCredentialReceiver == null) { + Slog.w(TAG, "onCDCASuccess null!"); + return; + } + try { + mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); + mConfirmDeviceCredentialReceiver.onAuthenticationSucceeded(); + if (mCurrentAuthSession != null) { + mCurrentAuthSession.mState = STATE_AUTH_IDLE; + mCurrentAuthSession = null; + } + } catch (RemoteException e) { + Slog.e(TAG, "RemoteException", e); + } + mConfirmDeviceCredentialReceiver = null; + } + + private void handleOnConfirmDeviceCredentialError(int error, String message) { + if (mConfirmDeviceCredentialReceiver == null) { + Slog.w(TAG, "onCDCAError null! Error: " + error + " " + message); + return; + } + try { + mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); + mConfirmDeviceCredentialReceiver.onError(error, message); + if (mCurrentAuthSession != null) { + mCurrentAuthSession.mState = STATE_AUTH_IDLE; + mCurrentAuthSession = null; + } + } catch (RemoteException e) { + Slog.e(TAG, "RemoteException", e); + } + mConfirmDeviceCredentialReceiver = null; + } + + private void handleRegisterCancellationCallback( + IBiometricConfirmDeviceCredentialCallback callback) { + if (mCurrentAuthSession == null) { + Slog.d(TAG, "Current auth session null"); + return; + } + Slog.d(TAG, "Updating cancel callback"); + mCurrentAuthSession.mConfirmDeviceCredentialCallback = callback; + } + private void handleOnError(int cookie, int error, String message) { Slog.d(TAG, "Error: " + error + " cookie: " + cookie); // Errors can either be from the current auth session or the pending auth session. @@ -1127,7 +1248,18 @@ public class BiometricService extends SystemService { // of their intended receivers. try { if (mCurrentAuthSession != null && mCurrentAuthSession.containsCookie(cookie)) { - if (mCurrentAuthSession.mState == STATE_AUTH_STARTED) { + + if (mCurrentAuthSession.isFromConfirmDeviceCredential()) { + // If we were invoked by ConfirmDeviceCredential, do not delete the current + // auth session since we still need to respond to cancel signal while + if (DEBUG) Slog.d(TAG, "From CDC, transition to CANCELED_SHOWING_CDC state"); + + // Send the error to ConfirmDeviceCredential so that it goes to Pin/Pattern/Pass + // screen + mCurrentAuthSession.mClientReceiver.onError(error, message); + mCurrentAuthSession.mState = STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC; + mStatusBarService.hideBiometricDialog(); + } else if (mCurrentAuthSession.mState == STATE_AUTH_STARTED) { mStatusBarService.onBiometricError(message); if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) { mActivityTaskManager.unregisterTaskStackListener( @@ -1227,9 +1359,16 @@ public class BiometricService extends SystemService { KeyStore.getInstance().addAuthToken(mCurrentAuthSession.mTokenEscrow); mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded(); } - mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); - mCurrentAuthSession.mState = STATE_AUTH_IDLE; - mCurrentAuthSession = null; + + // Do not clean up yet if we are from ConfirmDeviceCredential. We should be in the + // STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC. The session should only be removed when + // ConfirmDeviceCredential is confirmed or canceled. + // TODO(b/123378871): Remove when moved + if (!mCurrentAuthSession.isFromConfirmDeviceCredential()) { + mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); + mCurrentAuthSession.mState = STATE_AUTH_IDLE; + mCurrentAuthSession = null; + } } catch (RemoteException e) { Slog.e(TAG, "Remote exception", e); } @@ -1248,7 +1387,8 @@ public class BiometricService extends SystemService { mCurrentAuthSession.mCallingUid, mCurrentAuthSession.mCallingPid, mCurrentAuthSession.mCallingUserId, - mCurrentAuthSession.mModality); + mCurrentAuthSession.mModality, + mCurrentAuthSession.mConfirmDeviceCredentialCallback); } private void handleOnReadyForAuthentication(int cookie, boolean requireConfirmation, @@ -1303,7 +1443,8 @@ public class BiometricService extends SystemService { private void handleAuthenticate(IBinder token, long sessionId, int userId, IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle, - int callingUid, int callingPid, int callingUserId) { + int callingUid, int callingPid, int callingUserId, + IBiometricConfirmDeviceCredentialCallback callback) { mHandler.post(() -> { final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId); @@ -1341,7 +1482,7 @@ public class BiometricService extends SystemService { // Start preparing for authentication. Authentication starts when // all modalities requested have invoked onReadyForAuthentication. authenticateInternal(token, sessionId, userId, receiver, opPackageName, bundle, - callingUid, callingPid, callingUserId, modality); + callingUid, callingPid, callingUserId, modality, callback); }); } @@ -1356,7 +1497,8 @@ public class BiometricService extends SystemService { */ private void authenticateInternal(IBinder token, long sessionId, int userId, IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle, - int callingUid, int callingPid, int callingUserId, int modality) { + int callingUid, int callingPid, int callingUserId, int modality, + IBiometricConfirmDeviceCredentialCallback callback) { try { boolean requireConfirmation = bundle.getBoolean( BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true /* default */); @@ -1376,7 +1518,7 @@ public class BiometricService extends SystemService { authenticators.put(modality, cookie); mPendingAuthSession = new AuthSession(authenticators, token, sessionId, userId, receiver, opPackageName, bundle, callingUid, callingPid, callingUserId, - modality, requireConfirmation); + modality, requireConfirmation, callback); mPendingAuthSession.mState = STATE_AUTH_CALLED; // No polymorphism :( if ((modality & TYPE_FINGERPRINT) != 0) { @@ -1403,10 +1545,23 @@ public class BiometricService extends SystemService { return; } - // We need to check the current authenticators state. If we're pending confirm - // or idle, we need to dismiss the dialog and send an ERROR_CANCELED to the client, - // since we won't be getting an onError from the driver. - if (mCurrentAuthSession != null && mCurrentAuthSession.mState != STATE_AUTH_STARTED) { + if (mCurrentAuthSession != null + && mCurrentAuthSession.mState == STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC) { + if (DEBUG) Slog.d(TAG, "Cancel received while ConfirmDeviceCredential showing"); + try { + mCurrentAuthSession.mConfirmDeviceCredentialCallback.cancel(); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to cancel ConfirmDeviceCredential", e); + } + + // TODO(b/123378871): Remove when moved. Piggy back on this for now to clean up. + handleOnConfirmDeviceCredentialError(BiometricConstants.BIOMETRIC_ERROR_CANCELED, + getContext().getString(R.string.biometric_error_canceled)); + } else if (mCurrentAuthSession != null + && mCurrentAuthSession.mState != STATE_AUTH_STARTED) { + // We need to check the current authenticators state. If we're pending confirm + // or idle, we need to dismiss the dialog and send an ERROR_CANCELED to the client, + // since we won't be getting an onError from the driver. try { // Send error to client mCurrentAuthSession.mClientReceiver.onError( @@ -1422,11 +1577,22 @@ public class BiometricService extends SystemService { Slog.e(TAG, "Remote exception", e); } } else { - cancelInternal(token, opPackageName, true /* fromClient */); + boolean fromCDC = false; + if (mCurrentAuthSession != null) { + fromCDC = mCurrentAuthSession.mBundle.getBoolean( + BiometricPrompt.KEY_FROM_CONFIRM_DEVICE_CREDENTIAL, false); + } + + if (fromCDC) { + if (DEBUG) Slog.d(TAG, "Cancelling from CDC"); + cancelInternal(token, opPackageName, false /* fromClient */); + } else { + cancelInternal(token, opPackageName, true /* fromClient */); + } + } } - void cancelInternal(IBinder token, String opPackageName, boolean fromClient) { final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); |