Face VHAL for user build
Bug: 326227403
Test: atest android.hardware.biometrics.face.* -c
Test: atest CtsBiometricsTestCases -c
Flag: TEST_ONLY
Change-Id: I869b68eba81166aacfc2a8a35c303940722214f3
diff --git a/Android.bp b/Android.bp
index 5b9f2cb..8c01585 100644
--- a/Android.bp
+++ b/Android.bp
@@ -99,6 +99,7 @@
":android.hardware.biometrics.common-V4-java-source",
":android.hardware.biometrics.fingerprint-V5-java-source",
":android.hardware.biometrics.fingerprint.virtualhal-java-source",
+ ":android.hardware.biometrics.face.virtualhal-java-source",
":android.hardware.biometrics.face-V4-java-source",
":android.hardware.gnss-V2-java-source",
":android.hardware.graphics.common-V3-java-source",
diff --git a/core/java/android/hardware/face/FaceSensorConfigurations.java b/core/java/android/hardware/face/FaceSensorConfigurations.java
index 1247168..51c5f4c 100644
--- a/core/java/android/hardware/face/FaceSensorConfigurations.java
+++ b/core/java/android/hardware/face/FaceSensorConfigurations.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.SensorProps;
+import android.hardware.biometrics.face.virtualhal.IVirtualHal;
import android.os.Binder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -160,6 +161,41 @@
dest.writeByte((byte) (mResetLockoutRequiresChallenge ? 1 : 0));
dest.writeMap(mSensorPropsMap);
}
+ /**
+ * Remap fqName of VHAL because the `virtual` instance is registered
+ * with IVirtulalHal now (IFace previously)
+ * @param fqName fqName to be translated
+ * @return real fqName
+ */
+ public static String remapFqName(String fqName) {
+ if (!fqName.contains(IFace.DESCRIPTOR + "/virtual")) {
+ return fqName; //no remap needed for real hardware HAL
+ } else {
+ //new Vhal instance name
+ return fqName.replace("IFace", "virtualhal.IVirtualHal");
+ }
+ }
+ /**
+ * @param fqName aidl interface instance name
+ * @return aidl interface
+ */
+ public static IFace getIFace(String fqName) {
+ if (fqName.contains("virtual")) {
+ String fqNameMapped = remapFqName(fqName);
+ Slog.i(TAG, "getIFace fqName is mapped: " + fqName + "->" + fqNameMapped);
+ try {
+ IVirtualHal vhal = IVirtualHal.Stub.asInterface(
+ Binder.allowBlocking(ServiceManager.waitForService(fqNameMapped)));
+ return vhal.getFaceHal();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception in vhal.getFaceHal() call" + fqNameMapped);
+ }
+ }
+
+ return IFace.Stub.asInterface(
+ Binder.allowBlocking(ServiceManager.waitForDeclaredService(fqName)));
+ }
+
/**
* Returns face sensor props for the HAL {@param instance}.
@@ -173,14 +209,13 @@
return props;
}
- final String fqName = IFace.DESCRIPTOR + "/" + instance;
- IFace face = IFace.Stub.asInterface(Binder.allowBlocking(
- ServiceManager.waitForDeclaredService(fqName)));
try {
- if (face != null) {
- props = face.getSensorProps();
+ final String fqName = IFace.DESCRIPTOR + "/" + instance;
+ final IFace fp = getIFace(fqName);
+ if (fp != null) {
+ props = fp.getSensorProps();
} else {
- Slog.e(TAG, "Unable to get declared service: " + fqName);
+ Log.d(TAG, "IFace null for instance " + instance);
}
} catch (RemoteException e) {
Log.d(TAG, "Unable to get sensor properties!");
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 5d85089..2d802b2 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -49,7 +49,6 @@
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.biometrics.SensorPropertiesInternal;
-import android.hardware.biometrics.face.IFace;
import android.hardware.face.FaceSensorConfigurations;
import android.hardware.face.FaceSensorProperties;
import android.hardware.face.FaceSensorPropertiesInternal;
@@ -73,6 +72,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.server.SystemService;
+import com.android.server.biometrics.sensors.face.FaceService;
import com.android.server.biometrics.sensors.fingerprint.FingerprintService;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
@@ -211,7 +211,7 @@
*/
@VisibleForTesting
public String[] getFaceAidlInstances() {
- return ServiceManager.getDeclaredInstances(IFace.DESCRIPTOR);
+ return FaceService.getDeclaredInstances();
}
/**
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 bd6d593..8c98872 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
@@ -19,6 +19,7 @@
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.MANAGE_FACE;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
+import static android.hardware.face.FaceSensorConfigurations.getIFace;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -60,6 +61,7 @@
import android.view.Surface;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.SystemService;
@@ -753,7 +755,7 @@
public FaceService(Context context) {
this(context, null /* faceProviderFunction */, () -> IBiometricService.Stub.asInterface(
ServiceManager.getService(Context.BIOMETRIC_SERVICE)), null /* faceProvider */,
- () -> ServiceManager.getDeclaredInstances(IFace.DESCRIPTOR));
+ () -> getDeclaredInstances());
}
@VisibleForTesting FaceService(Context context,
@@ -778,8 +780,7 @@
mFaceProvider = faceProvider != null ? faceProvider : (name) -> {
final String fqName = IFace.DESCRIPTOR + "/" + name;
- final IFace face = IFace.Stub.asInterface(
- Binder.allowBlocking(ServiceManager.waitForDeclaredService(fqName)));
+ final IFace face = getIFace(fqName);
if (face == null) {
Slog.e(TAG, "Unable to get declared service: " + fqName);
return null;
@@ -835,6 +836,23 @@
*/
public static native void releaseSurfaceHandle(@NonNull NativeHandle handle);
+ /**
+ * Get all face hal instances declared in manifest
+ * @return instance names
+ */
+ public static String[] getDeclaredInstances() {
+ String[] a = ServiceManager.getDeclaredInstances(IFace.DESCRIPTOR);
+ Slog.i(TAG, "Before:getDeclaredInstances: IFace instance found, a.length="
+ + a.length);
+ if (!ArrayUtils.contains(a, "virtual")) {
+ // Now, the virtual hal is registered with IVirtualHal interface and it is also
+ // moved from vendor to system_ext partition without a device manifest. So
+ // if the old vhal is not declared, add here.
+ a = ArrayUtils.appendElement(String.class, a, "virtual");
+ }
+ Slog.i(TAG, "After:getDeclaredInstances: a.length=" + a.length);
+ return a;
+ }
void syncEnrollmentsNow() {
Utils.checkPermissionOrShell(getContext(), MANAGE_FACE);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
index dca1491..3ed01d5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
@@ -23,6 +23,9 @@
import android.hardware.biometrics.face.AuthenticationFrame;
import android.hardware.biometrics.face.BaseFrame;
import android.hardware.biometrics.face.EnrollmentFrame;
+import android.hardware.biometrics.face.virtualhal.AcquiredInfoAndVendorCode;
+import android.hardware.biometrics.face.virtualhal.EnrollmentProgressStep;
+import android.hardware.biometrics.face.virtualhal.NextEnrollment;
import android.hardware.face.Face;
import android.hardware.face.FaceAuthenticationFrame;
import android.hardware.face.FaceEnrollFrame;
@@ -50,6 +53,7 @@
public class BiometricTestSessionImpl extends ITestSession.Stub {
private static final String TAG = "face/aidl/BiometricTestSessionImpl";
+ private static final int VHAL_ENROLLMENT_ID = 9999;
@NonNull private final Context mContext;
private final int mSensorId;
@@ -144,16 +148,35 @@
super.setTestHalEnabled_enforcePermission();
- mProvider.setTestHalEnabled(enabled);
mSensor.setTestHalEnabled(enabled);
+ mProvider.setTestHalEnabled(enabled);
}
@android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
@Override
- public void startEnroll(int userId) {
+ public void startEnroll(int userId) throws RemoteException {
super.startEnroll_enforcePermission();
+ Slog.i(TAG, "startEnroll(): isVhalForTesting=" + mProvider.isVhalForTesting());
+ if (mProvider.isVhalForTesting()) {
+ final AcquiredInfoAndVendorCode[] acquiredInfoAndVendorCodes =
+ {new AcquiredInfoAndVendorCode()};
+ final EnrollmentProgressStep[] enrollmentProgressSteps =
+ {new EnrollmentProgressStep(), new EnrollmentProgressStep()};
+ enrollmentProgressSteps[0].durationMs = 100;
+ enrollmentProgressSteps[0].acquiredInfoAndVendorCodes = acquiredInfoAndVendorCodes;
+ enrollmentProgressSteps[1].durationMs = 200;
+ enrollmentProgressSteps[1].acquiredInfoAndVendorCodes = acquiredInfoAndVendorCodes;
+
+ final NextEnrollment nextEnrollment = new NextEnrollment();
+ nextEnrollment.id = VHAL_ENROLLMENT_ID;
+ nextEnrollment.progressSteps = enrollmentProgressSteps;
+ nextEnrollment.result = true;
+ mProvider.getVhal().setNextEnrollment(nextEnrollment);
+ mProvider.getVhal().setOperationAuthenticateDuration(6000);
+ }
+
mProvider.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
mContext.getOpPackageName(), new int[0] /* disabledFeatures */,
null /* previewSurface */, false /* debugConsent */,
@@ -166,6 +189,10 @@
super.finishEnroll_enforcePermission();
+ if (mProvider.isVhalForTesting()) {
+ return;
+ }
+
int nextRandomId = mRandom.nextInt();
while (mEnrollmentIds.contains(nextRandomId)) {
nextRandomId = mRandom.nextInt();
@@ -178,11 +205,16 @@
@android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
@Override
- public void acceptAuthentication(int userId) {
+ public void acceptAuthentication(int userId) throws RemoteException {
// Fake authentication with any of the existing faces
super.acceptAuthentication_enforcePermission();
+ if (mProvider.isVhalForTesting()) {
+ mProvider.getVhal().setEnrollmentHit(VHAL_ENROLLMENT_ID);
+ return;
+ }
+
List<Face> faces = FaceUtils.getInstance(mSensorId)
.getBiometricsForUser(mContext, userId);
if (faces.isEmpty()) {
@@ -196,10 +228,15 @@
@android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
@Override
- public void rejectAuthentication(int userId) {
+ public void rejectAuthentication(int userId) throws RemoteException {
super.rejectAuthentication_enforcePermission();
+ if (mProvider.isVhalForTesting()) {
+ mProvider.getVhal().setEnrollmentHit(VHAL_ENROLLMENT_ID + 1);
+ return;
+ }
+
mSensor.getSessionForUser(userId).getHalSessionCallback().onAuthenticationFailed();
}
@@ -236,11 +273,17 @@
@android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
@Override
- public void cleanupInternalState(int userId) {
+ public void cleanupInternalState(int userId) throws RemoteException {
super.cleanupInternalState_enforcePermission();
Slog.d(TAG, "cleanupInternalState: " + userId);
+
+ if (mProvider.isVhalForTesting()) {
+ Slog.i(TAG, "cleanup virtualhal configurations");
+ mProvider.getVhal().resetConfigurations(); //setEnrollments(new int[]{});
+ }
+
mProvider.scheduleInternalCleanup(mSensorId, userId, new ClientMonitorCallback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index bb213bf..5127e68 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -16,6 +16,9 @@
package com.android.server.biometrics.sensors.face.aidl;
+import static android.hardware.face.FaceSensorConfigurations.getIFace;
+import static android.hardware.face.FaceSensorConfigurations.remapFqName;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -32,6 +35,7 @@
import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.SensorProps;
+import android.hardware.biometrics.face.virtualhal.IVirtualHal;
import android.hardware.face.Face;
import android.hardware.face.FaceAuthenticateOptions;
import android.hardware.face.FaceEnrollOptions;
@@ -54,6 +58,7 @@
import com.android.server.biometrics.AuthenticationStatsCollector;
import com.android.server.biometrics.BiometricDanglingReceiver;
import com.android.server.biometrics.BiometricHandlerProvider;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
@@ -130,6 +135,11 @@
private AuthenticationStatsCollector mAuthenticationStatsCollector;
@Nullable
private IFace mDaemon;
+ @Nullable
+ private IVirtualHal mVhal;
+ @Nullable
+ private String mHalInstanceNameCurrent;
+
private final class BiometricTaskStackListener extends TaskStackListener {
@Override
@@ -286,14 +296,37 @@
if (mTestHalEnabled) {
return true;
}
- return ServiceManager.checkService(IFace.DESCRIPTOR + "/" + mHalInstanceName) != null;
+ return ServiceManager.checkService(
+ remapFqName(IFace.DESCRIPTOR + "/" + mHalInstanceName)) != null;
}
@Nullable
@VisibleForTesting
synchronized IFace getHalInstance() {
if (mTestHalEnabled) {
- return new TestHal();
+ if (Flags.useVhalForTesting()) {
+ if (!mHalInstanceNameCurrent.contains("virtual")) {
+ Slog.i(getTag(), "Switching face hal from " + mHalInstanceName
+ + " to virtual hal");
+ mHalInstanceNameCurrent = "virtual";
+ mDaemon = null;
+ }
+ } else {
+ // Enabling the test HAL for a single sensor in a multi-sensor HAL currently enables
+ // the test HAL for all sensors under that HAL. This can be updated in the future if
+ // necessary.
+ return new TestHal();
+ }
+ } else {
+ if (mHalInstanceNameCurrent == null) {
+ mHalInstanceNameCurrent = mHalInstanceName;
+ } else if (mHalInstanceNameCurrent.contains("virtual")
+ && mHalInstanceNameCurrent != mHalInstanceName) {
+ Slog.i(getTag(), "Switching face from virtual hal " + "to "
+ + mHalInstanceName);
+ mHalInstanceNameCurrent = mHalInstanceName;
+ mDaemon = null;
+ }
}
if (mDaemon != null) {
@@ -302,10 +335,7 @@
Slog.d(getTag(), "Daemon was null, reconnecting");
- mDaemon = IFace.Stub.asInterface(
- Binder.allowBlocking(
- ServiceManager.waitForDeclaredService(
- IFace.DESCRIPTOR + "/" + mHalInstanceName)));
+ mDaemon = getIFace(IFace.DESCRIPTOR + "/" + mHalInstanceNameCurrent);
if (mDaemon == null) {
Slog.e(getTag(), "Unable to get daemon");
return null;
@@ -833,7 +863,13 @@
}
void setTestHalEnabled(boolean enabled) {
+ final boolean changed = enabled != mTestHalEnabled;
mTestHalEnabled = enabled;
+ Slog.i(getTag(), "setTestHalEnabled(): isVhalForTestingFlags=" + Flags.useVhalForTesting()
+ + " mTestHalEnabled=" + mTestHalEnabled + " changed=" + changed);
+ if (changed && isVhalForTesting()) {
+ getHalInstance();
+ }
}
@Override
@@ -851,9 +887,40 @@
}
/**
+ * Return true if vhal_for_testing feature is enabled and test is active
+ */
+ public boolean isVhalForTesting() {
+ return (Flags.useVhalForTesting() && mTestHalEnabled);
+ }
+
+
+ /**
* Sends a face re enroll notification.
*/
public void sendFaceReEnrollNotification() {
mAuthenticationStatsCollector.sendFaceReEnrollNotification();
}
+
+ /**
+ * Sends a fingerprint enroll notification.
+ */
+ public void sendFingerprintReEnrollNotification() {
+ mAuthenticationStatsCollector.sendFingerprintReEnrollNotification();
+ }
+
+ /**
+ * Return virtual hal AIDL interface if it is used for testing
+ *
+ */
+ public IVirtualHal getVhal() throws RemoteException {
+ if (mVhal == null && isVhalForTesting()) {
+ mVhal = IVirtualHal.Stub.asInterface(
+ Binder.allowBlocking(
+ ServiceManager.waitForService(
+ IVirtualHal.DESCRIPTOR + "/"
+ + mHalInstanceNameCurrent)));
+ Slog.d(getTag(), "getVhal " + mHalInstanceNameCurrent);
+ }
+ return mVhal;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index 6f95349..9fddcfc 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -17,6 +17,7 @@
package com.android.server.biometrics.sensors.face.aidl;
import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE;
+import static android.hardware.face.FaceSensorConfigurations.remapFqName;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -337,7 +338,8 @@
if (mTestHalEnabled) {
return true;
}
- return ServiceManager.checkService(IFace.DESCRIPTOR + "/" + halInstanceName) != null;
+ return ServiceManager.checkService(
+ remapFqName(IFace.DESCRIPTOR + "/" + halInstanceName)) != null;
}
/**