Add support for fingerprint@2.2 and face@1.1

This CL adds the necessary plumbing, but not implementation, to do the
following:

biometrics.fingerprint@2.2:
- enroll_2_2(..., handle windowId)
- authenticate_2_2(..., handle windowId)

biometrics.face@1.1:
- enroll_1_1(..., handle windowId)
- enrollRemotely(...)

Bug: 148493694
Bug: 145562442
Test: enrolled and authenticated with Keyguard and BiometricPrompt
Test: compiled face@1.0 HAL as face@1.1 and made sure it works
Change-Id: I22e25df4d880d16d21a940e7dff6d233b64743d1
diff --git a/services/core/Android.bp b/services/core/Android.bp
index a603fa9..f33237f 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -115,8 +115,8 @@
         "android.hardware.health-V2.0-java",
         "android.hardware.light-java",
         "android.hardware.weaver-V1.0-java",
-        "android.hardware.biometrics.face-V1.0-java",
-        "android.hardware.biometrics.fingerprint-V2.1-java",
+        "android.hardware.biometrics.face-V1.1-java",
+        "android.hardware.biometrics.fingerprint-V2.2-java",
         "android.hardware.oemlock-V1.0-java",
         "android.hardware.configstore-V1.0-java",
         "android.hardware.contexthub-V1.0-java",
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
index 766e5c4..7bbda9f 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
@@ -20,11 +20,14 @@
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.IBiometricNativeHandle;
 import android.os.IBinder;
+import android.os.NativeHandle;
 import android.os.RemoteException;
 import android.security.KeyStore;
 import android.util.Slog;
 
+import java.io.IOException;
 import java.util.ArrayList;
 
 /**
@@ -41,6 +44,7 @@
     public static final int LOCKOUT_PERMANENT = 2;
 
     private final boolean mRequireConfirmation;
+    private final NativeHandle mWindowId;
 
     // We need to track this state since it's possible for applications to request for
     // authentication while the device is already locked out. In that case, the client is created
@@ -69,11 +73,25 @@
     public AuthenticationClient(Context context, Constants constants,
             BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
             BiometricServiceBase.ServiceListener listener, int targetUserId, int groupId, long opId,
-            boolean restricted, String owner, int cookie, boolean requireConfirmation) {
+            boolean restricted, String owner, int cookie, boolean requireConfirmation,
+            IBiometricNativeHandle windowId) {
         super(context, constants, daemon, halDeviceId, token, listener, targetUserId, groupId,
                 restricted, owner, cookie);
         mOpId = opId;
         mRequireConfirmation = requireConfirmation;
+        mWindowId = Utils.dupNativeHandle(windowId);
+    }
+
+    @Override
+    public void destroy() {
+        if (mWindowId != null && mWindowId.getFileDescriptors() != null) {
+            try {
+                mWindowId.close();
+            } catch (IOException e) {
+                Slog.e(getLogTag(), "Failed to close windowId NativeHandle: ", e);
+            }
+        }
+        super.destroy();
     }
 
     protected long getStartTimeMs() {
@@ -233,7 +251,7 @@
         onStart();
         try {
             mStartTimeMs = System.currentTimeMillis();
-            final int result = getDaemonWrapper().authenticate(mOpId, getGroupId());
+            final int result = getDaemonWrapper().authenticate(mOpId, getGroupId(), mWindowId);
             if (result != 0) {
                 Slog.w(getLogTag(), "startAuthentication failed, result=" + result);
                 mMetricsLogger.histogram(mConstants.tagAuthStartError(), result);
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index 687d935..0e70994 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -32,6 +32,7 @@
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.IBiometricNativeHandle;
 import android.hardware.biometrics.IBiometricService;
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
 import android.hardware.biometrics.IBiometricServiceReceiverInternal;
@@ -43,6 +44,7 @@
 import android.os.IBinder;
 import android.os.IHwBinder;
 import android.os.IRemoteCallback;
+import android.os.NativeHandle;
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
@@ -220,9 +222,10 @@
 
         public AuthenticationClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
                 IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId,
-                boolean restricted, String owner, int cookie, boolean requireConfirmation) {
+                boolean restricted, String owner, int cookie, boolean requireConfirmation,
+                IBiometricNativeHandle windowId) {
             super(context, getConstants(), daemon, halDeviceId, token, listener, targetUserId,
-                    groupId, opId, restricted, owner, cookie, requireConfirmation);
+                    groupId, opId, restricted, owner, cookie, requireConfirmation, windowId);
         }
 
         @Override
@@ -283,10 +286,10 @@
         public EnrollClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
                 IBinder token, ServiceListener listener, int userId, int groupId,
                 byte[] cryptoToken, boolean restricted, String owner,
-                final int[] disabledFeatures, int timeoutSec) {
+                final int[] disabledFeatures, int timeoutSec, IBiometricNativeHandle windowId) {
             super(context, getConstants(), daemon, halDeviceId, token, listener,
                     userId, groupId, cryptoToken, restricted, owner, getBiometricUtils(),
-                    disabledFeatures, timeoutSec);
+                    disabledFeatures, timeoutSec, windowId);
         }
 
         @Override
@@ -472,12 +475,13 @@
      */
     protected interface DaemonWrapper {
         int ERROR_ESRCH = 3; // Likely HAL is dead. see errno.h.
-        int authenticate(long operationId, int groupId) throws RemoteException;
+        int authenticate(long operationId, int groupId, NativeHandle windowId)
+                throws RemoteException;
         int cancel() throws RemoteException;
         int remove(int groupId, int biometricId) throws RemoteException;
         int enumerate() throws RemoteException;
         int enroll(byte[] token, int groupId, int timeout,
-                ArrayList<Integer> disabledFeatures) throws RemoteException;
+                ArrayList<Integer> disabledFeatures, NativeHandle windowId) throws RemoteException;
         void resetLockout(byte[] token) throws RemoteException;
     }
 
