diff --git a/Android.bp b/Android.bp index 5b9f2cbf2d0d..8c0158549d2d 100644 --- a/Android.bp +++ b/Android.bp @@ -99,6 +99,7 @@ filegroup { ":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 12471681f913..51c5f4c398a1 100644 --- a/core/java/android/hardware/face/FaceSensorConfigurations.java +++ b/core/java/android/hardware/face/FaceSensorConfigurations.java @@ -22,6 +22,7 @@ import android.annotation.Nullable; 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 @@ public class FaceSensorConfigurations implements Parcelable { 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 @@ public class FaceSensorConfigurations implements Parcelable { 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 5d850896d5de..2d802b21cf03 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.ITestSessionCallback; 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.R; 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 @@ public class AuthService extends SystemService { */ @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 bd6d59391e4a..8c988729a1ae 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 @@ package com.android.server.biometrics.sensors.face; 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.util.proto.ProtoOutputStream; 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 class FaceService extends SystemService { 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 @@ public class FaceService extends SystemService { 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 class FaceService extends SystemService { */ 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 dca14914a572..3ed01d5a2cc9 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.ITestSessionCallback; 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 @@ import java.util.Set; 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 @@ public class BiometricTestSessionImpl extends ITestSession.Stub { 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 @@ public class BiometricTestSessionImpl extends ITestSession.Stub { super.finishEnroll_enforcePermission(); + if (mProvider.isVhalForTesting()) { + return; + } + int nextRandomId = mRandom.nextInt(); while (mEnrollmentIds.contains(nextRandomId)) { nextRandomId = mRandom.nextInt(); @@ -178,11 +205,16 @@ public class BiometricTestSessionImpl extends ITestSession.Stub { @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 faces = FaceUtils.getInstance(mSensorId) .getBiometricsForUser(mContext, userId); if (faces.isEmpty()) { @@ -196,10 +228,15 @@ public class BiometricTestSessionImpl extends ITestSession.Stub { @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 @@ public class BiometricTestSessionImpl extends ITestSession.Stub { @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 bb213bfa79e6..5127e68a9df3 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.ITestSession; 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.AuthenticationStatsBroadcastReceiver; 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 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { 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 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { 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 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { 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 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { } 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 @@ -850,10 +886,41 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { return mTestHalEnabled; } + /** + * 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 6f9534993a3f..9fddcfc199b9 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 @@ public class Sensor { if (mTestHalEnabled) { return true; } - return ServiceManager.checkService(IFace.DESCRIPTOR + "/" + halInstanceName) != null; + return ServiceManager.checkService( + remapFqName(IFace.DESCRIPTOR + "/" + halInstanceName)) != null; } /**