diff options
author | 2020-03-13 23:53:16 +0000 | |
---|---|---|
committer | 2020-03-13 23:53:16 +0000 | |
commit | 6b7b2300c4154486cbc3bd6a2d80a68c49bf231e (patch) | |
tree | 3c953d63423538c197a89af649c2de2195089b01 | |
parent | e55493727d91d42ab51694c3930aa45d4eecf6a7 (diff) | |
parent | c8cb6852dfff210a622ec9db09d5a9df32a1dbe7 (diff) |
Merge "Pass operationId to LSS, add HAT to KeyStore" into rvc-dev
16 files changed, 215 insertions, 94 deletions
diff --git a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl index 61310f302fe4..8bcaf828be97 100644 --- a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl +++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl @@ -35,7 +35,7 @@ oneway interface IBiometricServiceReceiverInternal { // Notifies that a biometric has been acquired. void onAcquired(int acquiredInfo, String message); // 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 void onTryAgainPressed(); // Notifies that the user has pressed the "use password" button on SystemUI diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index 380a20c88d56..5b79b184b6a4 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -136,7 +136,8 @@ oneway interface IStatusBar // Used to show the authentication dialog (Biometrics, Device Credential) 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 void onBiometricAuthenticated(); // Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index 9907b9975afe..1dbd69c67831 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -105,7 +105,8 @@ interface IStatusBarService // Used to show the authentication dialog (Biometrics, Device Credential) 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 void onBiometricAuthenticated(); // Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index cbb19824eadf..0018d33bdacb 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 @@ public class AuthContainerView extends LinearLayout // 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 @@ public class AuthContainerView extends LinearLayout String mOpPackageName; int mModalityMask; boolean mSkipIntro; + long mOperationId; } public static class Builder { @@ -149,6 +152,11 @@ public class AuthContainerView extends LinearLayout 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 @@ public class AuthContainerView extends LinearLayout 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 @@ public class AuthContainerView extends LinearLayout mCredentialView.setContainerView(this); mCredentialView.setUserId(mConfig.mUserId); + mCredentialView.setOperationId(mConfig.mOperationId); mCredentialView.setEffectiveUserId(mEffectiveUserId); mCredentialView.setCredentialType(credentialType); mCredentialView.setCallback(mCredentialCallback); @@ -558,7 +568,7 @@ public class AuthContainerView extends LinearLayout 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 6149b0b8bfd4..c30477c77bbb 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_FACE; 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 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, 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 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, 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 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, } @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 @@ 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) { 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 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, @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 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, args.arg3 = requireConfirmation; args.argi2 = userId; args.arg4 = opPackageName; + args.arg5 = operationId; boolean skipAnimation = false; if (mCurrentDialog != null) { @@ -354,6 +368,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, 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 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, userId, type, opPackageName, - skipAnimation); + skipAnimation, + operationId); if (newDialog == null) { 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, - 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 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, .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 82c8a469a057..b986f6c9e680 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 @@ public class AuthCredentialPasswordView extends AuthCredentialView 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 03136a4b6c0f..6d16f4397115 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 @@ public class AuthCredentialPatternView extends AuthCredentialView { 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 48c66215bdde..0d9d4269230f 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 @@ import com.android.systemui.R; /** * 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 @@ public abstract class AuthCredentialView extends LinearLayout { 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 @@ public abstract class AuthCredentialView extends LinearLayout { mUserId = userId; } + void setOperationId(long operationId) { + mOperationId = operationId; + } + void setEffectiveUserId(int effectiveUserId) { mEffectiveUserId = effectiveUserId; } @@ -245,10 +250,13 @@ public abstract class AuthCredentialView extends LinearLayout { 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 12bb1228a53b..a47621d12122 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 @@ public interface AuthDialogCallback { /** * 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 3fe348f3ea02..94afde786e78 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 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< 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 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< @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 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< args.arg3 = requireConfirmation; args.argi2 = userId; args.arg4 = opPackageName; + args.arg5 = operationId; mHandler.obtainMessage(MSG_BIOMETRIC_SHOW, args) .sendToTarget(); } @@ -1164,7 +1167,8 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< 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 c6c7b87da544..1db8e4c3d73e 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 @@ public class AuthContainerViewTest extends SysuiTestCase { 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 @@ public class AuthContainerViewTest extends SysuiTestCase { 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 @@ public class AuthContainerViewTest extends SysuiTestCase { 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 @@ public class AuthContainerViewTest extends SysuiTestCase { 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 @@ public class AuthContainerViewTest extends SysuiTestCase { @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 65399bfb901e..fc1ddf74a448 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 com.android.systemui.statusbar.CommandQueue; 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 @@ public class AuthControllerTest extends SysuiTestCase { @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 @@ public class AuthControllerTest extends SysuiTestCase { 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 @@ public class AuthControllerTest extends SysuiTestCase { 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 @@ public class AuthControllerTest extends SysuiTestCase { 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 @@ public class AuthControllerTest extends SysuiTestCase { biometricModality, true /* requireConfirmation */, 0 /* userId */, - "testPackage"); + "testPackage", + 0 /* operationId */); } private Bundle createTestDialogBundle(int authenticators) { @@ -453,6 +490,13 @@ public class AuthControllerTest extends SysuiTestCase { 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 @@ public class AuthControllerTest extends SysuiTestCase { @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 7c6da630b3bf..cffcabb55e14 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 class CommandQueueTest extends SysuiTestCase { 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 diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index ecdafb0a7a79..e7c09baf3aeb 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -24,6 +24,7 @@ import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE; import static android.hardware.biometrics.BiometricManager.Authenticators; import android.annotation.IntDef; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.IActivityManager; import android.app.UserSwitchObserver; @@ -296,7 +297,7 @@ public class BiometricService extends SystemService { } case MSG_ON_DISMISSED: { - handleOnDismissed(msg.arg1); + handleOnDismissed(msg.arg1, (byte[]) msg.obj); break; } @@ -611,8 +612,12 @@ public class BiometricService extends SystemService { } @Override - public void onDialogDismissed(int reason) throws RemoteException { - mHandler.obtainMessage(MSG_ON_DISMISSED, reason, 0 /* arg2 */).sendToTarget(); + public void onDialogDismissed(int reason, @Nullable byte[] credentialAttestation) + throws RemoteException { + mHandler.obtainMessage(MSG_ON_DISMISSED, + reason, + 0 /* arg2 */, + credentialAttestation /* obj */).sendToTarget(); } @Override @@ -1422,7 +1427,8 @@ public class BiometricService extends SystemService { 0 /* biometricModality */, false /* requireConfirmation */, mCurrentAuthSession.mUserId, - mCurrentAuthSession.mOpPackageName); + mCurrentAuthSession.mOpPackageName, + mCurrentAuthSession.mSessionId); } else { mPendingAuthSession.mClientReceiver.onError(modality, error, vendorCode); 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) { Slog.e(TAG, "onDismissed: " + reason + ", auth session null"); return; @@ -1469,6 +1475,7 @@ public class BiometricService extends SystemService { try { switch (reason) { case BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED: + mKeyStore.addAuthToken(credentialAttestation); case BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED: case BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED: if (mCurrentAuthSession.mTokenEscrow != null) { @@ -1616,7 +1623,8 @@ public class BiometricService extends SystemService { try { mStatusBarService.showAuthenticationDialog(mCurrentAuthSession.mBundle, mInternalReceiver, modality, requireConfirmation, userId, - mCurrentAuthSession.mOpPackageName); + mCurrentAuthSession.mOpPackageName, + mCurrentAuthSession.mSessionId); } catch (RemoteException e) { Slog.e(TAG, "Remote exception", e); } @@ -1701,7 +1709,8 @@ public class BiometricService extends SystemService { 0 /* biometricModality */, false /* requireConfirmation */, mCurrentAuthSession.mUserId, - mCurrentAuthSession.mOpPackageName); + mCurrentAuthSession.mOpPackageName, + sessionId); } else { mPendingAuthSession.mState = STATE_AUTH_CALLED; for (AuthenticatorWrapper authenticator : mAuthenticators) { diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 84cd4cfd67be..78ef68c09988 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -664,12 +664,13 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D @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) { enforceBiometricDialog(); if (mBar != null) { try { mBar.showAuthenticationDialog(bundle, receiver, biometricModality, - requireConfirmation, userId, opPackageName); + requireConfirmation, userId, opPackageName, operationId); } catch (RemoteException ex) { } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java index 164ee3184f5a..3ad905476190 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java @@ -182,7 +182,8 @@ public class BiometricServiceTest { eq(0), anyBoolean() /* requireConfirmation */, anyInt() /* userId */, - eq(TEST_PACKAGE_NAME)); + eq(TEST_PACKAGE_NAME), + anyLong() /* sessionId */); } @Test @@ -264,7 +265,8 @@ public class BiometricServiceTest { eq(BiometricAuthenticator.TYPE_FACE), eq(false) /* requireConfirmation */, anyInt() /* userId */, - eq(TEST_PACKAGE_NAME)); + eq(TEST_PACKAGE_NAME), + anyLong() /* sessionId */); } @Test @@ -391,7 +393,8 @@ public class BiometricServiceTest { eq(BiometricAuthenticator.TYPE_FINGERPRINT), anyBoolean() /* requireConfirmation */, anyInt() /* userId */, - eq(TEST_PACKAGE_NAME)); + eq(TEST_PACKAGE_NAME), + anyLong() /* sessionId */); // Hardware authenticated mBiometricService.mInternalReceiver.onAuthenticationSucceeded( @@ -406,7 +409,8 @@ public class BiometricServiceTest { // SystemUI sends callback with dismissed reason mBiometricService.mInternalReceiver.onDialogDismissed( - BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED); + BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED, + null /* credentialAttestation */); waitForIdle(); // HAT sent to keystore verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class)); @@ -438,7 +442,8 @@ public class BiometricServiceTest { eq(0 /* biometricModality */), anyBoolean() /* requireConfirmation */, anyInt() /* userId */, - eq(TEST_PACKAGE_NAME)); + eq(TEST_PACKAGE_NAME), + anyLong() /* sessionId */); } @Test @@ -460,7 +465,8 @@ public class BiometricServiceTest { // SystemUI sends confirm, HAT is sent to keystore and client is notified. mBiometricService.mInternalReceiver.onDialogDismissed( - BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED); + BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED, + null /* credentialAttestation */); waitForIdle(); verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class)); verify(mReceiver1).onAuthenticationSucceeded( @@ -567,7 +573,8 @@ public class BiometricServiceTest { anyInt(), anyBoolean() /* requireConfirmation */, anyInt() /* userId */, - anyString()); + anyString(), + anyLong() /* sessionId */); } @Test @@ -627,8 +634,8 @@ public class BiometricServiceTest { verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt()); // SystemUI animation completed, client is notified, auth session is over - mBiometricService.mInternalReceiver - .onDialogDismissed(BiometricPrompt.DISMISSED_REASON_ERROR); + mBiometricService.mInternalReceiver.onDialogDismissed( + BiometricPrompt.DISMISSED_REASON_ERROR, null /* credentialAttestation */); waitForIdle(); verify(mReceiver1).onError( eq(BiometricAuthenticator.TYPE_FINGERPRINT), @@ -667,7 +674,8 @@ public class BiometricServiceTest { eq(0 /* biometricModality */), anyBoolean() /* requireConfirmation */, anyInt() /* userId */, - eq(TEST_PACKAGE_NAME)); + eq(TEST_PACKAGE_NAME), + anyLong() /* sessionId */); } @Test @@ -825,8 +833,8 @@ public class BiometricServiceTest { invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, null /* authenticators */); - mBiometricService.mInternalReceiver - .onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL); + mBiometricService.mInternalReceiver.onDialogDismissed( + BiometricPrompt.DISMISSED_REASON_USER_CANCEL, null /* credentialAttestation */); waitForIdle(); verify(mReceiver1).onError( eq(BiometricAuthenticator.TYPE_FINGERPRINT), @@ -854,7 +862,7 @@ public class BiometricServiceTest { BiometricConstants.BIOMETRIC_ERROR_TIMEOUT, 0 /* vendorCode */); mBiometricService.mInternalReceiver.onDialogDismissed( - BiometricPrompt.DISMISSED_REASON_NEGATIVE); + BiometricPrompt.DISMISSED_REASON_NEGATIVE, null /* credentialAttestation */); waitForIdle(); verify(mBiometricService.mAuthenticators.get(0).impl, @@ -880,7 +888,7 @@ public class BiometricServiceTest { BiometricConstants.BIOMETRIC_ERROR_TIMEOUT, 0 /* vendorCode */); mBiometricService.mInternalReceiver.onDialogDismissed( - BiometricPrompt.DISMISSED_REASON_USER_CANCEL); + BiometricPrompt.DISMISSED_REASON_USER_CANCEL, null /* credentialAttestation */); waitForIdle(); verify(mBiometricService.mAuthenticators.get(0).impl, @@ -903,7 +911,7 @@ public class BiometricServiceTest { true /* requireConfirmation */, new byte[69] /* HAT */); mBiometricService.mInternalReceiver.onDialogDismissed( - BiometricPrompt.DISMISSED_REASON_USER_CANCEL); + BiometricPrompt.DISMISSED_REASON_USER_CANCEL, null /* credentialAttestation */); waitForIdle(); // doesn't send cancel to HAL @@ -1160,7 +1168,8 @@ public class BiometricServiceTest { eq(BiometricAuthenticator.TYPE_FINGERPRINT /* biometricModality */), anyBoolean() /* requireConfirmation */, anyInt() /* userId */, - eq(TEST_PACKAGE_NAME)); + eq(TEST_PACKAGE_NAME), + anyLong() /* sessionId */); // Requesting strong and credential, when credential is setup resetReceiver(); @@ -1179,7 +1188,8 @@ public class BiometricServiceTest { eq(BiometricAuthenticator.TYPE_NONE /* biometricModality */), anyBoolean() /* requireConfirmation */, anyInt() /* userId */, - eq(TEST_PACKAGE_NAME)); + eq(TEST_PACKAGE_NAME), + anyLong() /* sessionId */); // Un-downgrading the authenticator allows successful strong auth for (BiometricService.AuthenticatorWrapper wrapper : mBiometricService.mAuthenticators) { @@ -1201,7 +1211,8 @@ public class BiometricServiceTest { eq(BiometricAuthenticator.TYPE_FINGERPRINT /* biometricModality */), anyBoolean() /* requireConfirmation */, anyInt() /* userId */, - eq(TEST_PACKAGE_NAME)); + eq(TEST_PACKAGE_NAME), + anyLong() /* sessionId */); } @Test(expected = IllegalStateException.class) |