Merge "Pass operationId to LSS, add HAT to KeyStore" into rvc-dev am: 6b7b2300c4
Change-Id: I7eead7a89a6d9b286050f0438be0c481c32bdcc9
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 */);
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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 */);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user