Update authentication when encrypted or lockout

Fingerprint authentication should not expose accept/reject/lockout
when the user is encrypted or locked out. This is possible with
IBiometricsFingerprint@2.1 since lockout is controlled by the framework.

IBiometricsFace@1.0 does not support this since lockout is controlled
in the HAL (or lower).

Bug: 79776455

Test: On fingerprint device, during encrypted or lockdown, any finger
      works, lockout never occurs
Test: BiometricPromptDemo, normal path is run (e.g. incorrect fingers
      are rejected)
Test: Test no effect on face device
Test: atest KeyguardUpdateMonitorTest

Change-Id: I9ded8efd80d4f8b92ce054262e721853703c6437
Merged-In: I6c9717d1f8ed3e844b3d92727396e2ce2e7fd94f
This commit is contained in:
Kevin Chyn
2020-07-15 19:17:38 -07:00
parent 7b1c1db80a
commit d24084e95a
9 changed files with 275 additions and 17 deletions

View File

@@ -19,6 +19,7 @@ package android.hardware.fingerprint;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.MANAGE_FINGERPRINT;
import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.USE_FINGERPRINT;
import android.annotation.NonNull;
@@ -75,11 +76,13 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
private static final int MSG_ERROR = 104;
private static final int MSG_REMOVED = 105;
private static final int MSG_ENUMERATED = 106;
private static final int MSG_FINGERPRINT_DETECTED = 107;
private IFingerprintService mService;
private Context mContext;
private IBinder mToken = new Binder();
private AuthenticationCallback mAuthenticationCallback;
private FingerprintDetectionCallback mFingerprintDetectionCallback;
private EnrollmentCallback mEnrollmentCallback;
private RemovalCallback mRemovalCallback;
private EnumerateCallback mEnumerateCallback;
@@ -107,6 +110,13 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
}
private class OnFingerprintDetectionCancelListener implements OnCancelListener {
@Override
public void onCancel() {
cancelFingerprintDetect();
}
}
/**
* A wrapper class for the crypto objects supported by FingerprintManager. Currently the
* framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
@@ -271,6 +281,18 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
public void onAuthenticationAcquired(int acquireInfo) {}
};
/**
* Callback structure provided for {@link #detectFingerprint(CancellationSignal,
* FingerprintDetectionCallback, int)}.
* @hide
*/
public interface FingerprintDetectionCallback {
/**
* Invoked when a fingerprint has been detected.
*/
void onFingerprintDetected(int userId, boolean isStrongBiometric);
}
/**
* Callback structure provided to {@link FingerprintManager#enroll(byte[], CancellationSignal,
* int, int, EnrollmentCallback)} must provide an implementation of this for listening to
@@ -453,6 +475,35 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
}
/**
* Uses the fingerprint hardware to detect for the presence of a finger, without giving details
* about accept/reject/lockout.
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
public void detectFingerprint(@NonNull CancellationSignal cancel,
@NonNull FingerprintDetectionCallback callback, int userId) {
if (mService == null) {
return;
}
if (cancel.isCanceled()) {
Slog.w(TAG, "Detection already cancelled");
return;
} else {
cancel.setOnCancelListener(new OnFingerprintDetectionCancelListener());
}
mFingerprintDetectionCallback = callback;
try {
mService.detectFingerprint(mToken, userId, mServiceReceiver,
mContext.getOpPackageName());
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception when requesting finger detect", e);
}
}
/**
* Request fingerprint enrollment. This call warms up the fingerprint hardware
* and starts scanning for fingerprints. Progress will be indicated by callbacks to the
@@ -797,6 +848,10 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
sendEnumeratedResult((Long) msg.obj /* deviceId */, msg.arg1 /* fingerId */,
msg.arg2 /* groupId */);
break;
case MSG_FINGERPRINT_DETECTED:
sendFingerprintDetected(msg.arg1 /* userId */,
(boolean) msg.obj /* isStrongBiometric */);
break;
}
}
};
@@ -891,6 +946,14 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
}
private void sendFingerprintDetected(int userId, boolean isStrongBiometric) {
if (mFingerprintDetectionCallback == null) {
Slog.e(TAG, "sendFingerprintDetected, callback null");
return;
}
mFingerprintDetectionCallback.onFingerprintDetected(userId, isStrongBiometric);
}
/**
* @hide
*/
@@ -927,6 +990,18 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
}
private void cancelFingerprintDetect() {
if (mService == null) {
return;
}
try {
mService.cancelFingerprintDetect(mToken, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* @hide
*/
@@ -1032,6 +1107,12 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
fp).sendToTarget();
}
@Override
public void onFingerprintDetected(long deviceId, int userId, boolean isStrongBiometric) {
mHandler.obtainMessage(MSG_FINGERPRINT_DETECTED, userId, 0, isStrongBiometric)
.sendToTarget();
}
@Override // binder call
public void onAuthenticationFailed(long deviceId) {
mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();

View File

@@ -33,6 +33,11 @@ interface IFingerprintService {
void authenticate(IBinder token, long sessionId, int userId,
IFingerprintServiceReceiver receiver, int flags, String opPackageName);
// Uses the fingerprint hardware to detect for the presence of a finger, without giving details
// about accept/reject/lockout.
void detectFingerprint(IBinder token, int userId, IFingerprintServiceReceiver receiver,
String opPackageName);
// This method prepares the service to start authenticating, but doesn't start authentication.
// This is protected by the MANAGE_BIOMETRIC signatuer permission. This method should only be
// called from BiometricService. The additional uid, pid, userId arguments should be determined
@@ -48,6 +53,9 @@ interface IFingerprintService {
// Cancel authentication for the given sessionId
void cancelAuthentication(IBinder token, String opPackageName);
// Cancel finger detection
void cancelFingerprintDetect(IBinder token, String opPackageName);
// Same as above, except this is protected by the MANAGE_BIOMETRIC signature permission. Takes
// an additional uid, pid, userid.
void cancelAuthenticationFromService(IBinder token, String opPackageName,

View File

@@ -26,6 +26,7 @@ oneway interface IFingerprintServiceReceiver {
void onAcquired(long deviceId, int acquiredInfo, int vendorCode);
void onAuthenticationSucceeded(long deviceId, in Fingerprint fp, int userId,
boolean isStrongBiometric);
void onFingerprintDetected(long deviceId, int userId, boolean isStrongBiometric);
void onAuthenticationFailed(long deviceId);
void onError(long deviceId, int error, int vendorCode);
void onRemoved(long deviceId, int fingerId, int groupId, int remaining);

View File

@@ -1072,6 +1072,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
}
private boolean isEncryptedOrLockdown(int userId) {
final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(userId);
final boolean isLockDown =
containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW)
|| containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
final boolean isEncrypted = containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT);
return isEncrypted || isLockDown;
}
public boolean userNeedsStrongAuth() {
return mStrongAuthTracker.getStrongAuthForUser(getCurrentUser())
!= LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
@@ -1248,6 +1257,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
};
// Trigger the fingerprint success path so the bouncer can be shown
private final FingerprintManager.FingerprintDetectionCallback mFingerprintDetectionCallback
= this::handleFingerprintAuthenticated;
private FingerprintManager.AuthenticationCallback mFingerprintAuthenticationCallback
= new AuthenticationCallback() {
@@ -2050,8 +2063,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mFingerprintCancelSignal.cancel();
}
mFingerprintCancelSignal = new CancellationSignal();
mFpm.authenticate(null, mFingerprintCancelSignal, 0, mFingerprintAuthenticationCallback,
null, userId);
if (isEncryptedOrLockdown(userId)) {
mFpm.detectFingerprint(mFingerprintCancelSignal, mFingerprintDetectionCallback,
userId);
} else {
mFpm.authenticate(null, mFingerprintCancelSignal, 0,
mFingerprintAuthenticationCallback, null, userId);
}
setFingerprintRunningState(BIOMETRIC_STATE_RUNNING);
}
}
@@ -2087,7 +2107,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private boolean isUnlockWithFingerprintPossible(int userId) {
return mFpm != null && mFpm.isHardwareDetected() && !isFingerprintDisabled(userId)
&& mFpm.getEnrolledFingerprints(userId).size() > 0;
&& mFpm.hasEnrolledTemplates(userId);
}
private boolean isUnlockWithFacePossible(int userId) {

View File

@@ -174,6 +174,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
when(mFaceManager.isHardwareDetected()).thenReturn(true);
when(mFaceManager.hasEnrolledTemplates()).thenReturn(true);
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
when(mUserManager.isUserUnlocked(anyInt())).thenReturn(true);
when(mUserManager.isPrimaryUser()).thenReturn(true);
when(mStrongAuthTracker.getStub()).thenReturn(mock(IStrongAuthTracker.Stub.class));
@@ -418,6 +420,43 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isTrue();
}
@Test
public void testTriesToAuthenticateFingerprint_whenKeyguard() {
mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
mTestableLooper.processAllMessages();
verify(mFingerprintManager).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
verify(mFingerprintManager, never()).detectFingerprint(any(), any(), anyInt());
}
@Test
public void testFingerprintDoesNotAuth_whenEncrypted() {
testFingerprintWhenStrongAuth(
KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT);
}
@Test
public void testFingerprintDoesNotAuth_whenDpmLocked() {
testFingerprintWhenStrongAuth(
KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW);
}
@Test
public void testFingerprintDoesNotAuth_whenUserLockdown() {
testFingerprintWhenStrongAuth(
KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
}
private void testFingerprintWhenStrongAuth(int strongAuth) {
when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(strongAuth);
mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
mTestableLooper.processAllMessages();
verify(mFingerprintManager, never())
.authenticate(any(), any(), anyInt(), any(), any(), anyInt());
verify(mFingerprintManager).detectFingerprint(any(), any(), anyInt());
}
@Test
public void testTriesToAuthenticate_whenBouncer() {
mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true);

View File

@@ -722,10 +722,9 @@ public abstract class BiometricServiceBase extends SystemService
}
}
protected void handleAuthenticated(BiometricAuthenticator.Identifier identifier,
ArrayList<Byte> token) {
protected void handleAuthenticated(boolean authenticated,
BiometricAuthenticator.Identifier identifier, ArrayList<Byte> token) {
ClientMonitor client = mCurrentClient;
final boolean authenticated = identifier.getBiometricId() != 0;
if (client != null && client.onAuthenticated(identifier, authenticated, token)) {
removeClient(client);

View File

@@ -16,11 +16,19 @@
package com.android.server.biometrics;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
import static android.hardware.biometrics.BiometricManager.Authenticators;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricPrompt;
@@ -32,6 +40,9 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
import com.android.internal.R;
import com.android.internal.widget.LockPatternUtils;
import java.util.List;
public class Utils {
@@ -285,4 +296,28 @@ public class Utils {
}
return false;
}
public static boolean isKeyguard(Context context, String clientPackage) {
final boolean hasPermission = context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL)
== PackageManager.PERMISSION_GRANTED;
final ComponentName keyguardComponent = ComponentName.unflattenFromString(
context.getResources().getString(R.string.config_keyguardComponent));
final String keyguardPackage = keyguardComponent != null
? keyguardComponent.getPackageName() : null;
return hasPermission && keyguardPackage != null && keyguardPackage.equals(clientPackage);
}
private static boolean containsFlag(int haystack, int needle) {
return (haystack & needle) != 0;
}
public static boolean isUserEncryptedOrLockdown(@NonNull LockPatternUtils lpu, int user) {
final int strongAuth = lpu.getStrongAuthForUser(user);
final boolean isEncrypted = containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT);
final boolean isLockDown = containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW)
|| containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
Slog.d(TAG, "isEncrypted: " + isEncrypted + " isLockdown: " + isLockDown);
return isEncrypted || isLockDown;
}
}