diff --git a/services/core/java/com/android/server/biometrics/EnrollClient.java b/services/core/java/com/android/server/biometrics/EnrollClient.java
index 7ebb7c0..684795e 100644
--- a/services/core/java/com/android/server/biometrics/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/EnrollClient.java
@@ -20,10 +20,13 @@
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.IBiometricNativeHandle;
 import android.os.IBinder;
+import android.os.NativeHandle;
 import android.os.RemoteException;
 import android.util.Slog;
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 
@@ -35,6 +38,7 @@
     private final BiometricUtils mBiometricUtils;
     private final int[] mDisabledFeatures;
     private final int mTimeoutSec;
+    private final NativeHandle mWindowId;
 
     private long mEnrollmentStartTimeMs;
 
@@ -44,13 +48,26 @@
             BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
             BiometricServiceBase.ServiceListener listener, int userId, int groupId,
             byte[] cryptoToken, boolean restricted, String owner, BiometricUtils utils,
-            final int[] disabledFeatures, int timeoutSec) {
+            final int[] disabledFeatures, int timeoutSec, IBiometricNativeHandle windowId) {
         super(context, constants, daemon, halDeviceId, token, listener, userId, groupId, restricted,
                 owner, 0 /* cookie */);
         mBiometricUtils = utils;
         mCryptoToken = Arrays.copyOf(cryptoToken, cryptoToken.length);
         mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length);
         mTimeoutSec = timeoutSec;
+        mWindowId = Utils.dupNativeHandle(windowId);
+    }
+
+    @Override
+    public void destroy() {
+        if (mWindowId != null && mWindowId.getFileDescriptors() != null) {
+            try {
+                mWindowId.close();
+            } catch (IOException e) {
+                Slog.e(getLogTag(), "Failed to close windowId NativeHandle: ", e);
+            }
+        }
+        super.destroy();
     }
 
     @Override
