31/n: Move Fingerprint2.1 into a wrapper with its own scheduler

This is a pretty big change since it basically allows/requires us to
have FingerprintService not extend from BiometricServiceBase anymore

Bug: 157790417
Bug: 158481661

1) Move Fingerprint2.1-specific code into wrapper class. This will
   A) Make it obvious where global dependencies still exist, and
   B) Make room for new HIDLs
2) Sensor-ID is creeping in. Eventually there will be Manager-level
   APIs for platform code to query functionality. Most likely all
   IFingerprintService/IFaceService interfaces will have sensorId
   as a parameter soon
3) Added Udfps interface and UdfpsHelper
4) FingerprintService no longer extends from BiometricServiceBase.
   BiometricServiceBase will be removed soon. A lot of permission
   checking is fingerprint-specific, since face operations all
   require internal permission. Thus, moved canUseBiometric into
   FingerprintService
5) Updated fingerprint retry logic in KeyguardUpdateMonitor. The
   retry should keep going if isHardwareDetected()==false. Also
   increased the retry counter, seems like HAL reload time has
   increased.

Test: enroll, auth (settings), auth (keyguard), auth (BiometricPrompt),
      rename, remove
Test: atest com.android.server.biometrics
Test: atest KeyguardUpdateMonitorTest
Test: Reset lockout
Test: atest AccessibilityFingerprintGestureTest
Test: Fingerprint stack recovers after HAL death, e.g.
      1) Go to keyguard
      2) adb shell ps | grep -i fingerprint
      3) adb shell killall -9 android.hardware.biometrics...
      4) fingerprint auth resumes after HAL reloads
Test: CtsVerifier
Test: adb shell dumpsys fingerprint
Test: Modify settings to not cancel auth when onPause, notice
      task stack listener stopping settings auth
Test: Invoke assistant on keyguard, auth continues
Test: No effect on face auth devices (enroll, auth on keyguard,
      BiometricPrompt)

Change-Id: Iffbdd841c093fe612e664195dcf79319d1d444ab
diff --git a/core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl b/core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl
index aa5ac03..754162c 100644
--- a/core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl
@@ -26,5 +26,5 @@
     /**
      * A wakelock will be held until the reciever calls back into {@param callback}
      */
