Merge "Pass operationId to LSS, add HAT to KeyStore" into rvc-dev am: 6b7b2300c4

Change-Id: I7eead7a89a6d9b286050f0438be0c481c32bdcc9
This commit is contained in:
Automerger Merge Worker
2020-03-14 00:06:45 +00:00
16 changed files with 215 additions and 94 deletions

View File

@@ -35,7 +35,7 @@ oneway interface IBiometricServiceReceiverInternal {
// Notifies that a biometric has been acquired. // Notifies that a biometric has been acquired.
void onAcquired(int acquiredInfo, String message); void onAcquired(int acquiredInfo, String message);
// Notifies that the SystemUI dialog has been dismissed. // Notifies that the SystemUI dialog has been dismissed.
void onDialogDismissed(int reason); void onDialogDismissed(int reason, in byte[] credentialAttestation);
// Notifies that the user has pressed the "try again" button on SystemUI // Notifies that the user has pressed the "try again" button on SystemUI
void onTryAgainPressed(); void onTryAgainPressed();
// Notifies that the user has pressed the "use password" button on SystemUI // Notifies that the user has pressed the "use password" button on SystemUI

View File

@@ -136,7 +136,8 @@ oneway interface IStatusBar
// Used to show the authentication dialog (Biometrics, Device Credential) // Used to show the authentication dialog (Biometrics, Device Credential)
void showAuthenticationDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, void showAuthenticationDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver,
int biometricModality, boolean requireConfirmation, int userId, String opPackageName); int biometricModality, boolean requireConfirmation, int userId, String opPackageName,
long operationId);
// Used to notify the authentication dialog that a biometric has been authenticated // Used to notify the authentication dialog that a biometric has been authenticated
void onBiometricAuthenticated(); void onBiometricAuthenticated();
// Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc // Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc

View File

@@ -105,7 +105,8 @@ interface IStatusBarService
// Used to show the authentication dialog (Biometrics, Device Credential) // Used to show the authentication dialog (Biometrics, Device Credential)
void showAuthenticationDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, void showAuthenticationDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver,
int biometricModality, boolean requireConfirmation, int userId, String opPackageName); int biometricModality, boolean requireConfirmation, int userId, String opPackageName,
long operationId);
// Used to notify the authentication dialog that a biometric has been authenticated // Used to notify the authentication dialog that a biometric has been authenticated
void onBiometricAuthenticated(); void onBiometricAuthenticated();
// Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc // Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc

View File

