Merge "Add auth type to BiometricPrompt.AuthenticationResult"

This commit is contained in:
Curtis Belmonte
2020-01-15 00:44:26 +00:00
committed by Android (Google) Code Review
8 changed files with 124 additions and 18 deletions

View File

@@ -16852,6 +16852,8 @@ package android.hardware.biometrics {
method @Nullable public CharSequence getSubtitle();
method @NonNull public CharSequence getTitle();
method public boolean isConfirmationRequired();
field public static final int AUTHENTICATION_RESULT_TYPE_BIOMETRIC = 2; // 0x2
field public static final int AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL = 1; // 0x1
field public static final int BIOMETRIC_ACQUIRED_GOOD = 0; // 0x0
field public static final int BIOMETRIC_ACQUIRED_IMAGER_DIRTY = 3; // 0x3
field public static final int BIOMETRIC_ACQUIRED_INSUFFICIENT = 2; // 0x2
@@ -16881,6 +16883,7 @@ package android.hardware.biometrics {
}
public static class BiometricPrompt.AuthenticationResult {
method public int getAuthenticationType();
method public android.hardware.biometrics.BiometricPrompt.CryptoObject getCryptoObject();
}

View File

@@ -18,6 +18,7 @@ package android.hardware.biometrics;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.hardware.biometrics.BiometricPrompt.AuthenticationResultType;
import android.os.CancellationSignal;
import android.os.Parcelable;
@@ -119,6 +120,7 @@ public interface BiometricAuthenticator {
class AuthenticationResult {
private Identifier mIdentifier;
private CryptoObject mCryptoObject;
private @AuthenticationResultType int mAuthenticationType;
private int mUserId;
/**
@@ -129,26 +131,40 @@ public interface BiometricAuthenticator {
/**
* Authentication result
* @param crypto
* @param authenticationType
* @param identifier
* @param userId
* @hide
*/
public AuthenticationResult(CryptoObject crypto, Identifier identifier,
public AuthenticationResult(CryptoObject crypto,
@AuthenticationResultType int authenticationType, Identifier identifier,
int userId) {
mCryptoObject = crypto;
mAuthenticationType = authenticationType;
mIdentifier = identifier;
mUserId = userId;
}
/**
* Obtain the crypto object associated with this transaction
* @return crypto object provided to {@link BiometricAuthenticator#authenticate(
* CryptoObject, CancellationSignal, Executor, AuthenticationCallback)}
* Provides the crypto object associated with this transaction.
* @return The crypto object provided to {@link BiometricPrompt#authenticate(
* BiometricPrompt.CryptoObject, CancellationSignal, Executor,
* BiometricPrompt.AuthenticationCallback)}
*/
public CryptoObject getCryptoObject() {
return mCryptoObject;
}
/**
* Provides the type of authentication (e.g. device credential or biometric) that was
* requested from and successfully provided by the user.
*
* @return An integer value representing the authentication method used.
*/
public @AuthenticationResultType int getAuthenticationType() {
return mAuthenticationType;
}
/**
* Obtain the biometric identifier associated with this operation. Applications are strongly
* discouraged from associating specific identifiers with specific applications or

View File

@@ -21,6 +21,7 @@ import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.hardware.biometrics.BiometricManager.Authenticators;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -40,6 +41,8 @@ import android.util.Log;
import com.android.internal.R;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.security.Signature;
import java.util.concurrent.Executor;
@@ -397,9 +400,11 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
new IBiometricServiceReceiver.Stub() {
@Override
public void onAuthenticationSucceeded() throws RemoteException {
public void onAuthenticationSucceeded(@AuthenticationResultType int authenticationType)
throws RemoteException {
mExecutor.execute(() -> {
final AuthenticationResult result = new AuthenticationResult(mCryptoObject);
final AuthenticationResult result =
new AuthenticationResult(mCryptoObject, authenticationType);
mAuthenticationCallback.onAuthenticationSucceeded(result);
});
}
@@ -576,28 +581,62 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
}
/**
* Container for callback data from {@link #authenticate( CancellationSignal, Executor,
* Authentication type reported by {@link AuthenticationResult} when the user authenticated by
* entering their device PIN, pattern, or password.
*/
public static final int AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL = 1;
/**
* Authentication type reported by {@link AuthenticationResult} when the user authenticated by
* presenting some form of biometric (e.g. fingerprint or face).
*/
public static final int AUTHENTICATION_RESULT_TYPE_BIOMETRIC = 2;
/**
* An {@link IntDef} representing the type of auth, as reported by {@link AuthenticationResult}.
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL, AUTHENTICATION_RESULT_TYPE_BIOMETRIC})
public @interface AuthenticationResultType {
}
/**
* Container for callback data from {@link #authenticate(CancellationSignal, Executor,
* AuthenticationCallback)} and {@link #authenticate(CryptoObject, CancellationSignal, Executor,
* AuthenticationCallback)}
* AuthenticationCallback)}.
*/
public static class AuthenticationResult extends BiometricAuthenticator.AuthenticationResult {
/**
* Authentication result
* @param crypto
* @param authenticationType
* @hide
*/
public AuthenticationResult(CryptoObject crypto) {
public AuthenticationResult(CryptoObject crypto,
@AuthenticationResultType int authenticationType) {
// Identifier and userId is not used for BiometricPrompt.
super(crypto, null /* identifier */, 0 /* userId */);
super(crypto, authenticationType, null /* identifier */, 0 /* userId */);
}
/**
* Obtain the crypto object associated with this transaction
* @return crypto object provided to {@link #authenticate( CryptoObject, CancellationSignal,
* Executor, AuthenticationCallback)}
* Provides the crypto object associated with this transaction.
* @return The crypto object provided to {@link #authenticate(CryptoObject,
* CancellationSignal, Executor, AuthenticationCallback)}
*/
public CryptoObject getCryptoObject() {
return (CryptoObject) super.getCryptoObject();
}
/**
* Provides the type of authentication (e.g. device credential or biometric) that was
* requested from and successfully provided by the user.
*
* @return An integer value representing the authentication method used.
*/
public @AuthenticationResultType int getAuthenticationType() {
return super.getAuthenticationType();
}
}
/**

View File

@@ -20,8 +20,8 @@ package android.hardware.biometrics;
* @hide
*/
oneway interface IBiometricServiceReceiver {
// Notify BiometricPrompt that authentication was successful
void onAuthenticationSucceeded();
// Notify BiometricPrompt that authentication was successful.
void onAuthenticationSucceeded(int authenticationType);
// Noties that authentication failed.
void onAuthenticationFailed();
// Notify BiometricPrompt that an error has occurred.

View File

@@ -1400,7 +1400,8 @@ public class BiometricService extends SystemService {
if (mCurrentAuthSession.mTokenEscrow != null) {
mKeyStore.addAuthToken(mCurrentAuthSession.mTokenEscrow);
}
mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded(
Utils.getAuthenticationTypeForResult(reason));
break;
case BiometricPrompt.DISMISSED_REASON_NEGATIVE:

View File

@@ -22,6 +22,7 @@ import android.content.Context;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.BiometricPrompt.AuthenticationResultType;
import android.os.Build;
import android.os.Bundle;
import android.os.UserHandle;
@@ -210,4 +211,30 @@ public class Utils {
}
return biometricManagerCode;
}
/**
* Converts a {@link BiometricPrompt} dismissal reason to an authentication type at the level of
* granularity supported by {@link BiometricPrompt.AuthenticationResult}.
*
* @param reason The reason that the {@link BiometricPrompt} was dismissed. Must be one of:
* {@link BiometricPrompt#DISMISSED_REASON_CREDENTIAL_CONFIRMED},
* {@link BiometricPrompt#DISMISSED_REASON_BIOMETRIC_CONFIRMED}, or
* {@link BiometricPrompt#DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED}
* @return An integer representing the authentication type for {@link
* BiometricPrompt.AuthenticationResult}.
* @throws IllegalArgumentException if given an invalid dismissal reason.
*/
public static @AuthenticationResultType int getAuthenticationTypeForResult(int reason) {
switch (reason) {
case BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED:
return BiometricPrompt.AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL;
case BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED:
case BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED:
return BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC;
default:
throw new IllegalArgumentException("Unsupported dismissal reason: " + reason);
}
}
}

View File

@@ -411,7 +411,8 @@ public class BiometricServiceTest {
// HAT sent to keystore
verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class));
// Send onAuthenticated to client
verify(mReceiver1).onAuthenticationSucceeded();
verify(mReceiver1).onAuthenticationSucceeded(
BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC);
// Current session becomes null
assertNull(mBiometricService.mCurrentAuthSession);
}
@@ -461,7 +462,8 @@ public class BiometricServiceTest {
BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED);
waitForIdle();
verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class));
verify(mReceiver1).onAuthenticationSucceeded();
verify(mReceiver1).onAuthenticationSucceeded(
BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC);
}
@Test

View File

@@ -223,4 +223,22 @@ public class UtilsTest {
Utils.biometricConstantsToBiometricManager(testCases[i][0]));
}
}
@Test
public void testGetAuthenticationTypeForResult_getsCorrectType() {
assertEquals(Utils.getAuthenticationTypeForResult(
BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED),
BiometricPrompt.AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL);
assertEquals(Utils.getAuthenticationTypeForResult(
BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED),
BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC);
assertEquals(Utils.getAuthenticationTypeForResult(
BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED),
BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC);
}
@Test(expected = IllegalArgumentException.class)
public void testGetAuthResultType_throwsForInvalidReason() {
Utils.getAuthenticationTypeForResult(BiometricPrompt.DISMISSED_REASON_NEGATIVE);
}
}