Merge changes I704914b2,I51560dfc,I866e84b2 into rvc-dev am: 127e3957d6

Change-Id: Iab9bb3ed728bd18373fd2aae27160cc8f6139280
This commit is contained in:
Kevin Chyn
2020-03-25 22:12:17 +00:00
committed by Automerger Merge Worker
8 changed files with 117 additions and 40 deletions

View File

@@ -26,7 +26,9 @@ package android.hardware.biometrics;
oneway interface IBiometricServiceReceiverInternal {
// Notify BiometricService that authentication was successful. If user confirmation is required,
// the auth token must be submitted into KeyStore.
void onAuthenticationSucceeded(boolean requireConfirmation, in byte[] token);
// TODO(b/151967372): Strength should be changed to authenticatorId
void onAuthenticationSucceeded(boolean requireConfirmation, in byte[] token,
boolean isStrongBiometric);
// Notify BiometricService authentication was rejected.
void onAuthenticationFailed();
// Notify BiometricService than an error has occured. Forward to the correct receiver depending

View File

@@ -202,8 +202,7 @@ public class AuthService extends SystemService {
// Only allow internal clients to call canAuthenticate with a different userId.
final int callingUserId = UserHandle.getCallingUserId();
Slog.d(TAG, "canAuthenticate, userId: " + userId + ", callingUserId: " + callingUserId
+ ", authenticators: " + authenticators);
if (userId != callingUserId) {
checkInternalPermission();
} else {
@@ -212,8 +211,14 @@ public class AuthService extends SystemService {
final long identity = Binder.clearCallingIdentity();
try {
return mBiometricService.canAuthenticate(
final int result = mBiometricService.canAuthenticate(
opPackageName, userId, callingUserId, authenticators);
Slog.d(TAG, "canAuthenticate"
+ ", userId: " + userId
+ ", callingUserId: " + callingUserId
+ ", authenticators: " + authenticators
+ ", result: " + result);
return result;
} finally {
Binder.restoreCallingIdentity(identity);
}

View File

@@ -66,6 +66,8 @@ public abstract class AuthenticationClient extends ClientMonitor {
public abstract boolean wasUserDetected();
public abstract boolean isStrongBiometric();
public AuthenticationClient(Context context, Constants constants,
BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
BiometricServiceBase.ServiceListener listener, int targetUserId, int groupId, long opId,
@@ -167,9 +169,15 @@ public abstract class AuthenticationClient extends ClientMonitor {
}
if (isBiometricPrompt() && listener != null) {
// BiometricService will add the token to keystore
listener.onAuthenticationSucceededInternal(mRequireConfirmation, byteToken);
listener.onAuthenticationSucceededInternal(mRequireConfirmation, byteToken,
isStrongBiometric());
} else if (!isBiometricPrompt() && listener != null) {
KeyStore.getInstance().addAuthToken(byteToken);
if (isStrongBiometric()) {
KeyStore.getInstance().addAuthToken(byteToken);
} else {
Slog.d(getLogTag(), "Skipping addAuthToken");
}
try {
// Explicitly have if/else here to make it super obvious in case the code is
// touched in the future.

View File

@@ -266,7 +266,8 @@ public class BiometricService extends SystemService {
SomeArgs args = (SomeArgs) msg.obj;
handleAuthenticationSucceeded(
(boolean) args.arg1 /* requireConfirmation */,
(byte[]) args.arg2 /* token */);
(byte[]) args.arg2 /* token */,
(boolean) args.arg3 /* isStrongBiometric */);
args.recycle();
break;
}
@@ -568,10 +569,12 @@ public class BiometricService extends SystemService {
final IBiometricServiceReceiverInternal mInternalReceiver =
new IBiometricServiceReceiverInternal.Stub() {
@Override
public void onAuthenticationSucceeded(boolean requireConfirmation, byte[] token) {
public void onAuthenticationSucceeded(boolean requireConfirmation, byte[] token,
boolean isStrongBiometric) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = requireConfirmation;
args.arg2 = token;
args.arg3 = isStrongBiometric;
mHandler.obtainMessage(MSG_ON_AUTHENTICATION_SUCCEEDED, args).sendToTarget();
}
@@ -761,8 +764,13 @@ public class BiometricService extends SystemService {
+ " config_biometric_sensors?");
}
// Note that we allow BIOMETRIC_CONVENIENCE to register because BiometricService
// also does / will do other things such as keep track of lock screen timeout, etc.
// Just because a biometric is registered does not mean it can participate in
// the android.hardware.biometrics APIs.
if (strength != Authenticators.BIOMETRIC_STRONG
&& strength != Authenticators.BIOMETRIC_WEAK) {
&& strength != Authenticators.BIOMETRIC_WEAK
&& strength != Authenticators.BIOMETRIC_CONVENIENCE) {
throw new IllegalStateException("Unsupported strength");
}
@@ -1189,8 +1197,10 @@ public class BiometricService extends SystemService {
BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL);
}
} else {
// This should not be possible via the public API surface and is here mainly for
// "correctness". An exception should have been thrown before getting here.
Slog.e(TAG, "No authenticators requested");
return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT);
}
}
@@ -1286,7 +1296,8 @@ public class BiometricService extends SystemService {
return modality;
}
private void handleAuthenticationSucceeded(boolean requireConfirmation, byte[] token) {
private void handleAuthenticationSucceeded(boolean requireConfirmation, byte[] token,
boolean isStrongBiometric) {
try {
// Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
// after user dismissed/canceled dialog).
@@ -1295,9 +1306,16 @@ public class BiometricService extends SystemService {
return;
}
// Store the auth token and submit it to keystore after the dialog is confirmed /
// animating away.
mCurrentAuthSession.mTokenEscrow = token;
if (isStrongBiometric) {
// Store the auth token and submit it to keystore after the dialog is confirmed /
// animating away.
mCurrentAuthSession.mTokenEscrow = token;
} else {
if (token != null) {
Slog.w(TAG, "Dropping authToken for non-strong biometric");
}
}
if (!requireConfirmation) {
mCurrentAuthSession.mState = STATE_AUTHENTICATED_PENDING_SYSUI;
} else {

View File

@@ -413,8 +413,8 @@ public abstract class BiometricServiceBase extends SystemService
throw new UnsupportedOperationException("Stub!");
}
default void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token)
throws RemoteException {
default void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token,
boolean isStrongBiometric) throws RemoteException {
throw new UnsupportedOperationException("Stub!");
}
@@ -451,10 +451,11 @@ public abstract class BiometricServiceBase extends SystemService
}
@Override
public void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token)
throws RemoteException {
public void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token,
boolean isStrongBiometric) throws RemoteException {
if (getWrapperReceiver() != null) {
getWrapperReceiver().onAuthenticationSucceeded(requireConfirmation, token);
getWrapperReceiver().onAuthenticationSucceeded(requireConfirmation, token,
isStrongBiometric);
}
}

View File

@@ -235,6 +235,11 @@ public class FaceService extends BiometricServiceBase {
&& mLastAcquire != FaceManager.FACE_ACQUIRED_SENSOR_DIRTY;
}
@Override
public boolean isStrongBiometric() {
return FaceService.this.isStrongBiometric();
}
@Override
public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
boolean authenticated, ArrayList<Byte> token) {

View File

@@ -160,6 +160,11 @@ public class FingerprintService extends BiometricServiceBase {
return false;
}
@Override
public boolean isStrongBiometric() {
return FingerprintService.this.isStrongBiometric();
}
@Override
public int handleFailedAttempt() {
final int currentUser = ActivityManager.getCurrentUser();

View File

@@ -64,10 +64,13 @@ import com.android.internal.statusbar.IStatusBarService;
import org.junit.Before;
import org.junit.Test;
import org.mockito.AdditionalMatchers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Random;
@SmallTest
public class BiometricServiceTest {
@@ -347,9 +350,19 @@ public class BiometricServiceTest {
}
@Test
public void testAuthenticate_happyPathWithoutConfirmation() throws Exception {
public void testAuthenticate_happyPathWithoutConfirmation_strongBiometric() throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
testAuthenticate_happyPathWithoutConfirmation(true /* isStrongBiometric */);
}
@Test
public void testAuthenticate_happyPathWithoutConfirmation_weakBiometric() throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_WEAK);
testAuthenticate_happyPathWithoutConfirmation(false /* isStrongBiometric */);
}
private void testAuthenticate_happyPathWithoutConfirmation(boolean isStrongBiometric)
throws Exception {
// Start testing the happy path
invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
null /* authenticators */);
@@ -397,9 +410,11 @@ public class BiometricServiceTest {
anyLong() /* sessionId */);
// Hardware authenticated
final byte[] HAT = generateRandomHAT();
mBiometricService.mInternalReceiver.onAuthenticationSucceeded(
false /* requireConfirmation */,
new byte[69] /* HAT */);
HAT,
isStrongBiometric /* isStrongBiometric */);
waitForIdle();
// Waiting for SystemUI to send dismissed callback
assertEquals(mBiometricService.mCurrentAuthSession.mState,
@@ -413,7 +428,11 @@ public class BiometricServiceTest {
null /* credentialAttestation */);
waitForIdle();
// HAT sent to keystore
verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class));
if (isStrongBiometric) {
verify(mBiometricService.mKeyStore).addAuthToken(AdditionalMatchers.aryEq(HAT));
} else {
verify(mBiometricService.mKeyStore, never()).addAuthToken(any(byte[].class));
}
// Send onAuthenticated to client
verify(mReceiver1).onAuthenticationSucceeded(
BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC);
@@ -447,16 +466,29 @@ public class BiometricServiceTest {
}
@Test
public void testAuthenticate_happyPathWithConfirmation() throws Exception {
public void testAuthenticate_happyPathWithConfirmation_strongBiometric() throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
testAuthenticate_happyPathWithConfirmation(true /* isStrongBiometric */);
}
@Test
public void testAuthenticate_happyPathWithConfirmation_weakBiometric() throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_WEAK);
testAuthenticate_happyPathWithConfirmation(false /* isStrongBiometric */);
}
private void testAuthenticate_happyPathWithConfirmation(boolean isStrongBiometric)
throws Exception {
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
true /* requireConfirmation */, null /* authenticators */);
// Test authentication succeeded goes to PENDING_CONFIRMATION and that the HAT is not
// sent to KeyStore yet
final byte[] HAT = generateRandomHAT();
mBiometricService.mInternalReceiver.onAuthenticationSucceeded(
true /* requireConfirmation */,
new byte[69] /* HAT */);
HAT,
isStrongBiometric /* isStrongBiometric */);
waitForIdle();
// Waiting for SystemUI to send confirmation callback
assertEquals(mBiometricService.mCurrentAuthSession.mState,
@@ -468,7 +500,11 @@ public class BiometricServiceTest {
BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED,
null /* credentialAttestation */);
waitForIdle();
verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class));
if (isStrongBiometric) {
verify(mBiometricService.mKeyStore).addAuthToken(AdditionalMatchers.aryEq(HAT));
} else {
verify(mBiometricService.mKeyStore, never()).addAuthToken(any(byte[].class));
}
verify(mReceiver1).onAuthenticationSucceeded(
BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC);
}
@@ -909,7 +945,8 @@ public class BiometricServiceTest {
mBiometricService.mInternalReceiver.onAuthenticationSucceeded(
true /* requireConfirmation */,
new byte[69] /* HAT */);
new byte[69] /* HAT */,
true /* isStrongBiometric */);
mBiometricService.mInternalReceiver.onDialogDismissed(
BiometricPrompt.DISMISSED_REASON_USER_CANCEL, null /* credentialAttestation */);
waitForIdle();
@@ -927,6 +964,7 @@ public class BiometricServiceTest {
eq(BiometricAuthenticator.TYPE_FACE),
eq(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED),
eq(0 /* vendorCode */));
verify(mBiometricService.mKeyStore, never()).addAuthToken(any(byte[].class));
assertNull(mBiometricService.mCurrentAuthSession);
}
@@ -1238,20 +1276,6 @@ public class BiometricServiceTest {
mFingerprintAuthenticator);
}
@Test(expected = IllegalStateException.class)
public void testRegistrationWithUnsupportedStrength_throwsIllegalStateException()
throws Exception {
mBiometricService = new BiometricService(mContext, mInjector);
mBiometricService.onStart();
// Only STRONG and WEAK are supported. Let's enforce that CONVENIENCE cannot be
// registered. If there is a compelling reason, we can remove this constraint.
mBiometricService.mImpl.registerAuthenticator(
0 /* id */, 2 /* modality */,
Authenticators.BIOMETRIC_CONVENIENCE /* strength */,
mFingerprintAuthenticator);
}
@Test(expected = IllegalArgumentException.class)
public void testRegistrationWithNullAuthenticator_throwsIllegalArgumentException()
throws Exception {
@@ -1508,4 +1532,13 @@ public class BiometricServiceTest {
private static void waitForIdle() {
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
private byte[] generateRandomHAT() {
byte[] HAT = new byte[69];
Random random = new Random();
random.nextBytes(HAT);
return HAT;
}
}