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