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

Commit f11d2163 authored by Jeff Pu's avatar Jeff Pu Committed by Android (Google) Code Review
Browse files

Merge "Added the view dismissed reason due to confirm button pressing" into udc-dev

parents 5be383fb 89436e69
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) {