Merge "Update BiometricContext#subscribe API" into main
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricContext.java b/services/core/java/com/android/server/biometrics/log/BiometricContext.java
index f8a9867..7f04628 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricContext.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricContext.java
@@ -87,10 +87,30 @@
*
* @param context context that will be modified when changed
* @param consumer callback when the context is modified
+ *
+ * @deprecated instead use {@link BiometricContext#subscribe(OperationContextExt, Consumer,
+ * Consumer, AuthenticateOptions)}
+ * TODO (b/294161627): Delete this API once Flags.DE_HIDL is removed.
*/
+ @Deprecated
void subscribe(@NonNull OperationContextExt context,
@NonNull Consumer<OperationContext> consumer);
+ /**
+ * Subscribe to context changes and start the HAL operation.
+ *
+ * Note that this method only notifies for properties that are visible to the HAL.
+ *
+ * @param context context that will be modified when changed
+ * @param startHalConsumer callback to start HAL operation after subscription is done
+ * @param updateContextConsumer callback when the context is modified
+ * @param options authentication options for updating the context
+ */
+ void subscribe(@NonNull OperationContextExt context,
+ @NonNull Consumer<OperationContext> startHalConsumer,
+ @NonNull Consumer<OperationContext> updateContextConsumer,
+ @Nullable AuthenticateOptions options);
+
/** Unsubscribe from context changes. */
void unsubscribe(@NonNull OperationContextExt context);
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java b/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
index 535b7b7..d8dfa60 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
@@ -238,6 +238,19 @@
}
@Override
+ public void subscribe(@NonNull OperationContextExt context,
+ @NonNull Consumer<OperationContext> startHalConsumer,
+ @NonNull Consumer<OperationContext> updateContextConsumer,
+ @Nullable AuthenticateOptions options) {
+ mSubscribers.put(updateContext(context, context.isCrypto()), updateContextConsumer);
+ if (options != null) {
+ startHalConsumer.accept(context.toAidlContext(options));
+ } else {
+ startHalConsumer.accept(context.toAidlContext());
+ }
+ }
+
+ @Override
public void unsubscribe(@NonNull OperationContextExt context) {
mSubscribers.remove(context);
}
diff --git a/services/core/java/com/android/server/biometrics/log/OperationContextExt.java b/services/core/java/com/android/server/biometrics/log/OperationContextExt.java
index 0045d44..da4e515 100644
--- a/services/core/java/com/android/server/biometrics/log/OperationContextExt.java
+++ b/services/core/java/com/android/server/biometrics/log/OperationContextExt.java
@@ -102,6 +102,24 @@
* @return the underlying AIDL context
*/
@NonNull
+ public OperationContext toAidlContext(@NonNull AuthenticateOptions options) {
+ if (options instanceof FaceAuthenticateOptions) {
+ return toAidlContext((FaceAuthenticateOptions) options);
+ }
+ if (options instanceof FingerprintAuthenticateOptions) {
+ return toAidlContext((FingerprintAuthenticateOptions) options);
+ }
+ throw new IllegalStateException("Authenticate options are invalid.");
+ }
+
+ /**
+ * Gets the subset of the context that can be shared with the HAL and updates
+ * it with the given options.
+ *
+ * @param options authenticate options
+ * @return the underlying AIDL context
+ */
+ @NonNull
public OperationContext toAidlContext(@NonNull FaceAuthenticateOptions options) {
mAidlContext.authenticateReason = AuthenticateReason
.faceAuthenticateReason(getAuthReason(options));
diff --git a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
index 0f01510..b0649b9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
@@ -22,6 +22,7 @@
import android.hardware.biometrics.BiometricAuthenticator;
import android.os.IBinder;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.log.OperationContextExt;
@@ -93,6 +94,9 @@
}
protected OperationContextExt getOperationContext() {
+ if (Flags.deHidl()) {
+ return mOperationContext;
+ }
return getBiometricContext().updateContext(mOperationContext, isCryptoOperation());
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index 470dc4b..22e399c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -37,6 +37,7 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+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;
@@ -153,7 +154,11 @@
0 /* vendorCode */);
mCallback.onClientFinished(this, false /* success */);
} else {
- mCancellationSignal = doAuthenticate();
+ if (Flags.deHidl()) {
+ startAuthenticate();
+ } else {
+ mCancellationSignal = doAuthenticate();
+ }
}
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting auth", e);
@@ -182,6 +187,33 @@
}
}
+ private void startAuthenticate() throws RemoteException {
+ final AidlSession session = getFreshDaemon();
+
+ if (session.hasContextMethods()) {
+ final OperationContextExt opContext = getOperationContext();
+ getBiometricContext().subscribe(opContext, ctx -> {
+ try {
+ mCancellationSignal = session.getSession().authenticateWithContext(
+ mOperationId, ctx);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when requesting auth", e);
+ onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */);
+ mCallback.onClientFinished(this, false /* success */);
+ }
+ }, ctx -> {
+ try {
+ session.getSession().onContextChanged(ctx);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to notify context changed", e);
+ }
+ }, getOptions());
+ } else {
+ mCancellationSignal = session.getSession().authenticate(mOperationId);
+ }
+ }
+
@Override
protected void stopHalOperation() {
unsubscribeBiometricContext();
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
index a529fb9..5ddddda 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
@@ -29,6 +29,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.log.OperationContextExt;
@@ -111,7 +112,11 @@
}
try {
- mCancellationSignal = doDetectInteraction();
+ if (Flags.deHidl()) {
+ startDetect();
+ } else {
+ mCancellationSignal = doDetectInteraction();
+ }
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting face detect", e);
mCallback.onClientFinished(this, false /* success */);
@@ -138,6 +143,30 @@
}
}
+ private void startDetect() throws RemoteException {
+ final AidlSession session = getFreshDaemon();
+
+ if (session.hasContextMethods()) {
+ final OperationContextExt opContext = getOperationContext();
+ getBiometricContext().subscribe(opContext, ctx -> {
+ try {
+ mCancellationSignal = session.getSession().detectInteractionWithContext(ctx);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when requesting face detect", e);
+ mCallback.onClientFinished(this, false /* success */);
+ }
+ }, ctx -> {
+ try {
+ session.getSession().onContextChanged(ctx);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to notify context changed", e);
+ }
+ }, mOptions);
+ } else {
+ mCancellationSignal = session.getSession().detectInteraction();
+ }
+ }
+
@Override
public void onInteractionDetected() {
vibrateSuccess();
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index 0af6e40..f5c4529 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -36,6 +36,7 @@
import android.view.Surface;
import com.android.internal.R;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
@@ -187,7 +188,11 @@
features[i] = featureList.get(i);
}
- mCancellationSignal = doEnroll(features);
+ if (Flags.deHidl()) {
+ startEnroll(features);
+ } else {
+ mCancellationSignal = doEnroll(features);
+ }
} catch (RemoteException | IllegalArgumentException e) {
Slog.e(TAG, "Exception when requesting enroll", e);
onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */);
@@ -231,6 +236,48 @@
}
}
+ private void startEnroll(byte[] features) throws RemoteException {
+ final AidlSession session = getFreshDaemon();
+ final HardwareAuthToken hat =
+ HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken);
+
+ if (session.hasContextMethods()) {
+ final OperationContextExt opContext = getOperationContext();
+ getBiometricContext().subscribe(opContext, ctx -> {
+ try {
+ if (session.supportsFaceEnrollOptions()) {
+ FaceEnrollOptions options = new FaceEnrollOptions();
+ options.hardwareAuthToken = hat;
+ options.enrollmentType = EnrollmentType.DEFAULT;
+ options.features = features;
+ options.nativeHandlePreview = null;
+ options.context = ctx;
+ options.surfacePreview = mPreviewSurface;
+ mCancellationSignal = session.getSession().enrollWithOptions(options);
+ } else {
+ mCancellationSignal = session.getSession().enrollWithContext(
+ hat, EnrollmentType.DEFAULT, features, mHwPreviewHandle, ctx);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception when requesting enroll", e);
+ onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS,
+ 0 /* vendorCode */);
+ mCallback.onClientFinished(this, false /* success */);
+ }
+ }, ctx -> {
+ try {
+ session.getSession().onContextChanged(ctx);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to notify context changed", e);
+ }
+ }, null /* options */);
+ } else {
+ mCancellationSignal = session.getSession().enroll(hat, EnrollmentType.DEFAULT, features,
+ mHwPreviewHandle);
+ }
+ }
+
+
@Override
protected void stopHalOperation() {
unsubscribeBiometricContext();
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 145885d..f7e8123 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -44,6 +44,7 @@
import android.util.Slog;
import com.android.internal.R;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.log.CallbackWithProbe;
@@ -297,7 +298,11 @@
}
try {
- mCancellationSignal = doAuthenticate();
+ if (Flags.deHidl()) {
+ startAuthentication();
+ } else {
+ mCancellationSignal = doAuthenticate();
+ }
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
onError(
@@ -353,6 +358,51 @@
return cancel;
}
+ private void startAuthentication() {
+ final AidlSession session = getFreshDaemon();
+ final OperationContextExt opContext = getOperationContext();
+
+ getBiometricContext().subscribe(opContext, ctx -> {
+ try {
+ if (session.hasContextMethods()) {
+ mCancellationSignal = session.getSession().authenticateWithContext(mOperationId,
+ ctx);
+ } else {
+ mCancellationSignal = session.getSession().authenticate(mOperationId);
+ }
+
+ if (getBiometricContext().isAwake()) {
+ mALSProbeCallback.getProbe().enable();
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ onError(
+ BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */);
+ mSensorOverlays.hide(getSensorId());
+ if (sidefpsControllerRefactor()) {
+ mAuthenticationStateListeners.onAuthenticationStopped();
+ }
+ mCallback.onClientFinished(this, false /* success */);
+ }
+ }, ctx -> {
+ if (session.hasContextMethods()) {
+ try {
+ session.getSession().onContextChanged(ctx);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to notify context changed", e);
+ }
+ }
+
+ final boolean isAwake = getBiometricContext().isAwake();
+ if (isAwake) {
+ mALSProbeCallback.getProbe().enable();
+ } else {
+ mALSProbeCallback.getProbe().disable();
+ }
+ }, getOptions());
+ }
+
@Override
protected void stopHalOperation() {
mSensorOverlays.hide(getSensorId());
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index 3aab7b3..a7fb774 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -31,6 +31,7 @@
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.log.OperationContextExt;
@@ -105,7 +106,11 @@
this);
try {
- mCancellationSignal = doDetectInteraction();
+ if (Flags.deHidl()) {
+ startDetectInteraction();
+ } else {
+ mCancellationSignal = doDetectInteraction();
+ }
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting finger detect", e);
mSensorOverlays.hide(getSensorId());
@@ -139,6 +144,32 @@
}
}
+ private void startDetectInteraction() throws RemoteException {
+ final AidlSession session = getFreshDaemon();
+
+ if (session.hasContextMethods()) {
+ final OperationContextExt opContext = getOperationContext();
+ getBiometricContext().subscribe(opContext, ctx -> {
+ try {
+ mCancellationSignal = session.getSession().detectInteractionWithContext(
+ ctx);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to start detect interaction", e);
+ mSensorOverlays.hide(getSensorId());
+ mCallback.onClientFinished(this, false /* success */);
+ }
+ }, ctx -> {
+ try {
+ session.getSession().onContextChanged(ctx);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to notify context changed", e);
+ }
+ }, mOptions);
+ } else {
+ mCancellationSignal = session.getSession().detectInteraction();
+ }
+ }
+
@Override
public void onInteractionDetected() {
vibrateSuccess();
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index bf5011d..3fb9223 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -39,6 +39,7 @@
import android.util.Slog;
import android.view.accessibility.AccessibilityManager;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
@@ -200,7 +201,11 @@
BiometricNotificationUtils.cancelBadCalibrationNotification(getContext());
try {
- mCancellationSignal = doEnroll();
+ if (Flags.deHidl()) {
+ startEnroll();
+ } else {
+ mCancellationSignal = doEnroll();
+ }
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting enroll", e);
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_UNABLE_TO_PROCESS,
@@ -237,6 +242,35 @@
}
}
+ private void startEnroll() throws RemoteException {
+ final AidlSession session = getFreshDaemon();
+ final HardwareAuthToken hat =
+ HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken);
+
+ if (session.hasContextMethods()) {
+ final OperationContextExt opContext = getOperationContext();
+ getBiometricContext().subscribe(opContext, ctx -> {
+ try {
+ mCancellationSignal = session.getSession().enrollWithContext(
+ hat, ctx);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when requesting enroll", e);
+ onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_UNABLE_TO_PROCESS,
+ 0 /* vendorCode */);
+ mCallback.onClientFinished(this, false /* success */);
+ }
+ }, ctx -> {
+ try {
+ session.getSession().onContextChanged(ctx);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to notify context changed", e);
+ }
+ }, null /* options */);
+ } else {
+ mCancellationSignal = session.getSession().enroll(hat);
+ }
+ }
+
@Override
protected void stopHalOperation() {
mSensorOverlays.hide(getSensorId());
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
index a8eace0..c7300bb 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
@@ -20,6 +20,7 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.mock;
@@ -33,11 +34,15 @@
import android.hardware.biometrics.AuthenticateOptions;
import android.hardware.biometrics.IBiometricContextListener;
import android.hardware.biometrics.IBiometricContextListener.FoldState;
+import android.hardware.biometrics.common.DisplayState;
import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.common.OperationReason;
import android.hardware.display.DisplayManagerGlobal;
+import android.hardware.fingerprint.FingerprintAuthenticateOptions;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.testing.TestableContext;
import android.view.Display;
import android.view.DisplayInfo;
@@ -72,6 +77,9 @@
@Rule
public TestableContext mContext = new TestableContext(
InstrumentationRegistry.getInstrumentation().getContext());
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
@Mock
private IStatusBarService mStatusBarService;
@@ -395,6 +403,37 @@
}
}
+ @Test
+ public void testSubscribe_thenStartHal() throws RemoteException {
+ Consumer<OperationContext> updateConsumer = mock(Consumer.class);
+ Consumer<OperationContext> startHalConsumer = mock(Consumer.class);
+ AuthenticateOptions options = new FingerprintAuthenticateOptions.Builder().build();
+ OperationContextExt context = mProvider.updateContext(mOpContext, false /* crypto */);
+
+ assertThat(context.getDisplayState()).isEqualTo(DisplayState.UNKNOWN);
+ assertThat(context.getFoldState()).isEqualTo(IBiometricContextListener.FoldState.UNKNOWN);
+
+ mListener.onDisplayStateChanged(DisplayState.LOCKSCREEN);
+ mListener.onFoldChanged(FoldState.FULLY_CLOSED);
+ mProvider.subscribe(context, startHalConsumer, updateConsumer, options);
+
+ assertThat(context.getDisplayState()).isEqualTo(DisplayState.LOCKSCREEN);
+ assertThat(context.getFoldState()).isEqualTo(FoldState.FULLY_CLOSED);
+ verify(updateConsumer, never()).accept(context.toAidlContext());
+ verify(startHalConsumer).accept(context.toAidlContext(options));
+ }
+
+ @Test
+ public void testSubscribe_withInvalidOptions() {
+ Consumer<OperationContext> updateConsumer = mock(Consumer.class);
+ Consumer<OperationContext> startHalConsumer = mock(Consumer.class);
+ AuthenticateOptions options = mock(AuthenticateOptions.class);
+ OperationContextExt context = mProvider.updateContext(mOpContext, false /* crypto */);
+
+ assertThrows(IllegalStateException.class, () -> mProvider.subscribe(
+ context, startHalConsumer, updateConsumer, options));
+ }
+
private static byte reason(int type) {
if (type == StatusBarManager.SESSION_BIOMETRIC_PROMPT) {
return OperationReason.BIOMETRIC_PROMPT;