@@ -99,6 +99,8 @@ public class AuthContainerView extends LinearLayout
// Non-null only if the dialog is in the act of dismissing and has not sent the reason yet. // Non-null only if the dialog is in the act of dismissing and has not sent the reason yet.
@Nullable @AuthDialogCallback.DismissedReason Integer mPendingCallbackReason; @Nullable @AuthDialogCallback.DismissedReason Integer mPendingCallbackReason;
// HAT received from LockSettingsService when credential is verified.
@Nullable byte[] mCredentialAttestation;
static class Config { static class Config {
Context mContext; Context mContext;
@@ -109,6 +111,7 @@ public class AuthContainerView extends LinearLayout
String mOpPackageName; String mOpPackageName;
int mModalityMask; int mModalityMask;
boolean mSkipIntro; boolean mSkipIntro;
long mOperationId;
} }
public static class Builder { public static class Builder {
@@ -149,6 +152,11 @@ public class AuthContainerView extends LinearLayout
return this; return this;
} }
public Builder setOperationId(long operationId) {
mConfig.mOperationId = operationId;
return this;
}
public AuthContainerView build(int modalityMask) { public AuthContainerView build(int modalityMask) {
mConfig.mModalityMask = modalityMask; mConfig.mModalityMask = modalityMask;
return new AuthContainerView(mConfig, new Injector()); return new AuthContainerView(mConfig, new Injector());
@@ -224,7 +232,8 @@ public class AuthContainerView extends LinearLayout
final class CredentialCallback implements AuthCredentialView.Callback { final class CredentialCallback implements AuthCredentialView.Callback {
@Override @Override
public void onCredentialMatched() { public void onCredentialMatched(byte[] attestation) {
mCredentialAttestation = attestation;
animateAway(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED); animateAway(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED);
} }
} }
@@ -341,6 +350,7 @@ public class AuthContainerView extends LinearLayout
mCredentialView.setContainerView(this); mCredentialView.setContainerView(this);
mCredentialView.setUserId(mConfig.mUserId); mCredentialView.setUserId(mConfig.mUserId);
mCredentialView.setOperationId(mConfig.mOperationId);
mCredentialView.setEffectiveUserId(mEffectiveUserId); mCredentialView.setEffectiveUserId(mEffectiveUserId);
mCredentialView.setCredentialType(credentialType); mCredentialView.setCredentialType(credentialType);
mCredentialView.setCallback(mCredentialCallback); mCredentialView.setCallback(mCredentialCallback);
@@ -558,7 +568,7 @@ public class AuthContainerView extends LinearLayout
private void sendPendingCallbackIfNotNull() { private void sendPendingCallbackIfNotNull() {
Log.d(TAG, "pendingCallback: " + mPendingCallbackReason); Log.d(TAG, "pendingCallback: " + mPendingCallbackReason);
if (mPendingCallbackReason != null) { if (mPendingCallbackReason != null) {
mConfig.mCallback.onDismissed(mPendingCallbackReason); mConfig.mCallback.onDismissed(mPendingCallbackReason, mCredentialAttestation);
mPendingCallbackReason = null; mPendingCallbackReason = null;
} }
} }

View File

@@ -20,6 +20,7 @@ import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricManager.Authenticators; import static android.hardware.biometrics.BiometricManager.Authenticators;
import android.annotation.Nullable;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.ActivityTaskManager; import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager; import android.app.IActivityTaskManager;
@@ -99,7 +100,8 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
try { try {
if (mReceiver != null) { if (mReceiver != null) {
mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL); mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL,
null /* credentialAttestation */);
mReceiver = null; mReceiver = null;
} }
} catch (RemoteException e) { } catch (RemoteException e) {
@@ -124,7 +126,8 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
mCurrentDialog = null; mCurrentDialog = null;
if (mReceiver != null) { if (mReceiver != null) {
mReceiver.onDialogDismissed( mReceiver.onDialogDismissed(
BiometricPrompt.DISMISSED_REASON_USER_CANCEL); BiometricPrompt.DISMISSED_REASON_USER_CANCEL,
null /* credentialAttestation */);
mReceiver = null; mReceiver = null;
} }
} }
@@ -162,35 +165,42 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
} }
@Override @Override
public void onDismissed(@DismissedReason int reason) { public void onDismissed(@DismissedReason int reason, @Nullable byte[] credentialAttestation) {
switch (reason) { switch (reason) {
case AuthDialogCallback.DISMISSED_USER_CANCELED: case AuthDialogCallback.DISMISSED_USER_CANCELED:
sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_USER_CANCEL); sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_USER_CANCEL,
credentialAttestation);
break; break;
case AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE: case AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE:
sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_NEGATIVE); sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_NEGATIVE,
credentialAttestation);
break; break;
case AuthDialogCallback.DISMISSED_BUTTON_POSITIVE: case AuthDialogCallback.DISMISSED_BUTTON_POSITIVE:
sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED); sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED,
credentialAttestation);
break; break;
case AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED: case AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED:
sendResultAndCleanUp( sendResultAndCleanUp(
BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED); BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED,
credentialAttestation);
break; break;
case AuthDialogCallback.DISMISSED_ERROR: case AuthDialogCallback.DISMISSED_ERROR:
sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_ERROR); sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_ERROR,
credentialAttestation);
break; break;
case AuthDialogCallback.DISMISSED_BY_SYSTEM_SERVER: case AuthDialogCallback.DISMISSED_BY_SYSTEM_SERVER:
sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED); sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED,
credentialAttestation);
break; break;
case AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED: case AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED:
sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED); sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED,
credentialAttestation);
break; break;
default: default:
@@ -199,13 +209,14 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
} }
} }
private void sendResultAndCleanUp(@DismissedReason int reason) { private void sendResultAndCleanUp(@DismissedReason int reason,
@Nullable byte[] credentialAttestation) {
if (mReceiver == null) { if (mReceiver == null) {
Log.e(TAG, "sendResultAndCleanUp: Receiver is null"); Log.e(TAG, "sendResultAndCleanUp: Receiver is null");
return; return;
} }
try { try {
mReceiver.onDialogDismissed(reason); mReceiver.onDialogDismissed(reason, credentialAttestation);
} catch (RemoteException e) { } catch (RemoteException e) {
Log.w(TAG, "Remote exception", e); Log.w(TAG, "Remote exception", e);
} }
@@ -251,13 +262,15 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
@Override @Override
public void showAuthenticationDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver, public void showAuthenticationDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
int biometricModality, boolean requireConfirmation, int userId, String opPackageName) { int biometricModality, boolean requireConfirmation, int userId, String opPackageName,
long operationId) {
final int authenticators = Utils.getAuthenticators(bundle); final int authenticators = Utils.getAuthenticators(bundle);
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "showAuthenticationDialog, authenticators: " + authenticators Log.d(TAG, "showAuthenticationDialog, authenticators: " + authenticators
+ ", biometricModality: " + biometricModality + ", biometricModality: " + biometricModality
+ ", requireConfirmation: " + requireConfirmation); + ", requireConfirmation: " + requireConfirmation
+ ", operationId: " + operationId);
} }
SomeArgs args = SomeArgs.obtain(); SomeArgs args = SomeArgs.obtain();
args.arg1 = bundle; args.arg1 = bundle;
@@ -266,6 +279,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
args.arg3 = requireConfirmation; args.arg3 = requireConfirmation;
args.argi2 = userId; args.argi2 = userId;
args.arg4 = opPackageName; args.arg4 = opPackageName;
args.arg5 = operationId;
boolean skipAnimation = false; boolean skipAnimation = false;
if (mCurrentDialog != null) { if (mCurrentDialog != null) {
@@ -354,6 +368,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
final boolean requireConfirmation = (boolean) args.arg3; final boolean requireConfirmation = (boolean) args.arg3;
final int userId = args.argi2; final int userId = args.argi2;
final String opPackageName = (String) args.arg4; final String opPackageName = (String) args.arg4;
final long operationId = (long) args.arg5;
// Create a new dialog but do not replace the current one yet. // Create a new dialog but do not replace the current one yet.
final AuthDialog newDialog = buildDialog( final AuthDialog newDialog = buildDialog(
@@ -362,7 +377,8 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
userId, userId,
type, type,
opPackageName, opPackageName,
skipAnimation); skipAnimation,
operationId);
if (newDialog == null) { if (newDialog == null) {
Log.e(TAG, "Unsupported type: " + type); Log.e(TAG, "Unsupported type: " + type);
@@ -429,7 +445,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
} }
protected AuthDialog buildDialog(Bundle biometricPromptBundle, boolean requireConfirmation, protected AuthDialog buildDialog(Bundle biometricPromptBundle, boolean requireConfirmation,
int userId, int type, String opPackageName, boolean skipIntro) { int userId, int type, String opPackageName, boolean skipIntro, long operationId) {
return new AuthContainerView.Builder(mContext) return new AuthContainerView.Builder(mContext)
.setCallback(this) .setCallback(this)
.setBiometricPromptBundle(biometricPromptBundle) .setBiometricPromptBundle(biometricPromptBundle)
@@ -437,6 +453,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
.setUserId(userId) .setUserId(userId)
.setOpPackageName(opPackageName) .setOpPackageName(opPackageName)
.setSkipIntro(skipIntro) .setSkipIntro(skipIntro)
.setOperationId(operationId)
.build(type); .build(type);
} }
} }