-    void onLockoutReset(IRemoteCallback callback);
+    void onLockoutReset(int sensorId, IRemoteCallback callback);
 }
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index c7fc2ad..b4546a4 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -592,7 +592,7 @@
                         new IBiometricServiceLockoutResetCallback.Stub() {
 
                             @Override
-                            public void onLockoutReset(IRemoteCallback serverCallback)
+                            public void onLockoutReset(int sensorId, IRemoteCallback serverCallback)
                                     throws RemoteException {
                                 try {
                                     final PowerManager.WakeLock wakeLock = powerManager.newWakeLock(
@@ -601,7 +601,7 @@
                                     wakeLock.acquire();
                                     mHandler.post(() -> {
                                         try {
-                                            callback.onLockoutReset();
+                                            callback.onLockoutReset(sensorId);
                                         } finally {
                                             wakeLock.release();
                                         }
@@ -978,7 +978,7 @@
          * authentication
          * again.
          */
-        public void onLockoutReset() {
+        public void onLockoutReset(int sensorId) {
         }
     }
 
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 9dacca7..4ca75d9 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -347,7 +347,7 @@
          * Called when lockout period expired and clients are allowed to listen for fingerprint
          * again.
          */
-        public void onLockoutReset() { }
+        public void onLockoutReset(int sensorId) { }
     };
 
     /**
@@ -751,7 +751,7 @@
                         new IBiometricServiceLockoutResetCallback.Stub() {
 
                     @Override
-                    public void onLockoutReset(IRemoteCallback serverCallback)
+                    public void onLockoutReset(int sensorId, IRemoteCallback serverCallback)
                             throws RemoteException {
                         try {
                             final PowerManager.WakeLock wakeLock = powerManager.newWakeLock(
@@ -759,7 +759,7 @@
                             wakeLock.acquire();
                             mHandler.post(() -> {
                                 try {
-                                    callback.onLockoutReset();
+                                    callback.onLockoutReset(sensorId);
                                 } finally {
                                     wakeLock.release();
                                 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index e45cfe2..e9b58c2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -295,7 +295,7 @@
     private int mHardwareFingerprintUnavailableRetryCount = 0;
     private int mHardwareFaceUnavailableRetryCount = 0;
     private static final int HAL_ERROR_RETRY_TIMEOUT = 500; // ms
-    private static final int HAL_ERROR_RETRY_MAX = 10;
+    private static final int HAL_ERROR_RETRY_MAX = 20;
 
     private final Runnable mCancelNotReceived = new Runnable() {
         @Override
@@ -682,7 +682,12 @@
         public void run() {
             Log.w(TAG, "Retrying fingerprint after HW unavailable, attempt " +
                     mHardwareFingerprintUnavailableRetryCount);
-            updateFingerprintListeningState();
+            if (mFpm.isHardwareDetected()) {
+                updateFingerprintListeningState();
+            } else if (mHardwareFingerprintUnavailableRetryCount < HAL_ERROR_RETRY_MAX) {
+                mHardwareFingerprintUnavailableRetryCount++;
+                mHandler.postDelayed(mRetryFingerprintAuthentication, HAL_ERROR_RETRY_TIMEOUT);
+            }
         }
     };
 
@@ -704,11 +709,7 @@
         }
 
         if (msgId == FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE) {
-            if (mHardwareFingerprintUnavailableRetryCount < HAL_ERROR_RETRY_MAX) {
-                mHardwareFingerprintUnavailableRetryCount++;
-                mHandler.removeCallbacks(mRetryFingerprintAuthentication);
-                mHandler.postDelayed(mRetryFingerprintAuthentication, HAL_ERROR_RETRY_TIMEOUT);
-            }
+            mHandler.postDelayed(mRetryFingerprintAuthentication, HAL_ERROR_RETRY_TIMEOUT);
         }
 
         if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) {
@@ -1234,7 +1235,7 @@
     private final FingerprintManager.LockoutResetCallback mFingerprintLockoutResetCallback
             = new FingerprintManager.LockoutResetCallback() {
         @Override
-        public void onLockoutReset() {
+        public void onLockoutReset(int sensorId) {
             handleFingerprintLockoutReset();
         }
     };
@@ -1242,7 +1243,7 @@
     private final FaceManager.LockoutResetCallback mFaceLockoutResetCallback
             = new FaceManager.LockoutResetCallback() {
         @Override
-        public void onLockoutReset() {
+        public void onLockoutReset(int sensorId) {
             handleFaceLockoutReset();
         }
     };
@@ -1838,7 +1839,7 @@
         if (mHandler.hasMessages(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE)) {
             return;
         }
-        mHandler.removeCallbacks(mRetryFingerprintAuthentication);
+
         boolean shouldListenForFingerprint = shouldListenForFingerprint();
         boolean runningOrRestarting = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING
                 || mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING;
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index 6be4574..29b8493 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -16,6 +16,7 @@
 
 package com.android.server.biometrics;
 
+import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
 import static android.hardware.biometrics.BiometricManager.Authenticators;
 
 import static com.android.server.biometrics.PreAuthInfo.AUTHENTICATOR_OK;
@@ -30,19 +31,32 @@
 import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_NO_HARDWARE;
 import static com.android.server.biometrics.PreAuthInfo.CREDENTIAL_NOT_ENROLLED;
 
+import android.app.ActivityManager;
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.BiometricPrompt;
 import android.hardware.biometrics.BiometricPrompt.AuthenticationResultType;
+import android.hardware.biometrics.IBiometricService;
 import android.hardware.biometrics.PromptInfo;
+import android.os.Binder;
 import android.os.Build;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.provider.Settings;
 import android.util.Slog;
 
+import com.android.internal.R;
+
 public class Utils {
+
+    private static final String TAG = "BiometricUtils";
+
     public static boolean isDebugEnabled(Context context, int targetUserId) {
         if (targetUserId == UserHandle.USER_NULL) {
             return false;
@@ -331,4 +345,54 @@
         }
         return false;
     }
+
+    public static void checkPermission(Context context, String permission) {
+        context.enforceCallingOrSelfPermission(permission,
+                "Must have " + permission + " permission.");
+    }
+
+    public static boolean isCurrentUserOrProfile(Context context, int userId) {
+        UserManager um = UserManager.get(context);
+        if (um == null) {
+            Slog.e(TAG, "Unable to get UserManager");
+            return false;
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            // Allow current user or profiles of the current user...
+            for (int profileId : um.getEnabledProfileIds(ActivityManager.getCurrentUser())) {
+                if (profileId == userId) {
+                    return true;
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+
+        return false;
+    }
+
+    public static boolean isStrongBiometric(int sensorId) {
+        IBiometricService service = IBiometricService.Stub.asInterface(
+                ServiceManager.getService(Context.BIOMETRIC_SERVICE));
+        try {
+            return Utils.isAtLeastStrength(service.getCurrentStrength(sensorId),
+                    Authenticators.BIOMETRIC_STRONG);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "RemoteException", e);
+            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);
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
index ac3d867..14baaa7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
@@ -45,6 +45,7 @@
     private final PowerManager mPowerManager;
     private final VibrationEffect mSuccessVibrationEffect;
     private final VibrationEffect mErrorVibrationEffect;
+    private boolean mErrorAlreadySent;
 
     /**
      * Stops the HAL operation specific to the ClientMonitor subclass.
@@ -74,15 +75,43 @@
 
     @Override
     public void onError(int errorCode, int vendorCode) {
-        logOnError(getContext(), errorCode, vendorCode, getTargetUserId());
+        // Errors from the HAL always finish the client
+        onErrorInternal(errorCode, vendorCode, true /* finish */);
+    }
+
+    protected void onErrorInternal(int errorCode, int vendorCode, boolean finish) {
+        // In some cases, the framework will send an error to the caller before a true terminal
+        // case (success, failure, or error) is received from the HAL (e.g. versions of fingerprint
+        // that do not handle lockout under the HAL. In these cases, ensure that the framework only
+        // sends errors once per ClientMonitor.
+        if (!mErrorAlreadySent) {
+            logOnError(getContext(), errorCode, vendorCode, getTargetUserId());
+            try {
+                if (getListener() != null) {
+                    mErrorAlreadySent = true;
+                    getListener().onError(getSensorId(), getCookie(), errorCode, vendorCode);
+                }
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to invoke sendError", e);
+            }
+        }
+
+        if (finish) {
+            mFinishCallback.onClientFinished(this, false /* success */);
+        }
+    }
+
+    @Override
+    public void cancelWithoutStarting(@NonNull FinishCallback finishCallback) {
+        final int errorCode = BiometricConstants.BIOMETRIC_ERROR_CANCELED;
         try {
             if (getListener() != null) {
-                getListener().onError(getSensorId(), getCookie(), errorCode, vendorCode);
+                getListener().onError(getSensorId(), getCookie(), errorCode, 0 /* vendorCode */);
             }
         } catch (RemoteException e) {
             Slog.w(TAG, "Failed to invoke sendError", e);
         }
-        mFinishCallback.onClientFinished(this, false /* success */);
+        finishCallback.onClientFinished(this, true /* success */);
     }
 
     /**
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index df836d5..5392f0f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -119,6 +119,13 @@
                     + ", requireConfirmation: " + mRequireConfirmation
                     + ", user: " + getTargetUserId());
 
+            final PerformanceTracker pm = PerformanceTracker.getInstanceForSensorId(getSensorId());
+            if (isCryptoOperation()) {
+                pm.incrementCryptoAuthForUser(getTargetUserId(), authenticated);
+            } else {
+                pm.incrementAuthForUser(getTargetUserId(), authenticated);
+            }
+
             if (authenticated) {
                 mAlreadyDone = true;
 
@@ -184,6 +191,18 @@
         }
     }
 
+    @Override
+    public void onAcquired(int acquiredInfo, int vendorCode) {
+        super.onAcquired(acquiredInfo, vendorCode);
+
+        final @LockoutTracker.LockoutMode int lockoutMode =
+                mLockoutTracker.getLockoutModeForUser(getTargetUserId());
+        if (lockoutMode == LockoutTracker.LOCKOUT_NONE) {
+            PerformanceTracker pt = PerformanceTracker.getInstanceForSensorId(getSensorId());
+            pt.incrementAcquireForUser(getTargetUserId(), isCryptoOperation());
+        }
+    }
+
     /**
      * Start authentication
      */
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
new file mode 100644
index 0000000..c9ab313
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -0,0 +1,464 @@
+/*
+ * Copyright (C) 2020 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.sensors;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.biometrics.IBiometricService;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityTracker;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.text.SimpleDateFormat;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Queue;
+
+/**
+ * A scheduler for biometric HAL operations. Maintains a queue of {@link ClientMonitor} operations,
+ * without caring about its implementation details. Operations may perform one or more
+ * interactions with the HAL before finishing.
+ */
+public class BiometricScheduler {
+
+    private static final String BASE_TAG = "BiometricScheduler";
+
+    /**
+     * Contains all the necessary information for a HAL operation.
+     */
+    private static final class Operation {
+
+        /**
+         * The operation is added to the list of pending operations and waiting for its turn.
+         */
+        static final int STATE_WAITING_IN_QUEUE = 0;
+
+        /**
+         * The operation is added to the list of pending operations, but a subsequent operation
+         * has been added. This state only applies to {@link Interruptable} operations. When this
+         * operation reaches the head of the queue, it will send ERROR_CANCELED and finish.
+         */
+        static final int STATE_WAITING_IN_QUEUE_CANCELING = 1;
+
+        /**
+         * The operation has reached the front of the queue and has started.
+         */
+        static final int STATE_STARTED = 2;
+
+        /**
+         * The operation was started, but is now canceling. Operations should wait for the HAL to
+         * acknowledge that the operation was canceled, at which point it finishes.
+         */
+        static final int STATE_STARTED_CANCELING = 3;
+
+        /**
+         * The operation has reached the head of the queue but is waiting for BiometricService
+         * to acknowledge and start the operation.
+         */
+        static final int STATE_WAITING_FOR_COOKIE = 4;
+
+        /**
+         * The {@link ClientMonitor.FinishCallback} has been invoked and the client is finished.
+         */
+        static final int STATE_FINISHED = 5;
+
+        @IntDef({STATE_WAITING_IN_QUEUE,
+                STATE_WAITING_IN_QUEUE_CANCELING,
+                STATE_STARTED,
+                STATE_STARTED_CANCELING,
+                STATE_WAITING_FOR_COOKIE,
+                STATE_FINISHED})
+        @Retention(RetentionPolicy.SOURCE)
+        @interface OperationState {}
+
+        @NonNull final ClientMonitor<?> clientMonitor;
+        @Nullable final ClientMonitor.FinishCallback clientFinishCallback;
+        @OperationState int state;
+
+        Operation(@NonNull ClientMonitor<?> clientMonitor,
+                @Nullable ClientMonitor.FinishCallback finishCallback) {
+            this.clientMonitor = clientMonitor;
+            this.clientFinishCallback = finishCallback;
+            state = STATE_WAITING_IN_QUEUE;
+        }
+
+        @Override
+        public String toString() {
+            return clientMonitor + ", State: " + state;
+        }
+    }
+
+    /**
+     * Monitors an operation's cancellation. If cancellation takes too long, the watchdog will
+     * kill the current operation and forcibly start the next.
+     */
+    private static final class CancellationWatchdog implements Runnable {
+        static final int DELAY_MS = 3000;
+
+        final String tag;
+        final Operation operation;
+        CancellationWatchdog(String tag, Operation operation) {
+            this.tag = tag;
+            this.operation = operation;
+        }
+
+        @Override
+        public void run() {
+            if (operation.state != Operation.STATE_FINISHED) {
+                Slog.e(tag, "[Watchdog] Running for: " + operation);
+                operation.clientMonitor.mFinishCallback
+                        .onClientFinished(operation.clientMonitor, false /* success */);
+            }
+        }
+    }
+
+    private static final class CrashState {
+        static final int NUM_ENTRIES = 10;
+        final String timestamp;
+        final String currentOperation;
+        final List<String> pendingOperations;
+
+        CrashState(String timestamp, String currentOperation, List<String> pendingOperations) {
+            this.timestamp = timestamp;
+            this.currentOperation = currentOperation;
+            this.pendingOperations = pendingOperations;
+        }
+
+        @Override
+        public String toString() {
+            final StringBuilder sb = new StringBuilder();
+            sb.append(timestamp).append(": ");
+            sb.append("Current Operation: {").append(currentOperation).append("}");
+            sb.append(", Pending Operations(").append(pendingOperations.size()).append(")");
+
+            if (!pendingOperations.isEmpty()) {
+                sb.append(": ");
+            }
+            for (int i = 0; i < pendingOperations.size(); i++) {
+                sb.append(pendingOperations.get(i));
+                if (i < pendingOperations.size() - 1) {
+                    sb.append(", ");
+                }
+            }
+            return sb.toString();
+        }
+    }
+
+    @NonNull private final String mBiometricTag;
+    @Nullable private final GestureAvailabilityTracker mGestureAvailabilityTracker;
+    @NonNull private final IBiometricService mBiometricService;
+    @NonNull private final Handler mHandler = new Handler(Looper.getMainLooper());
+    @NonNull private final InternalFinishCallback mInternalFinishCallback;
+    @NonNull private final Queue<Operation> mPendingOperations;
+    @Nullable private Operation mCurrentOperation;
+    @NonNull private final ArrayDeque<CrashState> mCrashStates;
+
+    // Internal finish callback, notified when an operation is complete. Notifies the requester
+    // that the operation is complete, before performing internal scheduler work (such as
+    // starting the next client).
+    private class InternalFinishCallback implements ClientMonitor.FinishCallback {
+        @Override
+        public void onClientFinished(ClientMonitor<?> clientMonitor, boolean success) {
+            mHandler.post(() -> {
+                if (mCurrentOperation == null) {
+                    Slog.e(getTag(), "[Finishing] " + clientMonitor
+                            + " but current operation is null, success: " + success
+                            + ", possible lifecycle bug in clientMonitor implementation?");
+                    return;
+                }
+
+                if (mCurrentOperation.clientFinishCallback != null) {
+                    mCurrentOperation.clientFinishCallback.onClientFinished(clientMonitor, success);
+                }
+
+                if (clientMonitor != mCurrentOperation.clientMonitor) {
+                    throw new IllegalStateException("Mismatched operation, "
+                            + " current: " + mCurrentOperation.clientMonitor
+                            + " received: " + clientMonitor);
+                }
+
+                Slog.d(getTag(), "[Finished] " + clientMonitor + ", success: " + success);
+                if (mGestureAvailabilityTracker != null) {
+                    mGestureAvailabilityTracker.markSensorActive(
+                            mCurrentOperation.clientMonitor.getSensorId(), false /* active */);
+                }
+
+                mCurrentOperation.state = Operation.STATE_FINISHED;
+                mCurrentOperation = null;
+                startNextOperationIfIdle();
+            });
+        }
+    }
+
+    /**
+     * Creates a new scheduler.
+     * @param tag for the specific instance of the scheduler. Should be unique.
+     * @param gestureAvailabilityTracker may be null if the sensor does not support gestures (such
+     *                                   as fingerprint swipe).
+     */
+    public BiometricScheduler(@NonNull String tag,
+            @Nullable GestureAvailabilityTracker gestureAvailabilityTracker) {
+        mBiometricTag = tag;
+        mInternalFinishCallback = new InternalFinishCallback();
+        mGestureAvailabilityTracker = gestureAvailabilityTracker;
+        mPendingOperations = new LinkedList<>();
+        mBiometricService = IBiometricService.Stub.asInterface(
+                ServiceManager.getService(Context.BIOMETRIC_SERVICE));
+        mCrashStates = new ArrayDeque<>();
+    }
+
+    private String getTag() {
+        return BASE_TAG + "/" + mBiometricTag;
+    }
+
+    private void startNextOperationIfIdle() {
+        if (mCurrentOperation != null) {
+            Slog.v(getTag(), "Not idle, current operation: " + mCurrentOperation);
+            return;
+        }
+        if (mPendingOperations.isEmpty()) {
+            Slog.d(getTag(), "No operations, returning to idle");
+            return;
+        }
+
+        mCurrentOperation = mPendingOperations.poll();
+        final ClientMonitor<?> currentClient = mCurrentOperation.clientMonitor;
+
+        // If the operation at the front of the queue has been marked for cancellation, send
+        // ERROR_CANCELED. No need to start this client.
+        if (mCurrentOperation.state == Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
+            Slog.d(getTag(), "[Now Cancelling] " + mCurrentOperation);
+            if (!(currentClient instanceof Interruptable)) {
+                throw new IllegalStateException("Mis-implemented client or scheduler, "
+                        + "trying to cancel non-interruptable operation: " + mCurrentOperation);
+            }
+
+            final Interruptable interruptable = (Interruptable) currentClient;
+            interruptable.cancelWithoutStarting(mInternalFinishCallback);
+            // Now we wait for the client to send its FinishCallback, which kicks off the next
+            // operation.
+            return;
+        }
+
+        if (mGestureAvailabilityTracker != null
+                && mCurrentOperation.clientMonitor instanceof AcquisitionClient) {
+            mGestureAvailabilityTracker.markSensorActive(
+                    mCurrentOperation.clientMonitor.getSensorId(),
+                    true /* active */);
+        }
+
+        // Not all operations start immediately. BiometricPrompt waits for its operation
+        // to arrive at the head of the queue, before pinging it to start.
+        final boolean shouldStartNow = currentClient.getCookie() == 0;
+        if (shouldStartNow) {
+            Slog.d(getTag(), "[Starting] " + mCurrentOperation);
+            currentClient.start(mInternalFinishCallback);
+            mCurrentOperation.state = Operation.STATE_STARTED;
+        } else {
+            try {
+                mBiometricService.onReadyForAuthentication(currentClient.getCookie());
+            } catch (RemoteException e) {
+                Slog.e(getTag(), "Remote exception when contacting BiometricService", e);
+            }
+            Slog.d(getTag(), "Waiting for cookie before starting: " + mCurrentOperation);
+            mCurrentOperation.state = Operation.STATE_WAITING_FOR_COOKIE;
+        }
+    }
+
+    /**
+     * Starts the {@link #mCurrentOperation} if
+     * 1) its state is {@link Operation#STATE_WAITING_FOR_COOKIE} and
+     * 2) its cookie matches this cookie
+     * @param cookie of the operation to be started
+     */
+    public void startPreparedClient(int cookie) {
+        if (mCurrentOperation == null) {
+            Slog.e(getTag(), "Current operation null");
+            return;
+        }
+        if (mCurrentOperation.state != Operation.STATE_WAITING_FOR_COOKIE) {
+            Slog.e(getTag(), "Operation in wrong state: " + mCurrentOperation);
+            return;
+        }
+        if (mCurrentOperation.clientMonitor.getCookie() != cookie) {
+            Slog.e(getTag(), "Mismatched cookie for operation: " + mCurrentOperation
+                    + ", received: " + cookie);
+            return;
+        }
+
+        Slog.d(getTag(), "[Starting] Prepared client: " + mCurrentOperation);
+        mCurrentOperation.state = Operation.STATE_STARTED;
+        mCurrentOperation.clientMonitor.start(mInternalFinishCallback);
+    }
+
+    /**
+     * Adds a {@link ClientMonitor} to the pending queue
+     *
+     * @param clientMonitor operation to be scheduled
+     */
+    public void scheduleClientMonitor(@NonNull ClientMonitor<?> clientMonitor) {
+        scheduleClientMonitor(clientMonitor, null /* clientFinishCallback */);
+    }
+
+    /**
+     * Adds a {@link ClientMonitor} to the pending queue
+     *
+     * @param clientMonitor        operation to be scheduled
+     * @param clientFinishCallback optional callback, invoked when the client is finished, but
+     *                             before it has been removed from the queue.
+     */
+    public void scheduleClientMonitor(@NonNull ClientMonitor<?> clientMonitor,
+            @Nullable ClientMonitor.FinishCallback clientFinishCallback) {
+        // Mark any interruptable pending clients as canceling. Once they reach the head of the
+        // queue, the scheduler will send ERROR_CANCELED and skip the operation.
+        for (Operation operation : mPendingOperations) {
+            if (operation.clientMonitor instanceof Interruptable
+                    && operation.state != Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
+                Slog.d(getTag(), "New client incoming, marking pending client as canceling: "
+                        + operation.clientMonitor);
+                operation.state = Operation.STATE_WAITING_IN_QUEUE_CANCELING;
+            }
+        }
+
+        mPendingOperations.add(new Operation(clientMonitor, clientFinishCallback));
+        Slog.d(getTag(), "[Added] " + clientMonitor
+                + ", new queue size: " + mPendingOperations.size());
+
+        // If the current operation is cancellable, start the cancellation process.
+        if (mCurrentOperation != null && mCurrentOperation.clientMonitor instanceof Interruptable
+                && mCurrentOperation.state != Operation.STATE_STARTED_CANCELING) {
+            cancelInternal(mCurrentOperation);
+        }
+
+        startNextOperationIfIdle();
+    }
+
+    private void cancelInternal(Operation operation) {
+        if (operation != mCurrentOperation) {
+            Slog.e(getTag(), "cancelInternal invoked on non-current operation: " + operation);
+            return;
+        }
+        if (!(operation.clientMonitor instanceof Interruptable)) {
+            Slog.w(getTag(), "Operation not interruptable: " + operation);
+            return;
+        }
+        if (operation.state == Operation.STATE_STARTED_CANCELING) {
+            Slog.w(getTag(), "Cancel already invoked for operation: " + operation);
+            return;
+        }
+        Slog.d(getTag(), "[Cancelling] Current client: " + operation.clientMonitor);
+        final Interruptable interruptable = (Interruptable) operation.clientMonitor;
+        interruptable.cancel();
+        operation.state = Operation.STATE_STARTED_CANCELING;
+
+        // Add a watchdog. If the HAL does not acknowledge within the timeout, we will
+        // forcibly finish this client.
+        mHandler.postDelayed(new CancellationWatchdog(getTag(), operation),
+                CancellationWatchdog.DELAY_MS);
+    }
+
+    /**
+     * Requests to cancel enrollment.
+     * @param token from the caller, should match the token passed in when requesting enrollment
+     */
+    public void cancelEnrollment(IBinder token) {
+        if (mCurrentOperation == null) {
+            Slog.e(getTag(), "Unable to cancel enrollment, null operation");
+            return;
+        }
+        final boolean isEnrolling = mCurrentOperation.clientMonitor instanceof EnrollClient;
+        final boolean tokenMatches = mCurrentOperation.clientMonitor.getToken() == token;
+        if (!isEnrolling || !tokenMatches) {
+            Slog.w(getTag(), "Not cancelling enrollment, isEnrolling: " + isEnrolling
+                    + " tokenMatches: " + tokenMatches);
+            return;
+        }
+
+        cancelInternal(mCurrentOperation);
+    }
+
+    /**
+     * Requests to cancel authentication.
+     * @param token from the caller, should match the token passed in when requesting authentication
+     */
+    public void cancelAuthentication(IBinder token) {
+        if (mCurrentOperation == null) {
+            Slog.e(getTag(), "Unable to cancel authentication, null operation");
+            return;
+        }
+        final boolean isAuthenticating =
+                mCurrentOperation.clientMonitor instanceof AuthenticationClient;
+        final boolean tokenMatches = mCurrentOperation.clientMonitor.getToken() == token;
+        if (!isAuthenticating || !tokenMatches) {
+            Slog.w(getTag(), "Not cancelling authentication, isEnrolling: " + isAuthenticating
+                    + " tokenMatches: " + tokenMatches);
+            return;
+        }
+
+        cancelInternal(mCurrentOperation);
+    }
+
+    /**
+     * @return the current operation
+     */
+    public ClientMonitor<?> getCurrentClient() {
+        if (mCurrentOperation == null) {
+            return null;
+        }
+        return mCurrentOperation.clientMonitor;
+    }
+
+    public void recordCrashState() {
+        if (mCrashStates.size() >= CrashState.NUM_ENTRIES) {
+            mCrashStates.removeFirst();
+        }
+        final SimpleDateFormat dateFormat =
+                new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
+        final String timestamp = dateFormat.format(new Date(System.currentTimeMillis()));
+        final List<String> pendingOperations = new ArrayList<>();
+        for (Operation operation : mPendingOperations) {
+            pendingOperations.add(operation.toString());
+        }
+
+        final CrashState crashState = new CrashState(timestamp,
+                mCurrentOperation != null ? mCurrentOperation.toString() : null,
+                pendingOperations);
+        mCrashStates.add(crashState);
+        Slog.e(getTag(), "Recorded crash state: " + crashState.toString());
+    }
+
+    public void dump(PrintWriter pw) {
+        pw.println("Dump of BiometricScheduler " + getTag());
+        for (CrashState crashState : mCrashStates) {
+            pw.println("Crash State " + crashState);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java
index a3901e00..9aa72a7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java
@@ -16,7 +16,6 @@
 
 package com.android.server.biometrics.sensors;
 
-import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
 import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE;
 
 import android.app.ActivityManager;
@@ -25,28 +24,23 @@
 import android.app.IActivityTaskManager;
 import android.app.SynchronousUserSwitchObserver;
 import android.app.TaskStackListener;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
-import android.hardware.biometrics.BiometricManager.Authenticators;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.IBiometricService;
-import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IHwBinder;
 import android.os.Looper;
-import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Slog;
 
-import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.util.FrameworkStatsLog;
@@ -74,7 +68,6 @@
     private static final long CANCEL_TIMEOUT_LIMIT = 3000; // max wait for onCancel() from HAL,in ms
 
     private final Context mContext;
-    private final String mKeyguardPackage;
     protected final IActivityTaskManager mActivityTaskManager;
     protected final BiometricTaskStackListener mTaskStackListener =
             new BiometricTaskStackListener();
@@ -157,17 +150,6 @@
      */
     protected abstract String getManageBiometricPermission();
 
-    /**
-     * Checks if the caller has permission to use the biometric service - throws a SecurityException
-     * if not.
-     */
-    protected abstract void checkUseBiometricPermission();
-
-    /**
-     * Checks if the caller passes the app ops check
-     */
-    protected abstract boolean checkAppOps(int uid, String opPackageName);
-
     protected abstract List<? extends BiometricAuthenticator.Identifier> getEnrolledTemplates(
             int userId);
 
@@ -180,11 +162,6 @@
 
     protected abstract int statsModality();
 
-    /**
-     * @return one of the AuthenticationClient LOCKOUT constants
-     */
-    protected abstract @LockoutTracker.LockoutMode int getLockoutMode(int userId);
-
     private final Runnable mOnTaskStackChangedRunnable = new Runnable() {
         @Override
         public void run() {
@@ -262,9 +239,6 @@
         mContext = context;
         mStatusBarService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
-        final ComponentName keyguardComponent = ComponentName.unflattenFromString(
-                context.getResources().getString(R.string.config_keyguardComponent));
-        mKeyguardPackage = keyguardComponent != null ? keyguardComponent.getPackageName() : null;
         mAppOps = context.getSystemService(AppOpsManager.class);
         mActivityTaskManager = ActivityTaskManager.getService();
         mPerformanceTracker = PerformanceTracker.getInstanceForSensorId(getSensorId());
@@ -298,24 +272,12 @@
         mSensorId = sensorId;
     }
 
-    protected ClientMonitor getCurrentClient() {
+    protected ClientMonitor<?> getCurrentClient() {
         return mCurrentClient;
     }
 
-    protected ClientMonitor getPendingClient() {
-        return mPendingClient;
-    }
-
     protected boolean isStrongBiometric() {
-        IBiometricService service = IBiometricService.Stub.asInterface(
-                ServiceManager.getService(Context.BIOMETRIC_SERVICE));
-        try {
-            return Utils.isAtLeastStrength(service.getCurrentStrength(mSensorId),
-                    Authenticators.BIOMETRIC_STRONG);
-        } catch (RemoteException e) {
-            Slog.e(getTag(), "RemoteException", e);
-            return false;
-        }
+        return Utils.isStrongBiometric(mSensorId);
     }
 
     protected int getSensorId() {
@@ -327,61 +289,46 @@
      */
 
     protected void handleAcquired(int acquiredInfo, int vendorCode) {
-        final ClientMonitor client = mCurrentClient;
+        final ClientMonitor<?> client = mCurrentClient;
         if (!(client instanceof AcquisitionClient)) {
             final String clientName = client != null ? client.getClass().getSimpleName() : "null";
             Slog.e(getTag(), "handleAcquired for non-acquire consumer: " + clientName);
             return;
         }
 
-        final AcquisitionClient acquisitionClient = (AcquisitionClient) client;
+        final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client;
         acquisitionClient.onAcquired(acquiredInfo, vendorCode);
-
-        if (client instanceof AuthenticationClient) {
-            final int userId = client.getTargetUserId();
-            if (getLockoutMode(userId) == LockoutTracker.LOCKOUT_NONE) {
-                mPerformanceTracker.incrementAcquireForUser(userId, client.isCryptoOperation());
-            }
-        }
     }
 
     protected void handleAuthenticated(BiometricAuthenticator.Identifier identifier,
             ArrayList<Byte> token) {
-        final ClientMonitor client = mCurrentClient;
+        final ClientMonitor<?> client = mCurrentClient;
         if (!(client instanceof AuthenticationClient)) {
             final String clientName = client != null ? client.getClass().getSimpleName() : "null";
             Slog.e(getTag(), "handleAuthenticated for non-authentication client: " + clientName);
             return;
         }
 
-        final AuthenticationClient authenticationClient = (AuthenticationClient) client;
+        final AuthenticationClient<?> authenticationClient = (AuthenticationClient<?>) client;
         final boolean authenticated = identifier.getBiometricId() != 0;
-
-        final int userId = authenticationClient.getTargetUserId();
-        if (authenticationClient.isCryptoOperation()) {
-            mPerformanceTracker.incrementCryptoAuthForUser(userId, authenticated);
-        } else {
-            mPerformanceTracker.incrementAuthForUser(userId, authenticated);
-        }
-
         authenticationClient.onAuthenticated(identifier, authenticated, token);
     }
 
     protected void handleEnrollResult(BiometricAuthenticator.Identifier identifier,
             int remaining) {
-        final ClientMonitor client = mCurrentClient;
+        final ClientMonitor<?> client = mCurrentClient;
         if (!(client instanceof EnrollClient)) {
             final String clientName = client != null ? client.getClass().getSimpleName() : "null";
             Slog.e(getTag(), "handleEnrollResult for non-enroll client: " + clientName);
             return;
         }
 
-        final EnrollClient enrollClient = (EnrollClient) client;
+        final EnrollClient<?> enrollClient = (EnrollClient<?>) client;
         enrollClient.onEnrollResult(identifier, remaining);
     }
 
     protected void handleError(int error, int vendorCode) {
-        final ClientMonitor client = mCurrentClient;
+        final ClientMonitor<?> client = mCurrentClient;
 
         if (DEBUG) Slog.v(getTag(), "handleError(client="
                 + (client != null ? client.getOwnerString() : "null") + ", error = " + error + ")");
@@ -411,7 +358,7 @@
                 + ", dev=" + identifier.getDeviceId()
                 + ", rem=" + remaining);
 
-        final ClientMonitor client = mCurrentClient;
+        final ClientMonitor<?> client = mCurrentClient;
         if (!(client instanceof RemovalConsumer)) {
             final String clientName = client != null ? client.getClass().getSimpleName() : "null";
             Slog.e(getTag(), "handleRemoved for non-removal consumer: " + clientName);
@@ -423,7 +370,7 @@
     }
 
     protected void handleEnumerate(BiometricAuthenticator.Identifier identifier, int remaining) {
-        final ClientMonitor client = mCurrentClient;
+        final ClientMonitor<?> client = mCurrentClient;
         if (!(client instanceof EnumerateConsumer)) {
             final String clientName = client != null ? client.getClass().getSimpleName() : "null";
             Slog.e(getTag(), "handleEnumerate for non-enumerate consumer: "
@@ -446,7 +393,7 @@
 
         // Group ID is arbitrarily set to parent profile user ID. It just represents
         // the default biometrics for the user.
-        if (!isCurrentUserOrProfile(userId)) {
+        if (!Utils.isCurrentUserOrProfile(mContext, userId)) {
             return;
         }
 
@@ -457,10 +404,10 @@
 
     protected void cancelEnrollmentInternal(IBinder token) {
         mHandler.post(() -> {
-            ClientMonitor client = mCurrentClient;
+            ClientMonitor<?> client = mCurrentClient;
             if (client instanceof EnrollClient && client.getToken() == token) {
                 if (DEBUG) Slog.v(getTag(), "Cancelling enrollment");
-                ((EnrollClient) client).cancel();
+                ((EnrollClient<?>) client).cancel();
             }
         });
     }
@@ -478,58 +425,25 @@
     }
 
     protected void authenticateInternal(AuthenticationClient<T> client, String opPackageName) {
-        final int callingUid = Binder.getCallingUid();
-        final int callingPid = Binder.getCallingPid();
-        final int callingUserId = UserHandle.getCallingUserId();
-        authenticateInternal(client, opPackageName, callingUid, callingPid, callingUserId);
-    }
-
-    protected void authenticateInternal(AuthenticationClient<T> client,
-            String opPackageName, int callingUid, int callingPid, int callingUserId) {
-        if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
-                callingUserId)) {
-            if (DEBUG) Slog.v(getTag(), "authenticate(): reject " + opPackageName);
-            return;
-        }
-
         mHandler.post(() -> {
             startAuthentication(client, opPackageName);
         });
     }
 
-    protected void cancelAuthenticationInternal(final IBinder token, final String opPackageName) {
-        final int callingUid = Binder.getCallingUid();
-        final int callingPid = Binder.getCallingPid();
-        final int callingUserId = UserHandle.getCallingUserId();
-        cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid, callingUserId,
-                true /* fromClient */);
-    }
-
     protected void cancelAuthenticationInternal(final IBinder token, final String opPackageName,
-            int callingUid, int callingPid, int callingUserId, boolean fromClient) {
+            boolean fromClient) {
 
         if (DEBUG) Slog.v(getTag(), "cancelAuthentication(" + opPackageName + ")");
-        if (fromClient) {
-            // Only check this if cancel was called from the client (app). If cancel was called
-            // from BiometricService, it means the dialog was dismissed due to user interaction.
-            if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
-                    callingUserId)) {
-                if (DEBUG) {
-                    Slog.v(getTag(), "cancelAuthentication(): reject " + opPackageName);
-                }
-                return;
-            }
-        }
 
         mHandler.post(() -> {
-            ClientMonitor client = mCurrentClient;
+            ClientMonitor<?> client = mCurrentClient;
             if (client instanceof AuthenticationClient) {
                 if (client.getToken() == token || !fromClient) {
                     if (DEBUG) Slog.v(getTag(), "Stopping client " + client.getOwnerString()
                             + ", fromClient: " + fromClient);
                     // If cancel was from BiometricService, it means the dialog was dismissed
                     // and authentication should be canceled.
-                    ((AuthenticationClient) client).cancel();
+                    ((AuthenticationClient<?>) client).cancel();
                 } else {
                     if (DEBUG) Slog.v(getTag(), "Can't stop client " + client.getOwnerString()
                             + " since tokens don't match. fromClient: " + fromClient);
@@ -570,74 +484,10 @@
      */
 
     /**
-     * @param opPackageName name of package for caller
-     * @param requireForeground only allow this call while app is in the foreground
-     * @return true if caller can use the biometric API
-     */
-    protected boolean canUseBiometric(String opPackageName, boolean requireForeground, int uid,
-            int pid, int userId) {
-        checkUseBiometricPermission();
-
-
-        if (Binder.getCallingUid() == Process.SYSTEM_UID) {
-            return true; // System process (BiometricService, etc) is always allowed
-        }
-        if (isKeyguard(opPackageName)) {
-            return true; // Keyguard is always allowed
-        }
-        if (!isCurrentUserOrProfile(userId)) {
-            Slog.w(getTag(), "Rejecting " + opPackageName + "; not a current user or profile");
-            return false;
-        }
-        if (!checkAppOps(uid, opPackageName)) {
-            Slog.w(getTag(), "Rejecting " + opPackageName + "; permission denied");
-            return false;
-        }
-
-        if (requireForeground && !(isForegroundActivity(uid, pid) || isCurrentClient(
-                opPackageName))) {
-            Slog.w(getTag(), "Rejecting " + opPackageName + "; not in foreground");
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * @param opPackageName package of the caller
-     * @return true if this is the same client currently using the biometric
-     */
-    private boolean isCurrentClient(String opPackageName) {
-        return mCurrentClient != null && mCurrentClient.getOwnerString().equals(opPackageName);
-    }
-
-    /**
      * @return true if this is keyguard package
      */
     public boolean isKeyguard(String clientPackage) {
-        return mKeyguardPackage.equals(clientPackage);
-    }
-
-    private boolean isForegroundActivity(int uid, int pid) {
-        try {
-            final List<ActivityManager.RunningAppProcessInfo> procs =
-                    ActivityManager.getService().getRunningAppProcesses();
-            if (procs == null) {
-                Slog.e(getTag(), "Processes null, defaulting to true");
-                return true;
-            }
-
-            int N = procs.size();
-            for (int i = 0; i < N; i++) {
-                ActivityManager.RunningAppProcessInfo proc = procs.get(i);
-                if (proc.pid == pid && proc.uid == uid
-                        && proc.importance <= IMPORTANCE_FOREGROUND_SERVICE) {
-                    return true;
-                }
-            }
-        } catch (RemoteException e) {
-            Slog.w(getTag(), "am.getRunningAppProcesses() failed");
-        }
-        return false;
+        return Utils.isKeyguard(mContext, clientPackage);
     }
 
     /**
@@ -650,7 +500,7 @@
      */
     @VisibleForTesting
     protected void startClient(ClientMonitor<T> newClient, boolean initiatedByClient) {
-        ClientMonitor currentClient = mCurrentClient;
+        ClientMonitor<?> currentClient = mCurrentClient;
         if (currentClient != null) {
             if (DEBUG) Slog.v(getTag(), "request stop current client " +
                     currentClient.getOwnerString());
@@ -680,7 +530,7 @@
             // <Biometric>Service#startPreparedClient is called. BiometricService waits until all
             // modalities are ready before initiating authentication.
             if (newClient instanceof AuthenticationClient) {
-                AuthenticationClient client = (AuthenticationClient) newClient;
+                AuthenticationClient<?> client = (AuthenticationClient<?>) newClient;
                 if (client.isBiometricPrompt()) {
                     if (DEBUG) Slog.v(getTag(), "Returning cookie: " + client.getCookie());
                     mCurrentClient = newClient;
@@ -734,7 +584,7 @@
         notifyClientActiveCallbacks(true);
     }
 
-    protected void removeClient(ClientMonitor client) {
+    protected void removeClient(ClientMonitor<?> client) {
         if (client != null) {
             client.destroy();
             if (client != mCurrentClient && mCurrentClient != null) {
@@ -790,28 +640,6 @@
                 "Must have " + permission + " permission.");
     }
 
-    protected boolean isCurrentUserOrProfile(int userId) {
-        UserManager um = UserManager.get(mContext);
-        if (um == null) {
-            Slog.e(getTag(), "Unable to acquire UserManager");
-            return false;
-        }
-
-        final long token = Binder.clearCallingIdentity();
-        try {
-            // Allow current user or profiles of the current user...
-            for (int profileId : um.getEnabledProfileIds(ActivityManager.getCurrentUser())) {
-                if (profileId == userId) {
-                    return true;
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-
-        return false;
-    }
-
     /**
      * @return authenticator id for the calling user
      */
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
index 35f7279..8cabdf5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
@@ -36,6 +36,9 @@
     private static final String TAG = "Biometrics/ClientMonitor";
     protected static final boolean DEBUG = BiometricServiceBase.DEBUG;
 
+    // Counter used to distinguish between ClientMonitor instances to help debugging.
+    private static int sCount = 0;
+
     /**
      * Interface that ClientMonitor holders should use to receive callbacks.
      */
@@ -49,7 +52,7 @@
          * @param clientMonitor Reference of the ClientMonitor that finished.
          * @param success True if the operation completed successfully.
          */
-        void onClientFinished(ClientMonitor clientMonitor, boolean success);
+        void onClientFinished(ClientMonitor<?> clientMonitor, boolean success);
     }
 
     /**
@@ -62,6 +65,7 @@
         T getDaemon();
     }
 
+    private final int mSequentialId;
     @NonNull private final Context mContext;
     @NonNull protected final LazyDaemon<T> mLazyDaemon;
     private final int mTargetUserId;
@@ -95,6 +99,7 @@
             @NonNull String owner, int cookie, int sensorId, int statsModality, int statsAction,
             int statsClient) {
         super(statsModality, statsAction, statsClient);
+        mSequentialId = sCount++;
         mContext = context;
         mLazyDaemon = lazyDaemon;
         mToken = token;
@@ -200,4 +205,12 @@
     public final T getFreshDaemon() {
         return mLazyDaemon.getDaemon();
     }
+
+    @Override
+    public String toString() {
+        return "{[" + mSequentialId + "] "
+                + this.getClass().getSimpleName()
+                + ", " + getOwnerString()
+                + ", " + getCookie() + "}";
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/Interruptable.java b/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
index 1b8392e..35d9177 100644
--- a/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
+++ b/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
@@ -16,6 +16,8 @@
 
 package com.android.server.biometrics.sensors;
 
+import android.annotation.NonNull;
+
 /**
  * Interface that {@link ClientMonitor} subclasses eligible/interested in error callbacks should
  * implement.
@@ -27,8 +29,18 @@
     void cancel();
 
     /**
+     * Notifies the client of errors from the HAL.
      * @param errorCode defined by the HIDL interface
      * @param vendorCode defined by the vendor
      */
     void onError(int errorCode, int vendorCode);
+
+    /**
+     * Notifies the client that it needs to finish before
+     * {@link ClientMonitor#start(ClientMonitor.FinishCallback)} was invoked. This usually happens
+     * if the client is still waiting in the pending queue and got notified that a subsequent
+     * operation is preempting it.
+     * @param finishCallback invoked when the operation is completed.
+     */
+    void cancelWithoutStarting(@NonNull ClientMonitor.FinishCallback finishCallback);
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/LockoutResetTracker.java b/services/core/java/com/android/server/biometrics/sensors/LockoutResetTracker.java
index 9bb5c6e..22c10b3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/LockoutResetTracker.java
+++ b/services/core/java/com/android/server/biometrics/sensors/LockoutResetTracker.java
@@ -29,7 +29,8 @@
 
 /**
  * Allows clients (such as keyguard) to register for notifications on when biometric lockout
- * ends.
+ * ends. This class keeps track of all client callbacks. Individual sensors should notify this
+ * when lockout for a specific sensor has been reset.
  */
 public class LockoutResetTracker implements IBinder.DeathRecipient {
 
@@ -54,11 +55,11 @@
                     "LockoutResetMonitor:SendLockoutReset");
         }
 
-        void sendLockoutReset() {
+        void sendLockoutReset(int sensorId) {
             if (mCallback != null) {
                 try {
                     mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
-                    mCallback.onLockoutReset(new IRemoteCallback.Stub() {
+                    mCallback.onLockoutReset(sensorId, new IRemoteCallback.Stub() {
                         @Override
                         public void sendResult(Bundle data) {
                             releaseWakelock();
@@ -114,9 +115,9 @@
         }
     }
 
-    public void notifyLockoutResetCallbacks() {
+    public void notifyLockoutResetCallbacks(int sensorId) {
         for (ClientCallback callback : mClientCallbacks) {
-            callback.sendLockoutReset();
+            callback.sendLockoutReset(sensorId);
         }
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/PerformanceTracker.java b/services/core/java/com/android/server/biometrics/sensors/PerformanceTracker.java
index fa490ee..9fdba4b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/PerformanceTracker.java
+++ b/services/core/java/com/android/server/biometrics/sensors/PerformanceTracker.java
@@ -107,7 +107,7 @@
         mAllUsersInfo.get(userId).mPermanentLockout++;
     }
 
-    void incrementHALDeathCount() {
+    public void incrementHALDeathCount() {
         mHALDeathCount++;
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index a7f8220..7a05192 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -21,7 +21,6 @@
 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
 
 import android.app.ActivityManager;
-import android.app.AppOpsManager;
 import android.app.NotificationManager;
 import android.content.Context;
 import android.content.pm.UserInfo;
@@ -82,8 +81,6 @@
  * A service to manage multiple clients that want to access the face HAL API.
  * The service is responsible for maintaining a list of clients and dispatching all
  * face-related events.
- *
- * @hide
  */
 public class FaceService extends BiometricServiceBase<IBiometricsFace> {
 
@@ -115,7 +112,7 @@
                 String opPackageName) {
             checkPermission(MANAGE_BIOMETRIC);
 
-            final GenerateChallengeClient client = new FaceGenerateChallengeClient(getContext(),
+            final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(getContext(),
                     mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), opPackageName,
                     getSensorId());
             generateChallengeInternal(client);
@@ -125,7 +122,7 @@
         public void revokeChallenge(IBinder token, String owner) {
             checkPermission(MANAGE_BIOMETRIC);
 
-            final RevokeChallengeClient client = new FaceRevokeChallengeClient(getContext(),
+            final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(getContext(),
                     mLazyDaemon, token, owner, getSensorId());
 
             // TODO(b/137106905): Schedule binder calls in FaceService to avoid deadlocks.
@@ -152,7 +149,7 @@
                         UserHandle.CURRENT);
             });
 
-            final EnrollClient client = new FaceEnrollClient(getContext(), mLazyDaemon, token,
+            final FaceEnrollClient client = new FaceEnrollClient(getContext(), mLazyDaemon, token,
                     new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
                     opPackageName, getBiometricUtils(), disabledFeatures, ENROLL_TIMEOUT_SEC,
                     convertSurfaceToNativeHandle(surface), getSensorId());
@@ -183,7 +180,7 @@
             final boolean restricted = isRestricted();
             final int statsClient = isKeyguard(opPackageName) ? BiometricsProtoEnums.CLIENT_KEYGUARD
                     : BiometricsProtoEnums.CLIENT_UNKNOWN;
-            final AuthenticationClient client = new FaceAuthenticationClient(getContext(),
+            final FaceAuthenticationClient client = new FaceAuthenticationClient(getContext(),
                     mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, opId,
                     restricted, opPackageName, 0 /* cookie */, false /* requireConfirmation */,
                     getSensorId(), isStrongBiometric(), statsClient, mTaskStackListener,
@@ -200,13 +197,12 @@
             updateActiveGroup(userId);
 
             final boolean restricted = true; // BiometricPrompt is always restricted
-            final AuthenticationClient client = new FaceAuthenticationClient(getContext(),
+            final FaceAuthenticationClient client = new FaceAuthenticationClient(getContext(),
                     mLazyDaemon, token, new ClientMonitorCallbackConverter(sensorReceiver), userId,
                     opId, restricted, opPackageName, cookie, requireConfirmation, getSensorId(),
                     isStrongBiometric(), BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
                     mTaskStackListener, mLockoutTracker, mUsageStats);
-            authenticateInternal(client, opPackageName, callingUid, callingPid,
-                    callingUserId);
+            authenticateInternal(client, opPackageName);
         }
 
         @Override // Binder call
@@ -218,7 +214,7 @@
         @Override // Binder call
         public void cancelAuthentication(final IBinder token, final String opPackageName) {
             checkPermission(USE_BIOMETRIC_INTERNAL);
-            cancelAuthenticationInternal(token, opPackageName);
+            cancelAuthenticationInternal(token, opPackageName, true /* fromClient */);
         }
 
         @Override // Binder call
@@ -226,8 +222,7 @@
                 int callingUid, int callingPid, int callingUserId) {
             checkPermission(USE_BIOMETRIC_INTERNAL);
             // Cancellation is from system server in this case.
-            cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid,
-                    callingUserId, false /* fromClient */);
+            cancelAuthenticationInternal(token, opPackageName, false /* fromClient */);
         }
 
         @Override // Binder call
@@ -241,7 +236,7 @@
                 return;
             }
 
-            final RemovalClient client = new FaceRemovalClient(getContext(), mLazyDaemon, token,
+            final FaceRemovalClient client = new FaceRemovalClient(getContext(), mLazyDaemon, token,
                     new ClientMonitorCallbackConverter(receiver), faceId, userId, opPackageName,
                     getBiometricUtils(), getSensorId(), mAuthenticatorIds);
             removeInternal(client);
@@ -282,11 +277,6 @@
         @Override // Binder call
         public boolean isHardwareDetected(String opPackageName) {
             checkPermission(USE_BIOMETRIC_INTERNAL);
-            if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
-                    Binder.getCallingUid(), Binder.getCallingPid(),
-                    UserHandle.getCallingUserId())) {
-                return false;
-            }
 
             final long token = Binder.clearCallingIdentity();
             try {
@@ -300,24 +290,12 @@
         @Override // Binder call
         public List<Face> getEnrolledFaces(int userId, String opPackageName) {
             checkPermission(MANAGE_BIOMETRIC);
-            if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
-                    Binder.getCallingUid(), Binder.getCallingPid(),
-                    UserHandle.getCallingUserId())) {
-                return null;
-            }
-
             return FaceService.this.getEnrolledTemplates(userId);
         }
 
         @Override // Binder call
         public boolean hasEnrolledFaces(int userId, String opPackageName) {
             checkPermission(USE_BIOMETRIC_INTERNAL);
-            if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
-                    Binder.getCallingUid(), Binder.getCallingPid(),
-                    UserHandle.getCallingUserId())) {
-                return false;
-            }
-
             return FaceService.this.hasEnrolledBiometrics(userId);
         }
 
@@ -427,7 +405,7 @@
     @GuardedBy("this")
     private IBiometricsFace mDaemon;
     private UsageStats mUsageStats;
-    private RevokeChallengeClient mPendingRevokeChallenge;
+    private FaceRevokeChallengeClient mPendingRevokeChallenge;
 
     private NotificationManager mNotificationManager;
 
@@ -546,7 +524,7 @@
 
             mHandler.post(() -> {
                 if (duration == 0) {
-                    mLockoutResetTracker.notifyLockoutResetCallbacks();
+                    mLockoutResetTracker.notifyLockoutResetCallbacks(getSensorId());
                 }
             });
         }
@@ -562,7 +540,7 @@
     }
 
     @Override
-    protected void removeClient(ClientMonitor client) {
+    protected void removeClient(ClientMonitor<?> client) {
         super.removeClient(client);
         if (mPendingRevokeChallenge != null) {
             revokeChallengeInternal(mPendingRevokeChallenge);
@@ -670,17 +648,6 @@
     }
 
     @Override
-    protected void checkUseBiometricPermission() {
-        // noop for Face. The permission checks are all done on the incoming binder call.
-    }
-
-    @Override
-    protected boolean checkAppOps(int uid, String opPackageName) {
-        return mAppOps.noteOp(AppOpsManager.OP_USE_BIOMETRIC, uid, opPackageName)
-                == AppOpsManager.MODE_ALLOWED;
-    }
-
-    @Override
     protected List<Face> getEnrolledTemplates(int userId) {
         return FaceUtils.getInstance().getBiometricsForUser(getContext(), userId);
     }
@@ -696,11 +663,6 @@
     }
 
     @Override
-    protected @LockoutTracker.LockoutMode int getLockoutMode(int userId) {
-        return mLockoutTracker.getLockoutModeForUser(userId);
-    }
-
-    @Override
     protected void doTemplateCleanupForUser(int userId) {
         final List<Face> enrolledList = getEnrolledTemplates(userId);
         final FaceInternalCleanupClient client = new FaceInternalCleanupClient(getContext(),
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java
new file mode 100644
index 0000000..5d48e56
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java
@@ -0,0 +1,653 @@
+/*
+ * Copyright (C) 2020 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.sensors.fingerprint;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
+import android.app.SynchronousUserSwitchObserver;
+import android.app.TaskStackListener;
+import android.app.UserSwitchObserver;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprintClientCallback;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IHwBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+import android.view.Surface;
+
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.fingerprint.FingerprintServiceDumpProto;
+import com.android.server.biometrics.fingerprint.FingerprintUserStatsProto;
+import com.android.server.biometrics.fingerprint.PerformanceStatsProto;
+import com.android.server.biometrics.sensors.AcquisitionClient;
+import com.android.server.biometrics.sensors.AuthenticationClient;
+import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.ClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.EnumerateConsumer;
+import com.android.server.biometrics.sensors.Interruptable;
+import com.android.server.biometrics.sensors.LockoutResetTracker;
+import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.PerformanceTracker;
+import com.android.server.biometrics.sensors.RemovalConsumer;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Supports a single instance of the {@link android.hardware.biometrics.fingerprint.V2_1} or
+ * its extended minor versions.
+ */
+class Fingerprint21 implements IHwBinder.DeathRecipient {
+
+    private static final String TAG = "Fingerprint21";
+    private static final int ENROLL_TIMEOUT_SEC = 60;
+
+    private final Context mContext;
+    private final IActivityTaskManager mActivityTaskManager;;
+    private final SensorProperties mSensorProperties;
+    private final BiometricScheduler mScheduler;
+    private final Handler mHandler;
+    private final LockoutResetTracker mLockoutResetTracker;
+    private final LockoutFrameworkImpl mLockoutTracker;
+    private final BiometricTaskStackListener mTaskStackListener;
+    private final ClientMonitor.LazyDaemon<IBiometricsFingerprint> mLazyDaemon;
+    private final Map<Integer, Long> mAuthenticatorIds;
+
+    private IBiometricsFingerprint mDaemon;
+    private int mCurrentUserId = UserHandle.USER_NULL;
+
+    /**
+     * Static properties that never change for a given sensor.
+     */
+    private static final class SensorProperties {
+        final int sensorId;
+        final Boolean isUdfps;
+
+        SensorProperties(int sensorId, Boolean isUdfps) {
+            this.sensorId = sensorId;
+            this.isUdfps = isUdfps;
+        }
+    }
+
+    private final class BiometricTaskStackListener extends TaskStackListener {
+        @Override
+        public void onTaskStackChanged() {
+            mHandler.post(() -> {
+                final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                if (!(client instanceof AuthenticationClient)) {
+                    Slog.e(TAG, "Task stack changed for client: " + client);
+                    return;
+                }
+                if (Utils.isKeyguard(mContext, client.getOwnerString())) {
+                    return; // Keyguard is always allowed
+                }
+
+                try {
+                    final List<ActivityManager.RunningTaskInfo> runningTasks =
+                            mActivityTaskManager.getTasks(1);
+                    if (!runningTasks.isEmpty()) {
+                        final String topPackage = runningTasks.get(0).topActivity.getPackageName();
+                        if (!topPackage.contentEquals(client.getOwnerString())
+                                && !client.isAlreadyDone()) {
+                            Slog.e(TAG, "Stopping background authentication, top: "
+                                    + topPackage + " currentClient: " + client);
+                            mScheduler.cancelAuthentication(client.getToken());
+                        }
+                    }
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Unable to get running tasks", e);
+                }
+            });
+        }
+    }
+
+    private final LockoutFrameworkImpl.LockoutResetCallback mLockoutResetCallback =
+            new LockoutFrameworkImpl.LockoutResetCallback() {
+        @Override
+        public void onLockoutReset(int userId) {
+            mLockoutResetTracker.notifyLockoutResetCallbacks(mSensorProperties.sensorId);
+        }
+    };
+
+    private final UserSwitchObserver mUserSwitchObserver = new SynchronousUserSwitchObserver() {
+        @Override
+        public void onUserSwitching(int newUserId) {
+            scheduleInternalCleanup(newUserId);
+        }
+    };
+
+    private IBiometricsFingerprintClientCallback mDaemonCallback =
+            new IBiometricsFingerprintClientCallback.Stub() {
+
+        @Override
+        public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
+            mHandler.post(() -> {
+                final CharSequence name = FingerprintUtils.getInstance()
+                        .getUniqueName(mContext, mCurrentUserId);
+                final Fingerprint fingerprint = new Fingerprint(name, groupId, fingerId, deviceId);
+
+                final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                if (!(client instanceof FingerprintEnrollClient)) {
+                    final String clientName = client != null
+                            ? client.getClass().getSimpleName(): "null";
+                    Slog.e(TAG, "onEnrollResult for non-enroll client: " + clientName);
+                    return;
+                }
+
+                final FingerprintEnrollClient enrollClient = (FingerprintEnrollClient) client;
+                enrollClient.onEnrollResult(fingerprint, remaining);
+
+                if (remaining == 0) {
+                    // Update the framework's authenticatorId cache for this user
+                    scheduleUpdateActiveUserWithoutHandler(mCurrentUserId);
+                }
+            });
+        }
+
+        @Override
+        public void onAcquired(long deviceId, int acquiredInfo, int vendorCode) {
+            onAcquired_2_2(deviceId, acquiredInfo, vendorCode);
+        }
+
+        @Override
+        public void onAcquired_2_2(long deviceId, int acquiredInfo, int vendorCode) {
+            mHandler.post(() -> {
+                final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                if (!(client instanceof AcquisitionClient)) {
+                    final String clientName = client != null
+                            ? client.getClass().getSimpleName(): "null";
+                    Slog.e(TAG, "onAcquired for non-acquisition client: " + clientName);
+                    return;
+                }
+
+                final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client;
+                acquisitionClient.onAcquired(acquiredInfo, vendorCode);
+            });
+        }
+
+        @Override
+        public void onAuthenticated(long deviceId, int fingerId, int groupId,
+                ArrayList<Byte> token) {
+            mHandler.post(() -> {
+                final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                if (!(client instanceof FingerprintAuthenticationClient)) {
+                    final String clientName = client != null
+                            ? client.getClass().getSimpleName(): "null";
+                    Slog.e(TAG, "onAuthenticated for non-authentication client: " + clientName);
+                    return;
+                }
+
+                final FingerprintAuthenticationClient authenticationClient =
+                        (FingerprintAuthenticationClient) client;
+                final boolean authenticated = fingerId != 0;
+                final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
+                authenticationClient.onAuthenticated(fp, authenticated, token);
+            });
+        }
+
+        @Override
+        public void onError(long deviceId, int error, int vendorCode) {
+            mHandler.post(() -> {
+                final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                Slog.d(TAG, "handleError"
+                        + ", client: " + (client != null ? client.getOwnerString() : null)
+                        + ", error: " + error
+                        + ", vendorCode: " + vendorCode);
+                if (!(client instanceof Interruptable)) {
+                    final String clientName = client != null
+                            ? client.getClass().getSimpleName(): "null";
+                    Slog.e(TAG, "onError for non-error consumer: " + clientName);
+                    return;
+                }
+
+                final Interruptable interruptable = (Interruptable) client;
+                interruptable.onError(error, vendorCode);
+
+                if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
+                    Slog.e(TAG, "Got ERROR_HW_UNAVAILABLE");
+                    mDaemon = null;
+                    mCurrentUserId = UserHandle.USER_NULL;
+                }
+            });
+        }
+
+        @Override
+        public void onRemoved(long deviceId, int fingerId, int groupId, int remaining) {
+            mHandler.post(() -> {
+                Slog.d(TAG, "Removed, fingerId: " + fingerId + ", remaining: " + remaining);
+                final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                if (!(client instanceof RemovalConsumer)) {
+                    final String clientName = client != null
+                            ? client.getClass().getSimpleName(): "null";
+                    Slog.e(TAG, "onRemoved for non-removal consumer: " + clientName);
+                    return;
+                }
+
+                final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
+                final RemovalConsumer removalConsumer = (RemovalConsumer) client;
+                removalConsumer.onRemoved(fp, remaining);
+            });
+        }
+
+        @Override
+        public void onEnumerate(long deviceId, int fingerId, int groupId, int remaining) {
+            mHandler.post(() -> {
+                final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                if (!(client instanceof EnumerateConsumer)) {
+                    final String clientName = client != null
+                            ? client.getClass().getSimpleName(): "null";
+                    Slog.e(TAG, "onEnumerate for non-enumerate consumer: " + clientName);
+                    return;
+                }
+
+                final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
+                final EnumerateConsumer enumerateConsumer = (EnumerateConsumer) client;
+                enumerateConsumer.onEnumerationResult(fp, remaining);
+            });
+        }
+    };
+
+    Fingerprint21(@NonNull Context context, int sensorId,
+            @NonNull LockoutResetTracker lockoutResetTracker,
+            @NonNull GestureAvailabilityTracker gestureAvailabilityTracker) {
+        mContext = context;
+        mActivityTaskManager = ActivityTaskManager.getService();
+        mHandler = new Handler(Looper.getMainLooper());
+        mTaskStackListener = new BiometricTaskStackListener();
+        mAuthenticatorIds = Collections.synchronizedMap(new HashMap<>());
+        mLazyDaemon = Fingerprint21.this::getDaemon;
+        mLockoutResetTracker = lockoutResetTracker;
+        mLockoutTracker = new LockoutFrameworkImpl(context, mLockoutResetCallback);
+        mScheduler = new BiometricScheduler(TAG, gestureAvailabilityTracker);
+
+        try {
+            ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unable to register user switch observer");
+        }
+
+        final IBiometricsFingerprint daemon = getDaemon();
+        Boolean isUdfps = false;
+        android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint extension =
+                android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint.castFrom(
+                        daemon);
+        if (extension != null) {
+            try {
+                isUdfps = extension.isUdfps(sensorId);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Remote exception while quering udfps", e);
+                isUdfps = null;
+            }
+        }
+        mSensorProperties = new SensorProperties(sensorId, isUdfps);
+    }
+
+    @Override
+    public void serviceDied(long cookie) {
+        Slog.e(TAG, "HAL died");
+        mHandler.post(() -> {
+            PerformanceTracker.getInstanceForSensorId(mSensorProperties.sensorId)
+                    .incrementHALDeathCount();
+            mDaemon = null;
+            mCurrentUserId = UserHandle.USER_NULL;
+
+            final ClientMonitor<?> client = mScheduler.getCurrentClient();
+            if (client instanceof Interruptable) {
+                Slog.e(TAG, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
+                final Interruptable interruptable = (Interruptable) client;
+                interruptable.onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+                        0 /* vendorCode */);
+
+                mScheduler.recordCrashState();
+
+                FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+                        BiometricsProtoEnums.MODALITY_FINGERPRINT,
+                        BiometricsProtoEnums.ISSUE_HAL_DEATH);
+            }
+        });
+    }
+
+    private synchronized IBiometricsFingerprint getDaemon() {
+        if (mDaemon != null) {
+            return mDaemon;
+        }
+
+        Slog.d(TAG, "Daemon was null, reconnecting, current operation: "
+                + mScheduler.getCurrentClient());
+        try {
+            mDaemon = IBiometricsFingerprint.getService();
+        } catch (java.util.NoSuchElementException e) {
+            // Service doesn't exist or cannot be opened.
+            Slog.w(TAG, "NoSuchElementException", e);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to get fingerprint HAL", e);
+        }
+
+        if (mDaemon == null) {
+            Slog.w(TAG, "Fingerprint HAL not available");
+            return null;
+        }
+
+        mDaemon.asBinder().linkToDeath(this, 0 /* flags */);
+
+        // HAL ID for these HIDL versions are only used to determine if callbacks have been
+        // successfully set.
+        long halId = 0;
+        try {
+            halId = mDaemon.setNotify(mDaemonCallback);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to set callback for fingerprint HAL", e);
+            mDaemon = null;
+        }
+
+        Slog.d(TAG, "Fingerprint HAL ready, HAL ID: " + halId);
+        if (halId != 0) {
+            scheduleLoadAuthenticatorIds();
+            scheduleInternalCleanup(ActivityManager.getCurrentUser());
+        } else {
+            Slog.e(TAG, "Unable to set callback");
+            mDaemon = null;
+        }
+
+        return mDaemon;
+    }
+
+    @LockoutTracker.LockoutMode int getLockoutModeForUser(int userId) {
+        return mLockoutTracker.getLockoutModeForUser(userId);
+    }
+
+    private void scheduleLoadAuthenticatorIds() {
+        // Note that this can be performed on the scheduler (as opposed to being done immediately
+        // when the HAL is (re)loaded, since
+        // 1) If this is truly the first time it's being performed (e.g. system has just started),
+        //    this will be run very early and way before any applications need to generate keys.
+        // 2) If this is being performed to refresh the authenticatorIds (e.g. HAL crashed and has
+        //    just been reloaded), the framework already has a cache of the authenticatorIds. This
+        //    is safe because authenticatorIds only change when A) new template has been enrolled,
+        //    or B) all templates are removed.
+        mHandler.post(() -> {
+            for (UserInfo user : UserManager.get(mContext).getUsers(true /* excludeDying */)) {
+                final int targetUserId = user.id;
+                if (!mAuthenticatorIds.containsKey(targetUserId)) {
+                    scheduleUpdateActiveUserWithoutHandler(targetUserId);
+                }
+            }
+        });
+
+    }
+
+    private void scheduleUpdateActiveUserWithoutHandler(int targetUserId) {
+        final boolean hasEnrolled = !getEnrolledFingerprints(targetUserId).isEmpty();
+        final FingerprintUpdateActiveUserClient client =
+                new FingerprintUpdateActiveUserClient(mContext, mLazyDaemon, targetUserId,
+                        mContext.getOpPackageName(), mSensorProperties.sensorId, mCurrentUserId,
+                        hasEnrolled, mAuthenticatorIds);
+        mScheduler.scheduleClientMonitor(client, (clientMonitor, success) -> {
+            if (success) {
+                mCurrentUserId = targetUserId;
+            }
+        });
+    }
+
+    void scheduleResetLockout(int userId, byte[] hardwareAuthToken) {
+        // Fingerprint2.1 keeps track of lockout in the framework. Let's just do it on the handler
+        // thread.
+        mHandler.post(() -> {
+            mLockoutTracker.resetFailedAttemptsForUser(true /* clearAttemptCounter */, userId);
+        });
+    }
+
+    void scheduleGenerateChallenge(@NonNull IBinder token,
+            @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName) {
+        mHandler.post(() -> {
+            final FingerprintGenerateChallengeClient client =
+                    new FingerprintGenerateChallengeClient(mContext, mLazyDaemon, token,
+                            new ClientMonitorCallbackConverter(receiver), opPackageName,
+                            mSensorProperties.sensorId);
+            mScheduler.scheduleClientMonitor(client);
+        });
+    }
+
+    void scheduleRevokeChallenge(@NonNull IBinder token, @NonNull String opPackageName) {
+        mHandler.post(() -> {
+            final FingerprintRevokeChallengeClient client = new FingerprintRevokeChallengeClient(
+                    mContext, mLazyDaemon, token, opPackageName, mSensorProperties.sensorId);
+            mScheduler.scheduleClientMonitor(client);
+        });
+    }
+
+    void scheduleEnroll(@NonNull IBinder token, @NonNull byte[] hardwareAuthToken, int userId,
+            @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
+            @Nullable Surface surface) {
+        mHandler.post(() -> {
+            scheduleUpdateActiveUserWithoutHandler(userId);
+
+            final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext,
+                    mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
+                    hardwareAuthToken, opPackageName, FingerprintUtils.getInstance(),
+                    ENROLL_TIMEOUT_SEC, mSensorProperties.sensorId);
+            mScheduler.scheduleClientMonitor(client);
+        });
+
+    }
+
+    void cancelEnrollment(@NonNull IBinder token) {
+        mHandler.post(() -> {
+            mScheduler.cancelEnrollment(token);
+        });
+    }
+
+    void scheduleAuthenticate(@NonNull IBinder token, long operationId, int userId, int cookie,
+            @NonNull ClientMonitorCallbackConverter listener, @NonNull String opPackageName,
+            @Nullable Surface surface, boolean restricted, int statsClient) {
+        mHandler.post(() -> {
+            scheduleUpdateActiveUserWithoutHandler(userId);
+
+            final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorProperties.sensorId);
+            final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
+                    mContext, mLazyDaemon, token, listener, userId, operationId, restricted,
+                    opPackageName, cookie, false /* requireConfirmation */,
+                    mSensorProperties.sensorId, isStrongBiometric, surface, statsClient,
+                    mTaskStackListener, mLockoutTracker);
+            mScheduler.scheduleClientMonitor(client);
+        });
+    }
+
+    void startPreparedClient(int cookie) {
+        mHandler.post(() -> {
+            mScheduler.startPreparedClient(cookie);
+        });
+    }
+
+    void cancelAuthentication(@NonNull IBinder token) {
+        mHandler.post(() -> {
+            mScheduler.cancelAuthentication(token);
+        });
+    }
+
+    void scheduleRemove(@NonNull IBinder token, @NonNull IFingerprintServiceReceiver receiver,
+            int fingerId, int userId, @NonNull String opPackageName) {
+        mHandler.post(() -> {
+           scheduleUpdateActiveUserWithoutHandler(userId);
+
+           final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext,
+                   mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), fingerId,
+                   userId, opPackageName, FingerprintUtils.getInstance(),
+                   mSensorProperties.sensorId, mAuthenticatorIds);
+           mScheduler.scheduleClientMonitor(client);
+        });
+    }
+
+    private void scheduleInternalCleanup(int userId) {
+        mHandler.post(() -> {
+            scheduleUpdateActiveUserWithoutHandler(userId);
+
+            final List<Fingerprint> enrolledList = getEnrolledFingerprints(userId);
+            final FingerprintInternalCleanupClient client = new FingerprintInternalCleanupClient(
+                    mContext, mLazyDaemon, userId, mContext.getOpPackageName(),
+                    mSensorProperties.sensorId, enrolledList, FingerprintUtils.getInstance(),
+                    mAuthenticatorIds);
+            mScheduler.scheduleClientMonitor(client);
+        });
+    }
+
+    boolean isHardwareDetected() {
+        IBiometricsFingerprint daemon = getDaemon();
+        return daemon != null;
+    }
+
+    void rename(int fingerId, int userId, String name) {
+        mHandler.post(() -> {
+            FingerprintUtils.getInstance().renameBiometricForUser(mContext, userId, fingerId, name);
+        });
+    }
+
+    List<Fingerprint> getEnrolledFingerprints(int userId) {
+        return FingerprintUtils.getInstance().getBiometricsForUser(mContext, userId);
+    }
+
+    long getAuthenticatorId(int userId) {
+        return mAuthenticatorIds.get(userId);
+    }
+
+    void onFingerDown(int x, int y, float minor, float major) {
+        final ClientMonitor<?> client = mScheduler.getCurrentClient();
+        if (!(client instanceof Udfps)) {
+            Slog.w(TAG, "onFingerDown received during client: " + client);
+            return;
+        }
+        final Udfps udfps = (Udfps) client;
+        udfps.onFingerDown(x, y, minor, major);
+    }
+
+    void onFingerUp() {
+        final ClientMonitor<?> client = mScheduler.getCurrentClient();
+        if (!(client instanceof Udfps)) {
+            Slog.w(TAG, "onFingerDown received during client: " + client);
+            return;
+        }
+        final Udfps udfps = (Udfps) client;
+        udfps.onFingerUp();
+    }
+
+    boolean isUdfps() {
+        return mSensorProperties.isUdfps;
+    }
+
+    void dumpProto(FileDescriptor fd) {
+        PerformanceTracker tracker =
+                PerformanceTracker.getInstanceForSensorId(mSensorProperties.sensorId);
+
+        final ProtoOutputStream proto = new ProtoOutputStream(fd);
+        for (UserInfo user : UserManager.get(mContext).getUsers()) {
+            final int userId = user.getUserHandle().getIdentifier();
+
+            final long userToken = proto.start(FingerprintServiceDumpProto.USERS);
+
+            proto.write(FingerprintUserStatsProto.USER_ID, userId);
+            proto.write(FingerprintUserStatsProto.NUM_FINGERPRINTS,
+                    FingerprintUtils.getInstance().getBiometricsForUser(mContext, userId).size());
+
+            // Normal fingerprint authentications (e.g. lockscreen)
+            long countsToken = proto.start(FingerprintUserStatsProto.NORMAL);
+            proto.write(PerformanceStatsProto.ACCEPT, tracker.getAcceptForUser(userId));
+            proto.write(PerformanceStatsProto.REJECT, tracker.getRejectForUser(userId));
+            proto.write(PerformanceStatsProto.ACQUIRE, tracker.getAcquireForUser(userId));
+            proto.write(PerformanceStatsProto.LOCKOUT, tracker.getTimedLockoutForUser(userId));
+            proto.write(PerformanceStatsProto.PERMANENT_LOCKOUT,
+                    tracker.getPermanentLockoutForUser(userId));
+            proto.end(countsToken);
+
+            // Statistics about secure fingerprint transactions (e.g. to unlock password
+            // storage, make secure purchases, etc.)
+            countsToken = proto.start(FingerprintUserStatsProto.CRYPTO);
+            proto.write(PerformanceStatsProto.ACCEPT, tracker.getAcceptCryptoForUser(userId));
+            proto.write(PerformanceStatsProto.REJECT, tracker.getRejectCryptoForUser(userId));
+            proto.write(PerformanceStatsProto.ACQUIRE, tracker.getAcquireCryptoForUser(userId));
+            proto.write(PerformanceStatsProto.LOCKOUT, 0); // meaningless for crypto
+            proto.write(PerformanceStatsProto.PERMANENT_LOCKOUT, 0); // meaningless for crypto
+            proto.end(countsToken);
+
+            proto.end(userToken);
+        }
+        proto.flush();
+        tracker.clear();
+    }
+
+    void dumpInternal(PrintWriter pw) {
+        PerformanceTracker performanceTracker =
+                PerformanceTracker.getInstanceForSensorId(mSensorProperties.sensorId);
+
+        JSONObject dump = new JSONObject();
+        try {
+            dump.put("service", "Fingerprint Manager");
+
+            JSONArray sets = new JSONArray();
+            for (UserInfo user : UserManager.get(mContext).getUsers()) {
+                final int userId = user.getUserHandle().getIdentifier();
+                final int N = FingerprintUtils.getInstance()
+                        .getBiometricsForUser(mContext, userId).size();
+                JSONObject set = new JSONObject();
+                set.put("id", userId);
+                set.put("count", N);
+                set.put("accept", performanceTracker.getAcceptForUser(userId));
+                set.put("reject", performanceTracker.getRejectForUser(userId));
+                set.put("acquire", performanceTracker.getAcquireForUser(userId));
+                set.put("lockout", performanceTracker.getTimedLockoutForUser(userId));
+                set.put("permanentLockout", performanceTracker.getPermanentLockoutForUser(userId));
+                // cryptoStats measures statistics about secure fingerprint transactions
+                // (e.g. to unlock password storage, make secure purchases, etc.)
+                set.put("acceptCrypto", performanceTracker.getAcceptCryptoForUser(userId));
+                set.put("rejectCrypto", performanceTracker.getRejectCryptoForUser(userId));
+                set.put("acquireCrypto", performanceTracker.getAcquireCryptoForUser(userId));
+                sets.put(set);
+            }
+
+            dump.put("prints", sets);
+        } catch (JSONException e) {
+            Slog.e(TAG, "dump formatting failure", e);
+        }
+        pw.println(dump);
+        pw.println("HAL deaths since last reboot: " + performanceTracker.getHALDeathCount());
+        mScheduler.dump(pw);
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java
index b01b856..9cc0b0d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java
@@ -41,7 +41,8 @@
  * {@link android.hardware.biometrics.fingerprint.V2_1} and
  * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces.
  */
-class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFingerprint> {
+class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFingerprint>
+        implements Udfps {
 
     private static final String TAG = "Biometrics/FingerprintAuthClient";
 
@@ -79,12 +80,14 @@
                     mLockoutFrameworkImpl.getLockoutModeForUser(getTargetUserId());
             if (lockoutMode != LockoutTracker.LOCKOUT_NONE) {
                 Slog.w(TAG, "Fingerprint locked out, lockoutMode(" + lockoutMode + ")");
-                cancel();
                 final int errorCode = lockoutMode == LockoutTracker.LOCKOUT_TIMED
                         ? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
                         : BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
-                onError(errorCode, 0 /* vendorCode */);
-                mFinishCallback.onClientFinished(this, true /* success */);
+                // Send the error, but do not invoke the FinishCallback yet. Since lockout is not
+                // controlled by the HAL, the framework must stop the sensor before finishing the
+                // client.
+                onErrorInternal(errorCode, 0 /* vendorCode */, false /* finish */);
+                cancel();
             }
         }
     }
@@ -123,4 +126,14 @@
             mFinishCallback.onClientFinished(this, false /* success */);
         }
     }
+
+    @Override
+    public void onFingerDown(int x, int y, float minor, float major) {
+        UdfpsHelper.onFingerDown(getFreshDaemon(), x, y, minor, major);
+    }
+
+    @Override
+    public void onFingerUp() {
+        UdfpsHelper.onFingerUp(getFreshDaemon());
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java
index 1d56882..5282bdf 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java
@@ -34,7 +34,8 @@
  * {@link android.hardware.biometrics.fingerprint.V2_1} and
  * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces.
  */
-public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint> {
+public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint>
+        implements Udfps {
 
     private static final String TAG = "FingerprintEnrollClient";
 
@@ -72,4 +73,14 @@
             mFinishCallback.onClientFinished(this, false /* success */);
         }
     }
+
+    @Override
+    public void onFingerDown(int x, int y, float minor, float major) {
+        UdfpsHelper.onFingerDown(getFreshDaemon(), x, y, minor, major);
+    }
+
+    @Override
+    public void onFingerUp() {
+        UdfpsHelper.onFingerUp(getFreshDaemon());
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java
index 1d72c2e..571d2b8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
-import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
 import android.hardware.fingerprint.Fingerprint;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index b05400a..11e6bf2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -23,64 +23,38 @@
 import static android.Manifest.permission.USE_BIOMETRIC;
 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
 import static android.Manifest.permission.USE_FINGERPRINT;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
 
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.IBiometricSensorReceiver;
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprintClientCallback;
 import android.hardware.fingerprint.Fingerprint;
 import android.hardware.fingerprint.IFingerprintClientActiveCallback;
 import android.hardware.fingerprint.IFingerprintService;
 import android.hardware.fingerprint.IFingerprintServiceReceiver;
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.os.Binder;
-import android.os.Build;
-import android.os.Environment;
 import android.os.IBinder;
 import android.os.NativeHandle;
+import android.os.Process;
 import android.os.RemoteException;
-import android.os.SELinux;
 import android.os.UserHandle;
-import android.os.UserManager;
 import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
 import android.view.Surface;
 
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.logging.MetricsLogger;
 import com.android.internal.util.DumpUtils;
-import com.android.server.SystemServerInitThreadPool;
-import com.android.server.biometrics.fingerprint.FingerprintServiceDumpProto;
-import com.android.server.biometrics.fingerprint.FingerprintUserStatsProto;
-import com.android.server.biometrics.fingerprint.PerformanceStatsProto;
-import com.android.server.biometrics.sensors.AuthenticationClient;
-import com.android.server.biometrics.sensors.BiometricServiceBase;
-import com.android.server.biometrics.sensors.BiometricUtils;
-import com.android.server.biometrics.sensors.ClientMonitor;
+import com.android.server.SystemService;
+import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
-import com.android.server.biometrics.sensors.EnrollClient;
-import com.android.server.biometrics.sensors.GenerateChallengeClient;
 import com.android.server.biometrics.sensors.LockoutResetTracker;
 import com.android.server.biometrics.sensors.LockoutTracker;
-import com.android.server.biometrics.sensors.PerformanceTracker;
-import com.android.server.biometrics.sensors.RemovalClient;
-import com.android.server.biometrics.sensors.RevokeChallengeClient;
 
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.File;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
@@ -88,159 +62,127 @@
  * A service to manage multiple clients that want to access the fingerprint HAL API.
  * The service is responsible for maintaining a list of clients and dispatching all
  * fingerprint-related events.
- *
- * @hide
  */
-public class FingerprintService extends BiometricServiceBase<IBiometricsFingerprint> {
+public class FingerprintService extends SystemService {
 
     protected static final String TAG = "FingerprintService";
-    private static final boolean DEBUG = true;
-    private static final String FP_DATA_DIR = "fpdata";
 
+    private final AppOpsManager mAppOps;
     private final LockoutResetTracker mLockoutResetTracker;
-    private final ClientMonitor.LazyDaemon<IBiometricsFingerprint> mLazyDaemon;
     private final GestureAvailabilityTracker mGestureAvailabilityTracker;
+    private Fingerprint21 mFingerprint21;
+    private IUdfpsOverlayController mUdfpsOverlayController;
 
     /**
      * Receives the incoming binder calls from FingerprintManager.
      */
     private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
-        private static final int ENROLL_TIMEOUT_SEC = 60;
-
-        /**
-         * The following methods contain common code which is shared in biometrics/common.
-         */
-
         @Override // Binder call
         public void generateChallenge(IBinder token, IFingerprintServiceReceiver receiver,
                 String opPackageName) {
-            checkPermission(MANAGE_FINGERPRINT);
-
-            final GenerateChallengeClient client = new FingerprintGenerateChallengeClient(
-                    getContext(), mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver),
-                    opPackageName, getSensorId());
-            generateChallengeInternal(client);
+            Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
+            mFingerprint21.scheduleGenerateChallenge(token, receiver, opPackageName);
         }
 
         @Override // Binder call
         public void revokeChallenge(IBinder token, String owner) {
-            checkPermission(MANAGE_FINGERPRINT);
-
-            final RevokeChallengeClient client = new FingerprintRevokeChallengeClient(getContext(),
-                    mLazyDaemon, token, owner, getSensorId());
-            revokeChallengeInternal(client);
+            Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
+            mFingerprint21.scheduleRevokeChallenge(token, owner);
         }
 
         @Override // Binder call
-        public void enroll(final IBinder token, final byte[] cryptoToken, final int userId,
+        public void enroll(final IBinder token, final byte[] hardwareAuthToken, final int userId,
                 final IFingerprintServiceReceiver receiver, final String opPackageName,
-                final Surface surface) {
-            checkPermission(MANAGE_FINGERPRINT);
-            updateActiveGroup(userId);
-
-            final EnrollClient client = new FingerprintEnrollClient(getContext(), mLazyDaemon,
-                    token, new ClientMonitorCallbackConverter(receiver), userId, cryptoToken,
-                    opPackageName, getBiometricUtils(), ENROLL_TIMEOUT_SEC, getSensorId());
-
-            enrollInternal(client, userId);
+                Surface surface) {
+            Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
+            mFingerprint21.scheduleEnroll(token, hardwareAuthToken, userId, receiver, opPackageName,
+                    surface);
         }
 
         @Override // Binder call
         public void cancelEnrollment(final IBinder token) {
-            checkPermission(MANAGE_FINGERPRINT);
-            cancelEnrollmentInternal(token);
+            Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
+            mFingerprint21.cancelEnrollment(token);
         }
 
         @Override // Binder call
-        public void authenticate(final IBinder token, final long opId, final int userId,
+        public void authenticate(final IBinder token, final long operationId, final int userId,
                 final IFingerprintServiceReceiver receiver, final String opPackageName,
-                final Surface surface) {
-            updateActiveGroup(userId);
+                Surface surface) {
+            final int callingUid = Binder.getCallingUid();
+            final int callingPid = Binder.getCallingPid();
+            final int callingUserId = UserHandle.getCallingUserId();
 
-            final boolean isStrongBiometric;
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                isStrongBiometric = isStrongBiometric();
-            } finally {
-                Binder.restoreCallingIdentity(ident);
+            if (!canUseFingerprint(opPackageName, true /* requireForeground */, callingUid,
+                    callingPid, callingUserId)) {
+                Slog.w(TAG, "Authenticate rejecting package: " + opPackageName);
+                return;
             }
 
-            final boolean restricted = isRestricted();
-            final int statsClient = isKeyguard(opPackageName) ? BiometricsProtoEnums.CLIENT_KEYGUARD
+            final boolean restricted = getContext().checkCallingPermission(MANAGE_FINGERPRINT)
+                    != PackageManager.PERMISSION_GRANTED;
+            final int statsClient = Utils.isKeyguard(getContext(), opPackageName)
+                    ? BiometricsProtoEnums.CLIENT_KEYGUARD
                     : BiometricsProtoEnums.CLIENT_FINGERPRINT_MANAGER;
-            final AuthenticationClient client = new FingerprintAuthenticationClient(getContext(),
-                    mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, opId,
-                    restricted, opPackageName, 0 /* cookie */, false /* requireConfirmation */,
-                    getSensorId(), isStrongBiometric, surface, statsClient, mTaskStackListener,
-                    mLockoutTracker);
-            authenticateInternal(client, opPackageName);
+            mFingerprint21.scheduleAuthenticate(token, operationId, userId, 0 /* cookie */,
+                    new ClientMonitorCallbackConverter(receiver), opPackageName, surface,
+                    restricted, statsClient);
         }
 
         @Override // Binder call
-        public void prepareForAuthentication(IBinder token, long opId, int userId,
+        public void prepareForAuthentication(IBinder token, long operationId, int userId,
                 IBiometricSensorReceiver sensorReceiver, String opPackageName,
                 int cookie, int callingUid, int callingPid, int callingUserId,
                 Surface surface) {
-            checkPermission(MANAGE_BIOMETRIC);
-            updateActiveGroup(userId);
+            Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
 
             final boolean restricted = true; // BiometricPrompt is always restricted
-            final AuthenticationClient client = new FingerprintAuthenticationClient(getContext(),
-                    mLazyDaemon, token, new ClientMonitorCallbackConverter(sensorReceiver), userId,
-                    opId, restricted, opPackageName, cookie, false /* requireConfirmation */,
-                    getSensorId(), isStrongBiometric(), surface,
-                    BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, mTaskStackListener,
-                    mLockoutTracker);
-            authenticateInternal(client, opPackageName, callingUid, callingPid,
-                    callingUserId);
+            mFingerprint21.scheduleAuthenticate(token, operationId, userId, cookie,
+                    new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, surface,
+                    restricted, BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT);
         }
 
         @Override // Binder call
         public void startPreparedClient(int cookie) {
-            checkPermission(MANAGE_BIOMETRIC);
-            startCurrentClient(cookie);
+            Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
+            mFingerprint21.startPreparedClient(cookie);
         }
 
 
         @Override // Binder call
         public void cancelAuthentication(final IBinder token, final String opPackageName) {
-            cancelAuthenticationInternal(token, opPackageName);
+            final int callingUid = Binder.getCallingUid();
+            final int callingPid = Binder.getCallingPid();
+            final int callingUserId = UserHandle.getCallingUserId();
+
+            if (!canUseFingerprint(opPackageName, true /* requireForeground */, callingUid,
+                    callingPid, callingUserId)) {
+                Slog.w(TAG, "cancelAuthentication rejecting package: " + opPackageName);
+                return;
+            }
+
+            mFingerprint21.cancelAuthentication(token);
         }
 
         @Override // Binder call
         public void cancelAuthenticationFromService(final IBinder token, final String opPackageName,
                 int callingUid, int callingPid, int callingUserId) {
-            checkPermission(MANAGE_BIOMETRIC);
-            // Cancellation is from system server in this case.
-            cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid,
-                    callingUserId, false /* fromClient */);
+            Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
+            mFingerprint21.cancelAuthentication(token);
         }
 
         @Override // Binder call
         public void remove(final IBinder token, final int fingerId, final int userId,
-                final IFingerprintServiceReceiver receiver, final String opPackageName)
-                throws RemoteException {
-            checkPermission(MANAGE_FINGERPRINT);
-            updateActiveGroup(userId);
-
-            if (token == null) {
-                Slog.w(TAG, "remove(): token is null");
-                return;
-            }
-
-            final RemovalClient client = new FingerprintRemovalClient(getContext(), mLazyDaemon,
-                    token, new ClientMonitorCallbackConverter(receiver), fingerId, userId,
-                    opPackageName, getBiometricUtils(), getSensorId(), mAuthenticatorIds);
-            removeInternal(client);
+                final IFingerprintServiceReceiver receiver, final String opPackageName) {
+            Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
+            mFingerprint21.scheduleRemove(token, receiver, fingerId, userId, opPackageName);
         }
 
         @Override
         public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback,
                 final String opPackageName) {
-            checkPermission(USE_BIOMETRIC_INTERNAL);
-            mHandler.post(() -> {
-                mLockoutResetTracker.addCallback(callback, opPackageName);
-            });
+            Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+            mLockoutResetTracker.addCallback(callback, opPackageName);
         }
 
         @Override // Binder call
@@ -252,23 +194,18 @@
             final long ident = Binder.clearCallingIdentity();
             try {
                 if (args.length > 0 && "--proto".equals(args[0])) {
-                    dumpProto(fd);
+                    mFingerprint21.dumpProto(fd);
                 } else {
-                    dumpInternal(pw);
+                    mFingerprint21.dumpInternal(pw);
                 }
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
         }
 
-        /**
-         * The following methods don't use any common code from BiometricService
-         */
-
-        // TODO: refactor out common code here
         @Override // Binder call
         public boolean isHardwareDetected(String opPackageName) {
-            if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
+            if (!canUseFingerprint(opPackageName, false /* foregroundOnly */,
                     Binder.getCallingUid(), Binder.getCallingPid(),
                     UserHandle.getCallingUserId())) {
                 return false;
@@ -276,8 +213,7 @@
 
             final long token = Binder.clearCallingIdentity();
             try {
-                IBiometricsFingerprint daemon = getFingerprintDaemon();
-                return daemon != null;
+                return mFingerprint21.isHardwareDetected();
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -285,162 +221,106 @@
 
         @Override // Binder call
         public void rename(final int fingerId, final int userId, final String name) {
-            checkPermission(MANAGE_FINGERPRINT);
-            if (!isCurrentUserOrProfile(userId)) {
+            Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
+            if (!Utils.isCurrentUserOrProfile(getContext(), userId)) {
                 return;
             }
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    getBiometricUtils().renameBiometricForUser(getContext(), userId, fingerId,
-                            name);
-                }
-            });
+
+            mFingerprint21.rename(fingerId, userId, name);
         }
 
         @Override // Binder call
         public List<Fingerprint> getEnrolledFingerprints(int userId, String opPackageName) {
-            if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
+            if (!canUseFingerprint(opPackageName, false /* foregroundOnly */,
                     Binder.getCallingUid(), Binder.getCallingPid(),
                     UserHandle.getCallingUserId())) {
                 return Collections.emptyList();
             }
 
-            return FingerprintService.this.getEnrolledTemplates(userId);
+            if (userId != UserHandle.getCallingUserId()) {
+                Utils.checkPermission(getContext(), INTERACT_ACROSS_USERS);
+            }
+            return mFingerprint21.getEnrolledFingerprints(userId);
         }
 
         @Override // Binder call
         public boolean hasEnrolledFingerprints(int userId, String opPackageName) {
-            if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
+            if (!canUseFingerprint(opPackageName, false /* foregroundOnly */,
                     Binder.getCallingUid(), Binder.getCallingPid(),
                     UserHandle.getCallingUserId())) {
                 return false;
             }
 
-            return FingerprintService.this.hasEnrolledBiometrics(userId);
+            if (userId != UserHandle.getCallingUserId()) {
+                Utils.checkPermission(getContext(), INTERACT_ACROSS_USERS);
+            }
+            return mFingerprint21.getEnrolledFingerprints(userId).size() > 0;
         }
 
         @Override // Binder call
         public @LockoutTracker.LockoutMode int getLockoutModeForUser(int userId) {
-            checkPermission(USE_BIOMETRIC_INTERNAL);
-            return mLockoutTracker.getLockoutModeForUser(userId);
+            Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+            return mFingerprint21.getLockoutModeForUser(userId);
         }
 
         @Override // Binder call
-        public long getAuthenticatorId(int callingUserId) {
-            checkPermission(USE_BIOMETRIC_INTERNAL);
-            return FingerprintService.this.getAuthenticatorId(callingUserId);
+        public long getAuthenticatorId(int userId) {
+            Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+            return mFingerprint21.getAuthenticatorId(userId);
         }
 
         @Override // Binder call
-        public void resetLockout(int userId, byte [] hardwareAuthToken) throws RemoteException {
-            checkPermission(RESET_FINGERPRINT_LOCKOUT);
-            if (!FingerprintService.this.hasEnrolledBiometrics(userId)) {
-                Slog.w(TAG, "Ignoring lockout reset, no templates enrolled for user: " + userId);
-                return;
-            }
-
-            // TODO: confirm security token when we move timeout management into the HAL layer.
-            mHandler.post(() -> {
-                mLockoutTracker.resetFailedAttemptsForUser(true /* clearAttemptCounter */, userId);
-            });
+        public void resetLockout(int userId, byte [] hardwareAuthToken) {
+            Utils.checkPermission(getContext(), RESET_FINGERPRINT_LOCKOUT);
+            mFingerprint21.scheduleResetLockout(userId, hardwareAuthToken);
         }
 
         @Override
         public boolean isClientActive() {
-            checkPermission(MANAGE_FINGERPRINT);
-            synchronized(FingerprintService.this) {
-                return (getCurrentClient() != null) || (getPendingClient() != null);
-            }
+            Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
+            return mGestureAvailabilityTracker.isAnySensorActive();
         }
 
         @Override
         public void addClientActiveCallback(IFingerprintClientActiveCallback callback) {
-            checkPermission(MANAGE_FINGERPRINT);
+            Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
             mGestureAvailabilityTracker.registerCallback(callback);
         }
 
         @Override
         public void removeClientActiveCallback(IFingerprintClientActiveCallback callback) {
-            checkPermission(MANAGE_FINGERPRINT);
+            Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
             mGestureAvailabilityTracker.removeCallback(callback);
         }
 
         @Override // Binder call
         public void initializeConfiguration(int sensorId) {
-            checkPermission(USE_BIOMETRIC_INTERNAL);
-            initializeConfigurationInternal(sensorId);
+            Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+            mFingerprint21 = new Fingerprint21(getContext(), sensorId, mLockoutResetTracker,
+                    mGestureAvailabilityTracker);
         }
 
         @Override
         public void onFingerDown(int x, int y, float minor, float major) {
-            checkPermission(USE_BIOMETRIC_INTERNAL);
-            IBiometricsFingerprint daemon = getFingerprintDaemon();
-            if (daemon == null) {
-                Slog.e(TAG, "onFingerDown | daemon is null");
-            } else {
-                android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint extension =
-                        android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint.castFrom(
-                                daemon);
-                if (extension == null) {
-                    Slog.v(TAG, "onFingerDown | failed to cast the HIDL to V2_3");
-                } else {
-                    try {
-                        extension.onFingerDown(x, y, minor, major);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "onFingerDown | RemoteException: ", e);
-                    }
-                }
-            }
+            Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+            mFingerprint21.onFingerDown(x, y, minor, major);
         }
 
         @Override
         public void onFingerUp() {
-            checkPermission(USE_BIOMETRIC_INTERNAL);
-            IBiometricsFingerprint daemon = getFingerprintDaemon();
-            if (daemon == null) {
-                Slog.e(TAG, "onFingerUp | daemon is null");
-            } else {
-                android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint extension =
-                        android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint.castFrom(
-                                daemon);
-                if (extension == null) {
-                    Slog.v(TAG, "onFingerUp | failed to cast the HIDL to V2_3");
-                } else {
-                    try {
-                        extension.onFingerUp();
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "onFingerUp | RemoteException: ", e);
-                    }
-                }
-            }
+            Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+            mFingerprint21.onFingerUp();
         }
 
         @Override
         public boolean isUdfps(int sensorId) {
-            checkPermission(USE_BIOMETRIC_INTERNAL);
-            IBiometricsFingerprint daemon = getFingerprintDaemon();
-            if (daemon == null) {
-                Slog.e(TAG, "isUdfps | daemon is null");
-            } else {
-                android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint extension =
-                        android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint.castFrom(
-                                daemon);
-                if (extension == null) {
-                    Slog.v(TAG, "isUdfps | failed to cast the HIDL to V2_3");
-                } else {
-                    try {
-                        return extension.isUdfps(sensorId);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "isUdfps | RemoteException: ", e);
-                    }
-                }
-            }
-            return false;
+            Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+            return mFingerprint21.isUdfps();
         }
 
         @Override
         public void showUdfpsOverlay() {
+            Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
             if (mUdfpsOverlayController == null) {
                 Slog.e(TAG, "showUdfpsOverlay | mUdfpsOverlayController is null");
                 return;
@@ -454,6 +334,7 @@
 
         @Override
         public void hideUdfpsOverlay() {
+            Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
             if (mUdfpsOverlayController == null) {
                 Slog.e(TAG, "hideUdfpsOverlay | mUdfpsOverlayController is null");
                 return;
@@ -465,209 +346,55 @@
             }
         }
 
+        @Override
         public void setUdfpsOverlayController(IUdfpsOverlayController controller) {
+            Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
             mUdfpsOverlayController = controller;
         }
     }
 
-    private final LockoutFrameworkImpl mLockoutTracker;
-    private IUdfpsOverlayController mUdfpsOverlayController;
-
-    @GuardedBy("this")
-    private IBiometricsFingerprint mDaemon;
-
-    /**
-     * Receives callbacks from the HAL.
-     */
-    private IBiometricsFingerprintClientCallback mDaemonCallback =
-            new IBiometricsFingerprintClientCallback.Stub() {
-        @Override
-        public void onEnrollResult(final long deviceId, final int fingerId, final int groupId,
-                final int remaining) {
-            mHandler.post(() -> {
-                final Fingerprint fingerprint =
-                        new Fingerprint(getBiometricUtils().getUniqueName(getContext(), groupId),
-                                groupId, fingerId, deviceId);
-                FingerprintService.super.handleEnrollResult(fingerprint, remaining);
-            });
-        }
-
-        @Override
-        public void onAcquired(final long deviceId, final int acquiredInfo, final int vendorCode) {
-            onAcquired_2_2(deviceId, acquiredInfo, vendorCode);
-        }
-
-        @Override
-        public void onAcquired_2_2(long deviceId, int acquiredInfo, int vendorCode) {
-            mHandler.post(() -> {
-                FingerprintService.super.handleAcquired(acquiredInfo, vendorCode);
-            });
-        }
-
-        @Override
-        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);
-            });
-        }
-
-        @Override
-        public void onError(final long deviceId, final int error, final int vendorCode) {
-            mHandler.post(() -> {
-                FingerprintService.super.handleError(error, vendorCode);
-                // TODO: this chunk of code should be common to all biometric services
-                if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
-                    // If we get HW_UNAVAILABLE, try to connect again later...
-                    Slog.w(TAG, "Got ERROR_HW_UNAVAILABLE; try reconnecting next client.");
-                    synchronized (this) {
-                        mDaemon = null;
-                        mCurrentUserId = UserHandle.USER_NULL;
-                    }
-                }
-            });
-        }
-
-        @Override
-        public void onRemoved(final long deviceId, final int fingerId, final int groupId,
-                final int remaining) {
-            mHandler.post(() -> {
-                final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
-                FingerprintService.super.handleRemoved(fp, remaining);
-            });
-        }
-
-        @Override
-        public void onEnumerate(final long deviceId, final int fingerId, final int groupId,
-                final int remaining) {
-            mHandler.post(() -> {
-                final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
-                FingerprintService.super.handleEnumerate(fp, remaining);
-            });
-
-        }
-    };
-
     public FingerprintService(Context context) {
         super(context);
-        mLazyDaemon = FingerprintService.this::getFingerprintDaemon;
+        mAppOps = context.getSystemService(AppOpsManager.class);
         mGestureAvailabilityTracker = new GestureAvailabilityTracker();
         mLockoutResetTracker = new LockoutResetTracker(context);
-        final LockoutFrameworkImpl.LockoutResetCallback lockoutResetCallback = userId -> {
-            mLockoutResetTracker.notifyLockoutResetCallbacks();
-        };
-        mLockoutTracker = new LockoutFrameworkImpl(context, lockoutResetCallback);
     }
 
     @Override
     public void onStart() {
-        super.onStart();
         publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
-        SystemServerInitThreadPool.submit(this::getFingerprintDaemon, TAG + ".onStart");
     }
 
-    @Override
-    protected String getTag() {
-        return TAG;
-    }
-
-    @Override
-    protected IBiometricsFingerprint getDaemon() {
-        return getFingerprintDaemon();
-    }
-
-    @Override
-    protected BiometricUtils getBiometricUtils() {
-        return FingerprintUtils.getInstance();
-    }
-
-    @Override
-    protected boolean hasReachedEnrollmentLimit(int userId) {
-        final int limit = getContext().getResources().getInteger(
-                com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
-        final int enrolled = FingerprintService.this.getEnrolledTemplates(userId).size();
-        if (enrolled >= limit) {
-            Slog.w(TAG, "Too many fingerprints registered");
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public void serviceDied(long cookie) {
-        super.serviceDied(cookie);
-        mDaemon = null;
-    }
-
-    @Override
-    protected void updateActiveGroup(int userId) {
-        IBiometricsFingerprint daemon = getFingerprintDaemon();
-
-        if (daemon != null) {
-            try {
-                if (userId != mCurrentUserId) {
-                    int firstSdkInt = Build.VERSION.FIRST_SDK_INT;
-                    if (firstSdkInt < Build.VERSION_CODES.BASE) {
-                        Slog.e(TAG, "First SDK version " + firstSdkInt + " is invalid; must be " +
-                                "at least VERSION_CODES.BASE");
-                    }
-                    File baseDir;
-                    if (firstSdkInt <= Build.VERSION_CODES.O_MR1) {
-                        baseDir = Environment.getUserSystemDirectory(userId);
-                    } else {
-                        baseDir = Environment.getDataVendorDeDirectory(userId);
-                    }
-
-                    File fpDir = new File(baseDir, FP_DATA_DIR);
-                    if (!fpDir.exists()) {
-                        if (!fpDir.mkdir()) {
-                            Slog.v(TAG, "Cannot make directory: " + fpDir.getAbsolutePath());
-                            return;
-                        }
-                        // Calling mkdir() from this process will create a directory with our
-                        // permissions (inherited from the containing dir). This command fixes
-                        // the label.
-                        if (!SELinux.restorecon(fpDir)) {
-                            Slog.w(TAG, "Restorecons failed. Directory will have wrong label.");
-                            return;
-                        }
-                    }
-
-                    daemon.setActiveGroup(userId, fpDir.getAbsolutePath());
-                    mCurrentUserId = userId;
-                }
-                mAuthenticatorIds.put(userId,
-                        hasEnrolledBiometrics(userId) ? daemon.getAuthenticatorId() : 0L);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to setActiveGroup():", e);
-            }
-        }
-    }
-
-    @Override
-    protected boolean hasEnrolledBiometrics(int userId) {
-        if (userId != UserHandle.getCallingUserId()) {
-            checkPermission(INTERACT_ACROSS_USERS);
-        }
-        return getBiometricUtils().getBiometricsForUser(getContext(), userId).size() > 0;
-    }
-
-    @Override
-    protected String getManageBiometricPermission() {
-        return MANAGE_FINGERPRINT;
-    }
-
-    @Override
-    protected void checkUseBiometricPermission() {
+    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
+    private boolean canUseFingerprint(String opPackageName, boolean requireForeground, int uid,
+            int pid, int userId) {
         if (getContext().checkCallingPermission(USE_FINGERPRINT)
                 != PackageManager.PERMISSION_GRANTED) {
-            checkPermission(USE_BIOMETRIC);
+            Utils.checkPermission(getContext(), USE_BIOMETRIC);
         }
+
+        if (Binder.getCallingUid() == Process.SYSTEM_UID) {
+            return true; // System process (BiometricService, etc) is always allowed
+        }
+        if (Utils.isKeyguard(getContext(), opPackageName)) {
+            return true;
+        }
+        if (!Utils.isCurrentUserOrProfile(getContext(), userId)) {
+            Slog.w(TAG, "Rejecting " + opPackageName + "; not a current user or profile");
+            return false;
+        }
+        if (!checkAppOps(uid, opPackageName)) {
+            Slog.w(TAG, "Rejecting " + opPackageName + "; permission denied");
+            return false;
+        }
+        if (requireForeground && !(isForegroundActivity(uid, pid))) {
+            Slog.w(TAG, "Rejecting " + opPackageName + "; not in foreground");
+            return false;
+        }
+        return true;
     }
 
-    @Override
-    protected boolean checkAppOps(int uid, String opPackageName) {
+    private boolean checkAppOps(int uid, String opPackageName) {
         boolean appOpsOk = false;
         if (mAppOps.noteOp(AppOpsManager.OP_USE_BIOMETRIC, uid, opPackageName)
                 == AppOpsManager.MODE_ALLOWED) {
@@ -679,154 +406,28 @@
         return appOpsOk;
     }
 
-    @Override
-    protected List<Fingerprint> getEnrolledTemplates(int userId) {
-        if (userId != UserHandle.getCallingUserId()) {
-            checkPermission(INTERACT_ACROSS_USERS);
+    private boolean isForegroundActivity(int uid, int pid) {
+        try {
+            final List<ActivityManager.RunningAppProcessInfo> procs =
+                    ActivityManager.getService().getRunningAppProcesses();
+            if (procs == null) {
+                Slog.e(TAG, "Processes null, defaulting to true");
+                return true;
+            }
+
+            int N = procs.size();
+            for (int i = 0; i < N; i++) {
+                ActivityManager.RunningAppProcessInfo proc = procs.get(i);
+                if (proc.pid == pid && proc.uid == uid
+                        && proc.importance <= IMPORTANCE_FOREGROUND_SERVICE) {
+                    return true;
+                }
+            }
+        } catch (RemoteException e) {
+            Slog.w(TAG, "am.getRunningAppProcesses() failed");
         }
-        return FingerprintUtils.getInstance().getBiometricsForUser(getContext(), userId);
-    }
-
-    @Override
-    protected void notifyClientActiveCallbacks(boolean isActive) {
-        mGestureAvailabilityTracker.notifyClientActiveCallbacks(isActive);
-    }
-
-    @Override
-    protected int statsModality() {
-        return BiometricsProtoEnums.MODALITY_FINGERPRINT;
-    }
-
-    @Override
-    protected @LockoutTracker.LockoutMode int getLockoutMode(int userId) {
-        return mLockoutTracker.getLockoutModeForUser(userId);
-    }
-
-    @Override
-    protected void doTemplateCleanupForUser(int userId) {
-        final List<Fingerprint> enrolledList = getEnrolledTemplates(userId);
-        final FingerprintInternalCleanupClient client = new FingerprintInternalCleanupClient(
-                getContext(), mLazyDaemon, userId, getContext().getOpPackageName(), getSensorId(),
-                enrolledList, getBiometricUtils(), mAuthenticatorIds);
-        cleanupInternal(client);
-    }
-
-    /** Gets the fingerprint daemon */
-    private synchronized IBiometricsFingerprint getFingerprintDaemon() {
-        if (mDaemon == null) {
-            Slog.v(TAG, "mDaemon was null, reconnect to fingerprint");
-            try {
-                mDaemon = IBiometricsFingerprint.getService();
-            } catch (java.util.NoSuchElementException e) {
-                // Service doesn't exist or cannot be opened. Logged below.
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to get biometric interface", e);
-            }
-            if (mDaemon == null) {
-                Slog.w(TAG, "fingerprint HIDL not available");
-                return null;
-            }
-
-            mDaemon.asBinder().linkToDeath(this, 0);
-
-            long halId = 0;
-            try {
-                halId = mDaemon.setNotify(mDaemonCallback);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to open fingerprint HAL", e);
-                mDaemon = null; // try again later!
-            }
-
-            if (DEBUG) Slog.v(TAG, "Fingerprint HAL id: " + halId);
-            if (halId != 0) {
-                loadAuthenticatorIds();
-                final int userId = ActivityManager.getCurrentUser();
-                updateActiveGroup(userId);
-                doTemplateCleanupForUser(userId);
-            } else {
-                Slog.w(TAG, "Failed to open Fingerprint HAL!");
-                MetricsLogger.count(getContext(), "fingerprintd_openhal_error", 1);
-                mDaemon = null;
-            }
-        }
-        return mDaemon;
+        return false;
     }
 
     private native NativeHandle convertSurfaceToNativeHandle(Surface surface);
-
-    private void dumpInternal(PrintWriter pw) {
-        PerformanceTracker performanceTracker =
-                PerformanceTracker.getInstanceForSensorId(getSensorId());
-
-        JSONObject dump = new JSONObject();
-        try {
-            dump.put("service", "Fingerprint Manager");
-
-            JSONArray sets = new JSONArray();
-            for (UserInfo user : UserManager.get(getContext()).getUsers()) {
-                final int userId = user.getUserHandle().getIdentifier();
-                final int N = getBiometricUtils().getBiometricsForUser(getContext(), userId).size();
-                JSONObject set = new JSONObject();
-                set.put("id", userId);
-                set.put("count", N);
-                set.put("accept", performanceTracker.getAcceptForUser(userId));
-                set.put("reject", performanceTracker.getRejectForUser(userId));
-                set.put("acquire", performanceTracker.getAcquireForUser(userId));
-                set.put("lockout", performanceTracker.getTimedLockoutForUser(userId));
-                set.put("permanentLockout", performanceTracker.getPermanentLockoutForUser(userId));
-                // cryptoStats measures statistics about secure fingerprint transactions
-                // (e.g. to unlock password storage, make secure purchases, etc.)
-                set.put("acceptCrypto", performanceTracker.getAcceptCryptoForUser(userId));
-                set.put("rejectCrypto", performanceTracker.getRejectCryptoForUser(userId));
-                set.put("acquireCrypto", performanceTracker.getAcquireCryptoForUser(userId));
-                sets.put(set);
-            }
-
-            dump.put("prints", sets);
-        } catch (JSONException e) {
-            Slog.e(TAG, "dump formatting failure", e);
-        }
-        pw.println(dump);
-        pw.println("HAL deaths since last reboot: " + performanceTracker.getHALDeathCount());
-    }
-
-    private void dumpProto(FileDescriptor fd) {
-        PerformanceTracker tracker =
-                PerformanceTracker.getInstanceForSensorId(getSensorId());
-
-        final ProtoOutputStream proto = new ProtoOutputStream(fd);
-        for (UserInfo user : UserManager.get(getContext()).getUsers()) {
-            final int userId = user.getUserHandle().getIdentifier();
-
-            final long userToken = proto.start(FingerprintServiceDumpProto.USERS);
-
-            proto.write(FingerprintUserStatsProto.USER_ID, userId);
-            proto.write(FingerprintUserStatsProto.NUM_FINGERPRINTS,
-                    getBiometricUtils().getBiometricsForUser(getContext(), userId).size());
-
-            // Normal fingerprint authentications (e.g. lockscreen)
-            long countsToken = proto.start(FingerprintUserStatsProto.NORMAL);
-            proto.write(PerformanceStatsProto.ACCEPT, tracker.getAcceptForUser(userId));
-            proto.write(PerformanceStatsProto.REJECT, tracker.getRejectForUser(userId));
-            proto.write(PerformanceStatsProto.ACQUIRE, tracker.getAcquireForUser(userId));
-            proto.write(PerformanceStatsProto.LOCKOUT, tracker.getTimedLockoutForUser(userId));
-            proto.write(PerformanceStatsProto.PERMANENT_LOCKOUT,
-                    tracker.getPermanentLockoutForUser(userId));
-            proto.end(countsToken);
-
-            // Statistics about secure fingerprint transactions (e.g. to unlock password
-            // storage, make secure purchases, etc.)
-            countsToken = proto.start(FingerprintUserStatsProto.CRYPTO);
-            proto.write(PerformanceStatsProto.ACCEPT, tracker.getAcceptCryptoForUser(userId));
-            proto.write(PerformanceStatsProto.REJECT, tracker.getRejectCryptoForUser(userId));
-            proto.write(PerformanceStatsProto.ACQUIRE, tracker.getAcquireCryptoForUser(userId));
-            proto.write(PerformanceStatsProto.LOCKOUT, 0); // meaningless for crypto
-            proto.write(PerformanceStatsProto.PERMANENT_LOCKOUT, 0); // meaningless for crypto
-            proto.end(countsToken);
-
-            proto.end(userToken);
-        }
-        proto.flush();
-        tracker.clear();
-    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUpdateActiveUserClient.java
new file mode 100644
index 0000000..e1082ae
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUpdateActiveUserClient.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2020 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.sensors.fingerprint;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.os.Build;
+import android.os.Environment;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SELinux;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.ClientMonitor;
+
+import java.io.File;
+import java.util.Map;
+
+/**
+ * Sets the HAL's current active user, and updates the framework's authenticatorId cache.
+ */
+public class FingerprintUpdateActiveUserClient extends ClientMonitor<IBiometricsFingerprint> {
+
+    private static final String TAG = "FingerprintUpdateActiveUserClient";
+    private static final String FP_DATA_DIR = "fpdata";
+
+    private final int mCurrentUserId;
+    private final boolean mHasEnrolledBiometrics;
+    private final Map<Integer, Long> mAuthenticatorIds;
+    private File mDirectory;
+
+    FingerprintUpdateActiveUserClient(@NonNull Context context,
+            @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, int userId,
+            @NonNull String owner, int sensorId, int currentUserId, boolean hasEnrolledBiometrics,
+            @NonNull Map<Integer, Long> authenticatorIds) {
+        super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
+                0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
+                BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+        mCurrentUserId = currentUserId;
+        mHasEnrolledBiometrics = hasEnrolledBiometrics;
+        mAuthenticatorIds = authenticatorIds;
+    }
+
+    @Override
+    public void start(@NonNull FinishCallback finishCallback) {
+        super.start(finishCallback);
+
+        if (mCurrentUserId == getTargetUserId()) {
+            Slog.d(TAG, "Already user: " + mCurrentUserId + ", refreshing authenticatorId");
+            try {
+                mAuthenticatorIds.put(getTargetUserId(), mHasEnrolledBiometrics
+                        ? getFreshDaemon().getAuthenticatorId() : 0L);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Unable to refresh authenticatorId", e);
+            }
+            finishCallback.onClientFinished(this, true /* success */);
+            return;
+        }
+
+        int firstSdkInt = Build.VERSION.FIRST_SDK_INT;
+        if (firstSdkInt < Build.VERSION_CODES.BASE) {
+            Slog.e(TAG, "First SDK version " + firstSdkInt + " is invalid; must be " +
+                    "at least VERSION_CODES.BASE");
+        }
+        File baseDir;
+        if (firstSdkInt <= Build.VERSION_CODES.O_MR1) {
+            baseDir = Environment.getUserSystemDirectory(getTargetUserId());
+        } else {
+            baseDir = Environment.getDataVendorDeDirectory(getTargetUserId());
+        }
+
+        mDirectory = new File(baseDir, FP_DATA_DIR);
+        if (!mDirectory.exists()) {
+            if (!mDirectory.mkdir()) {
+                Slog.e(TAG, "Cannot make directory: " + mDirectory.getAbsolutePath());
+                finishCallback.onClientFinished(this, false /* success */);
+                return;
+            }
+            // Calling mkdir() from this process will create a directory with our
+            // permissions (inherited from the containing dir). This command fixes
+            // the label.
+            if (!SELinux.restorecon(mDirectory)) {
+                Slog.e(TAG, "Restorecons failed. Directory will have wrong label.");
+                finishCallback.onClientFinished(this, false /* success */);
+                return;
+            }
+        }
+
+        startHalOperation();
+    }
+
+    @Override
+    public void unableToStart() {
+        // Nothing to do here
+    }
+
+    @Override
+    protected void startHalOperation() {
+        try {
+            getFreshDaemon().setActiveGroup(getTargetUserId(), mDirectory.getAbsolutePath());
+            mAuthenticatorIds.put(getTargetUserId(), mHasEnrolledBiometrics
+                    ? getFreshDaemon().getAuthenticatorId() : 0L);
+            mFinishCallback.onClientFinished(this, true /* success */);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to setActiveGroup: " + e);
+            mFinishCallback.onClientFinished(this, false /* success */);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/GestureAvailabilityTracker.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/GestureAvailabilityTracker.java
index 6292ecf..5023c83 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/GestureAvailabilityTracker.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/GestureAvailabilityTracker.java
@@ -18,16 +18,54 @@
 
 import android.hardware.fingerprint.IFingerprintClientActiveCallback;
 import android.os.RemoteException;
+import android.util.Slog;
 
+import java.util.HashMap;
+import java.util.Map;
 import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
  * Keeps track of sensor gesture availability (e.g. swipe), and notifies clients when its
  * availability changes
  */
-class GestureAvailabilityTracker {
-    private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks =
-            new CopyOnWriteArrayList<>();
+public class GestureAvailabilityTracker {
+    private static final String TAG = "GestureAvailabilityTracker";
+
+    private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks;
+    private final Map<Integer, Boolean> mActiveSensors;
+
+    private boolean mIsActive;
+
+    GestureAvailabilityTracker() {
+        mClientActiveCallbacks = new CopyOnWriteArrayList<>();
+        mActiveSensors = new HashMap<>();
+    }
+
+    /**
+     * @return true if any sensor is active.
+     */
+    public boolean isAnySensorActive() {
+        return mIsActive;
+    }
+
+    public void markSensorActive(int sensorId, boolean active) {
+        mActiveSensors.put(sensorId, active);
+
+        final boolean wasActive = mIsActive;
+        boolean isActive = false;
+        for (Boolean b : mActiveSensors.values()) {
+            if (b) {
+                isActive = true;
+                break;
+            }
+        }
+
+        if (wasActive != isActive) {
+            Slog.d(TAG, "Notifying gesture availability, active=" + mIsActive);
+            mIsActive = isActive;
+            notifyClientActiveCallbacks(mIsActive);
+        }
+    }
 
     void registerCallback(IFingerprintClientActiveCallback callback) {
         mClientActiveCallbacks.add(callback);
@@ -37,7 +75,7 @@
         mClientActiveCallbacks.remove(callback);
     }
 
-    void notifyClientActiveCallbacks(boolean isActive) {
+    private void notifyClientActiveCallbacks(boolean isActive) {
         for (IFingerprintClientActiveCallback callback : mClientActiveCallbacks) {
             try {
                 callback.onClientActiveChanged(isActive);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java
new file mode 100644
index 0000000..e0806ff
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 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.sensors.fingerprint;
+
+/**
+ * Interface for under-display fingerprint sensors.
+ * {@link com.android.server.biometrics.sensors.ClientMonitor} subclass that require knowledge of
+ * finger position (e.g. enroll, authenticate) should implement this.
+ */
+public interface Udfps {
+    void onFingerDown(int x, int y, float minor, float major);
+    void onFingerUp();
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java
new file mode 100644
index 0000000..e6e1533
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 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.sensors.fingerprint;
+
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.os.RemoteException;
+import android.util.Slog;
+
+/**
+ * Contains helper methods for under-display fingerprint HIDL.
+ */
+public class UdfpsHelper {
+
+    private static final String TAG = "UdfpsHelper";
+
+    static void onFingerDown(IBiometricsFingerprint daemon, int x, int y, float minor,
+            float major) {
+        android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint extension =
+                android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint.castFrom(
+                        daemon);
+        if (extension == null) {
+            Slog.v(TAG, "onFingerDown | failed to cast the HIDL to V2_3");
+            return;
+        }
+
+        try {
+            extension.onFingerDown(x, y, minor, major);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "onFingerDown | RemoteException: ", e);
+        }
+    }
+
+    static void onFingerUp(IBiometricsFingerprint daemon) {
+        android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint extension =
+                android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint.castFrom(
+                        daemon);
+        if (extension == null) {
+            Slog.v(TAG, "onFingerUp | failed to cast the HIDL to V2_3");
+            return;
+        }
+
+        try {
+            extension.onFingerUp();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "onFingerUp | RemoteException: ", e);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/iris/IrisService.java b/services/core/java/com/android/server/biometrics/sensors/iris/IrisService.java
index 023ed46..58ab20d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/iris/IrisService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/iris/IrisService.java
@@ -115,16 +115,6 @@
     }
 
     @Override
-    protected void checkUseBiometricPermission() {
-
-    }
-
-    @Override
-    protected boolean checkAppOps(int uid, String opPackageName) {
-        return false;
-    }
-
-    @Override
     protected List<? extends BiometricAuthenticator.Identifier> getEnrolledTemplates(int userId) {
         return null;
     }
@@ -133,9 +123,4 @@
     protected int statsModality() {
         return BiometricsProtoEnums.MODALITY_IRIS;
     }
-
-    @Override
-    protected int getLockoutMode(int userId) {
-        return LockoutTracker.LOCKOUT_NONE;
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricServiceBaseTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricServiceBaseTest.java
index d4b299d..0c66ee7 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricServiceBaseTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricServiceBaseTest.java
@@ -80,15 +80,6 @@
         }
 
         @Override
-        protected void checkUseBiometricPermission() {
-        }
-
-        @Override
-        protected boolean checkAppOps(int uid, String opPackageName) {
-            return false;
-        }
-
-        @Override
         protected List<? extends BiometricAuthenticator.Identifier> getEnrolledTemplates(
                 int userId) {
             return null;
@@ -98,11 +89,6 @@
         protected int statsModality() {
             return 0;
         }
-
-        @Override
-        protected int getLockoutMode(int userId) {
-            return 0;
-        }
     }
 
     private static final int CLIENT_COOKIE = 0xc00c1e;