Pass operationId to LSS, add HAT to KeyStore
Since we're now allowing auth-per-use credential keys, we need to
pass the operationId to SystemUI, which owns the call to verify
credential. Upon receiving a non-null HAT, it should be sent to
KeyStore.
Bug: 148425329
Test: atest com.android.systemui.biometrics
Test: atest com.android.server.biometrics
Change-Id: Iea737bf2dc0d81d87419df96d5cb43d51f10c6e3
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index cbb1982..0018d33 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -99,6 +99,8 @@
// Non-null only if the dialog is in the act of dismissing and has not sent the reason yet.
@Nullable @AuthDialogCallback.DismissedReason Integer mPendingCallbackReason;
+ // HAT received from LockSettingsService when credential is verified.
+ @Nullable byte[] mCredentialAttestation;
static class Config {
Context mContext;
@@ -109,6 +111,7 @@
String mOpPackageName;
int mModalityMask;
boolean mSkipIntro;
+ long mOperationId;
}
public static class Builder {
@@ -149,6 +152,11 @@
return this;
}
+ public Builder setOperationId(long operationId) {
+ mConfig.mOperationId = operationId;
+ return this;
+ }
+
public AuthContainerView build(int modalityMask) {
mConfig.mModalityMask = modalityMask;
return new AuthContainerView(mConfig, new Injector());
@@ -224,7 +232,8 @@
final class CredentialCallback implements AuthCredentialView.Callback {
@Override
- public void onCredentialMatched() {
+ public void onCredentialMatched(byte[] attestation) {
+ mCredentialAttestation = attestation;
animateAway(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED);
}
}
@@ -341,6 +350,7 @@
mCredentialView.setContainerView(this);
mCredentialView.setUserId(mConfig.mUserId);
+ mCredentialView.setOperationId(mConfig.mOperationId);
mCredentialView.setEffectiveUserId(mEffectiveUserId);
mCredentialView.setCredentialType(credentialType);
mCredentialView.setCallback(mCredentialCallback);
@@ -558,7 +568,7 @@
private void sendPendingCallbackIfNotNull() {
Log.d(TAG, "pendingCallback: " + mPendingCallbackReason);
if (mPendingCallbackReason != null) {
- mConfig.mCallback.onDismissed(mPendingCallbackReason);
+ mConfig.mCallback.onDismissed(mPendingCallbackReason, mCredentialAttestation);
mPendingCallbackReason = null;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 6149b0b..c30477c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -20,6 +20,7 @@
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricManager.Authenticators;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
@@ -99,7 +100,8 @@
try {
if (mReceiver != null) {
- mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+ mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL,
+ null /* credentialAttestation */);
mReceiver = null;
}
} catch (RemoteException e) {
@@ -124,7 +126,8 @@
mCurrentDialog = null;
if (mReceiver != null) {
mReceiver.onDialogDismissed(
- BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+ BiometricPrompt.DISMISSED_REASON_USER_CANCEL,
+ null /* credentialAttestation */);
mReceiver = null;
}
}
@@ -162,35 +165,42 @@
}
@Override
- public void onDismissed(@DismissedReason int reason) {
+ public void onDismissed(@DismissedReason int reason, @Nullable byte[] credentialAttestation) {
switch (reason) {
case AuthDialogCallback.DISMISSED_USER_CANCELED:
- sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+ sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_USER_CANCEL,
+ credentialAttestation);
break;
case AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE:
- sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_NEGATIVE);
+ sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_NEGATIVE,
+ credentialAttestation);
break;
case AuthDialogCallback.DISMISSED_BUTTON_POSITIVE:
- sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED);
+ sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED,
+ credentialAttestation);
break;
case AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED:
sendResultAndCleanUp(
- BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED);
+ BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED,
+ credentialAttestation);
break;
case AuthDialogCallback.DISMISSED_ERROR:
- sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_ERROR);
+ sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_ERROR,
+ credentialAttestation);
break;
case AuthDialogCallback.DISMISSED_BY_SYSTEM_SERVER:
- sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED);
+ sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED,
+ credentialAttestation);
break;
case AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED:
- sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED);
+ sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED,
+ credentialAttestation);
break;
default:
@@ -199,13 +209,14 @@
}
}
- private void sendResultAndCleanUp(@DismissedReason int reason) {
+ private void sendResultAndCleanUp(@DismissedReason int reason,
+ @Nullable byte[] credentialAttestation) {
if (mReceiver == null) {
Log.e(TAG, "sendResultAndCleanUp: Receiver is null");
return;
}
try {
- mReceiver.onDialogDismissed(reason);
+ mReceiver.onDialogDismissed(reason, credentialAttestation);
} catch (RemoteException e) {
Log.w(TAG, "Remote exception", e);
}
@@ -251,13 +262,15 @@
@Override
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);
if (DEBUG) {
Log.d(TAG, "showAuthenticationDialog, authenticators: " + authenticators
+ ", biometricModality: " + biometricModality
- + ", requireConfirmation: " + requireConfirmation);
+ + ", requireConfirmation: " + requireConfirmation
+ + ", operationId: " + operationId);
}
SomeArgs args = SomeArgs.obtain();
args.arg1 = bundle;
@@ -266,6 +279,7 @@
args.arg3 = requireConfirmation;
args.argi2 = userId;
args.arg4 = opPackageName;
+ args.arg5 = operationId;
boolean skipAnimation = false;
if (mCurrentDialog != null) {
@@ -354,6 +368,7 @@
final boolean requireConfirmation = (boolean) args.arg3;
final int userId = args.argi2;
final String opPackageName = (String) args.arg4;
+ final long operationId = (long) args.arg5;
// Create a new dialog but do not replace the current one yet.
final AuthDialog newDialog = buildDialog(
@@ -362,7 +377,8 @@
userId,
type,
opPackageName,
- skipAnimation);
+ skipAnimation,
+ operationId);
if (newDialog == null) {
Log.e(TAG, "Unsupported type: " + type);
@@ -429,7 +445,7 @@
}
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)
.setCallback(this)
.setBiometricPromptBundle(biometricPromptBundle)
@@ -437,6 +453,7 @@
.setUserId(userId)
.setOpPackageName(opPackageName)
.setSkipIntro(skipIntro)
+ .setOperationId(operationId)
.build(type);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
index 82c8a46..b986f6c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
@@ -103,14 +103,16 @@
return;
}
- mPendingLockCheck = LockPatternChecker.checkCredential(mLockPatternUtils,
- password, mEffectiveUserId, this::onCredentialChecked);
+ mPendingLockCheck = LockPatternChecker.verifyCredential(mLockPatternUtils,
+ password, mOperationId, mEffectiveUserId, this::onCredentialVerified);
}
}
@Override
- protected void onCredentialChecked(boolean matched, int timeoutMs) {
- super.onCredentialChecked(matched, timeoutMs);
+ protected void onCredentialVerified(byte[] attestation, int timeoutMs) {
+ super.onCredentialVerified(attestation, timeoutMs);
+
+ final boolean matched = attestation != null;
if (matched) {
mImm.hideSoftInputFromWindow(getWindowToken(), 0 /* flags */);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
index 03136a4..6d16f43 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
@@ -61,21 +61,22 @@
if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
// 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;
}
try (LockscreenCredential credential = LockscreenCredential.createPattern(pattern)) {
- mPendingLockCheck = LockPatternChecker.checkCredential(
+ mPendingLockCheck = LockPatternChecker.verifyCredential(
mLockPatternUtils,
credential,
+ mOperationId,
mEffectiveUserId,
- this::onPatternChecked);
+ this::onPatternVerified);
}
}
- private void onPatternChecked(boolean matched, int timeoutMs) {
- AuthCredentialPatternView.this.onCredentialChecked(matched, timeoutMs);
+ private void onPatternVerified(byte[] attestation, int timeoutMs) {
+ AuthCredentialPatternView.this.onCredentialVerified(attestation, timeoutMs);
if (timeoutMs > 0) {
mLockPatternView.setEnabled(false);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
index 48c6621..0d9d426 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
@@ -42,7 +42,7 @@
/**
* 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 {
@@ -70,11 +70,12 @@
protected Callback mCallback;
protected AsyncTask<?, ?, ?> mPendingLockCheck;
protected int mUserId;
+ protected long mOperationId;
protected int mEffectiveUserId;
protected ErrorTimer mErrorTimer;
interface Callback {
- void onCredentialMatched();
+ void onCredentialMatched(byte[] attestation);
}
protected static class ErrorTimer extends CountDownTimer {
@@ -148,6 +149,10 @@
mUserId = userId;
}
+ void setOperationId(long operationId) {
+ mOperationId = operationId;
+ }
+
void setEffectiveUserId(int effectiveUserId) {
mEffectiveUserId = effectiveUserId;
}
@@ -245,10 +250,13 @@
protected void onErrorTimeoutFinish() {}
- protected void onCredentialChecked(boolean matched, int timeoutMs) {
+ protected void onCredentialVerified(byte[] attestation, int timeoutMs) {
+
+ final boolean matched = attestation != null;
+
if (matched) {
mClearErrorRunnable.run();
- mCallback.onCredentialMatched();
+ mCallback.onCredentialMatched(attestation);
} else {
if (timeoutMs > 0) {
mHandler.removeCallbacks(mClearErrorRunnable);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java
index 12bb122..a47621d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java
@@ -17,6 +17,7 @@
package com.android.systemui.biometrics;
import android.annotation.IntDef;
+import android.annotation.Nullable;
/**
* Callback interface for dialog views. These should be implemented by the controller (e.g.
@@ -44,8 +45,9 @@
/**
* Invoked when the dialog is dismissed
* @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
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 3fe348f..94afde78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -262,7 +262,8 @@
default void showAuthenticationDialog(Bundle bundle,
IBiometricServiceReceiverInternal receiver, int biometricModality,
- boolean requireConfirmation, int userId, String opPackageName) { }
+ boolean requireConfirmation, int userId, String opPackageName,
+ long operationId) { }
default void onBiometricAuthenticated() { }
default void onBiometricHelp(String message) { }
default void onBiometricError(int modality, int error, int vendorCode) { }
@@ -780,7 +781,8 @@
@Override
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) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = bundle;
@@ -789,6 +791,7 @@
args.arg3 = requireConfirmation;
args.argi2 = userId;
args.arg4 = opPackageName;
+ args.arg5 = operationId;
mHandler.obtainMessage(MSG_BIOMETRIC_SHOW, args)
.sendToTarget();
}
@@ -1164,7 +1167,8 @@
someArgs.argi1 /* biometricModality */,
(boolean) someArgs.arg3 /* requireConfirmation */,
someArgs.argi2 /* userId */,
- (String) someArgs.arg4 /* opPackageName */);
+ (String) someArgs.arg4 /* opPackageName */,
+ (long) someArgs.arg5 /* operationId */);
}
someArgs.recycle();
break;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
index c6c7b87..1db8e4c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
@@ -78,7 +78,9 @@
mAuthContainer.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_AUTHENTICATED);
- verify(mCallback).onDismissed(eq(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED));
+ verify(mCallback).onDismissed(
+ eq(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED),
+ eq(null) /* credentialAttestation */);
}
@Test
@@ -87,7 +89,9 @@
mAuthContainer.mBiometricCallback.onAction(
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
@@ -96,7 +100,9 @@
mAuthContainer.mBiometricCallback.onAction(
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
@@ -114,7 +120,9 @@
mAuthContainer.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_ERROR);
- verify(mCallback).onDismissed(AuthDialogCallback.DISMISSED_ERROR);
+ verify(mCallback).onDismissed(
+ eq(AuthDialogCallback.DISMISSED_ERROR),
+ eq(null) /* credentialAttestation */);
}
@Test
@@ -219,7 +227,8 @@
@Override
public void animateAway(int reason) {
- mConfig.mCallback.onDismissed(reason);
+ // TODO: Credential attestation should be testable/tested
+ mConfig.mCallback.onDismissed(reason, null /* credentialAttestation */);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 65399bf..fc1ddf7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -57,12 +57,14 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.AdditionalMatchers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
+import java.util.Random;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@@ -110,52 +112,75 @@
@Test
public void testSendsReasonUserCanceled_whenDismissedByUserCancel() throws Exception {
showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
- mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED);
- verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED,
+ null /* credentialAttestation */);
+ verify(mReceiver).onDialogDismissed(
+ eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL),
+ eq(null) /* credentialAttestation */);
}
@Test
public void testSendsReasonNegative_whenDismissedByButtonNegative() throws Exception {
showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
- mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE);
- verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_NEGATIVE);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE,
+ null /* credentialAttestation */);
+ verify(mReceiver).onDialogDismissed(
+ eq(BiometricPrompt.DISMISSED_REASON_NEGATIVE),
+ eq(null) /* credentialAttestation */);
}
@Test
public void testSendsReasonConfirmed_whenDismissedByButtonPositive() throws Exception {
showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
- mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_POSITIVE);
- verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_POSITIVE,
+ null /* credentialAttestation */);
+ verify(mReceiver).onDialogDismissed(
+ eq(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED),
+ eq(null) /* credentialAttestation */);
}
@Test
public void testSendsReasonConfirmNotRequired_whenDismissedByAuthenticated() throws Exception {
showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
- mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED,
+ null /* credentialAttestation */);
verify(mReceiver).onDialogDismissed(
- BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED);
+ eq(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED),
+ eq(null) /* credentialAttestation */);
}
@Test
public void testSendsReasonError_whenDismissedByError() throws Exception {
showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
- mAuthController.onDismissed(AuthDialogCallback.DISMISSED_ERROR);
- verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_ERROR);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_ERROR,
+ null /* credentialAttestation */);
+ verify(mReceiver).onDialogDismissed(
+ eq(BiometricPrompt.DISMISSED_REASON_ERROR),
+ eq(null) /* credentialAttestation */);
}
@Test
public void testSendsReasonServerRequested_whenDismissedByServer() throws Exception {
showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
- mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BY_SYSTEM_SERVER);
- verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BY_SYSTEM_SERVER,
+ null /* credentialAttestation */);
+ verify(mReceiver).onDialogDismissed(
+ eq(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED),
+ eq(null) /* credentialAttestation */);
}
@Test
public void testSendsReasonCredentialConfirmed_whenDeviceCredentialAuthenticated()
throws Exception {
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
@@ -302,8 +327,13 @@
showDialog(Authenticators.DEVICE_CREDENTIAL, BiometricPrompt.TYPE_NONE);
verify(mDialog1).show(any(), any());
- 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));
mAuthController.hideAuthenticationDialog();
}
@@ -395,20 +425,24 @@
assertNull(mAuthController.mCurrentDialog);
assertNull(mAuthController.mReceiver);
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
public void testDoesNotCrash_whenTryAgainPressedAfterDismissal() {
showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
- mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED,
+ null /* credentialAttestation */);
mAuthController.onTryAgainPressed();
}
@Test
public void testDoesNotCrash_whenDeviceCredentialPressedAfterDismissal() {
showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
- mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED,
+ null /* credentialAttestation */);
mAuthController.onDeviceCredentialPressed();
}
@@ -422,7 +456,9 @@
assertNull(mAuthController.mCurrentDialog);
assertNull(mAuthController.mReceiver);
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
@@ -433,7 +469,8 @@
biometricModality,
true /* requireConfirmation */,
0 /* userId */,
- "testPackage");
+ "testPackage",
+ 0 /* operationId */);
}
private Bundle createTestDialogBundle(int authenticators) {
@@ -453,6 +490,13 @@
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 int mBuildCount = 0;
private Bundle mLastBiometricPromptBundle;
@@ -464,7 +508,7 @@
@Override
protected AuthDialog buildDialog(Bundle biometricPromptBundle,
boolean requireConfirmation, int userId, int type, String opPackageName,
- boolean skipIntro) {
+ boolean skipIntro, long operationId) {
mLastBiometricPromptBundle = biometricPromptBundle;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 7c6da63..cffcabb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -409,11 +409,12 @@
public void testShowAuthenticationDialog() {
Bundle bundle = new Bundle();
String packageName = "test";
+ final long operationId = 1;
mCommandQueue.showAuthenticationDialog(bundle, null /* receiver */, 1, true, 3,
- packageName);
+ packageName, operationId);
waitForIdleSync();
verify(mCallbacks).showAuthenticationDialog(eq(bundle), eq(null), eq(1), eq(true), eq(3),
- eq(packageName));
+ eq(packageName), eq(operationId));
}
@Test