View File

@@ -103,14 +103,16 @@ public class AuthCredentialPasswordView extends AuthCredentialView
return; return;
} }
mPendingLockCheck = LockPatternChecker.checkCredential(mLockPatternUtils, mPendingLockCheck = LockPatternChecker.verifyCredential(mLockPatternUtils,
password, mEffectiveUserId, this::onCredentialChecked); password, mOperationId, mEffectiveUserId, this::onCredentialVerified);
} }
} }
@Override @Override
protected void onCredentialChecked(boolean matched, int timeoutMs) { protected void onCredentialVerified(byte[] attestation, int timeoutMs) {
super.onCredentialChecked(matched, timeoutMs); super.onCredentialVerified(attestation, timeoutMs);
final boolean matched = attestation != null;
if (matched) { if (matched) {
mImm.hideSoftInputFromWindow(getWindowToken(), 0 /* flags */); mImm.hideSoftInputFromWindow(getWindowToken(), 0 /* flags */);

View File

@@ -61,21 +61,22 @@ public class AuthCredentialPatternView extends AuthCredentialView {
if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) { if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
// Pattern size is less than the minimum, do not count it as a failed attempt. // Pattern size is less than the minimum, do not count it as a failed attempt.
onPatternChecked(false /* matched */, 0 /* timeoutMs */); onPatternVerified(null /* attestation */, 0 /* timeoutMs */);
return; return;
} }
try (LockscreenCredential credential = LockscreenCredential.createPattern(pattern)) { try (LockscreenCredential credential = LockscreenCredential.createPattern(pattern)) {
mPendingLockCheck = LockPatternChecker.checkCredential( mPendingLockCheck = LockPatternChecker.verifyCredential(
mLockPatternUtils, mLockPatternUtils,
credential, credential,
mOperationId,
mEffectiveUserId, mEffectiveUserId,
this::onPatternChecked); this::onPatternVerified);
} }
} }
private void onPatternChecked(boolean matched, int timeoutMs) { private void onPatternVerified(byte[] attestation, int timeoutMs) {
AuthCredentialPatternView.this.onCredentialChecked(matched, timeoutMs); AuthCredentialPatternView.this.onCredentialVerified(attestation, timeoutMs);
if (timeoutMs > 0) { if (timeoutMs > 0) {
mLockPatternView.setEnabled(false); mLockPatternView.setEnabled(false);
} else { } else {

View File

@@ -42,7 +42,7 @@ import com.android.systemui.R;
/** /**
* Abstract base class for Pin, Pattern, or Password authentication, for * Abstract base class for Pin, Pattern, or Password authentication, for
* {@link BiometricPrompt.Builder#setDeviceCredentialAllowed(boolean)} * {@link BiometricPrompt.Builder#setAllowedAuthenticators(int)}}
*/ */
public abstract class AuthCredentialView extends LinearLayout { public abstract class AuthCredentialView extends LinearLayout {
@@ -70,11 +70,12 @@ public abstract class AuthCredentialView extends LinearLayout {
protected Callback mCallback; protected Callback mCallback;
protected AsyncTask<?, ?, ?> mPendingLockCheck; protected AsyncTask<?, ?, ?> mPendingLockCheck;
protected int mUserId; protected int mUserId;
protected long mOperationId;
protected int mEffectiveUserId; protected int mEffectiveUserId;
protected ErrorTimer mErrorTimer; protected ErrorTimer mErrorTimer;
interface Callback { interface Callback {
void onCredentialMatched(); void onCredentialMatched(byte[] attestation);
} }
protected static class ErrorTimer extends CountDownTimer { protected static class ErrorTimer extends CountDownTimer {
@@ -148,6 +149,10 @@ public abstract class AuthCredentialView extends LinearLayout {
mUserId = userId; mUserId = userId;
} }
void setOperationId(long operationId) {
mOperationId = operationId;
}
void setEffectiveUserId(int effectiveUserId) { void setEffectiveUserId(int effectiveUserId) {
mEffectiveUserId = effectiveUserId; mEffectiveUserId = effectiveUserId;
} }
@@ -245,10 +250,13 @@ public abstract class AuthCredentialView extends LinearLayout {
protected void onErrorTimeoutFinish() {} protected void onErrorTimeoutFinish() {}
protected void onCredentialChecked(boolean matched, int timeoutMs) { protected void onCredentialVerified(byte[] attestation, int timeoutMs) {
final boolean matched = attestation != null;
if (matched) { if (matched) {
mClearErrorRunnable.run(); mClearErrorRunnable.run();
mCallback.onCredentialMatched(); mCallback.onCredentialMatched(attestation);
} else { } else {
if (timeoutMs > 0) { if (timeoutMs > 0) {
mHandler.removeCallbacks(mClearErrorRunnable); mHandler.removeCallbacks(mClearErrorRunnable);

View File

@@ -17,6 +17,7 @@
package com.android.systemui.biometrics; package com.android.systemui.biometrics;
import android.annotation.IntDef; import android.annotation.IntDef;
import android.annotation.Nullable;
/** /**
* Callback interface for dialog views. These should be implemented by the controller (e.g. * Callback interface for dialog views. These should be implemented by the controller (e.g.
@@ -44,8 +45,9 @@ public interface AuthDialogCallback {
/** /**
* Invoked when the dialog is dismissed * Invoked when the dialog is dismissed
* @param reason * @param reason
* @param credentialAttestation the HAT received from LockSettingsService upon verification
*/ */
void onDismissed(@DismissedReason int reason); void onDismissed(@DismissedReason int reason, @Nullable byte[] credentialAttestation);
/** /**
* Invoked when the "try again" button is clicked * Invoked when the "try again" button is clicked

View File

@@ -262,7 +262,8 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
default void showAuthenticationDialog(Bundle bundle, default void showAuthenticationDialog(Bundle bundle,
IBiometricServiceReceiverInternal receiver, int biometricModality, IBiometricServiceReceiverInternal receiver, int biometricModality,
boolean requireConfirmation, int userId, String opPackageName) { } boolean requireConfirmation, int userId, String opPackageName,
long operationId) { }
default void onBiometricAuthenticated() { } default void onBiometricAuthenticated() { }
default void onBiometricHelp(String message) { } default void onBiometricHelp(String message) { }
default void onBiometricError(int modality, int error, int vendorCode) { } default void onBiometricError(int modality, int error, int vendorCode) { }
@@ -780,7 +781,8 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
@Override @Override
public void showAuthenticationDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver, public void showAuthenticationDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
int biometricModality, boolean requireConfirmation, int userId, String opPackageName) { int biometricModality, boolean requireConfirmation, int userId, String opPackageName,
long operationId) {
synchronized (mLock) { synchronized (mLock) {
SomeArgs args = SomeArgs.obtain(); SomeArgs args = SomeArgs.obtain();
args.arg1 = bundle; args.arg1 = bundle;
@@ -789,6 +791,7 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
args.arg3 = requireConfirmation; args.arg3 = requireConfirmation;
args.argi2 = userId; args.argi2 = userId;
args.arg4 = opPackageName; args.arg4 = opPackageName;
args.arg5 = operationId;
mHandler.obtainMessage(MSG_BIOMETRIC_SHOW, args) mHandler.obtainMessage(MSG_BIOMETRIC_SHOW, args)
.sendToTarget(); .sendToTarget();
} }
@@ -1164,7 +1167,8 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
someArgs.argi1 /* biometricModality */, someArgs.argi1 /* biometricModality */,
(boolean) someArgs.arg3 /* requireConfirmation */, (boolean) someArgs.arg3 /* requireConfirmation */,
someArgs.argi2 /* userId */, someArgs.argi2 /* userId */,
(String) someArgs.arg4 /* opPackageName */); (String) someArgs.arg4 /* opPackageName */,
(long) someArgs.arg5 /* operationId */);
} }
someArgs.recycle(); someArgs.recycle();
break; break;

View File

@@ -78,7 +78,9 @@ public class AuthContainerViewTest extends SysuiTestCase {
mAuthContainer.mBiometricCallback.onAction( mAuthContainer.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_AUTHENTICATED); AuthBiometricView.Callback.ACTION_AUTHENTICATED);
verify(mCallback).onDismissed(eq(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED)); verify(mCallback).onDismissed(
eq(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED),
eq(null) /* credentialAttestation */);
} }
@Test @Test
@@ -87,7 +89,9 @@ public class AuthContainerViewTest extends SysuiTestCase {
mAuthContainer.mBiometricCallback.onAction( mAuthContainer.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_USER_CANCELED); AuthBiometricView.Callback.ACTION_USER_CANCELED);
verify(mCallback).onDismissed(eq(AuthDialogCallback.DISMISSED_USER_CANCELED)); verify(mCallback).onDismissed(
eq(AuthDialogCallback.DISMISSED_USER_CANCELED),
eq(null) /* credentialAttestation */);
} }
@Test @Test
@@ -96,7 +100,9 @@ public class AuthContainerViewTest extends SysuiTestCase {
mAuthContainer.mBiometricCallback.onAction( mAuthContainer.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_BUTTON_NEGATIVE); AuthBiometricView.Callback.ACTION_BUTTON_NEGATIVE);
verify(mCallback).onDismissed(eq(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE)); verify(mCallback).onDismissed(
eq(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE),
eq(null) /* credentialAttestation */);
} }
@Test @Test
@@ -114,7 +120,9 @@ public class AuthContainerViewTest extends SysuiTestCase {
mAuthContainer.mBiometricCallback.onAction( mAuthContainer.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_ERROR); AuthBiometricView.Callback.ACTION_ERROR);
verify(mCallback).onDismissed(AuthDialogCallback.DISMISSED_ERROR); verify(mCallback).onDismissed(
eq(AuthDialogCallback.DISMISSED_ERROR),
eq(null) /* credentialAttestation */);
} }
@Test @Test
@@ -219,7 +227,8 @@ public class AuthContainerViewTest extends SysuiTestCase {
@Override @Override
public void animateAway(int reason) { public void animateAway(int reason) {
mConfig.mCallback.onDismissed(reason); // TODO: Credential attestation should be testable/tested
mConfig.mCallback.onDismissed(reason, null /* credentialAttestation */);
} }
} }

View File

@@ -57,12 +57,14 @@ import com.android.systemui.statusbar.CommandQueue;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.AdditionalMatchers;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Random;
@RunWith(AndroidTestingRunner.class) @RunWith(AndroidTestingRunner.class)
@RunWithLooper @RunWithLooper
@@ -110,52 +112,75 @@ public class AuthControllerTest extends SysuiTestCase {
@Test @Test
public void testSendsReasonUserCanceled_whenDismissedByUserCancel() throws Exception { public void testSendsReasonUserCanceled_whenDismissedByUserCancel() throws Exception {
showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED,
verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL); null /* credentialAttestation */);
verify(mReceiver).onDialogDismissed(
eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL),
eq(null) /* credentialAttestation */);
} }
@Test @Test
public void testSendsReasonNegative_whenDismissedByButtonNegative() throws Exception { public void testSendsReasonNegative_whenDismissedByButtonNegative() throws Exception {
showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE,
verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_NEGATIVE); null /* credentialAttestation */);
verify(mReceiver).onDialogDismissed(
eq(BiometricPrompt.DISMISSED_REASON_NEGATIVE),
eq(null) /* credentialAttestation */);
} }
@Test @Test
public void testSendsReasonConfirmed_whenDismissedByButtonPositive() throws Exception { public void testSendsReasonConfirmed_whenDismissedByButtonPositive() throws Exception {
showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_POSITIVE); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_POSITIVE,
verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED); null /* credentialAttestation */);
verify(mReceiver).onDialogDismissed(
eq(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED),
eq(null) /* credentialAttestation */);
} }
@Test @Test
public void testSendsReasonConfirmNotRequired_whenDismissedByAuthenticated() throws Exception { public void testSendsReasonConfirmNotRequired_whenDismissedByAuthenticated() throws Exception {
showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED,
null /* credentialAttestation */);
verify(mReceiver).onDialogDismissed( verify(mReceiver).onDialogDismissed(
BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED); eq(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED),
eq(null) /* credentialAttestation */);
} }
@Test @Test
public void testSendsReasonError_whenDismissedByError() throws Exception { public void testSendsReasonError_whenDismissedByError() throws Exception {
showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_ERROR); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_ERROR,
verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_ERROR); null /* credentialAttestation */);
verify(mReceiver).onDialogDismissed(
eq(BiometricPrompt.DISMISSED_REASON_ERROR),
eq(null) /* credentialAttestation */);
} }
@Test @Test
public void testSendsReasonServerRequested_whenDismissedByServer() throws Exception { public void testSendsReasonServerRequested_whenDismissedByServer() throws Exception {
showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BY_SYSTEM_SERVER); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BY_SYSTEM_SERVER,
verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED); null /* credentialAttestation */);
verify(mReceiver).onDialogDismissed(
eq(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED),
eq(null) /* credentialAttestation */);
} }
@Test @Test
public void testSendsReasonCredentialConfirmed_whenDeviceCredentialAuthenticated() public void testSendsReasonCredentialConfirmed_whenDeviceCredentialAuthenticated()
throws Exception { throws Exception {
showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED);
verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED); final byte[] credentialAttestation = generateRandomHAT();
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED,
credentialAttestation);
verify(mReceiver).onDialogDismissed(
eq(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED),
AdditionalMatchers.aryEq(credentialAttestation));
} }
// Statusbar tests // Statusbar tests
@@ -302,8 +327,13 @@ public class AuthControllerTest extends SysuiTestCase {
showDialog(Authenticators.DEVICE_CREDENTIAL, BiometricPrompt.TYPE_NONE); showDialog(Authenticators.DEVICE_CREDENTIAL, BiometricPrompt.TYPE_NONE);
verify(mDialog1).show(any(), any()); verify(mDialog1).show(any(), any());
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED); final byte[] credentialAttestation = generateRandomHAT();
verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED);
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED,
credentialAttestation);
verify(mReceiver).onDialogDismissed(
eq(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED),
AdditionalMatchers.aryEq(credentialAttestation));
mAuthController.hideAuthenticationDialog(); mAuthController.hideAuthenticationDialog();
} }
@@ -395,20 +425,24 @@ public class AuthControllerTest extends SysuiTestCase {
assertNull(mAuthController.mCurrentDialog); assertNull(mAuthController.mCurrentDialog);
assertNull(mAuthController.mReceiver); assertNull(mAuthController.mReceiver);
verify(mDialog1).dismissWithoutCallback(true /* animate */); verify(mDialog1).dismissWithoutCallback(true /* animate */);
verify(mReceiver).onDialogDismissed(eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL)); verify(mReceiver).onDialogDismissed(
eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL),
eq(null) /* credentialAttestation */);
} }
@Test @Test
public void testDoesNotCrash_whenTryAgainPressedAfterDismissal() { public void testDoesNotCrash_whenTryAgainPressedAfterDismissal() {
showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED,
null /* credentialAttestation */);
mAuthController.onTryAgainPressed(); mAuthController.onTryAgainPressed();
} }
@Test @Test
public void testDoesNotCrash_whenDeviceCredentialPressedAfterDismissal() { public void testDoesNotCrash_whenDeviceCredentialPressedAfterDismissal() {
showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED,
null /* credentialAttestation */);
mAuthController.onDeviceCredentialPressed(); mAuthController.onDeviceCredentialPressed();
} }
@@ -422,7 +456,9 @@ public class AuthControllerTest extends SysuiTestCase {
assertNull(mAuthController.mCurrentDialog); assertNull(mAuthController.mCurrentDialog);
assertNull(mAuthController.mReceiver); assertNull(mAuthController.mReceiver);
verify(mDialog1).dismissWithoutCallback(true /* animate */); verify(mDialog1).dismissWithoutCallback(true /* animate */);
verify(mReceiver).onDialogDismissed(eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL)); verify(mReceiver).onDialogDismissed(
eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL),
eq(null) /* credentialAttestation */);
} }
// Helpers // Helpers
@@ -433,7 +469,8 @@ public class AuthControllerTest extends SysuiTestCase {
biometricModality, biometricModality,
true /* requireConfirmation */, true /* requireConfirmation */,
0 /* userId */, 0 /* userId */,
"testPackage"); "testPackage",
0 /* operationId */);
} }
private Bundle createTestDialogBundle(int authenticators) { private Bundle createTestDialogBundle(int authenticators) {
@@ -453,6 +490,13 @@ public class AuthControllerTest extends SysuiTestCase {
return bundle; return bundle;
} }
private byte[] generateRandomHAT() {
byte[] HAT = new byte[69];
Random random = new Random();
random.nextBytes(HAT);
return HAT;
}
private final class TestableAuthController extends AuthController { private final class TestableAuthController extends AuthController {
private int mBuildCount = 0; private int mBuildCount = 0;
private Bundle mLastBiometricPromptBundle; private Bundle mLastBiometricPromptBundle;
@@ -464,7 +508,7 @@ public class AuthControllerTest extends SysuiTestCase {
@Override @Override
protected AuthDialog buildDialog(Bundle biometricPromptBundle, protected AuthDialog buildDialog(Bundle biometricPromptBundle,
boolean requireConfirmation, int userId, int type, String opPackageName, boolean requireConfirmation, int userId, int type, String opPackageName,
boolean skipIntro) { boolean skipIntro, long operationId) {
mLastBiometricPromptBundle = biometricPromptBundle; mLastBiometricPromptBundle = biometricPromptBundle;

View File

@@ -409,11 +409,12 @@ public class CommandQueueTest extends SysuiTestCase {
public void testShowAuthenticationDialog() { public void testShowAuthenticationDialog() {
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
String packageName = "test"; String packageName = "test";
final long operationId = 1;
mCommandQueue.showAuthenticationDialog(bundle, null /* receiver */, 1, true, 3, mCommandQueue.showAuthenticationDialog(bundle, null /* receiver */, 1, true, 3,
packageName); packageName, operationId);
waitForIdleSync(); waitForIdleSync();
verify(mCallbacks).showAuthenticationDialog(eq(bundle), eq(null), eq(1), eq(true), eq(3), verify(mCallbacks).showAuthenticationDialog(eq(bundle), eq(null), eq(1), eq(true), eq(3),
eq(packageName)); eq(packageName), eq(operationId));
} }
@Test @Test

View File

@@ -24,6 +24,7 @@ import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
import static android.hardware.biometrics.BiometricManager.Authenticators; import static android.hardware.biometrics.BiometricManager.Authenticators;
import android.annotation.IntDef; import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.IActivityManager; import android.app.IActivityManager;
import android.app.UserSwitchObserver; import android.app.UserSwitchObserver;
@@ -296,7 +297,7 @@ public class BiometricService extends SystemService {
} }
case MSG_ON_DISMISSED: { case MSG_ON_DISMISSED: {
handleOnDismissed(msg.arg1); handleOnDismissed(msg.arg1, (byte[]) msg.obj);
break; break;
} }
@@ -611,8 +612,12 @@ public class BiometricService extends SystemService {
} }
@Override @Override
public void onDialogDismissed(int reason) throws RemoteException { public void onDialogDismissed(int reason, @Nullable byte[] credentialAttestation)
mHandler.obtainMessage(MSG_ON_DISMISSED, reason, 0 /* arg2 */).sendToTarget(); throws RemoteException {
mHandler.obtainMessage(MSG_ON_DISMISSED,
reason,
0 /* arg2 */,
credentialAttestation /* obj */).sendToTarget();
} }
@Override @Override
@@ -1422,7 +1427,8 @@ public class BiometricService extends SystemService {
0 /* biometricModality */, 0 /* biometricModality */,
false /* requireConfirmation */, false /* requireConfirmation */,
mCurrentAuthSession.mUserId, mCurrentAuthSession.mUserId,
mCurrentAuthSession.mOpPackageName); mCurrentAuthSession.mOpPackageName,
mCurrentAuthSession.mSessionId);
} else { } else {
mPendingAuthSession.mClientReceiver.onError(modality, error, vendorCode); mPendingAuthSession.mClientReceiver.onError(modality, error, vendorCode);
mPendingAuthSession = null; mPendingAuthSession = null;
@@ -1458,7 +1464,7 @@ public class BiometricService extends SystemService {
} }
} }
private void handleOnDismissed(int reason) { private void handleOnDismissed(int reason, @Nullable byte[] credentialAttestation) {
if (mCurrentAuthSession == null) { if (mCurrentAuthSession == null) {
Slog.e(TAG, "onDismissed: " + reason + ", auth session null"); Slog.e(TAG, "onDismissed: " + reason + ", auth session null");
return; return;
@@ -1469,6 +1475,7 @@ public class BiometricService extends SystemService {
try { try {
switch (reason) { switch (reason) {
case BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED: case BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED:
mKeyStore.addAuthToken(credentialAttestation);
case BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED: case BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED:
case BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED: case BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED:
if (mCurrentAuthSession.mTokenEscrow != null) { if (mCurrentAuthSession.mTokenEscrow != null) {
@@ -1616,7 +1623,8 @@ public class BiometricService extends SystemService {
try { try {
mStatusBarService.showAuthenticationDialog(mCurrentAuthSession.mBundle, mStatusBarService.showAuthenticationDialog(mCurrentAuthSession.mBundle,
mInternalReceiver, modality, requireConfirmation, userId, mInternalReceiver, modality, requireConfirmation, userId,
mCurrentAuthSession.mOpPackageName); mCurrentAuthSession.mOpPackageName,
mCurrentAuthSession.mSessionId);
} catch (RemoteException e) { } catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e); Slog.e(TAG, "Remote exception", e);
} }
@@ -1701,7 +1709,8 @@ public class BiometricService extends SystemService {
0 /* biometricModality */, 0 /* biometricModality */,
false /* requireConfirmation */, false /* requireConfirmation */,
mCurrentAuthSession.mUserId, mCurrentAuthSession.mUserId,
mCurrentAuthSession.mOpPackageName); mCurrentAuthSession.mOpPackageName,
sessionId);
} else { } else {
mPendingAuthSession.mState = STATE_AUTH_CALLED; mPendingAuthSession.mState = STATE_AUTH_CALLED;
for (AuthenticatorWrapper authenticator : mAuthenticators) { for (AuthenticatorWrapper authenticator : mAuthenticators) {

View File

@@ -664,12 +664,13 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
@Override @Override
public void showAuthenticationDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver, public void showAuthenticationDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
int biometricModality, boolean requireConfirmation, int userId, String opPackageName) { int biometricModality, boolean requireConfirmation, int userId, String opPackageName,
long operationId) {
enforceBiometricDialog(); enforceBiometricDialog();
if (mBar != null) { if (mBar != null) {
try { try {
mBar.showAuthenticationDialog(bundle, receiver, biometricModality, mBar.showAuthenticationDialog(bundle, receiver, biometricModality,
requireConfirmation, userId, opPackageName); requireConfirmation, userId, opPackageName, operationId);
} catch (RemoteException ex) { } catch (RemoteException ex) {
} }
} }

View File

@@ -182,7 +182,8 @@ public class BiometricServiceTest {
eq(0), eq(0),
anyBoolean() /* requireConfirmation */, anyBoolean() /* requireConfirmation */,
anyInt() /* userId */, anyInt() /* userId */,
eq(TEST_PACKAGE_NAME)); eq(TEST_PACKAGE_NAME),
anyLong() /* sessionId */);
} }
@Test @Test
@@ -264,7 +265,8 @@ public class BiometricServiceTest {
eq(BiometricAuthenticator.TYPE_FACE), eq(BiometricAuthenticator.TYPE_FACE),
eq(false) /* requireConfirmation */, eq(false) /* requireConfirmation */,
anyInt() /* userId */, anyInt() /* userId */,
eq(TEST_PACKAGE_NAME)); eq(TEST_PACKAGE_NAME),
anyLong() /* sessionId */);
} }
@Test @Test
@@ -391,7 +393,8 @@ public class BiometricServiceTest {
eq(BiometricAuthenticator.TYPE_FINGERPRINT), eq(BiometricAuthenticator.TYPE_FINGERPRINT),
anyBoolean() /* requireConfirmation */, anyBoolean() /* requireConfirmation */,
anyInt() /* userId */, anyInt() /* userId */,
eq(TEST_PACKAGE_NAME)); eq(TEST_PACKAGE_NAME),
anyLong() /* sessionId */);
// Hardware authenticated // Hardware authenticated
mBiometricService.mInternalReceiver.onAuthenticationSucceeded( mBiometricService.mInternalReceiver.onAuthenticationSucceeded(
@@ -406,7 +409,8 @@ public class BiometricServiceTest {
// SystemUI sends callback with dismissed reason // SystemUI sends callback with dismissed reason
mBiometricService.mInternalReceiver.onDialogDismissed( mBiometricService.mInternalReceiver.onDialogDismissed(
BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED); BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED,
null /* credentialAttestation */);
waitForIdle(); waitForIdle();
// HAT sent to keystore // HAT sent to keystore
verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class)); verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class));
@@ -438,7 +442,8 @@ public class BiometricServiceTest {
eq(0 /* biometricModality */), eq(0 /* biometricModality */),
anyBoolean() /* requireConfirmation */, anyBoolean() /* requireConfirmation */,
anyInt() /* userId */, anyInt() /* userId */,
eq(TEST_PACKAGE_NAME)); eq(TEST_PACKAGE_NAME),
anyLong() /* sessionId */);
} }
@Test @Test
@@ -460,7 +465,8 @@ public class BiometricServiceTest {
// SystemUI sends confirm, HAT is sent to keystore and client is notified. // SystemUI sends confirm, HAT is sent to keystore and client is notified.
mBiometricService.mInternalReceiver.onDialogDismissed( mBiometricService.mInternalReceiver.onDialogDismissed(
BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED); BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED,
null /* credentialAttestation */);
waitForIdle(); waitForIdle();
verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class)); verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class));
verify(mReceiver1).onAuthenticationSucceeded( verify(mReceiver1).onAuthenticationSucceeded(
@@ -567,7 +573,8 @@ public class BiometricServiceTest {
anyInt(), anyInt(),
anyBoolean() /* requireConfirmation */, anyBoolean() /* requireConfirmation */,
anyInt() /* userId */, anyInt() /* userId */,
anyString()); anyString(),
anyLong() /* sessionId */);
} }
@Test @Test
@@ -627,8 +634,8 @@ public class BiometricServiceTest {
verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt()); verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt());
// SystemUI animation completed, client is notified, auth session is over // SystemUI animation completed, client is notified, auth session is over
mBiometricService.mInternalReceiver mBiometricService.mInternalReceiver.onDialogDismissed(
.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_ERROR); BiometricPrompt.DISMISSED_REASON_ERROR, null /* credentialAttestation */);
waitForIdle(); waitForIdle();
verify(mReceiver1).onError( verify(mReceiver1).onError(
eq(BiometricAuthenticator.TYPE_FINGERPRINT), eq(BiometricAuthenticator.TYPE_FINGERPRINT),
@@ -667,7 +674,8 @@ public class BiometricServiceTest {
eq(0 /* biometricModality */), eq(0 /* biometricModality */),
anyBoolean() /* requireConfirmation */, anyBoolean() /* requireConfirmation */,
anyInt() /* userId */, anyInt() /* userId */,
eq(TEST_PACKAGE_NAME)); eq(TEST_PACKAGE_NAME),
anyLong() /* sessionId */);
} }
@Test @Test
@@ -825,8 +833,8 @@ public class BiometricServiceTest {
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1, invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
false /* requireConfirmation */, null /* authenticators */); false /* requireConfirmation */, null /* authenticators */);
mBiometricService.mInternalReceiver mBiometricService.mInternalReceiver.onDialogDismissed(
.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL); BiometricPrompt.DISMISSED_REASON_USER_CANCEL, null /* credentialAttestation */);
waitForIdle(); waitForIdle();
verify(mReceiver1).onError( verify(mReceiver1).onError(
eq(BiometricAuthenticator.TYPE_FINGERPRINT), eq(BiometricAuthenticator.TYPE_FINGERPRINT),
@@ -854,7 +862,7 @@ public class BiometricServiceTest {
BiometricConstants.BIOMETRIC_ERROR_TIMEOUT, BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
0 /* vendorCode */); 0 /* vendorCode */);
mBiometricService.mInternalReceiver.onDialogDismissed( mBiometricService.mInternalReceiver.onDialogDismissed(
BiometricPrompt.DISMISSED_REASON_NEGATIVE); BiometricPrompt.DISMISSED_REASON_NEGATIVE, null /* credentialAttestation */);
waitForIdle(); waitForIdle();
verify(mBiometricService.mAuthenticators.get(0).impl, verify(mBiometricService.mAuthenticators.get(0).impl,
@@ -880,7 +888,7 @@ public class BiometricServiceTest {
BiometricConstants.BIOMETRIC_ERROR_TIMEOUT, BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
0 /* vendorCode */); 0 /* vendorCode */);
mBiometricService.mInternalReceiver.onDialogDismissed( mBiometricService.mInternalReceiver.onDialogDismissed(
BiometricPrompt.DISMISSED_REASON_USER_CANCEL); BiometricPrompt.DISMISSED_REASON_USER_CANCEL, null /* credentialAttestation */);
waitForIdle(); waitForIdle();
verify(mBiometricService.mAuthenticators.get(0).impl, verify(mBiometricService.mAuthenticators.get(0).impl,
@@ -903,7 +911,7 @@ public class BiometricServiceTest {
true /* requireConfirmation */, true /* requireConfirmation */,
new byte[69] /* HAT */); new byte[69] /* HAT */);
mBiometricService.mInternalReceiver.onDialogDismissed( mBiometricService.mInternalReceiver.onDialogDismissed(
BiometricPrompt.DISMISSED_REASON_USER_CANCEL); BiometricPrompt.DISMISSED_REASON_USER_CANCEL, null /* credentialAttestation */);
waitForIdle(); waitForIdle();
// doesn't send cancel to HAL // doesn't send cancel to HAL
@@ -1160,7 +1168,8 @@ public class BiometricServiceTest {
eq(BiometricAuthenticator.TYPE_FINGERPRINT /* biometricModality */), eq(BiometricAuthenticator.TYPE_FINGERPRINT /* biometricModality */),
anyBoolean() /* requireConfirmation */, anyBoolean() /* requireConfirmation */,
anyInt() /* userId */, anyInt() /* userId */,
eq(TEST_PACKAGE_NAME)); eq(TEST_PACKAGE_NAME),
anyLong() /* sessionId */);
// Requesting strong and credential, when credential is setup // Requesting strong and credential, when credential is setup
resetReceiver(); resetReceiver();
@@ -1179,7 +1188,8 @@ public class BiometricServiceTest {
eq(BiometricAuthenticator.TYPE_NONE /* biometricModality */), eq(BiometricAuthenticator.TYPE_NONE /* biometricModality */),
anyBoolean() /* requireConfirmation */, anyBoolean() /* requireConfirmation */,
anyInt() /* userId */, anyInt() /* userId */,
eq(TEST_PACKAGE_NAME)); eq(TEST_PACKAGE_NAME),
anyLong() /* sessionId */);
// Un-downgrading the authenticator allows successful strong auth // Un-downgrading the authenticator allows successful strong auth
for (BiometricService.AuthenticatorWrapper wrapper : mBiometricService.mAuthenticators) { for (BiometricService.AuthenticatorWrapper wrapper : mBiometricService.mAuthenticators) {
@@ -1201,7 +1211,8 @@ public class BiometricServiceTest {
eq(BiometricAuthenticator.TYPE_FINGERPRINT /* biometricModality */), eq(BiometricAuthenticator.TYPE_FINGERPRINT /* biometricModality */),
anyBoolean() /* requireConfirmation */, anyBoolean() /* requireConfirmation */,
anyInt() /* userId */, anyInt() /* userId */,
eq(TEST_PACKAGE_NAME)); eq(TEST_PACKAGE_NAME),
anyLong() /* sessionId */);
} }
@Test(expected = IllegalStateException.class) @Test(expected = IllegalStateException.class)