[1/n] FRR data capture

Add AuthenticationStatsCollector and AuthenticationStats to capture and calculate on-device FRR
data.

Bug: 258872351
Test: atest BiometricLoggerTest
Test: atest AuthenticationStatsTest
Test: atest AuthenticationStatsCollectorTest
Change-Id: Ib1d70fa953c6d350587d5db5ae4450827fd1830d
This commit is contained in:
Wenhui Yang
2023-06-28 05:38:13 +00:00
parent 336c012b6e
commit a0224693b0
14 changed files with 470 additions and 52 deletions

View File

@@ -5336,6 +5336,12 @@
<!-- Default value for performant auth feature. -->
<bool name="config_performantAuthDefault">false</bool>
<!-- Threshold for false rejection rate (FRR) of biometric authentication. Applies for both
fingerprint and face. If a dual-modality device only enrolled a single biometric and
experiences high FRR (above threshold), system notification will be sent to encourage user
to enroll the other eligible biometric. -->
<fraction name="config_biometricNotificationFrrThreshold">30%</fraction>
<!-- The component name for the default profile supervisor, which can be set as a profile owner
even after user setup is complete. The defined component should be used for supervision purposes
only. The component must be part of a system app. -->

View File

@@ -2581,6 +2581,9 @@
<java-symbol type="string" name="biometric_error_device_not_secured" />
<java-symbol type="string" name="biometric_error_generic" />
<!-- Biometric FRR config -->
<java-symbol type="fraction" name="config_biometricNotificationFrrThreshold" />
<!-- Device credential strings for BiometricManager -->
<java-symbol type="string" name="screen_lock_app_setting_name" />
<java-symbol type="string" name="screen_lock_dialog_default_subtitle" />

View File

@@ -0,0 +1,90 @@
/*
* Copyright (C) 2023 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;
/**
* Utility class for on-device biometric authentication data, including total authentication,
* rejections, and the number of sent enrollment notifications.
*/
public class AuthenticationStats {
private final int mUserId;
private int mTotalAttempts;
private int mRejectedAttempts;
private int mEnrollmentNotifications;
private final int mModality;
public AuthenticationStats(final int userId, int totalAttempts, int rejectedAttempts,
int enrollmentNotifications, final int modality) {
mUserId = userId;
mTotalAttempts = totalAttempts;
mRejectedAttempts = rejectedAttempts;
mEnrollmentNotifications = enrollmentNotifications;
mModality = modality;
}
public AuthenticationStats(final int userId, final int modality) {
mUserId = userId;
mTotalAttempts = 0;
mRejectedAttempts = 0;
mEnrollmentNotifications = 0;
mModality = modality;
}
public int getUserId() {
return mUserId;
}
public int getTotalAttempts() {
return mTotalAttempts;
}
public int getRejectedAttempts() {
return mRejectedAttempts;
}
public int getEnrollmentNotifications() {
return mEnrollmentNotifications;
}
public int getModality() {
return mModality;
}
/** Calculate FRR. */
public float getFrr() {
if (mTotalAttempts > 0) {
return mRejectedAttempts / (float) mTotalAttempts;
} else {
return -1.0f;
}
}
/** Update total authentication attempts and rejections. */
public void authenticate(boolean authenticated) {
if (!authenticated) {
mRejectedAttempts++;
}
mTotalAttempts++;
}
/** Reset total authentication attempts and rejections. */
public void resetData() {
mTotalAttempts = 0;
mRejectedAttempts = 0;
}
}

View File