@@ -102,7 +119,7 @@
             }
 
             final int result = getDaemonWrapper().enroll(mCryptoToken, getGroupId(), mTimeoutSec,
-                    disabledFeatures);
+                    disabledFeatures, mWindowId);
             if (result != 0) {
                 Slog.w(getLogTag(), "startEnroll failed, result=" + result);
                 mMetricsLogger.histogram(mConstants.tagEnrollStartError(), result);
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index 389763b..2d4ab63 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -23,12 +23,17 @@
 import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.BiometricPrompt;
 import android.hardware.biometrics.BiometricPrompt.AuthenticationResultType;
+import android.hardware.biometrics.IBiometricNativeHandle;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.NativeHandle;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Slog;
 
+import java.io.FileDescriptor;
+import java.io.IOException;
+
 public class Utils {
     public static boolean isDebugEnabled(Context context, int targetUserId) {
         if (targetUserId == UserHandle.USER_NULL) {
@@ -237,4 +242,31 @@
                 throw new IllegalArgumentException("Unsupported dismissal reason: " + reason);
         }
     }
+
+    /**
+     * Converts an {@link IBiometricNativeHandle} to a {@link NativeHandle} by duplicating the
+     * the underlying file descriptors.
+     *
+     * Both the original and new handle must be closed after use.
+     *
+     * @param h {@link IBiometricNativeHandle} received as a binder call argument. Usually used to
+     *          identify a WindowManager window. Can be null.
+     * @return A {@link NativeHandle} representation of {@code h}. Will be null if either {@code h}
+     *          or its contents are null.
+     */
+    public static NativeHandle dupNativeHandle(IBiometricNativeHandle h) {
+        NativeHandle handle = null;
+        if (h != null && h.fds != null && h.ints != null) {
+            FileDescriptor[] fds = new FileDescriptor[h.fds.length];
+            for (int i = 0; i < h.fds.length; ++i) {
+                try {
+                    fds[i] = h.fds[i].dup().getFileDescriptor();
+                } catch (IOException e) {
+                    return null;
+                }
+            }
+            handle = new NativeHandle(fds, h.ints, true /* own */);
+        }
+        return handle;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index b512475..31c3d4d 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -34,6 +34,7 @@
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.IBiometricNativeHandle;
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
 import android.hardware.biometrics.IBiometricServiceReceiverInternal;
 import android.hardware.biometrics.face.V1_0.IBiometricsFace;
@@ -214,9 +215,10 @@
         public FaceAuthClient(Context context,
                 DaemonWrapper daemon, long halDeviceId, IBinder token,
                 ServiceListener listener, int targetUserId, int groupId, long opId,
-                boolean restricted, String owner, int cookie, boolean requireConfirmation) {
+                boolean restricted, String owner, int cookie, boolean requireConfirmation,
+                IBiometricNativeHandle windowId) {
             super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId,
-                    restricted, owner, cookie, requireConfirmation);
+                    restricted, owner, cookie, requireConfirmation, windowId);
         }
 
         @Override
@@ -373,7 +375,7 @@
         @Override // Binder call
         public void enroll(int userId, final IBinder token, final byte[] cryptoToken,
                 final IFaceServiceReceiver receiver, final String opPackageName,
-                final int[] disabledFeatures) {
+                final int[] disabledFeatures, IBiometricNativeHandle windowId) {
             checkPermission(MANAGE_BIOMETRIC);
             updateActiveGroup(userId, opPackageName);
 
@@ -384,7 +386,7 @@
             final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper,
                     mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId,
                     0 /* groupId */, cryptoToken, restricted, opPackageName, disabledFeatures,
-                    ENROLL_TIMEOUT_SEC) {
+                    ENROLL_TIMEOUT_SEC, windowId) {
 
                 @Override
                 public int[] getAcquireIgnorelist() {
@@ -411,6 +413,14 @@
         }
 
         @Override // Binder call
+        public void enrollRemotely(int userId, final IBinder token, final byte[] cryptoToken,
+                final IFaceServiceReceiver receiver, final String opPackageName,
+                final int[] disabledFeatures) {
+            checkPermission(MANAGE_BIOMETRIC);
+            // TODO(b/145027036): Implement this.
+        }
+
+        @Override // Binder call
         public void cancelEnrollment(final IBinder token) {
             checkPermission(MANAGE_BIOMETRIC);
             cancelEnrollmentInternal(token);
@@ -426,7 +436,7 @@
             final AuthenticationClientImpl client = new FaceAuthClient(getContext(),
                     mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
                     mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName,
-                    0 /* cookie */, false /* requireConfirmation */);
+                    0 /* cookie */, false /* requireConfirmation */, null /* windowId */);
             authenticateInternal(client, opId, opPackageName);
         }
 
@@ -442,7 +452,7 @@
                     mDaemonWrapper, mHalDeviceId, token,
                     new BiometricPromptServiceListenerImpl(wrapperReceiver),
                     mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName, cookie,
-                    requireConfirmation);
+                    requireConfirmation, null /* windowId */);
             authenticateInternal(client, opId, opPackageName, callingUid, callingPid,
                     callingUserId);
         }
