Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 89436e69 authored by Jeff Pu's avatar Jeff Pu
Browse files

Added the view dismissed reason due to confirm button pressing

Bug: 280321186
Test: atest CtsBiometricsHostTestCases on devices with nonConvinence face sensor
Change-Id: I5d2669b0f234f3707085369d253fab5868b4218f
parent 92865c83
Loading
Loading
Loading
Loading
+11 −3
Original line number Diff line number Diff line
@@ -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));
                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:
+3 −0
Original line number Diff line number Diff line
@@ -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);
            }
+1 −1
Original line number Diff line number Diff line
@@ -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
+54 −18
Original line number Diff line number Diff line
@@ -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,7 +773,8 @@ public final class AuthSession implements IBinder.DeathRecipient {
                        + ", Latency: " + latency);
            }
            // Auth canceled
            BiometricFrameworkStatsLogger.getInstance().error(
            if (error != 0) {
                mBiometricFrameworkStatsLogger.error(
                        mBiometricContext.updateContext(new OperationContextExt(true /* isBP */),
                                isCrypto()),
                        statsModality(),
@@ -752,6 +787,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
                        mUserId);
            }
        }
    }

    void onDialogDismissed(@BiometricPrompt.DismissedReason int reason,
            @Nullable byte[] credentialAttestation) {
+88 −1
Original line number Diff line number Diff line
@@ -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) {