View File

@@ -896,8 +896,9 @@ public class FaceService extends BiometricServiceBase {
public void onAuthenticated(final long deviceId, final int faceId, final int userId,
ArrayList<Byte> token) {
mHandler.post(() -> {
Face face = new Face("", faceId, deviceId);
FaceService.super.handleAuthenticated(face, token);
final Face face = new Face("", faceId, deviceId);
final boolean authenticated = faceId != 0;
FaceService.super.handleAuthenticated(authenticated, face, token);
});
}

View File

@@ -56,6 +56,7 @@ import android.os.SELinux;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
@@ -64,6 +65,7 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.DumpUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.biometrics.AuthenticationClient;
import com.android.server.biometrics.BiometricServiceBase;
@@ -72,6 +74,7 @@ import com.android.server.biometrics.ClientMonitor;
import com.android.server.biometrics.Constants;
import com.android.server.biometrics.EnumerateClient;
import com.android.server.biometrics.RemovalClient;
import com.android.server.biometrics.Utils;
import org.json.JSONArray;
import org.json.JSONException;
@@ -124,6 +127,8 @@ public class FingerprintService extends BiometricServiceBase {
}
private final class FingerprintAuthClient extends AuthenticationClientImpl {
private final boolean mDetectOnly;
@Override
protected boolean isFingerprint() {
return true;
@@ -133,9 +138,10 @@ public class FingerprintService extends BiometricServiceBase {
DaemonWrapper daemon, long halDeviceId, IBinder token,
ServiceListener listener, int targetUserId, int groupId, long opId,
boolean restricted, String owner, int cookie,
boolean requireConfirmation) {
boolean requireConfirmation, boolean detectOnly) {
super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId,
restricted, owner, cookie, requireConfirmation);
mDetectOnly = detectOnly;
}
@Override
@@ -177,6 +183,10 @@ public class FingerprintService extends BiometricServiceBase {
return super.handleFailedAttempt();
}
boolean isDetectOnly() {
return mDetectOnly;
}
}
/**
@@ -234,18 +244,55 @@ public class FingerprintService extends BiometricServiceBase {
}
@Override // Binder call
public void authenticate(final IBinder token, final long opId, final int groupId,
public void authenticate(final IBinder token, final long opId, final int userId,
final IFingerprintServiceReceiver receiver, final int flags,
final String opPackageName) {
updateActiveGroup(groupId, opPackageName);
if (Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)
&& Utils.isKeyguard(getContext(), opPackageName)) {
// If this happens, something in KeyguardUpdateMonitor is wrong.
// SafetyNet for b/79776455
EventLog.writeEvent(0x534e4554, "79776455");
Slog.e(TAG, "Authenticate invoked when user is encrypted or lockdown");
return;
}
updateActiveGroup(userId, opPackageName);
final boolean restricted = isRestricted();
final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
mCurrentUserId, groupId, opId, restricted, opPackageName,
0 /* cookie */, false /* requireConfirmation */);
mCurrentUserId, userId, opId, restricted, opPackageName,
0 /* cookie */, false /* requireConfirmation */, false /* detectOnly */);
authenticateInternal(client, opId, opPackageName);
}
@Override
public void detectFingerprint(final IBinder token, final int userId,
final IFingerprintServiceReceiver receiver, final String opPackageName) {
checkPermission(USE_BIOMETRIC_INTERNAL);
if (!Utils.isKeyguard(getContext(), opPackageName)) {
Slog.w(TAG, "detectFingerprint called from non-sysui package: " + opPackageName);
return;
}
if (!Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)) {
// If this happens, something in KeyguardUpdateMonitor is wrong. This should only
// ever be invoked when the user is encrypted or lockdown.
Slog.e(TAG, "detectFingerprint invoked when user is not encrypted or lockdown");
return;
}
Slog.d(TAG, "detectFingerprint, owner: " + opPackageName + ", user: " + userId);
updateActiveGroup(userId, opPackageName);
final boolean restricted = isRestricted();
final int operationId = 0;
final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
mCurrentUserId, userId, operationId, restricted, opPackageName,
0 /* cookie */, false /* requireConfirmation */, true /* detectOnly */);
authenticateInternal(client, operationId, opPackageName);
}
@Override // Binder call
public void prepareForAuthentication(IBinder token, long opId, int groupId,
IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName,
@@ -257,7 +304,7 @@ public class FingerprintService extends BiometricServiceBase {
mDaemonWrapper, mHalDeviceId, token,
new BiometricPromptServiceListenerImpl(wrapperReceiver),
mCurrentUserId, groupId, opId, restricted, opPackageName, cookie,
false /* requireConfirmation */);
false /* requireConfirmation */, false /* detectOnly */);
authenticateInternal(client, opId, opPackageName, callingUid, callingPid,
callingUserId);
}
@@ -274,6 +321,17 @@ public class FingerprintService extends BiometricServiceBase {
cancelAuthenticationInternal(token, opPackageName);
}
@Override // Binder call
public void cancelFingerprintDetect(final IBinder token, final String opPackageName) {
checkPermission(USE_BIOMETRIC_INTERNAL);
if (!Utils.isKeyguard(getContext(), opPackageName)) {
Slog.w(TAG, "cancelFingerprintDetect called from non-sysui package: "
+ opPackageName);
return;
}
cancelAuthenticationInternal(token, opPackageName);
}
@Override // Binder call
public void cancelAuthenticationFromService(final IBinder token, final String opPackageName,
int callingUid, int callingPid, int callingUserId, boolean fromClient) {
@@ -518,7 +576,12 @@ public class FingerprintService extends BiometricServiceBase {
BiometricAuthenticator.Identifier biometric, int userId)
throws RemoteException {
if (mFingerprintServiceReceiver != null) {
if (biometric == null || biometric instanceof Fingerprint) {
final ClientMonitor client = getCurrentClient();
if (client instanceof FingerprintAuthClient
&& ((FingerprintAuthClient) client).isDetectOnly()) {
mFingerprintServiceReceiver
.onFingerprintDetected(deviceId, userId, isStrongBiometric());
} else if (biometric == null || biometric instanceof Fingerprint) {
mFingerprintServiceReceiver.onAuthenticationSucceeded(deviceId,
(Fingerprint) biometric, userId, isStrongBiometric());
} else {
@@ -575,6 +638,7 @@ public class FingerprintService extends BiometricServiceBase {
private final LockoutReceiver mLockoutReceiver = new LockoutReceiver();
protected final ResetFailedAttemptsForUserRunnable mResetFailedAttemptsForCurrentUserRunnable =
new ResetFailedAttemptsForUserRunnable();
private final LockPatternUtils mLockPatternUtils;
/**
* Receives callbacks from the HAL.
@@ -608,8 +672,17 @@ public class FingerprintService extends BiometricServiceBase {
public void onAuthenticated(final long deviceId, final int fingerId, final int groupId,
ArrayList<Byte> token) {
mHandler.post(() -> {
Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
FingerprintService.super.handleAuthenticated(fp, token);
boolean authenticated = fingerId != 0;
final ClientMonitor client = getCurrentClient();
if (client instanceof FingerprintAuthClient) {
if (((FingerprintAuthClient) client).isDetectOnly()) {
Slog.w(TAG, "Detect-only. Device is encrypted or locked down");
authenticated = true;
}
}
final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
FingerprintService.super.handleAuthenticated(authenticated, fp, token);
});
}
@@ -722,6 +795,7 @@ public class FingerprintService extends BiometricServiceBase {
mAlarmManager = context.getSystemService(AlarmManager.class);
context.registerReceiver(mLockoutReceiver, new IntentFilter(getLockoutResetIntent()),
getLockoutBroadcastPermission(), null /* handler */);
mLockPatternUtils = new LockPatternUtils(context);
}
@Override