Merge "Clean up framework stats logging."

This commit is contained in:
Joe Bolinger
2022-01-19 19:55:27 +00:00
committed by Android (Google) Code Review
21 changed files with 611 additions and 159 deletions

View File

@@ -0,0 +1,126 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.biometrics.log;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.util.Slog;
import com.android.internal.util.FrameworkStatsLog;
/**
* Wrapper for {@link FrameworkStatsLog} to isolate the testable parts.
*/
public class BiometricFrameworkStatsLogger {
private static final String TAG = "BiometricFrameworkStatsLogger";
private static final BiometricFrameworkStatsLogger sInstance =
new BiometricFrameworkStatsLogger();
private BiometricFrameworkStatsLogger() {}
public static BiometricFrameworkStatsLogger getInstance() {
return sInstance;
}
/** {@see FrameworkStatsLog.BIOMETRIC_ACQUIRED}. */
public void acquired(
int statsModality, int statsAction, int statsClient, boolean isDebug,
int acquiredInfo, int vendorCode, boolean isCrypto, int targetUserId) {
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ACQUIRED,
statsModality,
targetUserId,
isCrypto,
statsAction,
statsClient,
acquiredInfo,
vendorCode,
isDebug,
-1 /* sensorId */);
}
/** {@see FrameworkStatsLog.BIOMETRIC_AUTHENTICATED}. */
public void authenticate(
int statsModality, int statsAction, int statsClient, boolean isDebug, long latency,
boolean authenticated, int authState, boolean requireConfirmation, boolean isCrypto,
int targetUserId, boolean isBiometricPrompt, float ambientLightLux) {
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_AUTHENTICATED,
statsModality,
targetUserId,
isCrypto,
statsClient,
requireConfirmation,
authState,
sanitizeLatency(latency),
isDebug,
-1 /* sensorId */,
ambientLightLux);
}
/** {@see FrameworkStatsLog.BIOMETRIC_ENROLLED}. */
public void enroll(int statsModality, int statsAction, int statsClient,
int targetUserId, long latency, boolean enrollSuccessful, float ambientLightLux) {
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ENROLLED,
statsModality,
targetUserId,
sanitizeLatency(latency),
enrollSuccessful,
-1, /* sensorId */
ambientLightLux);
}
/** {@see FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED}. */
public void error(
int statsModality, int statsAction, int statsClient, boolean isDebug, long latency,
int error, int vendorCode, boolean isCrypto, int targetUserId) {
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED,
statsModality,
targetUserId,
isCrypto,
statsAction,
statsClient,
error,
vendorCode,
isDebug,
sanitizeLatency(latency),
-1 /* sensorId */);
}
/** {@see FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED}. */
public void reportUnknownTemplateEnrolledHal(int statsModality) {
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
statsModality,
BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL,
-1 /* sensorId */);
}
/** {@see FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED}. */
public void reportUnknownTemplateEnrolledFramework(int statsModality) {
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
statsModality,
BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_FRAMEWORK,
-1 /* sensorId */);
}
private long sanitizeLatency(long latency) {
if (latency < 0) {
Slog.w(TAG, "found a negative latency : " + latency);
return -1;
}
return latency;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020 The Android Open Source Project
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.server.biometrics.sensors;
package com.android.server.biometrics.log;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -29,71 +29,28 @@ import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.Utils;
/**
* Abstract class that adds logging functionality to the ClientMonitor classes.
* Logger for all reported Biometric framework events.
*/
public abstract class LoggableMonitor {
public class BiometricLogger {
public static final String TAG = "Biometrics/LoggableMonitor";
public static final String TAG = "BiometricLogger";
public static final boolean DEBUG = false;
final int mStatsModality;
private final int mStatsModality;
private final int mStatsAction;
private final int mStatsClient;
private final BiometricFrameworkStatsLogger mSink;
@NonNull private final SensorManager mSensorManager;
private long mFirstAcquireTimeMs;
private boolean mLightSensorEnabled = false;
private boolean mShouldLogMetrics = true;
/**
* Probe for loggable attributes that can be continuously monitored, such as ambient light.
*
* Disable probes when the sensors are in states that are not interesting for monitoring
* purposes to save power.
*/
protected interface Probe {
/** Ensure the probe is actively sampling for new data. */
void enable();
/** Stop sampling data. */
void disable();
}
/**
* Client monitor callback that exposes a probe.
*
* Disables the probe when the operation completes.
*/
protected static class CallbackWithProbe<T extends Probe>
implements BaseClientMonitor.Callback {
private final boolean mStartWithClient;
private final T mProbe;
public CallbackWithProbe(@NonNull T probe, boolean startWithClient) {
mProbe = probe;
mStartWithClient = startWithClient;
}
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
if (mStartWithClient) {
mProbe.enable();
}
}
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
mProbe.disable();
}
@NonNull
public T getProbe() {
return mProbe;
}
}
private class ALSProbe implements Probe {
@Override
public void enable() {
@@ -128,26 +85,30 @@ public abstract class LoggableMonitor {
* @param statsAction One of {@link BiometricsProtoEnums} ACTION_* constants.
* @param statsClient One of {@link BiometricsProtoEnums} CLIENT_* constants.
*/
public LoggableMonitor(@NonNull Context context, int statsModality, int statsAction,
int statsClient) {
public BiometricLogger(
@NonNull Context context, int statsModality, int statsAction, int statsClient) {
this(statsModality, statsAction, statsClient,
BiometricFrameworkStatsLogger.getInstance(),
context.getSystemService(SensorManager.class));
}
@VisibleForTesting
BiometricLogger(
int statsModality, int statsAction, int statsClient,
BiometricFrameworkStatsLogger logSink, SensorManager sensorManager) {
mStatsModality = statsModality;
mStatsAction = statsAction;
mStatsClient = statsClient;
mSensorManager = context.getSystemService(SensorManager.class);
mSink = logSink;
mSensorManager = sensorManager;
}
/**
* Only valid for AuthenticationClient.
* @return true if the client is authenticating for a crypto operation.
*/
protected boolean isCryptoOperation() {
return false;
}
protected void setShouldLog(boolean shouldLog) {
mShouldLogMetrics = shouldLog;
/** Disable logging metrics and only log critical events, such as system health issues. */
public void disableMetrics() {
mShouldLogMetrics = false;
}
/** {@link BiometricsProtoEnums} CLIENT_* constants */
public int getStatsClient() {
return mStatsClient;
}
@@ -171,8 +132,9 @@ public abstract class LoggableMonitor {
return shouldSkipLogging;
}
protected final void logOnAcquired(Context context, int acquiredInfo, int vendorCode,
int targetUserId) {
/** Log an acquisition event. */
public void logOnAcquired(Context context,
int acquiredInfo, int vendorCode, boolean isCrypto, int targetUserId) {
if (!mShouldLogMetrics) {
return;
}
@@ -192,7 +154,7 @@ public abstract class LoggableMonitor {
if (DEBUG) {
Slog.v(TAG, "Acquired! Modality: " + mStatsModality
+ ", User: " + targetUserId
+ ", IsCrypto: " + isCryptoOperation()
+ ", IsCrypto: " + isCrypto
+ ", Action: " + mStatsAction
+ ", Client: " + mStatsClient
+ ", AcquiredInfo: " + acquiredInfo
@@ -203,19 +165,14 @@ public abstract class LoggableMonitor {
return;
}
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ACQUIRED,
mStatsModality,
targetUserId,
isCryptoOperation(),
mStatsAction,
mStatsClient,
acquiredInfo,
vendorCode,
mSink.acquired(mStatsModality, mStatsAction, mStatsClient,
Utils.isDebugEnabled(context, targetUserId),
-1 /* sensorId */);
acquiredInfo, vendorCode, isCrypto, targetUserId);
}
protected final void logOnError(Context context, int error, int vendorCode, int targetUserId) {
/** Log an error during an operation. */
public void logOnError(Context context,
int error, int vendorCode, boolean isCrypto, int targetUserId) {
if (!mShouldLogMetrics) {
return;
}
@@ -226,7 +183,7 @@ public abstract class LoggableMonitor {
if (DEBUG) {
Slog.v(TAG, "Error! Modality: " + mStatsModality
+ ", User: " + targetUserId
+ ", IsCrypto: " + isCryptoOperation()
+ ", IsCrypto: " + isCrypto
+ ", Action: " + mStatsAction
+ ", Client: " + mStatsClient
+ ", Error: " + error
@@ -240,21 +197,15 @@ public abstract class LoggableMonitor {
return;
}
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED,
mStatsModality,
targetUserId,
isCryptoOperation(),
mStatsAction,
mStatsClient,
error,
vendorCode,
Utils.isDebugEnabled(context, targetUserId),
sanitizeLatency(latency),
-1 /* sensorId */);
mSink.error(mStatsModality, mStatsAction, mStatsClient,
Utils.isDebugEnabled(context, targetUserId), latency,
error, vendorCode, isCrypto, targetUserId);
}
protected final void logOnAuthenticated(Context context, boolean authenticated,
boolean requireConfirmation, int targetUserId, boolean isBiometricPrompt) {
/** Log authentication attempt. */
public void logOnAuthenticated(Context context,
boolean authenticated, boolean requireConfirmation, boolean isCrypto,
int targetUserId, boolean isBiometricPrompt) {
if (!mShouldLogMetrics) {
return;
}
@@ -279,7 +230,7 @@ public abstract class LoggableMonitor {
if (DEBUG) {
Slog.v(TAG, "Authenticated! Modality: " + mStatsModality
+ ", User: " + targetUserId
+ ", IsCrypto: " + isCryptoOperation()
+ ", IsCrypto: " + isCrypto
+ ", Client: " + mStatsClient
+ ", RequireConfirmation: " + requireConfirmation
+ ", State: " + authState
@@ -293,20 +244,14 @@ public abstract class LoggableMonitor {
return;
}
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_AUTHENTICATED,
mStatsModality,
targetUserId,
isCryptoOperation(),
mStatsClient,
requireConfirmation,
authState,
sanitizeLatency(latency),
mSink.authenticate(mStatsModality, mStatsAction, mStatsClient,
Utils.isDebugEnabled(context, targetUserId),
-1 /* sensorId */,
mLastAmbientLux /* ambientLightLux */);
latency, authenticated, authState, requireConfirmation, isCrypto,
targetUserId, isBiometricPrompt, mLastAmbientLux);
}
protected final void logOnEnrolled(int targetUserId, long latency, boolean enrollSuccessful) {
/** Log enrollment outcome. */
public void logOnEnrolled(int targetUserId, long latency, boolean enrollSuccessful) {
if (!mShouldLogMetrics) {
return;
}
@@ -326,25 +271,30 @@ public abstract class LoggableMonitor {
return;
}
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ENROLLED,
mStatsModality,
targetUserId,
sanitizeLatency(latency),
enrollSuccessful,
-1, /* sensorId */
mLastAmbientLux /* ambientLightLux */);
mSink.enroll(mStatsModality, mStatsAction, mStatsClient,
targetUserId, latency, enrollSuccessful, mLastAmbientLux);
}
private long sanitizeLatency(long latency) {
if (latency < 0) {
Slog.w(TAG, "found a negative latency : " + latency);
return -1;
/** Report unexpected enrollment reported by the HAL. */
public void logUnknownEnrollmentInHal() {
if (shouldSkipLogging()) {
return;
}
return latency;
mSink.reportUnknownTemplateEnrolledHal(mStatsModality);
}
/** Report unknown enrollment in framework settings */
public void logUnknownEnrollmentInFramework() {
if (shouldSkipLogging()) {
return;
}
mSink.reportUnknownTemplateEnrolledFramework(mStatsModality);
}
/**
* Get a callback to start/stop ALS capture when client runs.
* Get a callback to start/stop ALS capture when a client runs.
*
* If the probe should not run for the entire operation, do not set startWithClient and
* start/stop the problem when needed.
@@ -352,7 +302,7 @@ public abstract class LoggableMonitor {
* @param startWithClient if probe should start automatically when the operation starts.
*/
@NonNull
protected CallbackWithProbe<Probe> createALSCallback(boolean startWithClient) {
public CallbackWithProbe<Probe> createALSCallback(boolean startWithClient) {
return new CallbackWithProbe<>(new ALSProbe(), startWithClient);
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.biometrics.log;
import android.annotation.NonNull;
import com.android.server.biometrics.sensors.BaseClientMonitor;
/**
* Client monitor callback that exposes a probe.
*
* Disables the probe when the operation completes.
*
* @param <T> probe type
*/
public class CallbackWithProbe<T extends Probe> implements BaseClientMonitor.Callback {
private final boolean mStartWithClient;
private final T mProbe;
public CallbackWithProbe(@NonNull T probe, boolean startWithClient) {
mProbe = probe;
mStartWithClient = startWithClient;
}
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
if (mStartWithClient) {
mProbe.enable();
}
}
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
mProbe.disable();
}
@NonNull
public T getProbe() {
return mProbe;
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.biometrics.log;
/**
* Probe for loggable attributes that can be continuously monitored, such as ambient light.
*
* Disable probes when the sensors are in states that are not interesting for monitoring
* purposes to save power.
*/
public interface Probe {
/** Ensure the probe is actively sampling for new data. */
void enable();
/** Stop sampling data. */
void disable();
}

View File

@@ -105,7 +105,8 @@ public abstract class AcquisitionClient<T> extends HalClientMonitor<T> implement
// that do not handle lockout under the HAL. In these cases, ensure that the framework only
// sends errors once per ClientMonitor.
if (mShouldSendErrorToClient) {
logOnError(getContext(), errorCode, vendorCode, getTargetUserId());
getLogger().logOnError(getContext(), errorCode, vendorCode,
isCryptoOperation(), getTargetUserId());
try {
if (getListener() != null) {
mShouldSendErrorToClient = false;
@@ -163,7 +164,8 @@ public abstract class AcquisitionClient<T> extends HalClientMonitor<T> implement
protected final void onAcquiredInternal(int acquiredInfo, int vendorCode,
boolean shouldSend) {
super.logOnAcquired(getContext(), acquiredInfo, vendorCode, getTargetUserId());
getLogger().logOnAcquired(getContext(), acquiredInfo, vendorCode,
isCryptoOperation(), getTargetUserId());
if (DEBUG) {
Slog.v(TAG, "Acquired: " + acquiredInfo + " " + vendorCode
+ ", shouldSend: " + shouldSend);

View File

@@ -180,8 +180,8 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
@Override
public void onAuthenticated(BiometricAuthenticator.Identifier identifier,
boolean authenticated, ArrayList<Byte> hardwareAuthToken) {
super.logOnAuthenticated(getContext(), authenticated, mRequireConfirmation,
getTargetUserId(), isBiometricPrompt());
getLogger().logOnAuthenticated(getContext(), authenticated, mRequireConfirmation,
isCryptoOperation(), getTargetUserId(), isBiometricPrompt());
final ClientMonitorCallbackConverter listener = getListener();

View File

@@ -27,18 +27,18 @@ import android.os.RemoteException;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.log.BiometricLogger;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
/**
* Abstract base class for keeping track and dispatching events from the biometric's HAL to the
* Abstract base class for keeping track and dispatching events from the biometric's HAL to
* 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 BaseClientMonitor extends LoggableMonitor
implements IBinder.DeathRecipient {
public abstract class BaseClientMonitor implements IBinder.DeathRecipient {
private static final String TAG = "Biometrics/ClientMonitor";
protected static final boolean DEBUG = true;
@@ -108,6 +108,7 @@ public abstract class BaseClientMonitor extends LoggableMonitor
private final int mTargetUserId;
@NonNull private final String mOwner;
private final int mSensorId; // sensorId as configured by the framework
@NonNull private final BiometricLogger mLogger;
@Nullable private IBinder mToken;
private long mRequestId;
@@ -160,7 +161,14 @@ public abstract class BaseClientMonitor extends LoggableMonitor
@Nullable IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId,
@NonNull String owner, int cookie, int sensorId, int statsModality, int statsAction,
int statsClient) {
super(context, statsModality, statsAction, statsClient);
this(context, token, listener, userId, owner, cookie, sensorId,
new BiometricLogger(context, statsModality, statsAction, statsClient));
}
@VisibleForTesting
BaseClientMonitor(@NonNull Context context,
@Nullable IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId,
@NonNull String owner, int cookie, int sensorId, @NonNull BiometricLogger logger) {
mSequentialId = sCount++;
mContext = context;
mToken = token;
@@ -170,6 +178,7 @@ public abstract class BaseClientMonitor extends LoggableMonitor
mOwner = owner;
mCookie = cookie;
mSensorId = sensorId;
mLogger = logger;
try {
if (token != null) {
@@ -180,10 +189,6 @@ public abstract class BaseClientMonitor extends LoggableMonitor
}
}
public int getCookie() {
return mCookie;
}
/**
* Starts the ClientMonitor's lifecycle.
* @param callback invoked when the operation is complete (succeeds, fails, etc)
@@ -257,6 +262,20 @@ public abstract class BaseClientMonitor extends LoggableMonitor
}
}
/**
* Only valid for AuthenticationClient.
* @return true if the client is authenticating for a crypto operation.
*/
protected boolean isCryptoOperation() {
return false;
}
/** Logger for this client */
@NonNull
public BiometricLogger getLogger() {
return mLogger;
}
public final Context getContext() {
return mContext;
}
@@ -281,6 +300,11 @@ public abstract class BaseClientMonitor extends LoggableMonitor
return mSensorId;
}
/** Cookie set when this monitor was created. */
public int getCookie() {
return mCookie;
}
/** Unique request id. */
public final long getRequestId() {
return mRequestId;

View File

@@ -89,7 +89,8 @@ public abstract class EnrollClient<T> extends AcquisitionClient<T> implements En
if (remaining == 0) {
mBiometricUtils.addBiometricForUser(getContext(), getTargetUserId(), identifier);
logOnEnrolled(getTargetUserId(), System.currentTimeMillis() - mEnrollmentStartTimeMs,
getLogger().logOnEnrolled(getTargetUserId(),
System.currentTimeMillis() - mEnrollmentStartTimeMs,
true /* enrollSuccessful */);
mCallback.onClientFinished(this, true /* success */);
}
@@ -116,7 +117,8 @@ public abstract class EnrollClient<T> extends AcquisitionClient<T> implements En
*/
@Override
public void onError(int error, int vendorCode) {
logOnEnrolled(getTargetUserId(), System.currentTimeMillis() - mEnrollmentStartTimeMs,
getLogger().logOnEnrolled(getTargetUserId(),
System.currentTimeMillis() - mEnrollmentStartTimeMs,
false /* enrollSuccessful */);
super.onError(error, vendorCode);
}

View File

@@ -23,7 +23,6 @@ import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.util.Slog;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.BiometricsProto;
import java.util.ArrayList;
@@ -128,10 +127,9 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
mCurrentTask = getRemovalClient(getContext(), mLazyDaemon, getToken(),
template.mIdentifier.getBiometricId(), template.mUserId,
getContext().getPackageName(), mBiometricUtils, getSensorId(), mAuthenticatorIds);
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
mStatsModality,
BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL,
-1 /* sensorId */);
getLogger().logUnknownEnrollmentInHal();
mCurrentTask.start(mRemoveCallback);
}

View File

@@ -23,7 +23,6 @@ import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.util.Slog;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.BiometricsProto;
import java.util.ArrayList;
@@ -116,10 +115,8 @@ public abstract class InternalEnumerateClient<T> extends HalClientMonitor<T>
+ identifier.getBiometricId() + " " + identifier.getName());
mUtils.removeBiometricForUser(getContext(),
getTargetUserId(), identifier.getBiometricId());
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
mStatsModality,
BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_FRAMEWORK,
-1 /* sensorId */);
getLogger().logUnknownEnrollmentInFramework();
}
mEnrolledList.clear();
}

View File

@@ -105,7 +105,8 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements
@NonNull
@Override
protected Callback wrapCallbackForStart(@NonNull Callback callback) {
return new CompositeCallback(createALSCallback(true /* startWithClient */), callback);
return new CompositeCallback(
getLogger().createALSCallback(true /* startWithClient */), callback);
}
@Override
@@ -241,7 +242,8 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements
mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED);
// Lockout metrics are logged as an error code.
final int error = BiometricFaceConstants.FACE_ERROR_LOCKOUT;
logOnError(getContext(), error, 0 /* vendorCode */, getTargetUserId());
getLogger().logOnError(getContext(), error, 0 /* vendorCode */,
isCryptoOperation(), getTargetUserId());
try {
getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
@@ -256,7 +258,8 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements
mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_PERMANENT);
// Lockout metrics are logged as an error code.
final int error = BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT;
logOnError(getContext(), error, 0 /* vendorCode */, getTargetUserId());
getLogger().logOnError(getContext(), error, 0 /* vendorCode */,
isCryptoOperation(), getTargetUserId());
try {
getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);

View File

@@ -111,7 +111,7 @@ public class FaceEnrollClient extends EnrollClient<ISession> {
@Override
protected Callback wrapCallbackForStart(@NonNull Callback callback) {
return new CompositeCallback(mPreviewHandleDeleterCallback,
createALSCallback(true /* startWithClient */), callback);
getLogger().createALSCallback(true /* startWithClient */), callback);
}
@Override

View File

@@ -95,7 +95,8 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> {
@NonNull
@Override
protected Callback wrapCallbackForStart(@NonNull Callback callback) {
return new CompositeCallback(createALSCallback(true /* startWithClient */), callback);
return new CompositeCallback(
getLogger().createALSCallback(true /* startWithClient */), callback);
}
@Override

View File

@@ -70,7 +70,8 @@ public class FaceEnrollClient extends EnrollClient<IBiometricsFace> {
@NonNull
@Override
protected Callback wrapCallbackForStart(@NonNull Callback callback) {
return new CompositeCallback(createALSCallback(true /* startWithClient */), callback);
return new CompositeCallback(
getLogger().createALSCallback(true /* startWithClient */), callback);
}
@Override

View File

@@ -33,6 +33,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.log.CallbackWithProbe;
import com.android.server.biometrics.log.Probe;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
@@ -80,7 +82,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<ISession> imp
mLockoutCache = lockoutCache;
mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
mSensorProps = sensorProps;
mALSProbeCallback = createALSCallback(false /* startWithClient */);
mALSProbeCallback = getLogger().createALSCallback(false /* startWithClient */);
}
@Override
@@ -233,7 +235,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<ISession> imp
mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED);
// Lockout metrics are logged as an error code.
final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
logOnError(getContext(), error, 0 /* vendorCode */, getTargetUserId());
getLogger().logOnError(getContext(), error, 0 /* vendorCode */,
isCryptoOperation(), getTargetUserId());
try {
getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
@@ -251,7 +254,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<ISession> imp
mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_PERMANENT);
// Lockout metrics are logged as an error code.
final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
logOnError(getContext(), error, 0 /* vendorCode */, getTargetUserId());
getLogger().logOnError(getContext(), error, 0 /* vendorCode */,
isCryptoOperation(), getTargetUserId());
try {
getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);

View File

@@ -76,14 +76,15 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps {
mEnrollReason = enrollReason;
if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) {
setShouldLog(false);
getLogger().disableMetrics();
}
}
@NonNull
@Override
protected Callback wrapCallbackForStart(@NonNull Callback callback) {
return new CompositeCallback(createALSCallback(true /* startWithClient */), callback);
return new CompositeCallback(
getLogger().createALSCallback(true /* startWithClient */), callback);
}
@Override

View File

@@ -364,7 +364,7 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage
final ClientMonitorCallbackConverter listener = client.getListener();
final String opPackageName = client.getOwnerString();
final boolean restricted = authClient.isRestricted();
final int statsClient = client.getStatsClient();
final int statsClient = client.getLogger().getStatsClient();
final boolean isKeyguard = authClient.isKeyguard();
// Don't actually send cancel() to the HAL, since successful auth already finishes

View File

@@ -32,6 +32,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.log.CallbackWithProbe;
import com.android.server.biometrics.log.Probe;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
@@ -80,7 +82,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
mLockoutFrameworkImpl = lockoutTracker;
mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
mSensorProps = sensorProps;
mALSProbeCallback = createALSCallback(false /* startWithClient */);
mALSProbeCallback = getLogger().createALSCallback(false /* startWithClient */);
}
@Override

View File

@@ -127,8 +127,8 @@ class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint>
@Override
public void onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated,
ArrayList<Byte> hardwareAuthToken) {
logOnAuthenticated(getContext(), authenticated, false /* requireConfirmation */,
getTargetUserId(), false /* isBiometricPrompt */);
getLogger().logOnAuthenticated(getContext(), authenticated, false /* requireConfirmation */,
isCryptoOperation(), getTargetUserId(), false /* isBiometricPrompt */);
// Do not distinguish between success/failures.
vibrateSuccess();

View File

@@ -69,14 +69,15 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint
mEnrollReason = enrollReason;
if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) {
setShouldLog(false);
getLogger().disableMetrics();
}
}
@NonNull
@Override
protected Callback wrapCallbackForStart(@NonNull Callback callback) {
return new CompositeCallback(createALSCallback(true /* startWithClient */), callback);
return new CompositeCallback(
getLogger().createALSCallback(true /* startWithClient */), callback);
}
@Override

View File

@@ -0,0 +1,255 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.biometrics.log;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyFloat;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyLong;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.hardware.Sensor;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.input.InputSensorInfo;
import android.platform.test.annotations.Presubmit;
import android.testing.TestableContext;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@Presubmit
@SmallTest
public class BiometricLoggerTest {
private static final int DEFAULT_MODALITY = BiometricsProtoEnums.MODALITY_FINGERPRINT;
private static final int DEFAULT_ACTION = BiometricsProtoEnums.ACTION_AUTHENTICATE;
private static final int DEFAULT_CLIENT = BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT;
@Rule
public TestableContext mContext = new TestableContext(
InstrumentationRegistry.getInstrumentation().getContext());
@Mock
private BiometricFrameworkStatsLogger mSink;
@Mock
private SensorManager mSensorManager;
@Mock
private BaseClientMonitor mClient;
private BiometricLogger mLogger;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext.addMockSystemService(SensorManager.class, mSensorManager);
when(mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn(
new Sensor(new InputSensorInfo("", "", 0, 0, Sensor.TYPE_LIGHT, 0, 0, 0, 0, 0, 0,
"", "", 0, 0, 0))
);
}
private BiometricLogger createLogger() {
return createLogger(DEFAULT_MODALITY, DEFAULT_ACTION, DEFAULT_CLIENT);
}
private BiometricLogger createLogger(int statsModality, int statsAction, int statsClient) {
return new BiometricLogger(statsModality, statsAction, statsClient, mSink, mSensorManager);
}
@Test
public void testAcquired() {
mLogger = createLogger();
final int acquiredInfo = 2;
final int vendorCode = 3;
final boolean isCrypto = true;
final int targetUserId = 9;
mLogger.logOnAcquired(mContext, acquiredInfo, vendorCode, isCrypto, targetUserId);
verify(mSink).acquired(
eq(DEFAULT_MODALITY), eq(DEFAULT_ACTION), eq(DEFAULT_CLIENT), anyBoolean(),
eq(acquiredInfo), eq(vendorCode), eq(isCrypto), eq(targetUserId));
}
@Test
public void testAuth() {
mLogger = createLogger();
final boolean authenticated = true;
final boolean requireConfirmation = false;
final boolean isCrypto = false;
final int targetUserId = 11;
final boolean isBiometricPrompt = true;
mLogger.logOnAuthenticated(mContext,
authenticated, requireConfirmation, isCrypto, targetUserId, isBiometricPrompt);
verify(mSink).authenticate(
eq(DEFAULT_MODALITY), eq(DEFAULT_ACTION), eq(DEFAULT_CLIENT), anyBoolean(),
anyLong(), eq(authenticated), anyInt(), eq(requireConfirmation), eq(isCrypto),
eq(targetUserId), eq(isBiometricPrompt), anyFloat());
}
@Test
public void testEnroll() {
mLogger = createLogger();
final int targetUserId = 4;
final long latency = 44;
final boolean enrollSuccessful = true;
mLogger.logOnEnrolled(targetUserId, latency, enrollSuccessful);
verify(mSink).enroll(
eq(DEFAULT_MODALITY), eq(DEFAULT_ACTION), eq(DEFAULT_CLIENT),
eq(targetUserId), eq(latency), eq(enrollSuccessful), anyFloat());
}
@Test
public void testError() {
mLogger = createLogger();
final int error = 7;
final int vendorCode = 11;
final boolean isCrypto = false;
final int targetUserId = 9;
mLogger.logOnError(mContext, error, vendorCode, isCrypto, targetUserId);
verify(mSink).error(
eq(DEFAULT_MODALITY), eq(DEFAULT_ACTION), eq(DEFAULT_CLIENT), anyBoolean(),
anyLong(), eq(error), eq(vendorCode), eq(isCrypto), eq(targetUserId));
}
@Test
public void testBadModalityActsDisabled() {
mLogger = createLogger(
BiometricsProtoEnums.MODALITY_UNKNOWN, DEFAULT_ACTION, DEFAULT_CLIENT);
testDisabledMetrics(true /* isBadConfig */);
}
@Test
public void testBadActionActsDisabled() {
mLogger = createLogger(
DEFAULT_MODALITY, BiometricsProtoEnums.ACTION_UNKNOWN, DEFAULT_CLIENT);
testDisabledMetrics(true /* isBadConfig */);
}
@Test
public void testDisableLogger() {
mLogger = createLogger();
testDisabledMetrics(false /* isBadConfig */);
}
private void testDisabledMetrics(boolean isBadConfig) {
mLogger.disableMetrics();
mLogger.logOnAcquired(mContext,
0 /* acquiredInfo */,
1 /* vendorCode */,
true /* isCrypto */,
8 /* targetUserId */);
mLogger.logOnAuthenticated(mContext,
true /* authenticated */,
true /* requireConfirmation */,
false /* isCrypto */,
4 /* targetUserId */,
true/* isBiometricPrompt */);
mLogger.logOnEnrolled(2 /* targetUserId */,
10 /* latency */,
true /* enrollSuccessful */);
mLogger.logOnError(mContext,
4 /* error */,
0 /* vendorCode */,
false /* isCrypto */,
6 /* targetUserId */);
verify(mSink, never()).acquired(
anyInt(), anyInt(), anyInt(), anyBoolean(),
anyInt(), anyInt(), anyBoolean(), anyInt());
verify(mSink, never()).authenticate(
anyInt(), anyInt(), anyInt(), anyBoolean(),
anyLong(), anyBoolean(), anyInt(), anyBoolean(),
anyBoolean(), anyInt(), anyBoolean(), anyFloat());
verify(mSink, never()).enroll(
anyInt(), anyInt(), anyInt(), anyInt(), anyLong(), anyBoolean(), anyFloat());
verify(mSink, never()).error(
anyInt(), anyInt(), anyInt(), anyBoolean(),
anyLong(), anyInt(), anyInt(), anyBoolean(), anyInt());
mLogger.logUnknownEnrollmentInFramework();
mLogger.logUnknownEnrollmentInHal();
verify(mSink, times(isBadConfig ? 0 : 1))
.reportUnknownTemplateEnrolledHal(eq(DEFAULT_MODALITY));
verify(mSink, times(isBadConfig ? 0 : 1))
.reportUnknownTemplateEnrolledFramework(eq(DEFAULT_MODALITY));
}
@Test
public void systemHealthBadHalTemplate() {
mLogger = createLogger();
mLogger.logUnknownEnrollmentInHal();
verify(mSink).reportUnknownTemplateEnrolledHal(eq(DEFAULT_MODALITY));
}
@Test
public void systemHealthBadFrameworkTemplate() {
mLogger = createLogger();
mLogger.logUnknownEnrollmentInFramework();
verify(mSink).reportUnknownTemplateEnrolledFramework(eq(DEFAULT_MODALITY));
}
@Test
public void testALSCallback() {
mLogger = createLogger();
final CallbackWithProbe<Probe> callback =
mLogger.createALSCallback(true /* startWithClient */);
callback.onClientStarted(mClient);
verify(mSensorManager).registerListener(any(), any(), anyInt());
callback.onClientFinished(mClient, true /* success */);
verify(mSensorManager).unregisterListener(any(SensorEventListener.class));
}
@Test
public void testALSCallbackDoesNotStart() {
mLogger = createLogger();
final CallbackWithProbe<Probe> callback =
mLogger.createALSCallback(false /* startWithClient */);
callback.onClientStarted(mClient);
callback.onClientFinished(mClient, true /* success */);
verify(mSensorManager, never()).registerListener(any(), any(), anyInt());
}
}