@@ -0,0 +1,106 @@
/*
* Copyright (C) 2023 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;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import java.util.HashMap;
import java.util.Map;
/**
* Calculate and collect on-device False Rejection Rates (FRR).
* FRR = All [given biometric modality] unlock failures / all [given biometric modality] unlock
* attempts.
*/
public class AuthenticationStatsCollector {
private static final String TAG = "AuthenticationStatsCollector";
// The minimum number of attempts that will calculate the FRR and trigger the notification.
private static final int MINIMUM_ATTEMPTS = 500;
// The maximum number of eligible biometric enrollment notification can be sent.
private static final int MAXIMUM_ENROLLMENT_NOTIFICATIONS = 2;
private final float mThreshold;
private final int mModality;
@NonNull private final Map<Integer, AuthenticationStats> mUserAuthenticationStatsMap;
public AuthenticationStatsCollector(@NonNull Context context, int modality) {
mThreshold = context.getResources()
.getFraction(R.fraction.config_biometricNotificationFrrThreshold, 1, 1);
mUserAuthenticationStatsMap = new HashMap<>();
mModality = modality;
}
/** Update total authentication and rejected attempts. */
public void authenticate(int userId, boolean authenticated) {
// Check if this is a new user.
if (!mUserAuthenticationStatsMap.containsKey(userId)) {
mUserAuthenticationStatsMap.put(userId, new AuthenticationStats(userId, mModality));
}
AuthenticationStats authenticationStats = mUserAuthenticationStatsMap.get(userId);
authenticationStats.authenticate(authenticated);
persistDataIfNeeded(userId);
sendNotificationIfNeeded(userId);
}
private void sendNotificationIfNeeded(int userId) {
AuthenticationStats authenticationStats = mUserAuthenticationStatsMap.get(userId);
if (authenticationStats.getTotalAttempts() >= MINIMUM_ATTEMPTS) {
// Send notification if FRR exceeds the threshold
if (authenticationStats.getEnrollmentNotifications() < MAXIMUM_ENROLLMENT_NOTIFICATIONS
&& authenticationStats.getFrr() >= mThreshold) {
// TODO(wenhuiy): Send notifications.
}
authenticationStats.resetData();
}
}
private void persistDataIfNeeded(int userId) {
AuthenticationStats authenticationStats = mUserAuthenticationStatsMap.get(userId);
if (authenticationStats.getTotalAttempts() % 50 == 0) {
// TODO(wenhuiy): Persist data.
}
}
/**
* Only being used in tests. Callers should not make any changes to the returned
* authentication stats.
*
* @return AuthenticationStats of the user, or null if the stats doesn't exist.
*/
@Nullable
@VisibleForTesting
AuthenticationStats getAuthenticationStatsForUser(int userId) {
return mUserAuthenticationStatsMap.getOrDefault(userId, null);
}
@VisibleForTesting
void setAuthenticationStatsForUser(int userId, AuthenticationStats authenticationStats) {
mUserAuthenticationStatsMap.put(userId, authenticationStats);
}
}

View File

