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

Commit 7782d148 authored by Kevin Chyn's avatar Kevin Chyn
Browse files

Add logging for Biometrics

Bug: 117060268
Bug: 120161047

Test: With LoggableMonitor DEBUG turned on, manually tested and checked
      logs

Change-Id: Ib8b3ec9ed7913e38b17597ac7eda535925e1576a
parent 2348248e
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
@@ -815,7 +900,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
@@ -827,7 +916,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) {
@@ -839,9 +928,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