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

Commit 16f0d349 authored by Joe Bolinger's avatar Joe Bolinger
Browse files

Connect SysUI sessions with biometric operations logs.

Update framework stats logging with new atom changes.

The AoD flag is not yet reported accurately and requires the additional HAL
method because most operations start when the screen is on.

Bug: 204585936
Bug: 204584403

Test: atest com.android.server.biometrics.sensors
Test: statsd_testdrive 87
Test: statsd_testdrive 88

Change-Id: I62cb38c9e1f8254266c51119c26fd22dfc992b1c
parent 02814776
Loading
Loading
Loading
Loading
+18 −13
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@ import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.common.OperationContext;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -64,6 +65,7 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.log.BiometricFrameworkStatsLogger;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -681,16 +683,18 @@ public final class AuthSession implements IBinder.DeathRecipient {
                        + ", Latency: " + latency);
            }

            FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_AUTHENTICATED,
            final OperationContext operationContext = new OperationContext();
            operationContext.isCrypto = isCrypto();
            BiometricFrameworkStatsLogger.getInstance().authenticate(
                    operationContext,
                    statsModality(),
                    mUserId,
                    isCrypto(),
                    BiometricsProtoEnums.ACTION_UNKNOWN,
                    BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
                    mPreAuthInfo.confirmationRequested,
                    FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED,
                    latency,
                    mDebugEnabled,
                    -1 /* sensorId */,
                    latency,
                    FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED,
                    mPreAuthInfo.confirmationRequested,
                    mUserId,
                    -1f /* ambientLightLux */);
        } else {
            final long latency = System.currentTimeMillis() - mStartTimeMs;
@@ -711,17 +715,18 @@ public final class AuthSession implements IBinder.DeathRecipient {
                        + ", Latency: " + latency);
            }
            // Auth canceled
            FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED,
            final OperationContext operationContext = new OperationContext();
            operationContext.isCrypto = isCrypto();
            BiometricFrameworkStatsLogger.getInstance().error(
                    operationContext,
                    statsModality(),
                    mUserId,
                    isCrypto(),
                    BiometricsProtoEnums.ACTION_AUTHENTICATE,
                    BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
                    error,
                    0 /* vendorCode */,
                    mDebugEnabled,
                    latency,
                    -1 /* sensorId */);
                    error,
                    0 /* vendorCode */,
                    mUserId);
        }
    }

+15 −3
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@
package com.android.server.biometrics.log;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.common.OperationContext;

import java.util.function.Consumer;
@@ -26,11 +28,21 @@ import java.util.function.Consumer;
 * logging or optimizations.
 */