@@ -27,6 +27,7 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.AuthenticationStatsCollector;
import com.android.server.biometrics.Utils;
/**
@@ -41,6 +42,7 @@ public class BiometricLogger {
private final int mStatsAction;
private final int mStatsClient;
private final BiometricFrameworkStatsLogger mSink;
@NonNull private final AuthenticationStatsCollector mAuthenticationStatsCollector;
@NonNull private final ALSProbe mALSProbe;
private long mFirstAcquireTimeMs;
@@ -49,7 +51,8 @@ public class BiometricLogger {
/** Get a new logger with all unknown fields (for operations that do not require logs). */
public static BiometricLogger ofUnknown(@NonNull Context context) {
return new BiometricLogger(context, BiometricsProtoEnums.MODALITY_UNKNOWN,
BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN,
null /* AuthenticationStatsCollector */);
}
/**
@@ -64,26 +67,32 @@ public class BiometricLogger {
* @param statsClient One of {@link BiometricsProtoEnums} CLIENT_* constants.
*/
public BiometricLogger(
@NonNull Context context, int statsModality, int statsAction, int statsClient) {
@NonNull Context context, int statsModality, int statsAction, int statsClient,
AuthenticationStatsCollector authenticationStatsCollector) {
this(statsModality, statsAction, statsClient,
BiometricFrameworkStatsLogger.getInstance(),
authenticationStatsCollector,
context.getSystemService(SensorManager.class));
}
@VisibleForTesting
BiometricLogger(
int statsModality, int statsAction, int statsClient,
BiometricFrameworkStatsLogger logSink, SensorManager sensorManager) {
BiometricFrameworkStatsLogger logSink,
@NonNull AuthenticationStatsCollector statsCollector,
SensorManager sensorManager) {
mStatsModality = statsModality;
mStatsAction = statsAction;
mStatsClient = statsClient;
mSink = logSink;
mAuthenticationStatsCollector = statsCollector;
mALSProbe = new ALSProbe(sensorManager);
}
/** Creates a new logger with the action replaced with the new action. */
public BiometricLogger swapAction(@NonNull Context context, int statsAction) {
return new BiometricLogger(context, mStatsModality, statsAction, mStatsClient);
return new BiometricLogger(context, mStatsModality, statsAction, mStatsClient,
null /* AuthenticationStatsCollector */);
}
/** Disable logging metrics and only log critical events, such as system health issues. */
@@ -192,10 +201,13 @@ public class BiometricLogger {
public void logOnAuthenticated(Context context, OperationContextExt operationContext,
boolean authenticated, boolean requireConfirmation, int targetUserId,
boolean isBiometricPrompt) {
// Do not log metrics when fingerprint enrollment reason is ENROLL_FIND_SENSOR
if (!mShouldLogMetrics) {
return;
}
mAuthenticationStatsCollector.authenticate(targetUserId, authenticated);
int authState = FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__UNKNOWN;
if (!authenticated) {
authState = FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__REJECTED;

View File

@@ -49,6 +49,7 @@ import android.util.proto.ProtoOutputStream;
import android.view.Surface;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.AuthenticationStatsCollector;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
@@ -112,6 +113,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
private final BiometricContext mBiometricContext;
@NonNull
private final AuthSessionCoordinator mAuthSessionCoordinator;
@NonNull
private final AuthenticationStatsCollector mAuthenticationStatsCollector;
@Nullable
private IFace mDaemon;
@@ -173,6 +176,9 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
mAuthSessionCoordinator = mBiometricContext.getAuthSessionCoordinator();
mDaemon = daemon;
mAuthenticationStatsCollector = new AuthenticationStatsCollector(mContext,
BiometricsProtoEnums.MODALITY_FACE);
for (SensorProps prop : props) {
final int sensorId = prop.commonProps.sensorId;
@@ -283,7 +289,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
mContext, mFaceSensors.get(sensorId).getLazySession(), userId,
mContext.getOpPackageName(), sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
mBiometricContext,
mFaceSensors.get(sensorId).getAuthenticatorIds());
@@ -341,7 +348,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
final FaceInvalidationClient client = new FaceInvalidationClient(mContext,
mFaceSensors.get(sensorId).getLazySession(), userId, sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
mBiometricContext,
mFaceSensors.get(sensorId).getAuthenticatorIds(), callback);
scheduleForSensor(sensorId, client);
@@ -372,7 +380,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
mFaceSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), userId, opPackageName, sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
mBiometricContext);
scheduleForSensor(sensorId, client);
});
@@ -386,7 +395,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
mFaceSensors.get(sensorId).getLazySession(), token, userId,
opPackageName, sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
mBiometricContext, challenge);
scheduleForSensor(sensorId, client);
});
@@ -407,7 +417,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
opPackageName, id, FaceUtils.getInstance(sensorId), disabledFeatures,
ENROLL_TIMEOUT_SEC, previewSurface, sensorId,
createLogger(BiometricsProtoEnums.ACTION_ENROLL,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
mBiometricContext, maxTemplatesPerUser, debugConsent);
scheduleForSensor(sensorId, client, new ClientMonitorCompositeCallback(
mBiometricStateCallback, new ClientMonitorCallback() {
@@ -443,7 +454,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
final FaceDetectClient client = new FaceDetectClient(mContext,
mFaceSensors.get(sensorId).getLazySession(),
token, id, callback, options,
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
mAuthenticationStatsCollector),
mBiometricContext, isStrongBiometric);
scheduleForSensor(sensorId, client, mBiometricStateCallback);
});
@@ -471,7 +483,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
mContext, mFaceSensors.get(sensorId).getLazySession(), token, requestId,
callback, operationId, restricted, options, cookie,
false /* requireConfirmation */,
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
mAuthenticationStatsCollector),
mBiometricContext, isStrongBiometric,
mUsageStats, mFaceSensors.get(sensorId).getLockoutCache(),
allowBackgroundAuthentication, Utils.getCurrentStrength(sensorId));
@@ -540,7 +553,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
new ClientMonitorCallbackConverter(receiver), faceIds, userId,
opPackageName, FaceUtils.getInstance(sensorId), sensorId,
createLogger(BiometricsProtoEnums.ACTION_REMOVE,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
mBiometricContext,
mFaceSensors.get(sensorId).getAuthenticatorIds());
scheduleForSensor(sensorId, client, mBiometricStateCallback);
@@ -554,7 +568,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
mContext, mFaceSensors.get(sensorId).getLazySession(), userId,
mContext.getOpPackageName(), sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
mBiometricContext, hardwareAuthToken,
mFaceSensors.get(sensorId).getLockoutCache(), mLockoutResetDispatcher,
Utils.getCurrentStrength(sensorId));
@@ -624,7 +639,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
mFaceSensors.get(sensorId).getLazySession(), userId,
mContext.getOpPackageName(), sensorId,
createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
mBiometricContext,
FaceUtils.getInstance(sensorId),
mFaceSensors.get(sensorId).getAuthenticatorIds());
@@ -636,9 +652,10 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
});
}
private BiometricLogger createLogger(int statsAction, int statsClient) {
private BiometricLogger createLogger(int statsAction, int statsClient,
AuthenticationStatsCollector authenticationStatsCollector) {
return new BiometricLogger(mContext, BiometricsProtoEnums.MODALITY_FACE,
statsAction, statsClient);
statsAction, statsClient, authenticationStatsCollector);
}
@Override