@@ -985,7 +995,8 @@
      */
     private final DaemonWrapper mDaemonWrapper = new DaemonWrapper() {
         @Override
-        public int authenticate(long operationId, int groupId) throws RemoteException {
+        public int authenticate(long operationId, int groupId, NativeHandle windowId)
+                throws RemoteException {
             IBiometricsFace daemon = getFaceDaemon();
             if (daemon == null) {
                 Slog.w(TAG, "authenticate(): no face HAL!");
@@ -1026,7 +1037,7 @@
 
         @Override
         public int enroll(byte[] cryptoToken, int groupId, int timeout,
-                ArrayList<Integer> disabledFeatures) throws RemoteException {
+                ArrayList<Integer> disabledFeatures, NativeHandle windowId) throws RemoteException {
             IBiometricsFace daemon = getFaceDaemon();
             if (daemon == null) {
                 Slog.w(TAG, "enroll(): no face HAL!");
@@ -1036,7 +1047,17 @@
             for (int i = 0; i < cryptoToken.length; i++) {
                 token.add(cryptoToken[i]);
             }
-            return daemon.enroll(token, timeout, disabledFeatures);
+            android.hardware.biometrics.face.V1_1.IBiometricsFace daemon11 =
+                    android.hardware.biometrics.face.V1_1.IBiometricsFace.castFrom(
+                            daemon);
+            if (daemon11 != null) {
+                return daemon11.enroll_1_1(token, timeout, disabledFeatures, windowId);
+            } else if (windowId == null) {
+                return daemon.enroll(token, timeout, disabledFeatures);
+            } else {
+                Slog.e(TAG, "enroll(): windowId is only supported in @1.1 HAL");
+                return ERROR_ESRCH;
+            }
         }
 
         @Override
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintAuthenticator.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintAuthenticator.java
index 6150de1..7a4e62e 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintAuthenticator.java
@@ -38,7 +38,7 @@
             String opPackageName, int cookie, int callingUid, int callingPid, int callingUserId)
             throws RemoteException {
         mFingerprintService.prepareForAuthentication(token, sessionId, userId, wrapperReceiver,
-                opPackageName, cookie, callingUid, callingPid, callingUserId);
+                opPackageName, cookie, callingUid, callingPid, callingUserId, null /* windowId */);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
index 44797ad..57d1867 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -37,6 +37,7 @@
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.IBiometricNativeHandle;
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
 import android.hardware.biometrics.IBiometricServiceReceiverInternal;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
@@ -50,6 +51,7 @@
 import android.os.Build;
 import android.os.Environment;
 import android.os.IBinder;
+import android.os.NativeHandle;
 import android.os.RemoteException;
 import android.os.SELinux;
 import android.os.SystemClock;
@@ -132,9 +134,9 @@
                 DaemonWrapper daemon, long halDeviceId, IBinder token,
                 ServiceListener listener, int targetUserId, int groupId, long opId,
                 boolean restricted, String owner, int cookie,
-                boolean requireConfirmation) {
+                boolean requireConfirmation, IBiometricNativeHandle windowId) {
             super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId,
-                    restricted, owner, cookie, requireConfirmation);
+                    restricted, owner, cookie, requireConfirmation, windowId);
         }
 
         @Override
@@ -198,7 +200,7 @@
         @Override // Binder call
         public void enroll(final IBinder token, final byte[] cryptoToken, final int userId,
                 final IFingerprintServiceReceiver receiver, final int flags,
-                final String opPackageName) {
+                final String opPackageName, IBiometricNativeHandle windowId) {
             checkPermission(MANAGE_FINGERPRINT);
 
             final boolean restricted = isRestricted();
@@ -206,7 +208,7 @@
             final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper,
                     mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId, groupId,
                     cryptoToken, restricted, opPackageName, new int[0] /* disabledFeatures */,
-                    ENROLL_TIMEOUT_SEC) {
+                    ENROLL_TIMEOUT_SEC, windowId) {
                 @Override
                 public boolean shouldVibrate() {
                     return true;
@@ -230,20 +232,22 @@
         @Override // Binder call
         public void authenticate(final IBinder token, final long opId, final int groupId,
                 final IFingerprintServiceReceiver receiver, final int flags,
-                final String opPackageName) {
+                final String opPackageName, IBiometricNativeHandle windowId) {
             updateActiveGroup(groupId, opPackageName);
             final boolean restricted = isRestricted();
             final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(),
                     mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
                     mCurrentUserId, groupId, opId, restricted, opPackageName,
-                    0 /* cookie */, false /* requireConfirmation */);
+                    0 /* cookie */, false /* requireConfirmation */,
+                    windowId);
             authenticateInternal(client, opId, opPackageName);
         }
 
         @Override // Binder call
         public void prepareForAuthentication(IBinder token, long opId, int groupId,
                 IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName,
-                int cookie, int callingUid, int callingPid, int callingUserId) {
+                int cookie, int callingUid, int callingPid, int callingUserId,
+                IBiometricNativeHandle windowId) {
             checkPermission(MANAGE_BIOMETRIC);
             updateActiveGroup(groupId, opPackageName);
             final boolean restricted = true; // BiometricPrompt is always restricted
@@ -251,7 +255,8 @@
                     mDaemonWrapper, mHalDeviceId, token,
                     new BiometricPromptServiceListenerImpl(wrapperReceiver),
                     mCurrentUserId, groupId, opId, restricted, opPackageName, cookie,
-                    false /* requireConfirmation */);
+                    false /* requireConfirmation */,
+                    windowId);
             authenticateInternal(client, opId, opPackageName, callingUid, callingPid,
                     callingUserId);
         }
