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

Commit af32fffb authored by Kevin Chyn's avatar Kevin Chyn Committed by Android (Google) Code Review
Browse files

Merge "Add logging for Biometrics"

parents 73a85d5f 7782d148
Loading
Loading
Loading
Loading
+15 −4
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.server.biometrics;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.os.RemoteException;
import android.security.KeyStore;
@@ -71,6 +72,11 @@ public abstract class AuthenticationClient extends ClientMonitor {
        stop(false /* initiatedByClient */);
    }

    @Override
    protected int statsAction() {
        return BiometricsProtoEnums.ACTION_AUTHENTICATE;
    }

    public boolean isBiometricPrompt() {
        return getCookie() != 0;
    }
@@ -79,9 +85,17 @@ public abstract class AuthenticationClient extends ClientMonitor {
        return mRequireConfirmation;
    }

    @Override
    protected boolean isCryptoOperation() {
        return mOpId != 0;
    }

    @Override
    public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
            boolean authenticated, ArrayList<Byte> token) {
        super.logOnAuthenticated(authenticated, mRequireConfirmation, getTargetUserId(),
                isBiometricPrompt());

        final BiometricServiceBase.ServiceListener listener = getListener();

        mMetricsLogger.action(mMetrics.actionBiometricAuth(), authenticated);
@@ -142,10 +156,7 @@ public abstract class AuthenticationClient extends ClientMonitor {
                    final int errorCode = lockoutMode == LOCKOUT_TIMED
                            ? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
                            : BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
                    if (listener != null) {
                        listener.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */,
                                getCookie());
                    }
                    onError(getHalDeviceId(), errorCode, 0 /* vendorCode */);
                } else {
                    // Don't send onAuthenticationFailed if we're in lockout, it causes a
                    // janky UI on Keyguard/BiometricPrompt since "authentication failed"
+92 −6
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceReceiver;
@@ -64,6 +65,7 @@ import android.security.KeyStore;
import android.text.TextUtils;
import android.util.Pair;
import android.util.Slog;
import android.util.StatsLog;

import com.android.internal.R;
import com.android.internal.statusbar.IStatusBarService;
@@ -309,6 +311,7 @@ public class BiometricService extends SystemService {
            // Continue authentication with the same modality/modalities after "try again" is
            // pressed
            final int mModality;
            final boolean mRequireConfirmation;

            // The current state, which can be either idle, called, or started
            private int mState = STATE_AUTH_IDLE;
@@ -316,10 +319,13 @@ public class BiometricService extends SystemService {
            // the authentication.
            byte[] mTokenEscrow;

            // Timestamp when hardware authentication occurred
            private long mAuthenticatedTimeMs;

            AuthSession(HashMap<Integer, Integer> modalities, IBinder token, long sessionId,
                    int userId, IBiometricServiceReceiver receiver, String opPackageName,
                    Bundle bundle, int callingUid, int callingPid, int callingUserId,
                    int modality) {
                    int modality, boolean requireConfirmation) {
                mModalitiesWaiting = modalities;
                mToken = token;
                mSessionId = sessionId;
@@ -331,6 +337,11 @@ public class BiometricService extends SystemService {
                mCallingPid = callingPid;
                mCallingUserId = callingUserId;
                mModality = modality;
                mRequireConfirmation = requireConfirmation;
            }

            boolean isCrypto() {
                return mSessionId != 0;
            }

            boolean containsCookie(int cookie) {
@@ -412,6 +423,7 @@ public class BiometricService extends SystemService {
                        mCurrentAuthSession.mState = STATE_AUTH_IDLE;
                        mCurrentAuthSession = null;
                    } else {
                        mCurrentAuthSession.mAuthenticatedTimeMs = System.currentTimeMillis();
                        // Store the auth token and submit it to keystore after the confirmation
                        // button has been pressed.
                        mCurrentAuthSession.mTokenEscrow = token;
@@ -557,6 +569,8 @@ public class BiometricService extends SystemService {
                    return;
                }

                logDialogDismissed(reason);

                if (reason != BiometricPrompt.DISMISSED_REASON_POSITIVE) {
                    // Positive button is used by passive modalities as a "confirm" button,
                    // do not send to client
@@ -599,6 +613,77 @@ public class BiometricService extends SystemService {
                            mCurrentAuthSession.mModality);
                });
            }

            private void logDialogDismissed(int reason) {
                if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) {
                    // Explicit auth, authentication confirmed.
                    // Latency in this case is authenticated -> confirmed. <Biometric>Service
                    // should have the first half (first acquired -> authenticated).
                    final long latency = System.currentTimeMillis()
                            - mCurrentAuthSession.mAuthenticatedTimeMs;

                    if (LoggableMonitor.DEBUG) {
                        Slog.v(LoggableMonitor.TAG, "Confirmed! Modality: " + statsModality()
                                + ", User: " + mCurrentAuthSession.mUserId
                                + ", IsCrypto: " + mCurrentAuthSession.isCrypto()
                                + ", Client: " + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT
                                + ", RequireConfirmation: "
                                    + mCurrentAuthSession.mRequireConfirmation
                                + ", State: " + StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED
                                + ", Latency: " + latency);
                    }

                    StatsLog.write(StatsLog.BIOMETRIC_AUTHENTICATED,
                            statsModality(),
                            mCurrentAuthSession.mUserId,
                            mCurrentAuthSession.isCrypto(),
                            BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
                            mCurrentAuthSession.mRequireConfirmation,
                            StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED,
                            latency);
                } else {
                    int error = reason == BiometricPrompt.DISMISSED_REASON_NEGATIVE
                            ? BiometricConstants.BIOMETRIC_ERROR_NEGATIVE_BUTTON
                            : reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL
                                    ? BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED
                                    : 0;
                    if (LoggableMonitor.DEBUG) {
                        Slog.v(LoggableMonitor.TAG, "Dismissed! Modality: " + statsModality()
                                + ", User: " + mCurrentAuthSession.mUserId
                                + ", IsCrypto: " + mCurrentAuthSession.isCrypto()
                                + ", Action: " + BiometricsProtoEnums.ACTION_AUTHENTICATE
                                + ", Client: " + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT
                                + ", Error: " + error);
                    }
                    // Auth canceled
                    StatsLog.write(StatsLog.BIOMETRIC_ERROR_OCCURRED,
                            statsModality(),
                            mCurrentAuthSession.mUserId,
                            mCurrentAuthSession.isCrypto(),
                            BiometricsProtoEnums.ACTION_AUTHENTICATE,
                            BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
                            error,
                            0 /* vendorCode */);
                }
            }

            private int statsModality() {
                int modality = 0;
                if (mCurrentAuthSession == null) {
                    return BiometricsProtoEnums.MODALITY_UNKNOWN;
                }
                if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_FINGERPRINT)
                        != 0) {
                    modality |= BiometricsProtoEnums.MODALITY_FINGERPRINT;
                }
                if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_IRIS) != 0) {
                    modality |= BiometricsProtoEnums.MODALITY_IRIS;
                }
                if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_FACE) != 0) {
                    modality |= BiometricsProtoEnums.MODALITY_FACE;
                }
                return modality;
            }
        };

        @Override // Binder call