View File

@@ -52,6 +52,7 @@ import android.view.Surface;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.AuthenticationStatsCollector;
import com.android.server.biometrics.SensorServiceStateProto;
import com.android.server.biometrics.SensorStateProto;
import com.android.server.biometrics.UserStateProto;
@@ -124,6 +125,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
@Nullable private IBiometricsFace mDaemon;
@NonNull private final HalResultController mHalResultController;
@NonNull private final BiometricContext mBiometricContext;
@NonNull private final AuthenticationStatsCollector mAuthenticationStatsCollector;
// for requests that do not use biometric prompt
@NonNull private final AtomicLong mRequestCounter = new AtomicLong(0);
private int mCurrentUserId = UserHandle.USER_NULL;
@@ -364,6 +366,9 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
mCurrentUserId = UserHandle.USER_NULL;
});
mAuthenticationStatsCollector = new AuthenticationStatsCollector(mContext,
BiometricsProtoEnums.MODALITY_FACE);
try {
ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG);
} catch (RemoteException e) {
@@ -554,7 +559,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
opPackageName, mSensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext, sSystemClock.millis());
mGeneratedChallengeCache = client;
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@@ -586,7 +591,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext,
mLazyDaemon, token, userId, opPackageName, mSensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
@@ -617,7 +622,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
opPackageName, id, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures,
ENROLL_TIMEOUT_SEC, previewSurface, mSensorId,
createLogger(BiometricsProtoEnums.ACTION_ENROLL,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@@ -677,7 +682,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final FaceAuthenticationClient client = new FaceAuthenticationClient(mContext,
mLazyDaemon, token, requestId, receiver, operationId, restricted,
options, cookie, false /* requireConfirmation */,
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
mAuthenticationStatsCollector),
mBiometricContext, isStrongBiometric, mLockoutTracker,
mUsageStats, allowBackgroundAuthentication,
Utils.getCurrentStrength(mSensorId));
@@ -713,7 +719,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
new ClientMonitorCallbackConverter(receiver), faceId, userId, opPackageName,
FaceUtils.getLegacyInstance(mSensorId), mSensorId,
createLogger(BiometricsProtoEnums.ACTION_REMOVE,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext, mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
});
@@ -731,7 +737,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
opPackageName,
FaceUtils.getLegacyInstance(mSensorId), mSensorId,
createLogger(BiometricsProtoEnums.ACTION_REMOVE,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext, mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
});
@@ -750,7 +756,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final FaceResetLockoutClient client = new FaceResetLockoutClient(mContext,
mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext, hardwareAuthToken);
mScheduler.scheduleClientMonitor(client);
});
@@ -821,7 +827,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final FaceInternalCleanupClient client = new FaceInternalCleanupClient(mContext,
mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId,
createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext,
FaceUtils.getLegacyInstance(mSensorId), mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCompositeCallback(callback,
@@ -953,7 +959,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final FaceUpdateActiveUserClient client = new FaceUpdateActiveUserClient(mContext,
mLazyDaemon, targetUserId, mContext.getOpPackageName(), mSensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext, hasEnrolled, mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
@@ -968,9 +974,10 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
});
}
private BiometricLogger createLogger(int statsAction, int statsClient) {
private BiometricLogger createLogger(int statsAction, int statsClient,
AuthenticationStatsCollector authenticationStatsCollector) {
return new BiometricLogger(mContext, BiometricsProtoEnums.MODALITY_FACE,
statsAction, statsClient);
statsAction, statsClient, authenticationStatsCollector);
}
/**

View File

@@ -57,6 +57,7 @@ import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.AuthenticationStatsCollector;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
@@ -122,6 +123,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
@Nullable private IUdfpsOverlayController mUdfpsOverlayController;
@Nullable private ISidefpsController mSidefpsController;
private AuthSessionCoordinator mAuthSessionCoordinator;
@NonNull private final AuthenticationStatsCollector mAuthenticationStatsCollector;
private final class BiometricTaskStackListener extends TaskStackListener {
@Override
@@ -181,6 +183,9 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
mAuthSessionCoordinator = mBiometricContext.getAuthSessionCoordinator();
mDaemon = daemon;
mAuthenticationStatsCollector = new AuthenticationStatsCollector(mContext,
BiometricsProtoEnums.MODALITY_FINGERPRINT);
final List<SensorLocationInternal> workaroundLocations = getWorkaroundSensorProps(context);
for (SensorProps prop : props) {
@@ -338,7 +343,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
mFingerprintSensors.get(sensorId).getLazySession(), userId,
mContext.getOpPackageName(), sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
mBiometricContext,
mFingerprintSensors.get(sensorId).getAuthenticatorIds());
scheduleForSensor(sensorId, client);
@@ -363,7 +369,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
mContext, mFingerprintSensors.get(sensorId).getLazySession(), userId,
mContext.getOpPackageName(), sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
mBiometricContext, hardwareAuthToken,
mFingerprintSensors.get(sensorId).getLockoutCache(), mLockoutResetDispatcher,
Utils.getCurrentStrength(sensorId));
@@ -380,7 +387,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
mFingerprintSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
sensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext);
scheduleForSensor(sensorId, client);
});
@@ -395,7 +402,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
mFingerprintSensors.get(sensorId).getLazySession(), token,
userId, opPackageName, sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
mBiometricContext, challenge);
scheduleForSensor(sensorId, client);
});
@@ -415,7 +423,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
createLogger(BiometricsProtoEnums.ACTION_ENROLL,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext,
mFingerprintSensors.get(sensorId).getSensorProperties(),
mUdfpsOverlayController, mSidefpsController,
@@ -455,7 +463,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
final FingerprintDetectClient client = new FingerprintDetectClient(mContext,
mFingerprintSensors.get(sensorId).getLazySession(), token, id, callback,
options,
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
mAuthenticationStatsCollector),
mBiometricContext, mUdfpsOverlayController, isStrongBiometric);
scheduleForSensor(sensorId, client, mBiometricStateCallback);
});
@@ -477,7 +486,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
mContext, mFingerprintSensors.get(sensorId).getLazySession(), token, requestId,
callback, operationId, restricted, options, cookie,
false /* requireConfirmation */,
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
mAuthenticationStatsCollector),
mBiometricContext, isStrongBiometric,
mTaskStackListener, mFingerprintSensors.get(sensorId).getLockoutCache(),
mUdfpsOverlayController, mSidefpsController,
@@ -566,7 +576,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
new ClientMonitorCallbackConverter(receiver), fingerprintIds, userId,
opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
createLogger(BiometricsProtoEnums.ACTION_REMOVE,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
mBiometricContext,
mFingerprintSensors.get(sensorId).getAuthenticatorIds());
scheduleForSensor(sensorId, client, mBiometricStateCallback);
@@ -588,7 +599,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
mFingerprintSensors.get(sensorId).getLazySession(), userId,
mContext.getOpPackageName(), sensorId,
createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
mBiometricContext,
FingerprintUtils.getInstance(sensorId),
mFingerprintSensors.get(sensorId).getAuthenticatorIds());
@@ -600,9 +612,10 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
});
}
private BiometricLogger createLogger(int statsAction, int statsClient) {
private BiometricLogger createLogger(int statsAction, int statsClient,
AuthenticationStatsCollector authenticationStatsCollector) {
return new BiometricLogger(mContext, BiometricsProtoEnums.MODALITY_FINGERPRINT,
statsAction, statsClient);
statsAction, statsClient, authenticationStatsCollector);
}
@Override
@@ -635,7 +648,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
new FingerprintInvalidationClient(mContext,
mFingerprintSensors.get(sensorId).getLazySession(), userId, sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
mBiometricContext,
mFingerprintSensors.get(sensorId).getAuthenticatorIds(), callback);
scheduleForSensor(sensorId, client);

