Added the view dismissed reason due to confirm button pressing

Bug: 280321186
Test: atest CtsBiometricsHostTestCases on devices with nonConvinence face sensor
Change-Id: I5d2669b0f234f3707085369d253fab5868b4218f
This commit is contained in:
Jeff Pu
2023-05-16 14:09:46 +00:00
parent 92865c8372
commit 89436e692a
5 changed files with 157 additions and 23 deletions

View File

@@ -109,6 +109,7 @@ public abstract class AuthBiometricView extends LinearLayout implements AuthBiom
int ACTION_ERROR = 5;
int ACTION_USE_DEVICE_CREDENTIAL = 6;
int ACTION_START_DELAYED_FINGERPRINT_SENSOR = 7;
int ACTION_AUTHENTICATED_AND_CONFIRMED = 8;
/**
* When an action has occurred. The caller will only invoke this when the callback should
@@ -509,7 +510,8 @@ public abstract class AuthBiometricView extends LinearLayout implements AuthBiom
}
public void updateState(@BiometricState int newState) {
Log.v(TAG, "newState: " + newState);
Log.d(TAG, "newState: " + newState);
mIconController.updateState(mState, newState);
switch (newState) {
@@ -533,8 +535,14 @@ public abstract class AuthBiometricView extends LinearLayout implements AuthBiom
}
announceForAccessibility(getResources()
.getString(R.string.biometric_dialog_authenticated));
mHandler.postDelayed(() -> mCallback.onAction(Callback.ACTION_AUTHENTICATED),
getDelayAfterAuthenticatedDurationMs());
if (mState == STATE_PENDING_CONFIRMATION) {
mHandler.postDelayed(() -> mCallback.onAction(
Callback.ACTION_AUTHENTICATED_AND_CONFIRMED),
getDelayAfterAuthenticatedDurationMs());
} else {
mHandler.postDelayed(() -> mCallback.onAction(Callback.ACTION_AUTHENTICATED),
getDelayAfterAuthenticatedDurationMs());
}
break;
case STATE_PENDING_CONFIRMATION:

View File

@@ -213,6 +213,9 @@ public class AuthContainerView extends LinearLayout
case AuthBiometricView.Callback.ACTION_START_DELAYED_FINGERPRINT_SENSOR:
mConfig.mCallback.onStartFingerprintNow(getRequestId());
break;
case AuthBiometricView.Callback.ACTION_AUTHENTICATED_AND_CONFIRMED:
animateAway(AuthDialogCallback.DISMISSED_BUTTON_POSITIVE);
break;
default:
Log.e(TAG, "Unhandled action: " + action);
}

View File

@@ -99,7 +99,7 @@ class AuthBiometricFingerprintAndFaceViewTest : SysuiTestCase() {
waitForIdleSync()
assertThat(biometricView.isAuthenticated).isTrue()
verify(callback).onAction(AuthBiometricView.Callback.ACTION_AUTHENTICATED)
verify(callback).onAction(AuthBiometricView.Callback.ACTION_AUTHENTICATED_AND_CONFIRMED)
}
@Test

View File

@@ -76,7 +76,7 @@ import java.util.function.Function;
*/
public final class AuthSession implements IBinder.DeathRecipient {
private static final String TAG = "BiometricService/AuthSession";
private static final boolean DEBUG = false;
private static final boolean DEBUG = true;
/*
* Defined in biometrics.proto
@@ -118,6 +118,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
@VisibleForTesting final IBinder mToken;
// Info to be shown on BiometricDialog when all cookies are returned.
@VisibleForTesting final PromptInfo mPromptInfo;
@VisibleForTesting final BiometricFrameworkStatsLogger mBiometricFrameworkStatsLogger;
private final long mRequestId;
private final long mOperationId;
private final int mUserId;
@@ -164,6 +165,32 @@ public final class AuthSession implements IBinder.DeathRecipient {
@NonNull PromptInfo promptInfo,
boolean debugEnabled,
@NonNull List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties) {
this(context, biometricContext, statusBarService, sysuiReceiver, keystore, random,
clientDeathReceiver, preAuthInfo, token, requestId, operationId, userId,
sensorReceiver, clientReceiver, opPackageName, promptInfo, debugEnabled,
fingerprintSensorProperties, BiometricFrameworkStatsLogger.getInstance());
}
@VisibleForTesting
AuthSession(@NonNull Context context,
@NonNull BiometricContext biometricContext,
@NonNull IStatusBarService statusBarService,
@NonNull IBiometricSysuiReceiver sysuiReceiver,
@NonNull KeyStore keystore,
@NonNull Random random,
@NonNull ClientDeathReceiver clientDeathReceiver,
@NonNull PreAuthInfo preAuthInfo,
@NonNull IBinder token,
long requestId,
long operationId,
int userId,
@NonNull IBiometricSensorReceiver sensorReceiver,
@NonNull IBiometricServiceReceiver clientReceiver,
@NonNull String opPackageName,
@NonNull PromptInfo promptInfo,
boolean debugEnabled,
@NonNull List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties,
@NonNull BiometricFrameworkStatsLogger logger) {
Slog.d(TAG, "Creating AuthSession with: " + preAuthInfo);
mContext = context;
mBiometricContext = biometricContext;
@@ -184,6 +211,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
mDebugEnabled = debugEnabled;
mFingerprintSensorProperties = fingerprintSensorProperties;
mCancelled = false;
mBiometricFrameworkStatsLogger = logger;
try {
mClientReceiver.asBinder().linkToDeath(this, 0 /* flags */);
@@ -708,7 +736,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
+ ", Latency: " + latency);
}
BiometricFrameworkStatsLogger.getInstance().authenticate(
mBiometricFrameworkStatsLogger.authenticate(
mBiometricContext.updateContext(new OperationContextExt(true /* isBP */),
isCrypto()),
statsModality(),
@@ -723,11 +751,17 @@ public final class AuthSession implements IBinder.DeathRecipient {
} else {
final long latency = System.currentTimeMillis() - mStartTimeMs;
int error = reason == BiometricPrompt.DISMISSED_REASON_NEGATIVE
? BiometricConstants.BIOMETRIC_ERROR_NEGATIVE_BUTTON
: reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL
? BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED
: 0;
int error = 0;
switch(reason) {
case BiometricPrompt.DISMISSED_REASON_NEGATIVE:
error = BiometricConstants.BIOMETRIC_ERROR_NEGATIVE_BUTTON;
break;
case BiometricPrompt.DISMISSED_REASON_USER_CANCEL:
error = BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED;
break;
default:
}
if (DEBUG) {
Slog.v(TAG, "Dismissed! Modality: " + statsModality()
+ ", User: " + mUserId
@@ -739,17 +773,19 @@ public final class AuthSession implements IBinder.DeathRecipient {
+ ", Latency: " + latency);
}
// Auth canceled
BiometricFrameworkStatsLogger.getInstance().error(
mBiometricContext.updateContext(new OperationContextExt(true /* isBP */),
isCrypto()),
statsModality(),
BiometricsProtoEnums.ACTION_AUTHENTICATE,
BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
mDebugEnabled,
latency,
error,
0 /* vendorCode */,
mUserId);
if (error != 0) {
mBiometricFrameworkStatsLogger.error(
mBiometricContext.updateContext(new OperationContextExt(true /* isBP */),
isCrypto()),
statsModality(),
BiometricsProtoEnums.ACTION_AUTHENTICATE,
BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
mDebugEnabled,
latency,
error,
0 /* vendorCode */,
mUserId);
}
}
}

View File

@@ -18,6 +18,9 @@ package com.android.server.biometrics;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_NEGATIVE_BUTTON;
import static android.hardware.biometrics.BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED;
import static android.hardware.biometrics.BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED;
import static android.hardware.biometrics.BiometricPrompt.DISMISSED_REASON_NEGATIVE;
import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_CALLED;
@@ -32,6 +35,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyObject;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -45,6 +49,7 @@ import android.app.trust.ITrustManager;
import android.content.Context;
import android.content.res.Resources;
import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricSensorReceiver;
@@ -64,7 +69,10 @@ import android.security.KeyStore;
import androidx.test.filters.SmallTest;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricFrameworkStatsLogger;
import com.android.server.biometrics.log.OperationContextExt;
import org.junit.Before;
import org.junit.Test;
@@ -95,6 +103,7 @@ public class AuthSessionTest {
@Mock private IBiometricSysuiReceiver mSysuiReceiver;
@Mock private KeyStore mKeyStore;
@Mock private AuthSession.ClientDeathReceiver mClientDeathReceiver;
@Mock private BiometricFrameworkStatsLogger mBiometricFrameworkStatsLogger;
private Random mRandom;
private IBinder mToken;
@@ -395,6 +404,84 @@ public class AuthSessionTest {
eq(FingerprintManager.FINGERPRINT_ACQUIRED_VENDOR_BASE), eq(acquiredStrVendor));
}
@Test
public void testLogOnDialogDismissed_authenticatedWithConfirmation() throws RemoteException {
final IBiometricAuthenticator faceAuthenticator = mock(IBiometricAuthenticator.class);
setupFace(0 /* id */, false /* confirmationAlwaysRequired */, faceAuthenticator);
final AuthSession session = createAuthSession(mSensors,
false /* checkDevicePolicyManager */,
Authenticators.BIOMETRIC_STRONG,
TEST_REQUEST_ID,
0 /* operationId */,
0 /* userId */);
session.goToInitialState();
assertEquals(STATE_AUTH_CALLED, session.getState());
session.onDialogDismissed(DISMISSED_REASON_BIOMETRIC_CONFIRMED, null);
verify(mBiometricFrameworkStatsLogger, times(1)).authenticate(
(OperationContextExt) anyObject(),
eq(BiometricsProtoEnums.MODALITY_FACE),
eq(BiometricsProtoEnums.ACTION_UNKNOWN),
eq(BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT),
eq(false), /* debugEnabled */
anyLong(), /* latency */
eq(FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED),
eq(true), /* confirmationRequired */
eq(0) /* userId */,
eq(-1f) /* ambientLightLux */);
}
@Test
public void testLogOnDialogDismissed_authenticatedWithoutConfirmation() throws RemoteException {
final IBiometricAuthenticator faceAuthenticator = mock(IBiometricAuthenticator.class);
setupFace(0 /* id */, false /* confirmationAlwaysRequired */, faceAuthenticator);
final AuthSession session = createAuthSession(mSensors,
false /* checkDevicePolicyManager */,
Authenticators.BIOMETRIC_STRONG,
TEST_REQUEST_ID,
0 /* operationId */,
0 /* userId */);
session.goToInitialState();
assertEquals(STATE_AUTH_CALLED, session.getState());
session.onDialogDismissed(DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED, null);
verify(mBiometricFrameworkStatsLogger, never()).authenticate(
anyObject(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyLong(), anyInt(),
anyBoolean(), anyInt(), eq(-1f));
verify(mBiometricFrameworkStatsLogger, never()).error(
anyObject(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyLong(), anyInt(),
anyInt(), anyInt());
}
@Test
public void testLogOnDialogDismissed_error() throws RemoteException {
final IBiometricAuthenticator faceAuthenticator = mock(IBiometricAuthenticator.class);
setupFace(0 /* id */, false /* confirmationAlwaysRequired */, faceAuthenticator);
final AuthSession session = createAuthSession(mSensors,
false /* checkDevicePolicyManager */,
Authenticators.BIOMETRIC_STRONG,
TEST_REQUEST_ID,
0 /* operationId */,
0 /* userId */);
session.goToInitialState();
assertEquals(STATE_AUTH_CALLED, session.getState());
session.onDialogDismissed(DISMISSED_REASON_NEGATIVE, null);
verify(mBiometricFrameworkStatsLogger, times(1)).error(
(OperationContextExt) anyObject(),
eq(BiometricsProtoEnums.MODALITY_FACE),
eq(BiometricsProtoEnums.ACTION_AUTHENTICATE),
eq(BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT),
eq(false),
anyLong(),
eq(BIOMETRIC_ERROR_NEGATIVE_BUTTON),
eq(0) /* vendorCode */,
eq(0) /* userId */);
}
// TODO (b/208484275) : Enable these tests
// @Test
// public void testPreAuth_canAuthAndPrivacyDisabled() throws Exception {
@@ -498,7 +585,7 @@ public class AuthSessionTest {
return new AuthSession(mContext, mBiometricContext, mStatusBarService, mSysuiReceiver,
mKeyStore, mRandom, mClientDeathReceiver, preAuthInfo, mToken, requestId,
operationId, userId, mSensorReceiver, mClientReceiver, TEST_PACKAGE, promptInfo,
false /* debugEnabled */, mFingerprintSensorProps);
false /* debugEnabled */, mFingerprintSensorProps, mBiometricFrameworkStatsLogger);
}
private PromptInfo createPromptInfo(@Authenticators.Types int authenticators) {