Loading packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt +10 −9 Original line number Diff line number Diff line Loading @@ -457,17 +457,10 @@ object BiometricViewBinder { // Retry and confirmation when finger on sensor launch { combine( viewModel.canTryAgainNow, viewModel.hasFingerOnSensor, viewModel.isPendingConfirmation, ::Triple ) .collect { (canRetry, fingerAcquired, pendingConfirmation) -> combine(viewModel.canTryAgainNow, viewModel.hasFingerOnSensor, ::Pair) .collect { (canRetry, fingerAcquired) -> if (canRetry && fingerAcquired) { legacyCallback.onButtonTryAgain() } else if (pendingConfirmation && fingerAcquired) { viewModel.confirmAuthenticated() } } } Loading Loading @@ -497,13 +490,21 @@ class Spaghetti( @Deprecated("TODO(b/330788871): remove after replacing AuthContainerView") interface Callback { fun onAuthenticated() fun onUserCanceled() fun onButtonNegative() fun onButtonTryAgain() fun onContentViewMoreOptionsButtonPressed() fun onError() fun onUseDeviceCredential() fun onStartDelayedFingerprintSensor() fun onAuthenticatedAndConfirmed() } Loading packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt +13 −2 Original line number Diff line number Diff line Loading @@ -232,7 +232,6 @@ constructor( val fingerprintStartMode: Flow<FingerprintStartMode> = _fingerprintStartMode.asStateFlow() /** Whether a finger has been acquired by the sensor */ // TODO(b/331948073): Add support for detecting SFPS finger without authentication running val hasFingerBeenAcquired: Flow<Boolean> = combine(biometricStatusInteractor.fingerprintAcquiredStatus, modalities) { status, Loading Loading @@ -617,7 +616,8 @@ constructor( } /** If the icon can be used as a confirmation button. */ val isIconConfirmButton: Flow<Boolean> = size.map { it.isNotSmall }.distinctUntilChanged() val isIconConfirmButton: Flow<Boolean> = combine(modalities, size) { modalities, size -> modalities.hasUdfps && size.isNotSmall } /** If the negative button should be shown. */ val isNegativeButtonVisible: Flow<Boolean> = Loading Loading @@ -700,6 +700,9 @@ constructor( failedModality: BiometricModality = BiometricModality.None, ) = coroutineScope { if (_isAuthenticated.value.isAuthenticated) { if (_isAuthenticated.value.needsUserConfirmation && hapticFeedback) { vibrateOnError() } return@coroutineScope } Loading Loading @@ -823,6 +826,14 @@ constructor( helpMessage: String = "", ) { if (_isAuthenticated.value.isAuthenticated) { // Treat second authentication with a different modality as confirmation for the first if ( _isAuthenticated.value.needsUserConfirmation && modality != _isAuthenticated.value.authenticatedModality ) { confirmAuthenticated() return } // TODO(jbolinger): convert to go/tex-apc? Log.w(TAG, "Cannot show authenticated after authenticated") return Loading packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt +55 −1 Original line number Diff line number Diff line Loading @@ -847,6 +847,28 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.NO_HAPTICS) } @Test fun plays_haptic_on_error_after_auth_when_confirmation_needed() = runGenericTest { val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false) viewModel.showAuthenticated(testCase.authenticatedModality, 0) viewModel.showTemporaryError( "still sad", messageAfterError = "", authenticateAfterError = false, hapticFeedback = true, ) val haptics by collectLastValue(viewModel.hapticsToPlay) if (expectConfirmation) { assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.REJECT) assertThat(haptics?.flag).isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING) } else { assertThat(haptics?.hapticFeedbackConstant) .isEqualTo(HapticFeedbackConstants.CONFIRM) } } private suspend fun TestScope.showTemporaryErrors( restart: Boolean, helpAfterError: String = "", Loading Loading @@ -994,7 +1016,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa } @Test fun authenticated_at_most_once() = runGenericTest { fun authenticated_at_most_once_same_modality() = runGenericTest { val authenticating by collectLastValue(viewModel.isAuthenticating) val authenticated by collectLastValue(viewModel.isAuthenticated) Loading Loading @@ -1055,6 +1077,38 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa assertThat(canTryAgain).isFalse() } @Test fun second_authentication_acts_as_confirmation() = runGenericTest { val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false) viewModel.showAuthenticated(testCase.authenticatedModality, 0) val authenticating by collectLastValue(viewModel.isAuthenticating) val authenticated by collectLastValue(viewModel.isAuthenticated) val message by collectLastValue(viewModel.message) val size by collectLastValue(viewModel.size) val canTryAgain by collectLastValue(viewModel.canTryAgainNow) assertThat(authenticated?.needsUserConfirmation).isEqualTo(expectConfirmation) if (expectConfirmation) { assertThat(size).isEqualTo(PromptSize.MEDIUM) assertButtonsVisible( cancel = true, confirm = true, ) if (testCase.modalities.hasSfps) { viewModel.showAuthenticated(BiometricModality.Fingerprint, 0) assertThat(message).isEqualTo(PromptMessage.Empty) assertButtonsVisible() } } assertThat(authenticating).isFalse() assertThat(authenticated?.isAuthenticated).isTrue() assertThat(canTryAgain).isFalse() } @Test fun auto_confirm_authentication_when_finger_down() = runGenericTest { val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false) Loading services/core/java/com/android/server/biometrics/AuthSession.java +45 −12 Original line number Diff line number Diff line Loading @@ -108,6 +108,7 @@ public final class AuthSession implements IBinder.DeathRecipient { } private final Context mContext; private final BiometricManager mBiometricManager; @NonNull private final BiometricContext mBiometricContext; private final IStatusBarService mStatusBarService; @VisibleForTesting final IBiometricSysuiReceiver mSysuiReceiver; Loading @@ -131,6 +132,7 @@ public final class AuthSession implements IBinder.DeathRecipient { private final String mOpPackageName; private final boolean mDebugEnabled; private final List<FingerprintSensorPropertiesInternal> mFingerprintSensorProperties; private final List<Integer> mSfpsSensorIds; // The current state, which can be either idle, called, or started private @SessionState int mState = STATE_AUTH_IDLE; Loading Loading @@ -220,6 +222,11 @@ public final class AuthSession implements IBinder.DeathRecipient { mCancelled = false; mBiometricFrameworkStatsLogger = logger; mOperationContext = new OperationContextExt(true /* isBP */); mBiometricManager = mContext.getSystemService(BiometricManager.class); mSfpsSensorIds = mFingerprintSensorProperties.stream().filter( FingerprintSensorPropertiesInternal::isAnySidefpsType).map( prop -> prop.sensorId).toList(); try { mClientReceiver.asBinder().linkToDeath(this, 0 /* flags */); Loading Loading @@ -316,7 +323,7 @@ public final class AuthSession implements IBinder.DeathRecipient { Slog.w(TAG, "Received cookie but already cancelled (ignoring): " + cookie); return; } if (hasAuthenticated()) { if (hasAuthenticatedAndConfirmed()) { Slog.d(TAG, "onCookieReceived after successful auth"); return; } Loading Loading @@ -494,6 +501,7 @@ public final class AuthSession implements IBinder.DeathRecipient { } case STATE_AUTH_STARTED: case STATE_AUTH_PENDING_CONFIRM: case STATE_AUTH_STARTED_UI_SHOWING: { if (isAllowDeviceCredential() && errorLockout) { // SystemUI handles transition from biometric to device credential. Loading Loading @@ -539,7 +547,7 @@ public final class AuthSession implements IBinder.DeathRecipient { } void onAcquired(int sensorId, int acquiredInfo, int vendorCode) { if (hasAuthenticated()) { if (hasAuthenticatedAndConfirmed()) { Slog.d(TAG, "onAcquired after successful auth"); return; } Loading @@ -562,7 +570,7 @@ public final class AuthSession implements IBinder.DeathRecipient { } void onSystemEvent(int event) { if (hasAuthenticated()) { if (hasAuthenticatedAndConfirmed()) { Slog.d(TAG, "onSystemEvent after successful auth"); return; } Loading @@ -579,12 +587,15 @@ public final class AuthSession implements IBinder.DeathRecipient { void onDialogAnimatedIn(boolean startFingerprintNow) { if (mState != STATE_AUTH_STARTED && mState != STATE_ERROR_PENDING_SYSUI && mState != STATE_AUTH_PAUSED) { && mState != STATE_AUTH_PAUSED && mState != STATE_AUTH_PENDING_CONFIRM) { Slog.e(TAG, "onDialogAnimatedIn, unexpected state: " + mState); return; } if (mState != STATE_AUTH_PENDING_CONFIRM) { mState = STATE_AUTH_STARTED_UI_SHOWING; } if (startFingerprintNow) { startAllPreparedFingerprintSensors(); } else { Loading @@ -600,6 +611,7 @@ public final class AuthSession implements IBinder.DeathRecipient { if (mState != STATE_AUTH_STARTED && mState != STATE_AUTH_STARTED_UI_SHOWING && mState != STATE_AUTH_PAUSED && mState != STATE_AUTH_PENDING_CONFIRM && mState != STATE_ERROR_PENDING_SYSUI) { Slog.w(TAG, "onStartFingerprint, started from unexpected state: " + mState); } Loading @@ -608,7 +620,7 @@ public final class AuthSession implements IBinder.DeathRecipient { } void onTryAgainPressed() { if (hasAuthenticated()) { if (hasAuthenticatedAndConfirmed()) { Slog.d(TAG, "onTryAgainPressed after successful auth"); return; } Loading @@ -625,7 +637,7 @@ public final class AuthSession implements IBinder.DeathRecipient { } void onAuthenticationSucceeded(int sensorId, boolean strong, byte[] token) { if (hasAuthenticated()) { if (hasAuthenticatedAndConfirmed()) { Slog.d(TAG, "onAuthenticationSucceeded after successful auth"); return; } Loading Loading @@ -656,11 +668,17 @@ public final class AuthSession implements IBinder.DeathRecipient { Slog.e(TAG, "RemoteException", e); } if (mState == STATE_AUTH_PENDING_CONFIRM) { // Do not cancel Sfps sensors so auth can continue running cancelAllSensors( sensor -> sensor.id != sensorId && !mSfpsSensorIds.contains(sensor.id)); } else { cancelAllSensors(sensor -> sensor.id != sensorId); } } void onAuthenticationRejected(int sensorId) { if (hasAuthenticated()) { if (hasAuthenticatedAndConfirmed()) { Slog.d(TAG, "onAuthenticationRejected after successful auth"); return; } Loading @@ -678,7 +696,7 @@ public final class AuthSession implements IBinder.DeathRecipient { } void onAuthenticationTimedOut(int sensorId, int cookie, int error, int vendorCode) { if (hasAuthenticated()) { if (hasAuthenticatedAndConfirmed()) { Slog.d(TAG, "onAuthenticationTimedOut after successful auth"); return; } Loading @@ -703,7 +721,7 @@ public final class AuthSession implements IBinder.DeathRecipient { } void onDeviceCredentialPressed() { if (hasAuthenticated()) { if (hasAuthenticatedAndConfirmed()) { Slog.d(TAG, "onDeviceCredentialPressed after successful auth"); return; } Loading Loading @@ -739,6 +757,10 @@ public final class AuthSession implements IBinder.DeathRecipient { return mAuthenticatedSensorId != -1; } private boolean hasAuthenticatedAndConfirmed() { return mAuthenticatedSensorId != -1 && mState == STATE_AUTHENTICATED_PENDING_SYSUI; } private void logOnDialogDismissed(@BiometricPrompt.DismissedReason int reason) { if (reason == BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED) { // Explicit auth, authentication confirmed. Loading Loading @@ -828,6 +850,7 @@ public final class AuthSession implements IBinder.DeathRecipient { } else { Slog.e(TAG, "mTokenEscrow is null"); } mClientReceiver.onAuthenticationSucceeded( Utils.getAuthenticationTypeForResult(reason)); break; Loading Loading @@ -861,6 +884,16 @@ public final class AuthSession implements IBinder.DeathRecipient { } catch (RemoteException e) { Slog.e(TAG, "Remote exception", e); } finally { if (mTokenEscrow != null && mBiometricManager != null) { final byte[] byteToken = new byte[mTokenEscrow.length]; for (int i = 0; i < mTokenEscrow.length; i++) { byteToken[i] = mTokenEscrow[i]; } mBiometricManager.resetLockoutTimeBound(mToken, mContext.getOpPackageName(), mAuthenticatedSensorId, mUserId, byteToken); } // ensure everything is cleaned up when dismissed cancelAllSensors(); } Loading @@ -874,7 +907,7 @@ 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) { if (hasAuthenticated()) { if (hasAuthenticatedAndConfirmed()) { Slog.d(TAG, "onCancelAuthSession after successful auth"); return true; } Loading services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +7 −7 Original line number Diff line number Diff line Loading @@ -242,14 +242,14 @@ public abstract class AuthenticationClient<T, O extends AuthenticateOptions> byteToken[i] = hardwareAuthToken.get(i); } // For BP, BiometricService will add the authToken to Keystore. if (!isBiometricPrompt()) { if (mIsStrongBiometric) { mBiometricManager.resetLockoutTimeBound(getToken(), getContext().getOpPackageName(), getSensorId(), getTargetUserId(), byteToken); } // For BP, BiometricService will add the authToken to Keystore. if (!isBiometricPrompt() && mIsStrongBiometric) { final int result = KeyStoreAuthorization.getInstance().addAuthToken(byteToken); if (result != 0) { Slog.d(TAG, "Error adding auth token : " + result); Loading Loading
packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt +10 −9 Original line number Diff line number Diff line Loading @@ -457,17 +457,10 @@ object BiometricViewBinder { // Retry and confirmation when finger on sensor launch { combine( viewModel.canTryAgainNow, viewModel.hasFingerOnSensor, viewModel.isPendingConfirmation, ::Triple ) .collect { (canRetry, fingerAcquired, pendingConfirmation) -> combine(viewModel.canTryAgainNow, viewModel.hasFingerOnSensor, ::Pair) .collect { (canRetry, fingerAcquired) -> if (canRetry && fingerAcquired) { legacyCallback.onButtonTryAgain() } else if (pendingConfirmation && fingerAcquired) { viewModel.confirmAuthenticated() } } } Loading Loading @@ -497,13 +490,21 @@ class Spaghetti( @Deprecated("TODO(b/330788871): remove after replacing AuthContainerView") interface Callback { fun onAuthenticated() fun onUserCanceled() fun onButtonNegative() fun onButtonTryAgain() fun onContentViewMoreOptionsButtonPressed() fun onError() fun onUseDeviceCredential() fun onStartDelayedFingerprintSensor() fun onAuthenticatedAndConfirmed() } Loading
packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt +13 −2 Original line number Diff line number Diff line Loading @@ -232,7 +232,6 @@ constructor( val fingerprintStartMode: Flow<FingerprintStartMode> = _fingerprintStartMode.asStateFlow() /** Whether a finger has been acquired by the sensor */ // TODO(b/331948073): Add support for detecting SFPS finger without authentication running val hasFingerBeenAcquired: Flow<Boolean> = combine(biometricStatusInteractor.fingerprintAcquiredStatus, modalities) { status, Loading Loading @@ -617,7 +616,8 @@ constructor( } /** If the icon can be used as a confirmation button. */ val isIconConfirmButton: Flow<Boolean> = size.map { it.isNotSmall }.distinctUntilChanged() val isIconConfirmButton: Flow<Boolean> = combine(modalities, size) { modalities, size -> modalities.hasUdfps && size.isNotSmall } /** If the negative button should be shown. */ val isNegativeButtonVisible: Flow<Boolean> = Loading Loading @@ -700,6 +700,9 @@ constructor( failedModality: BiometricModality = BiometricModality.None, ) = coroutineScope { if (_isAuthenticated.value.isAuthenticated) { if (_isAuthenticated.value.needsUserConfirmation && hapticFeedback) { vibrateOnError() } return@coroutineScope } Loading Loading @@ -823,6 +826,14 @@ constructor( helpMessage: String = "", ) { if (_isAuthenticated.value.isAuthenticated) { // Treat second authentication with a different modality as confirmation for the first if ( _isAuthenticated.value.needsUserConfirmation && modality != _isAuthenticated.value.authenticatedModality ) { confirmAuthenticated() return } // TODO(jbolinger): convert to go/tex-apc? Log.w(TAG, "Cannot show authenticated after authenticated") return Loading
packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt +55 −1 Original line number Diff line number Diff line Loading @@ -847,6 +847,28 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.NO_HAPTICS) } @Test fun plays_haptic_on_error_after_auth_when_confirmation_needed() = runGenericTest { val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false) viewModel.showAuthenticated(testCase.authenticatedModality, 0) viewModel.showTemporaryError( "still sad", messageAfterError = "", authenticateAfterError = false, hapticFeedback = true, ) val haptics by collectLastValue(viewModel.hapticsToPlay) if (expectConfirmation) { assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.REJECT) assertThat(haptics?.flag).isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING) } else { assertThat(haptics?.hapticFeedbackConstant) .isEqualTo(HapticFeedbackConstants.CONFIRM) } } private suspend fun TestScope.showTemporaryErrors( restart: Boolean, helpAfterError: String = "", Loading Loading @@ -994,7 +1016,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa } @Test fun authenticated_at_most_once() = runGenericTest { fun authenticated_at_most_once_same_modality() = runGenericTest { val authenticating by collectLastValue(viewModel.isAuthenticating) val authenticated by collectLastValue(viewModel.isAuthenticated) Loading Loading @@ -1055,6 +1077,38 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa assertThat(canTryAgain).isFalse() } @Test fun second_authentication_acts_as_confirmation() = runGenericTest { val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false) viewModel.showAuthenticated(testCase.authenticatedModality, 0) val authenticating by collectLastValue(viewModel.isAuthenticating) val authenticated by collectLastValue(viewModel.isAuthenticated) val message by collectLastValue(viewModel.message) val size by collectLastValue(viewModel.size) val canTryAgain by collectLastValue(viewModel.canTryAgainNow) assertThat(authenticated?.needsUserConfirmation).isEqualTo(expectConfirmation) if (expectConfirmation) { assertThat(size).isEqualTo(PromptSize.MEDIUM) assertButtonsVisible( cancel = true, confirm = true, ) if (testCase.modalities.hasSfps) { viewModel.showAuthenticated(BiometricModality.Fingerprint, 0) assertThat(message).isEqualTo(PromptMessage.Empty) assertButtonsVisible() } } assertThat(authenticating).isFalse() assertThat(authenticated?.isAuthenticated).isTrue() assertThat(canTryAgain).isFalse() } @Test fun auto_confirm_authentication_when_finger_down() = runGenericTest { val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false) Loading
services/core/java/com/android/server/biometrics/AuthSession.java +45 −12 Original line number Diff line number Diff line Loading @@ -108,6 +108,7 @@ public final class AuthSession implements IBinder.DeathRecipient { } private final Context mContext; private final BiometricManager mBiometricManager; @NonNull private final BiometricContext mBiometricContext; private final IStatusBarService mStatusBarService; @VisibleForTesting final IBiometricSysuiReceiver mSysuiReceiver; Loading @@ -131,6 +132,7 @@ public final class AuthSession implements IBinder.DeathRecipient { private final String mOpPackageName; private final boolean mDebugEnabled; private final List<FingerprintSensorPropertiesInternal> mFingerprintSensorProperties; private final List<Integer> mSfpsSensorIds; // The current state, which can be either idle, called, or started private @SessionState int mState = STATE_AUTH_IDLE; Loading Loading @@ -220,6 +222,11 @@ public final class AuthSession implements IBinder.DeathRecipient { mCancelled = false; mBiometricFrameworkStatsLogger = logger; mOperationContext = new OperationContextExt(true /* isBP */); mBiometricManager = mContext.getSystemService(BiometricManager.class); mSfpsSensorIds = mFingerprintSensorProperties.stream().filter( FingerprintSensorPropertiesInternal::isAnySidefpsType).map( prop -> prop.sensorId).toList(); try { mClientReceiver.asBinder().linkToDeath(this, 0 /* flags */); Loading Loading @@ -316,7 +323,7 @@ public final class AuthSession implements IBinder.DeathRecipient { Slog.w(TAG, "Received cookie but already cancelled (ignoring): " + cookie); return; } if (hasAuthenticated()) { if (hasAuthenticatedAndConfirmed()) { Slog.d(TAG, "onCookieReceived after successful auth"); return; } Loading Loading @@ -494,6 +501,7 @@ public final class AuthSession implements IBinder.DeathRecipient { } case STATE_AUTH_STARTED: case STATE_AUTH_PENDING_CONFIRM: case STATE_AUTH_STARTED_UI_SHOWING: { if (isAllowDeviceCredential() && errorLockout) { // SystemUI handles transition from biometric to device credential. Loading Loading @@ -539,7 +547,7 @@ public final class AuthSession implements IBinder.DeathRecipient { } void onAcquired(int sensorId, int acquiredInfo, int vendorCode) { if (hasAuthenticated()) { if (hasAuthenticatedAndConfirmed()) { Slog.d(TAG, "onAcquired after successful auth"); return; } Loading @@ -562,7 +570,7 @@ public final class AuthSession implements IBinder.DeathRecipient { } void onSystemEvent(int event) { if (hasAuthenticated()) { if (hasAuthenticatedAndConfirmed()) { Slog.d(TAG, "onSystemEvent after successful auth"); return; } Loading @@ -579,12 +587,15 @@ public final class AuthSession implements IBinder.DeathRecipient { void onDialogAnimatedIn(boolean startFingerprintNow) { if (mState != STATE_AUTH_STARTED && mState != STATE_ERROR_PENDING_SYSUI && mState != STATE_AUTH_PAUSED) { && mState != STATE_AUTH_PAUSED && mState != STATE_AUTH_PENDING_CONFIRM) { Slog.e(TAG, "onDialogAnimatedIn, unexpected state: " + mState); return; } if (mState != STATE_AUTH_PENDING_CONFIRM) { mState = STATE_AUTH_STARTED_UI_SHOWING; } if (startFingerprintNow) { startAllPreparedFingerprintSensors(); } else { Loading @@ -600,6 +611,7 @@ public final class AuthSession implements IBinder.DeathRecipient { if (mState != STATE_AUTH_STARTED && mState != STATE_AUTH_STARTED_UI_SHOWING && mState != STATE_AUTH_PAUSED && mState != STATE_AUTH_PENDING_CONFIRM && mState != STATE_ERROR_PENDING_SYSUI) { Slog.w(TAG, "onStartFingerprint, started from unexpected state: " + mState); } Loading @@ -608,7 +620,7 @@ public final class AuthSession implements IBinder.DeathRecipient { } void onTryAgainPressed() { if (hasAuthenticated()) { if (hasAuthenticatedAndConfirmed()) { Slog.d(TAG, "onTryAgainPressed after successful auth"); return; } Loading @@ -625,7 +637,7 @@ public final class AuthSession implements IBinder.DeathRecipient { } void onAuthenticationSucceeded(int sensorId, boolean strong, byte[] token) { if (hasAuthenticated()) { if (hasAuthenticatedAndConfirmed()) { Slog.d(TAG, "onAuthenticationSucceeded after successful auth"); return; } Loading Loading @@ -656,11 +668,17 @@ public final class AuthSession implements IBinder.DeathRecipient { Slog.e(TAG, "RemoteException", e); } if (mState == STATE_AUTH_PENDING_CONFIRM) { // Do not cancel Sfps sensors so auth can continue running cancelAllSensors( sensor -> sensor.id != sensorId && !mSfpsSensorIds.contains(sensor.id)); } else { cancelAllSensors(sensor -> sensor.id != sensorId); } } void onAuthenticationRejected(int sensorId) { if (hasAuthenticated()) { if (hasAuthenticatedAndConfirmed()) { Slog.d(TAG, "onAuthenticationRejected after successful auth"); return; } Loading @@ -678,7 +696,7 @@ public final class AuthSession implements IBinder.DeathRecipient { } void onAuthenticationTimedOut(int sensorId, int cookie, int error, int vendorCode) { if (hasAuthenticated()) { if (hasAuthenticatedAndConfirmed()) { Slog.d(TAG, "onAuthenticationTimedOut after successful auth"); return; } Loading @@ -703,7 +721,7 @@ public final class AuthSession implements IBinder.DeathRecipient { } void onDeviceCredentialPressed() { if (hasAuthenticated()) { if (hasAuthenticatedAndConfirmed()) { Slog.d(TAG, "onDeviceCredentialPressed after successful auth"); return; } Loading Loading @@ -739,6 +757,10 @@ public final class AuthSession implements IBinder.DeathRecipient { return mAuthenticatedSensorId != -1; } private boolean hasAuthenticatedAndConfirmed() { return mAuthenticatedSensorId != -1 && mState == STATE_AUTHENTICATED_PENDING_SYSUI; } private void logOnDialogDismissed(@BiometricPrompt.DismissedReason int reason) { if (reason == BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED) { // Explicit auth, authentication confirmed. Loading Loading @@ -828,6 +850,7 @@ public final class AuthSession implements IBinder.DeathRecipient { } else { Slog.e(TAG, "mTokenEscrow is null"); } mClientReceiver.onAuthenticationSucceeded( Utils.getAuthenticationTypeForResult(reason)); break; Loading Loading @@ -861,6 +884,16 @@ public final class AuthSession implements IBinder.DeathRecipient { } catch (RemoteException e) { Slog.e(TAG, "Remote exception", e); } finally { if (mTokenEscrow != null && mBiometricManager != null) { final byte[] byteToken = new byte[mTokenEscrow.length]; for (int i = 0; i < mTokenEscrow.length; i++) { byteToken[i] = mTokenEscrow[i]; } mBiometricManager.resetLockoutTimeBound(mToken, mContext.getOpPackageName(), mAuthenticatedSensorId, mUserId, byteToken); } // ensure everything is cleaned up when dismissed cancelAllSensors(); } Loading @@ -874,7 +907,7 @@ 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) { if (hasAuthenticated()) { if (hasAuthenticatedAndConfirmed()) { Slog.d(TAG, "onCancelAuthSession after successful auth"); return true; } Loading
services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +7 −7 Original line number Diff line number Diff line Loading @@ -242,14 +242,14 @@ public abstract class AuthenticationClient<T, O extends AuthenticateOptions> byteToken[i] = hardwareAuthToken.get(i); } // For BP, BiometricService will add the authToken to Keystore. if (!isBiometricPrompt()) { if (mIsStrongBiometric) { mBiometricManager.resetLockoutTimeBound(getToken(), getContext().getOpPackageName(), getSensorId(), getTargetUserId(), byteToken); } // For BP, BiometricService will add the authToken to Keystore. if (!isBiometricPrompt() && mIsStrongBiometric) { final int result = KeyStoreAuthorization.getInstance().addAuthToken(byteToken); if (result != 0) { Slog.d(TAG, "Error adding auth token : " + result); Loading