View File

@@ -52,6 +52,7 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.AuthenticationStatsCollector;
import com.android.server.biometrics.SensorServiceStateProto;
import com.android.server.biometrics.SensorStateProto;
import com.android.server.biometrics.UserStateProto;
@@ -123,6 +124,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
@Nullable private IUdfpsOverlayController mUdfpsOverlayController;
@Nullable private ISidefpsController mSidefpsController;
@NonNull private final BiometricContext mBiometricContext;
@NonNull private final AuthenticationStatsCollector mAuthenticationStatsCollector;
// for requests that do not use biometric prompt
@NonNull private final AtomicLong mRequestCounter = new AtomicLong(0);
private int mCurrentUserId = UserHandle.USER_NULL;
@@ -351,6 +353,9 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
mCurrentUserId = UserHandle.USER_NULL;
});
mAuthenticationStatsCollector = new AuthenticationStatsCollector(mContext,
BiometricsProtoEnums.MODALITY_FINGERPRINT);
try {
ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG);
} catch (RemoteException e) {
@@ -497,7 +502,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
new FingerprintUpdateActiveUserClient(mContext, mLazyDaemon, targetUserId,
mContext.getOpPackageName(), mSensorProperties.sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
mBiometricContext,
this::getCurrentUser, hasEnrolled, mAuthenticatorIds, force);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@@ -544,7 +550,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
final FingerprintResetLockoutClient client = new FingerprintResetLockoutClient(mContext,
userId, mContext.getOpPackageName(), sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
mBiometricContext, mLockoutTracker);
mScheduler.scheduleClientMonitor(client);
});
@@ -559,7 +566,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
mSensorProperties.sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
mBiometricContext);
mScheduler.scheduleClientMonitor(client);
});
@@ -573,7 +581,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
mContext, mLazyDaemon, token, userId, opPackageName,
mSensorProperties.sensorId,
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
mBiometricContext);
mScheduler.scheduleClientMonitor(client);
});
@@ -594,7 +603,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
FingerprintUtils.getLegacyInstance(mSensorId), ENROLL_TIMEOUT_SEC,
mSensorProperties.sensorId,
createLogger(BiometricsProtoEnums.ACTION_ENROLL,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext, mUdfpsOverlayController, mSidefpsController, enrollReason);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
@@ -639,7 +648,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorProperties.sensorId);
final FingerprintDetectClient client = new FingerprintDetectClient(mContext,
mLazyDaemon, token, id, listener, options,
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
mAuthenticationStatsCollector),
mBiometricContext, mUdfpsOverlayController, isStrongBiometric);
mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
});
@@ -660,7 +670,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
mContext, mLazyDaemon, token, requestId, listener, operationId,
restricted, options, cookie, false /* requireConfirmation */,
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
mAuthenticationStatsCollector),
mBiometricContext, isStrongBiometric,
mTaskStackListener, mLockoutTracker,
mUdfpsOverlayController, mSidefpsController,
@@ -706,7 +717,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
userId, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId),
mSensorProperties.sensorId,
createLogger(BiometricsProtoEnums.ACTION_REMOVE,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext, mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
});
@@ -726,7 +737,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
FingerprintUtils.getLegacyInstance(mSensorId),
mSensorProperties.sensorId,
createLogger(BiometricsProtoEnums.ACTION_REMOVE,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext, mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
});
@@ -741,7 +752,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
mContext, mLazyDaemon, userId, mContext.getOpPackageName(),
mSensorProperties.sensorId,
createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
BiometricsProtoEnums.CLIENT_UNKNOWN),
BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext,
FingerprintUtils.getLegacyInstance(mSensorId), mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, callback);
@@ -762,9 +773,10 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
mBiometricStateCallback));
}
private BiometricLogger createLogger(int statsAction, int statsClient) {
private BiometricLogger createLogger(int statsAction, int statsClient,
AuthenticationStatsCollector authenticationStatsCollector) {
return new BiometricLogger(mContext, BiometricsProtoEnums.MODALITY_FINGERPRINT,
statsAction, statsClient);
statsAction, statsClient, authenticationStatsCollector);
}
@Override

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2023 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;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
import com.android.internal.R;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
public class AuthenticationStatsCollectorTest {
private AuthenticationStatsCollector mAuthenticationStatsCollector;
private static final float FRR_THRESHOLD = 0.2f;
private static final int USER_ID_1 = 1;
@Mock
private Context mContext;
@Mock
private Resources mResources;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mContext.getResources()).thenReturn(mResources);
when(mResources.getFraction(R.fraction.config_biometricNotificationFrrThreshold, 1, 1))
.thenReturn(FRR_THRESHOLD);
mAuthenticationStatsCollector = new AuthenticationStatsCollector(mContext,
0 /* modality */);
}
@Test
public void authenticate_authenticationSucceeded_mapShouldBeUpdated() {
// Assert that the user doesn't exist in the map initially.
assertNull(mAuthenticationStatsCollector.getAuthenticationStatsForUser(USER_ID_1));
mAuthenticationStatsCollector.authenticate(USER_ID_1, true /* authenticated*/);
AuthenticationStats authenticationStats =
mAuthenticationStatsCollector.getAuthenticationStatsForUser(USER_ID_1);
assertEquals(USER_ID_1, authenticationStats.getUserId());
assertEquals(1, authenticationStats.getTotalAttempts());
assertEquals(0, authenticationStats.getRejectedAttempts());
assertEquals(0, authenticationStats.getEnrollmentNotifications());
}
@Test
public void authenticate_authenticationFailed_mapShouldBeUpdated() {
// Assert that the user doesn't exist in the map initially.
assertNull(mAuthenticationStatsCollector.getAuthenticationStatsForUser(USER_ID_1));
mAuthenticationStatsCollector.authenticate(USER_ID_1, false /* authenticated*/);
AuthenticationStats authenticationStats =
mAuthenticationStatsCollector.getAuthenticationStatsForUser(USER_ID_1);
assertEquals(USER_ID_1, authenticationStats.getUserId());
assertEquals(1, authenticationStats.getTotalAttempts());
assertEquals(1, authenticationStats.getRejectedAttempts());
assertEquals(0, authenticationStats.getEnrollmentNotifications());
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2023 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;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class AuthenticationStatsTest {
@Test
public void authenticate_statsShouldBeUpdated() {
AuthenticationStats authenticationStats =
new AuthenticationStats(1 /* userId */ , 0 /* totalAttempts */,
0 /* rejectedAttempts */, 0 /* enrollmentNotifications */,
0 /* modality */);
authenticationStats.authenticate(true /* authenticated */);
assertEquals(authenticationStats.getTotalAttempts(), 1);
assertEquals(authenticationStats.getRejectedAttempts(), 0);
authenticationStats.authenticate(false /* authenticated */);
assertEquals(authenticationStats.getTotalAttempts(), 2);
assertEquals(authenticationStats.getRejectedAttempts(), 1);
}
}

View File

@@ -39,6 +39,7 @@ import android.testing.TestableContext;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.server.biometrics.AuthenticationStatsCollector;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import org.junit.Before;
@@ -65,6 +66,8 @@ public class BiometricLoggerTest {
@Mock
private BiometricFrameworkStatsLogger mSink;
@Mock
private AuthenticationStatsCollector mAuthenticationStatsCollector;
@Mock
private SensorManager mSensorManager;
@Mock
private BaseClientMonitor mClient;
@@ -87,7 +90,8 @@ public class BiometricLoggerTest {
}
private BiometricLogger createLogger(int statsModality, int statsAction, int statsClient) {
return new BiometricLogger(statsModality, statsAction, statsClient, mSink, mSensorManager);
return new BiometricLogger(statsModality, statsAction, statsClient, mSink,
mAuthenticationStatsCollector, mSensorManager);
}
@Test

View File

@@ -27,6 +27,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
import android.hardware.biometrics.common.CommonProps;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.ISession;
@@ -39,6 +40,7 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
@@ -59,11 +61,15 @@ public class FaceProviderTest {
private static final String TAG = "FaceProviderTest";
private static final float FRR_THRESHOLD = 0.2f;
@Mock
private Context mContext;
@Mock
private UserManager mUserManager;
@Mock
private Resources mResources;
@Mock
private IFace mDaemon;
@Mock
private BiometricContext mBiometricContext;
@@ -86,6 +92,10 @@ public class FaceProviderTest {
when(mUserManager.getAliveUsers()).thenReturn(new ArrayList<>());
when(mDaemon.createSession(anyInt(), anyInt(), any())).thenReturn(mock(ISession.class));
when(mContext.getResources()).thenReturn(mResources);
when(mResources.getFraction(R.fraction.config_biometricNotificationFrrThreshold, 1, 1))
.thenReturn(FRR_THRESHOLD);
final SensorProps sensor1 = new SensorProps();
sensor1.commonProps = new CommonProps();
sensor1.commonProps.sensorId = 0;

View File

@@ -26,6 +26,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.SensorProperties;
import android.hardware.face.FaceSensorProperties;
@@ -41,6 +42,7 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.BiometricStateCallback;
@@ -65,12 +67,15 @@ public class Face10Test {
private static final String TAG = "Face10Test";
private static final int SENSOR_ID = 1;
private static final int USER_ID = 20;
private static final float FRR_THRESHOLD = 0.2f;
@Mock
private Context mContext;
@Mock
private UserManager mUserManager;
@Mock
private Resources mResources;
@Mock
private BiometricScheduler mScheduler;
@Mock
private BiometricContext mBiometricContext;
@@ -93,6 +98,10 @@ public class Face10Test {
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
when(mUserManager.getAliveUsers()).thenReturn(new ArrayList<>());
when(mContext.getResources()).thenReturn(mResources);
when(mResources.getFraction(R.fraction.config_biometricNotificationFrrThreshold, 1, 1))
.thenReturn(FRR_THRESHOLD);
mLockoutResetDispatcher = new LockoutResetDispatcher(mContext);
final int maxEnrollmentsPerUser = 1;