@@ -818,7 +903,11 @@ public class BiometricService extends SystemService {
            try {
                boolean requireConfirmation = bundle.getBoolean(
                        BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true /* default */);

                if ((modality & TYPE_FACE) != 0) {
                    // Check if the user has forced confirmation to be required in Settings.
                    requireConfirmation = requireConfirmation
                            || mSettingObserver.getFaceAlwaysRequireConfirmation();
                }
                // Generate random cookies to pass to the services that should prepare to start
                // authenticating. Store the cookie here and wait for all services to "ack"
                // with the cookie. Once all cookies are received, we can show the prompt
@@ -830,7 +919,7 @@ public class BiometricService extends SystemService {
                authenticators.put(modality, cookie);
                mPendingAuthSession = new AuthSession(authenticators, token, sessionId, userId,
                        receiver, opPackageName, bundle, callingUid, callingPid, callingUserId,
                        modality);
                        modality, requireConfirmation);
                mPendingAuthSession.mState = STATE_AUTH_CALLED;
                // No polymorphism :(
                if ((modality & TYPE_FINGERPRINT) != 0) {
@@ -842,9 +931,6 @@ public class BiometricService extends SystemService {
                    Slog.w(TAG, "Iris unsupported");
                }
                if ((modality & TYPE_FACE) != 0) {
                    // Check if the user has forced confirmation to be required in Settings.
                    requireConfirmation = requireConfirmation
                            || mSettingObserver.getFaceAlwaysRequireConfirmation();
                    mFaceService.prepareForAuthentication(requireConfirmation,
                            token, sessionId, userId, mInternalReceiver, opPackageName,
                            cookie, callingUid, callingPid, callingUserId);
+27 −3
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.biometrics.IBiometricServiceReceiverInternal;
@@ -55,6 +56,7 @@ import android.os.UserManager;
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.StatsLog;

import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
@@ -220,8 +222,16 @@ public abstract class BiometricServiceBase extends SystemService
     */
    protected void notifyClientActiveCallbacks(boolean isActive) {}

    protected abstract int statsModality();

    protected abstract class AuthenticationClientImpl extends AuthenticationClient {

        // Used to check if the public API that was invoked was from FingerprintManager. Only
        // to be overridden by FingerprintService.
        protected boolean isFingerprint() {
            return false;
        }

        public AuthenticationClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
                IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId,
                boolean restricted, String owner, int cookie, boolean requireConfirmation) {
@@ -229,6 +239,19 @@ public abstract class BiometricServiceBase extends SystemService
                    groupId, opId, restricted, owner, cookie, requireConfirmation);
        }

        @Override
        protected int statsClient() {
            if (isKeyguard(getOwnerString())) {
                return BiometricsProtoEnums.CLIENT_KEYGUARD;
            } else if (isBiometricPrompt()) {
                return BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT;
            } else if (isFingerprint()) {
                return BiometricsProtoEnums.CLIENT_FINGERPRINT_MANAGER;
            } else {
                return BiometricsProtoEnums.CLIENT_UNKNOWN;
            }
        }

        @Override
        public void onStart() {
            try {
@@ -296,7 +319,7 @@ public abstract class BiometricServiceBase extends SystemService
        }
    }

    protected class RemovalClientImpl extends RemovalClient {
    protected abstract class RemovalClientImpl extends RemovalClient {
        private boolean mShouldNotify;

        public RemovalClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
@@ -318,7 +341,7 @@ public abstract class BiometricServiceBase extends SystemService
        }
    }

    protected class EnumerateClientImpl extends EnumerateClient {
    protected abstract class EnumerateClientImpl extends EnumerateClient {

        public EnumerateClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
                IBinder token, ServiceListener listener, int groupId, int userId,
@@ -600,6 +623,8 @@ public abstract class BiometricServiceBase extends SystemService
        mHALDeathCount++;
        handleError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
                0 /*vendorCode */);

        StatsLog.write(StatsLog.BIOMETRIC_HAL_DEATH_REPORTED, statsModality());
    }

    protected ClientMonitor getCurrentClient() {
@@ -653,7 +678,6 @@ public abstract class BiometricServiceBase extends SystemService
            } else {
                updateActiveGroup(mCurrentUserId, null);
            }

        }
    }

+3 −1
Original line number Diff line number Diff line
@@ -36,7 +36,7 @@ import java.util.NoSuchElementException;
 * the current client.  Subclasses are responsible for coordinating the interaction with
 * the biometric's HAL for the specific action (e.g. authenticate, enroll, enumerate, etc.).
 */
public abstract class ClientMonitor implements IBinder.DeathRecipient {
public abstract class ClientMonitor extends LoggableMonitor implements IBinder.DeathRecipient {
    protected static final int ERROR_ESRCH = 3; // Likely HAL is dead. See errno.h.
    protected static final boolean DEBUG = BiometricServiceBase.DEBUG;
    private static final AudioAttributes FINGERPRINT_SONFICATION_ATTRIBUTES =
@@ -157,6 +157,7 @@ public abstract class ClientMonitor implements IBinder.DeathRecipient {
     * @return true if client should be removed
     */
    public boolean onAcquired(int acquiredInfo, int vendorCode) {
        super.logOnAcquired(acquiredInfo, vendorCode, getTargetUserId());
        try {
            if (mListener != null) {
                mListener.onAcquired(getHalDeviceId(), acquiredInfo, vendorCode);
@@ -180,6 +181,7 @@ public abstract class ClientMonitor implements IBinder.DeathRecipient {
     * @return true if client should be removed
     */
    public boolean onError(long deviceId, int error, int vendorCode) {
        super.logOnError(error, vendorCode, getTargetUserId());
        try {
            if (mListener != null) {
                mListener.onError(deviceId, error, vendorCode, getCookie());
+6 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.server.biometrics;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
@@ -50,6 +51,11 @@ public abstract class EnrollClient extends ClientMonitor {
        mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length);
    }

    @Override
    protected int statsAction() {
        return BiometricsProtoEnums.ACTION_ENROLL;
    }

    @Override
    public boolean onEnrollResult(BiometricAuthenticator.Identifier identifier,
            int remaining) {
Loading