public interface BiometricContext {
    /** Gets the context source. */
    static BiometricContext getInstance() {
        return BiometricContextProvider.sInstance.get();
    /** Gets the context source from the system context. */
    static BiometricContext getInstance(@NonNull Context context) {
        return BiometricContextProvider.defaultProvider(context);
    }

    /** Update the given context with the most recent values and return it. */
    OperationContext updateContext(@NonNull OperationContext operationContext,
            boolean isCryptoOperation);

    /** The session id for keyguard entry, if active, or null. */
    @Nullable Integer getKeyguardEntrySessionId();

    /** The session id for biometric prompt usage, if active, or null. */
    @Nullable Integer getBiometricPromptSessionId();

    /** If the display is in AOD. */
    boolean isAoD();

+91 −12
Original line number Diff line number Diff line
@@ -18,16 +18,22 @@ package com.android.server.biometrics.log;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.StatusBarManager;
import android.content.Context;
import android.hardware.biometrics.IBiometricContextListener;
import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.common.OperationReason;
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Singleton;
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.UserHandle;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.InstanceId;
import com.android.internal.statusbar.ISessionListener;
import com.android.internal.statusbar.IStatusBarService;

import java.util.Map;
@@ -41,22 +47,41 @@ class BiometricContextProvider implements BiometricContext {

    private static final String TAG = "BiometricContextProvider";

    static final Singleton<BiometricContextProvider> sInstance =
            new Singleton<BiometricContextProvider>() {
                @Override
                protected BiometricContextProvider create() {
                    return new BiometricContextProvider(IStatusBarService.Stub.asInterface(
                            ServiceManager.getService(
    private static final int SESSION_TYPES =
            StatusBarManager.SESSION_KEYGUARD | StatusBarManager.SESSION_BIOMETRIC_PROMPT;

    private static BiometricContextProvider sInstance;

    static BiometricContextProvider defaultProvider(@NonNull Context context) {
        synchronized (BiometricContextProvider.class) {
            if (sInstance == null) {
                try {
                    sInstance = new BiometricContextProvider(
                            new AmbientDisplayConfiguration(context),
                            IStatusBarService.Stub.asInterface(ServiceManager.getServiceOrThrow(
                                    Context.STATUS_BAR_SERVICE)), null /* handler */);
                } catch (ServiceNotFoundException e) {
                    throw new IllegalStateException("Failed to find required service", e);
                }
            }
        }
        return sInstance;
    }
            };

    @NonNull
    private final Map<OperationContext, Consumer<OperationContext>> mSubscribers =
            new ConcurrentHashMap<>();

    @Nullable
    private final Map<Integer, InstanceId> mSession = new ConcurrentHashMap<>();

    private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
    private boolean mIsDozing = false;

    @VisibleForTesting
    BiometricContextProvider(@NonNull IStatusBarService service, @Nullable Handler handler) {
    BiometricContextProvider(@NonNull AmbientDisplayConfiguration ambientDisplayConfiguration,
            @NonNull IStatusBarService service, @Nullable Handler handler) {
        mAmbientDisplayConfiguration = ambientDisplayConfiguration;
        try {
            service.setBiometicContextListener(new IBiometricContextListener.Stub() {
                @Override
@@ -73,16 +98,70 @@ class BiometricContextProvider implements BiometricContext {
                    }
                }
            });
            service.registerSessionListener(SESSION_TYPES, new ISessionListener.Stub() {
                @Override
                public void onSessionStarted(int sessionType, InstanceId instance) {
                    mSession.put(sessionType, instance);
                }

                @Override
                public void onSessionEnded(int sessionType, InstanceId instance) {
                    final InstanceId id = mSession.remove(sessionType);
                    if (id != null && instance != null && id.getId() != instance.getId()) {
                        Slog.w(TAG, "session id mismatch");
                    }
                }
            });
        } catch (RemoteException e) {
            Slog.e(TAG, "Unable to register biometric context listener", e);
        }
    }

    private boolean mIsDozing = false;
    @Override
    public OperationContext updateContext(@NonNull OperationContext operationContext,
            boolean isCryptoOperation) {
        operationContext.isAoD = isAoD();
        operationContext.isCrypto = isCryptoOperation;
        setFirstSessionId(operationContext);
        return operationContext;
    }

    private void setFirstSessionId(@NonNull OperationContext operationContext) {
        Integer sessionId = getKeyguardEntrySessionId();
        if (sessionId != null) {
            operationContext.id = sessionId;
            operationContext.reason = OperationReason.KEYGUARD;
            return;
        }

        sessionId = getBiometricPromptSessionId();
        if (sessionId != null) {
            operationContext.id = sessionId;
            operationContext.reason = OperationReason.BIOMETRIC_PROMPT;
            return;
        }

        operationContext.id = 0;
        operationContext.reason = OperationReason.UNKNOWN;
    }

    @Nullable
    @Override
    public Integer getKeyguardEntrySessionId() {
        final InstanceId id = mSession.get(StatusBarManager.SESSION_KEYGUARD);
        return id != null ? id.getId() : null;
    }

    @Nullable
    @Override
    public Integer getBiometricPromptSessionId() {
        final InstanceId id = mSession.get(StatusBarManager.SESSION_BIOMETRIC_PROMPT);
        return id != null ? id.getId() : null;
    }

    @Override
    public boolean isAoD() {
        return mIsDozing;
        return mIsDozing && mAmbientDisplayConfiguration.alwaysOnEnabled(UserHandle.USER_CURRENT);
    }

    @Override
@@ -98,7 +177,7 @@ class BiometricContextProvider implements BiometricContext {

    private void notifySubscribers() {
        mSubscribers.forEach((context, consumer) -> {
            context.isAoD = mIsDozing;
            context.isAoD = isAoD();
            consumer.accept(context);
        });
    }
+35 −13
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@
package com.android.server.biometrics.log;

import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.common.OperationReason;
import android.util.Slog;

import com.android.internal.util.FrameworkStatsLog;
@@ -33,42 +35,49 @@ public class BiometricFrameworkStatsLogger {

    private BiometricFrameworkStatsLogger() {}

    /** Shared instance. */
    public static BiometricFrameworkStatsLogger getInstance() {
        return sInstance;
    }

    /** {@see FrameworkStatsLog.BIOMETRIC_ACQUIRED}. */
    public void acquired(
    public void acquired(OperationContext operationContext,
            int statsModality, int statsAction, int statsClient, boolean isDebug,
            int acquiredInfo, int vendorCode, boolean isCrypto, int targetUserId) {
            int acquiredInfo, int vendorCode, int targetUserId) {
        FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ACQUIRED,
                statsModality,
                targetUserId,
                isCrypto,
                operationContext.isCrypto,
                statsAction,
                statsClient,
                acquiredInfo,
                vendorCode,
                isDebug,
                -1 /* sensorId */);
                -1 /* sensorId */,
                operationContext.id,
                sessionType(operationContext.reason),
                operationContext.isAoD);
    }

    /** {@see FrameworkStatsLog.BIOMETRIC_AUTHENTICATED}. */
    public void authenticate(
    public void authenticate(OperationContext operationContext,
            int statsModality, int statsAction, int statsClient, boolean isDebug, long latency,
            boolean authenticated, int authState, boolean requireConfirmation, boolean isCrypto,
            int targetUserId, boolean isBiometricPrompt, float ambientLightLux) {
            int authState, boolean requireConfirmation,
            int targetUserId, float ambientLightLux) {
        FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_AUTHENTICATED,
                statsModality,
                targetUserId,
                isCrypto,
                operationContext.isCrypto,
                statsClient,
                requireConfirmation,
                authState,
                sanitizeLatency(latency),
                isDebug,
                -1 /* sensorId */,
                ambientLightLux);
                ambientLightLux,
                operationContext.id,
                sessionType(operationContext.reason),
                operationContext.isAoD);
    }

    /** {@see FrameworkStatsLog.BIOMETRIC_ENROLLED}. */
@@ -84,20 +93,23 @@ public class BiometricFrameworkStatsLogger {
    }

    /** {@see FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED}. */
    public void error(
    public void error(OperationContext operationContext,
            int statsModality, int statsAction, int statsClient, boolean isDebug, long latency,
            int error, int vendorCode, boolean isCrypto, int targetUserId) {
            int error, int vendorCode, int targetUserId) {
        FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED,
                statsModality,
                targetUserId,
                isCrypto,
                operationContext.isCrypto,
                statsAction,
                statsClient,
                error,
                vendorCode,
                isDebug,
                sanitizeLatency(latency),
                -1 /* sensorId */);
                -1 /* sensorId */,
                operationContext.id,
                sessionType(operationContext.reason),
                operationContext.isAoD);
    }

    /** {@see FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED}. */
@@ -123,4 +135,14 @@ public class BiometricFrameworkStatsLogger {
        }
        return latency;
    }

    private static int sessionType(@OperationReason byte reason) {
        if (reason == OperationReason.BIOMETRIC_PROMPT) {
            return BiometricsProtoEnums.SESSION_TYPE_BIOMETRIC_PROMPT;
        }
        if (reason == OperationReason.KEYGUARD) {
            return BiometricsProtoEnums.SESSION_TYPE_KEYGUARD_ENTRY;
        }
        return BiometricsProtoEnums.SESSION_TYPE_UNKNOWN;
    }
}
+16 −16
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.common.OperationContext;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.util.Slog;
@@ -144,8 +145,8 @@ public class BiometricLogger {
    }

    /** Log an acquisition event. */
    public void logOnAcquired(Context context,
            int acquiredInfo, int vendorCode, boolean isCrypto, int targetUserId) {
    public void logOnAcquired(Context context, OperationContext operationContext,
            int acquiredInfo, int vendorCode, int targetUserId) {
        if (!mShouldLogMetrics) {
            return;
        }
@@ -165,7 +166,7 @@ public class BiometricLogger {
        if (DEBUG) {
            Slog.v(TAG, "Acquired! Modality: " + mStatsModality
                    + ", User: " + targetUserId
                    + ", IsCrypto: " + isCrypto
                    + ", IsCrypto: " + operationContext.isCrypto
                    + ", Action: " + mStatsAction
                    + ", Client: " + mStatsClient
                    + ", AcquiredInfo: " + acquiredInfo
@@ -176,14 +177,14 @@ public class BiometricLogger {
            return;
        }

        mSink.acquired(mStatsModality, mStatsAction, mStatsClient,
        mSink.acquired(operationContext, mStatsModality, mStatsAction, mStatsClient,
                Utils.isDebugEnabled(context, targetUserId),
                acquiredInfo, vendorCode, isCrypto, targetUserId);
                acquiredInfo, vendorCode, targetUserId);
    }

    /** Log an error during an operation. */
    public void logOnError(Context context,
            int error, int vendorCode, boolean isCrypto, int targetUserId) {
    public void logOnError(Context context, OperationContext operationContext,
            int error, int vendorCode, int targetUserId) {
        if (!mShouldLogMetrics) {
            return;
        }
@@ -194,7 +195,7 @@ public class BiometricLogger {
        if (DEBUG) {
            Slog.v(TAG, "Error! Modality: " + mStatsModality
                    + ", User: " + targetUserId
                    + ", IsCrypto: " + isCrypto
                    + ", IsCrypto: " + operationContext.isCrypto
                    + ", Action: " + mStatsAction
                    + ", Client: " + mStatsClient
                    + ", Error: " + error
@@ -208,14 +209,14 @@ public class BiometricLogger {
            return;
        }

        mSink.error(mStatsModality, mStatsAction, mStatsClient,
        mSink.error(operationContext, mStatsModality, mStatsAction, mStatsClient,
                Utils.isDebugEnabled(context, targetUserId), latency,
                error, vendorCode, isCrypto, targetUserId);
                error, vendorCode, targetUserId);
    }

    /** Log authentication attempt. */
    public void logOnAuthenticated(Context context,
            boolean authenticated, boolean requireConfirmation, boolean isCrypto,
    public void logOnAuthenticated(Context context, OperationContext operationContext,
            boolean authenticated, boolean requireConfirmation,
            int targetUserId, boolean isBiometricPrompt) {
        if (!mShouldLogMetrics) {
            return;
@@ -241,7 +242,7 @@ public class BiometricLogger {
        if (DEBUG) {
            Slog.v(TAG, "Authenticated! Modality: " + mStatsModality
                    + ", User: " + targetUserId
                    + ", IsCrypto: " + isCrypto
                    + ", IsCrypto: " + operationContext.isCrypto
                    + ", Client: " + mStatsClient
                    + ", RequireConfirmation: " + requireConfirmation
                    + ", State: " + authState
@@ -255,10 +256,9 @@ public class BiometricLogger {
            return;
        }

        mSink.authenticate(mStatsModality, mStatsAction, mStatsClient,
        mSink.authenticate(operationContext, mStatsModality, mStatsAction, mStatsClient,
                Utils.isDebugEnabled(context, targetUserId),
                latency, authenticated, authState, requireConfirmation, isCrypto,
                targetUserId, isBiometricPrompt, mLastAmbientLux);
                latency, authState, requireConfirmation, targetUserId, mLastAmbientLux);
    }

    /** Log enrollment outcome. */
Loading