diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java index ef02a472969b3..8f364890aa06c 100644 --- a/services/core/java/com/android/server/biometrics/AuthSession.java +++ b/services/core/java/com/android/server/biometrics/AuthSession.java @@ -679,7 +679,8 @@ public final class AuthSession implements IBinder.DeathRecipient { FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED, latency, mDebugEnabled, - -1 /* sensorId */); + -1 /* sensorId */, + -1f /* ambientLightLux */); } else { final long latency = System.currentTimeMillis() - mStartTimeMs; diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java index 99f4e2cb280cc..f51b1c2a4f0c2 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java +++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java @@ -69,6 +69,31 @@ public abstract class BaseClientMonitor extends LoggableMonitor } } + /** Holder for wrapping multiple handlers into a single Callback. */ + protected static class CompositeCallback implements Callback { + @NonNull + private final Callback[] mCallbacks; + + public CompositeCallback(@NonNull Callback... callbacks) { + mCallbacks = callbacks; + } + + @Override + public final void onClientStarted(@NonNull BaseClientMonitor clientMonitor) { + for (int i = 0; i < mCallbacks.length; i++) { + mCallbacks[i].onClientStarted(clientMonitor); + } + } + + @Override + public final void onClientFinished(@NonNull BaseClientMonitor clientMonitor, + boolean success) { + for (int i = mCallbacks.length - 1; i >= 0; i--) { + mCallbacks[i].onClientFinished(clientMonitor, success); + } + } + } + private final int mSequentialId; @NonNull private final Context mContext; private final int mTargetUserId; @@ -125,7 +150,7 @@ 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(statsModality, statsAction, statsClient); + super(context, statsModality, statsAction, statsClient); mSequentialId = sCount++; mContext = context; mToken = token; @@ -153,10 +178,19 @@ public abstract class BaseClientMonitor extends LoggableMonitor * @param callback invoked when the operation is complete (succeeds, fails, etc) */ public void start(@NonNull Callback callback) { - mCallback = callback; + mCallback = wrapCallbackForStart(callback); mCallback.onClientStarted(this); } + /** + * Called during start to provide subclasses a hook for decorating the callback. + * + * Returns the original callback unless overridden. + */ + @NonNull + protected Callback wrapCallbackForStart(@NonNull Callback callback) { + return callback; + } public boolean isAlreadyDone() { return mAlreadyDone; diff --git a/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java b/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java index 4da644d23dec7..c8867ea2df1c4 100644 --- a/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java +++ b/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java @@ -16,7 +16,13 @@ package com.android.server.biometrics.sensors; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.face.FaceManager; @@ -37,9 +43,41 @@ public abstract class LoggableMonitor { final int mStatsModality; private final int mStatsAction; private final int mStatsClient; + @NonNull private final SensorManager mSensorManager; private long mFirstAcquireTimeMs; + private boolean mLightSensorEnabled = false; private boolean mShouldLogMetrics = true; + // report only the most recent value + // consider com.android.server.display.utils.AmbientFilter or similar if need arises + private volatile float mLastAmbientLux = 0; + + private final SensorEventListener mLightSensorListener = new SensorEventListener() { + @Override + public void onSensorChanged(SensorEvent event) { + mLastAmbientLux = event.values[0]; + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // Not used. + } + }; + + /** + * @param context system_server context + * @param statsModality One of {@link BiometricsProtoEnums} MODALITY_* constants. + * @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) { + mStatsModality = statsModality; + mStatsAction = statsAction; + mStatsClient = statsClient; + mSensorManager = context.getSystemService(SensorManager.class); + } + /** * Only valid for AuthenticationClient. * @return true if the client is authenticating for a crypto operation. @@ -48,17 +86,6 @@ public abstract class LoggableMonitor { return false; } - /** - * @param statsModality One of {@link BiometricsProtoEnums} MODALITY_* constants. - * @param statsAction One of {@link BiometricsProtoEnums} ACTION_* constants. - * @param statsClient One of {@link BiometricsProtoEnums} CLIENT_* constants. - */ - public LoggableMonitor(int statsModality, int statsAction, int statsClient) { - mStatsModality = statsModality; - mStatsAction = statsAction; - mStatsClient = statsClient; - } - protected void setShouldLog(boolean shouldLog) { mShouldLogMetrics = shouldLog; } @@ -131,7 +158,6 @@ public abstract class LoggableMonitor { } protected final void logOnError(Context context, int error, int vendorCode, int targetUserId) { - if (!mShouldLogMetrics) { return; } @@ -199,7 +225,8 @@ public abstract class LoggableMonitor { + ", Client: " + mStatsClient + ", RequireConfirmation: " + requireConfirmation + ", State: " + authState - + ", Latency: " + latency); + + ", Latency: " + latency + + ", Lux: " + mLastAmbientLux); } else { Slog.v(TAG, "Authentication latency: " + latency); } @@ -217,7 +244,8 @@ public abstract class LoggableMonitor { authState, sanitizeLatency(latency), Utils.isDebugEnabled(context, targetUserId), - -1 /* sensorId */); + -1 /* sensorId */, + mLastAmbientLux /* ambientLightLux */); } protected final void logOnEnrolled(int targetUserId, long latency, boolean enrollSuccessful) { @@ -230,6 +258,7 @@ public abstract class LoggableMonitor { + ", User: " + targetUserId + ", Client: " + mStatsClient + ", Latency: " + latency + + ", Lux: " + mLastAmbientLux + ", Success: " + enrollSuccessful); } else { Slog.v(TAG, "Enroll latency: " + latency); @@ -244,7 +273,8 @@ public abstract class LoggableMonitor { targetUserId, sanitizeLatency(latency), enrollSuccessful, - -1 /* sensorId */); + -1, /* sensorId */ + mLastAmbientLux /* ambientLightLux */); } private long sanitizeLatency(long latency) { @@ -255,4 +285,46 @@ public abstract class LoggableMonitor { return latency; } + /** Get a callback to start/stop ALS capture when client runs. */ + @NonNull + protected BaseClientMonitor.Callback createALSCallback() { + return new BaseClientMonitor.Callback() { + @Override + public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) { + setLightSensorLoggingEnabled(getAmbientLightSensor(mSensorManager)); + } + + @Override + public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, + boolean success) { + setLightSensorLoggingEnabled(null); + } + }; + } + + /** The sensor to use for ALS logging. */ + @Nullable + protected Sensor getAmbientLightSensor(@NonNull SensorManager sensorManager) { + return mShouldLogMetrics ? sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT) : null; + } + + private void setLightSensorLoggingEnabled(@Nullable Sensor lightSensor) { + if (DEBUG) { + Slog.v(TAG, "capturing ambient light using: " + + (lightSensor != null ? lightSensor : "[disabled]")); + } + + if (lightSensor != null) { + if (!mLightSensorEnabled) { + mLightSensorEnabled = true; + mLastAmbientLux = 0; + mSensorManager.registerListener(mLightSensorListener, lightSensor, + SensorManager.SENSOR_DELAY_NORMAL); + } + } else { + mLightSensorEnabled = false; + mLastAmbientLux = 0; + mSensorManager.unregisterListener(mLightSensorListener); + } + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java index 2ab1285ba8a75..2adf5f98cee5b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java @@ -99,6 +99,12 @@ class FaceAuthenticationClient extends AuthenticationClient implements "face_custom_success_error", 0) == 1; } + @NonNull + @Override + protected Callback wrapCallbackForStart(@NonNull Callback callback) { + return new CompositeCallback(createALSCallback(), callback); + } + @Override protected void startHalOperation() { try { @@ -229,7 +235,8 @@ class FaceAuthenticationClient extends AuthenticationClient implements } } - @Override public void onLockoutTimed(long durationMillis) { + @Override + public void onLockoutTimed(long durationMillis) { mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED); // Lockout metrics are logged as an error code. final int error = BiometricFaceConstants.FACE_ERROR_LOCKOUT; @@ -242,7 +249,8 @@ class FaceAuthenticationClient extends AuthenticationClient implements } } - @Override public void onLockoutPermanent() { + @Override + public void onLockoutPermanent() { mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_PERMANENT); // Lockout metrics are logged as an error code. final int error = BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT; diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java index f97414780247a..ff68aa87dbbb0 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java @@ -95,6 +95,12 @@ public class FaceEnrollClient extends EnrollClient { ReEnrollNotificationUtils.cancelNotification(getContext()); } + @NonNull + @Override + protected Callback wrapCallbackForStart(@NonNull Callback callback) { + return new CompositeCallback(createALSCallback(), callback); + } + @Override public void destroy() { try { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java index c4bdb320ddef6..01dd18fb5cf05 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java @@ -88,6 +88,12 @@ class FaceAuthenticationClient extends AuthenticationClient { "face_custom_success_error", 0) == 1; } + @NonNull + @Override + protected Callback wrapCallbackForStart(@NonNull Callback callback) { + return new CompositeCallback(createALSCallback(), callback); + } + @Override protected void startHalOperation() { try { @@ -186,7 +192,6 @@ class FaceAuthenticationClient extends AuthenticationClient { @Override public void onAcquired(int acquireInfo, int vendorCode) { - mLastAcquire = acquireInfo; if (acquireInfo == FaceManager.FACE_ACQUIRED_RECALIBRATE) { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java index fc1200a4b42af..d3bd18b6f7043 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java @@ -68,6 +68,12 @@ public class FaceEnrollClient extends EnrollClient { .getIntArray(R.array.config_face_acquire_vendor_enroll_ignorelist); } + @NonNull + @Override + protected Callback wrapCallbackForStart(@NonNull Callback callback) { + return new CompositeCallback(createALSCallback(), callback); + } + @Override protected boolean hasReachedEnrollmentLimit() { final int limit = getContext().getResources().getInteger( diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java index a5326b352264f..3c9d802cb4b18 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java @@ -70,6 +70,12 @@ class FingerprintAuthenticationClient extends AuthenticationClient imp mUdfpsOverlayController = udfpsOverlayController; } + @NonNull + @Override + protected Callback wrapCallbackForStart(@NonNull Callback callback) { + return new CompositeCallback(createALSCallback(), callback); + } + @Override public void onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated, ArrayList token) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java index edd30bc6b3331..11849661693b3 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java @@ -79,6 +79,12 @@ class FingerprintEnrollClient extends EnrollClient implements Udfps { } } + @NonNull + @Override + protected Callback wrapCallbackForStart(@NonNull Callback callback) { + return new CompositeCallback(createALSCallback(), callback); + } + @Override public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) { super.onEnrollResult(identifier, remaining); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java index 24ed85c42e4ef..45e35e34b62b8 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java @@ -69,6 +69,12 @@ class FingerprintAuthenticationClient extends AuthenticationClient token) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java index 4f3e930c4e880..a28a3f6bd5c3b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java @@ -72,6 +72,12 @@ public class FingerprintEnrollClient extends EnrollClient