@@ -654,13 +659,24 @@
      */
     private final DaemonWrapper mDaemonWrapper = new DaemonWrapper() {
         @Override
-        public int authenticate(long operationId, int groupId) throws RemoteException {
+        public int authenticate(long operationId, int groupId, NativeHandle windowId)
+                throws RemoteException {
             IBiometricsFingerprint daemon = getFingerprintDaemon();
             if (daemon == null) {
                 Slog.w(TAG, "authenticate(): no fingerprint HAL!");
                 return ERROR_ESRCH;
             }
-            return daemon.authenticate(operationId, groupId);
+            android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprint daemon22 =
+                    android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprint.castFrom(
+                            daemon);
+            if (daemon22 != null) {
+                return daemon22.authenticate_2_2(operationId, groupId, windowId);
+            } else if (windowId == null) {
+                return daemon.authenticate(operationId, groupId);
+            } else {
+                Slog.e(TAG, "authenticate(): windowId is only supported in @2.2 HAL");
+                return ERROR_ESRCH;
+            }
         }
 
         @Override
@@ -695,13 +711,27 @@
 
         @Override
         public int enroll(byte[] cryptoToken, int groupId, int timeout,
-                ArrayList<Integer> disabledFeatures) throws RemoteException {
+                ArrayList<Integer> disabledFeatures, NativeHandle windowId) throws RemoteException {
             IBiometricsFingerprint daemon = getFingerprintDaemon();
             if (daemon == null) {
                 Slog.w(TAG, "enroll(): no fingerprint HAL!");
                 return ERROR_ESRCH;
             }
-            return daemon.enroll(cryptoToken, groupId, timeout);
+            android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprint daemon22 =
+                    android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprint.castFrom(
+                            daemon);
+            if (daemon22 != null) {
+                ArrayList<Byte> cryptoTokenAsList = new ArrayList<>(cryptoToken.length);
+                for (byte b : cryptoToken) {
+                    cryptoTokenAsList.add(b);
+                }
+                return daemon22.enroll_2_2(cryptoTokenAsList, groupId, timeout, windowId);
+            } else if (windowId == null) {
+                return daemon.enroll(cryptoToken, groupId, timeout);
+            } else {
+                Slog.e(TAG, "enroll(): windowId is only supported in @2.2 HAL");
+                return ERROR_ESRCH;
+            }
         }
 
         @Override