diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index 7092e43596eca..7247ef77afb48 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -29,6 +29,7 @@ import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricFaceConstants; +import android.hardware.biometrics.BiometricStateListener; import android.hardware.biometrics.CryptoObject; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; import android.os.Binder; @@ -673,6 +674,45 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan return new ArrayList<>(); } + /** + * Forwards BiometricStateListener to FaceService. + * + * @param listener new BiometricStateListener being added + * @hide + */ + public void registerBiometricStateListener(@NonNull BiometricStateListener listener) { + try { + mService.registerBiometricStateListener(listener); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Adds a callback that gets called when the service registers all of the face + * authenticators (HALs). + * + * If the face authenticators are already registered when the callback is added, the + * callback is invoked immediately. + * + * The callback is automatically removed after it's invoked. + * + * @hide + */ + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + public void addAuthenticatorsRegisteredCallback( + IFaceAuthenticatorsRegisteredCallback callback) { + if (mService != null) { + try { + mService.addAuthenticatorsRegisteredCallback(callback); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } else { + Slog.w(TAG, "addAuthenticatorsRegisteredCallback(): Service not connected!"); + } + } + /** * @hide */ diff --git a/core/java/android/hardware/face/IFaceAuthenticatorsRegisteredCallback.aidl b/core/java/android/hardware/face/IFaceAuthenticatorsRegisteredCallback.aidl new file mode 100644 index 0000000000000..78f978d21ed79 --- /dev/null +++ b/core/java/android/hardware/face/IFaceAuthenticatorsRegisteredCallback.aidl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.face; + +import android.hardware.face.FaceSensorPropertiesInternal; +import java.util.List; + +/** + * Callback to notify FaceManager that FaceService has registered all of the + * face authenticators (HALs). + * See {@link android.hardware.face.IFaceService#registerAuthenticators}. + * + * @hide + */ +oneway interface IFaceAuthenticatorsRegisteredCallback { + /** + * Notifies FaceManager that all of the face authenticators have been registered. + * + * @param sensors A consolidated list of sensor properties for all of the authenticators. + */ + void onAllAuthenticatorsRegistered(in List sensors); +} diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl index 369248edd5809..9b56f43a0f22a 100644 --- a/core/java/android/hardware/face/IFaceService.aidl +++ b/core/java/android/hardware/face/IFaceService.aidl @@ -17,9 +17,11 @@ package android.hardware.face; import android.hardware.biometrics.IBiometricSensorReceiver; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; +import android.hardware.biometrics.IBiometricStateListener; import android.hardware.biometrics.IInvalidationCallback; import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.ITestSessionCallback; +import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; import android.hardware.face.IFaceServiceReceiver; import android.hardware.face.Face; import android.hardware.face.FaceSensorPropertiesInternal; @@ -163,4 +165,11 @@ interface IFaceService { // hidlSensors must be non-null and empty. See AuthService.java @EnforcePermission("USE_BIOMETRIC_INTERNAL") void registerAuthenticators(in List hidlSensors); + + // Adds a callback which gets called when the service registers all of the face + // authenticators. The callback is automatically removed after it's invoked. + void addAuthenticatorsRegisteredCallback(IFaceAuthenticatorsRegisteredCallback callback); + + // Registers BiometricStateListener. + void registerBiometricStateListener(IBiometricStateListener listener); } diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index cc7ed183ed64f..1ba9a0471c88a 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -202,8 +202,10 @@ interface IFingerprintService { void setSidefpsController(in ISidefpsController controller); // Registers BiometricStateListener. + @EnforcePermission("USE_BIOMETRIC_INTERNAL") void registerBiometricStateListener(IBiometricStateListener listener); // Sends a power button pressed event to all listeners. + @EnforcePermission("USE_BIOMETRIC_INTERNAL") oneway void onPowerPressed(); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 2f9bc1c596248..d75285283cbcc 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -54,6 +54,7 @@ import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.hardware.SensorPrivacyManager; +import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricSourceType; @@ -2018,12 +2019,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab // in case authenticators aren't registered yet at this point: mAuthController.addCallback(new AuthController.Callback() { @Override - public void onAllAuthenticatorsRegistered() { + public void onAllAuthenticatorsRegistered( + @BiometricAuthenticator.Modality int modality) { mainExecutor.execute(() -> updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE)); } @Override - public void onEnrollmentsChanged() { + public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) { mainExecutor.execute(() -> updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE)); } }); diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java index 06e1828ef9f4b..d6974dfac5703 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java @@ -16,6 +16,7 @@ package com.android.keyguard; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT; import static com.android.keyguard.LockIconView.ICON_FINGERPRINT; @@ -29,6 +30,7 @@ import android.content.res.Resources; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.drawable.AnimatedStateListDrawable; +import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricSourceType; import android.os.Process; import android.os.VibrationAttributes; @@ -701,13 +703,17 @@ public class LockIconViewController extends ViewController impleme private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() { @Override - public void onAllAuthenticatorsRegistered() { - updateUdfpsConfig(); + public void onAllAuthenticatorsRegistered(@BiometricAuthenticator.Modality int modality) { + if (modality == TYPE_FINGERPRINT) { + updateUdfpsConfig(); + } } @Override - public void onEnrollmentsChanged() { - updateUdfpsConfig(); + public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) { + if (modality == TYPE_FINGERPRINT) { + updateUdfpsConfig(); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 47ff59cfc2815..282f25104c443 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -46,6 +46,7 @@ import android.hardware.biometrics.PromptInfo; import android.hardware.display.DisplayManager; import android.hardware.face.FaceManager; import android.hardware.face.FaceSensorPropertiesInternal; +import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback; @@ -156,25 +157,6 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba } }; - private final IFingerprintAuthenticatorsRegisteredCallback - mFingerprintAuthenticatorsRegisteredCallback = - new IFingerprintAuthenticatorsRegisteredCallback.Stub() { - @Override - public void onAllAuthenticatorsRegistered( - List sensors) { - mHandler.post(() -> handleAllFingerprintAuthenticatorsRegistered(sensors)); - } - }; - - private final BiometricStateListener mBiometricStateListener = - new BiometricStateListener() { - @Override - public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) { - mHandler.post( - () -> handleEnrollmentsChanged(userId, sensorId, hasEnrollments)); - } - }; - @VisibleForTesting final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override @@ -249,8 +231,8 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba List sensors) { mExecution.assertIsMainThread(); if (DEBUG) { - Log.d(TAG, "handleAllAuthenticatorsRegistered | sensors: " + Arrays.toString( - sensors.toArray())); + Log.d(TAG, "handleAllFingerprintAuthenticatorsRegistered | sensors: " + + Arrays.toString(sensors.toArray())); } mAllFingerprintAuthenticatorsRegistered = true; mFpProps = sensors; @@ -292,15 +274,42 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba mSidefpsController = mSidefpsControllerFactory.get(); } - mFingerprintManager.registerBiometricStateListener(mBiometricStateListener); + mFingerprintManager.registerBiometricStateListener(new BiometricStateListener() { + @Override + public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) { + mHandler.post(() -> handleEnrollmentsChanged( + TYPE_FINGERPRINT, userId, sensorId, hasEnrollments)); + } + }); updateFingerprintLocation(); for (Callback cb : mCallbacks) { - cb.onAllAuthenticatorsRegistered(); + cb.onAllAuthenticatorsRegistered(TYPE_FINGERPRINT); } } - private void handleEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) { + private void handleAllFaceAuthenticatorsRegistered(List sensors) { + mExecution.assertIsMainThread(); + if (DEBUG) { + Log.d(TAG, "handleAllFaceAuthenticatorsRegistered | sensors: " + Arrays.toString( + sensors.toArray())); + } + + mFaceManager.registerBiometricStateListener(new BiometricStateListener() { + @Override + public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) { + mHandler.post(() -> handleEnrollmentsChanged( + TYPE_FACE, userId, sensorId, hasEnrollments)); + } + }); + + for (Callback cb : mCallbacks) { + cb.onAllAuthenticatorsRegistered(TYPE_FACE); + } + } + + private void handleEnrollmentsChanged(@Modality int modality, int userId, int sensorId, + boolean hasEnrollments) { mExecution.assertIsMainThread(); Log.d(TAG, "handleEnrollmentsChanged, userId: " + userId + ", sensorId: " + sensorId + ", hasEnrollments: " + hasEnrollments); @@ -314,7 +323,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba } } for (Callback cb : mCallbacks) { - cb.onEnrollmentsChanged(); + cb.onEnrollmentsChanged(modality); } } @@ -700,7 +709,26 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba if (mFingerprintManager != null) { mFingerprintManager.addAuthenticatorsRegisteredCallback( - mFingerprintAuthenticatorsRegisteredCallback); + new IFingerprintAuthenticatorsRegisteredCallback.Stub() { + @Override + public void onAllAuthenticatorsRegistered( + List sensors) { + mHandler.post(() -> + handleAllFingerprintAuthenticatorsRegistered(sensors)); + } + }); + } + if (mFaceManager != null) { + mFaceManager.addAuthenticatorsRegisteredCallback( + new IFaceAuthenticatorsRegisteredCallback.Stub() { + @Override + public void onAllAuthenticatorsRegistered( + List sensors) { + mHandler.post(() -> + handleAllFaceAuthenticatorsRegistered(sensors)); + } + } + ); } mStableDisplaySize = mDisplayManager.getStableDisplaySize(); @@ -1116,13 +1144,13 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba * Called when authenticators are registered. If authenticators are already * registered before this call, this callback will never be triggered. */ - default void onAllAuthenticatorsRegistered() {} + default void onAllAuthenticatorsRegistered(@Modality int modality) {} /** - * Called when UDFPS enrollments have changed. This is called after boot and on changes to + * Called when enrollments have changed. This is called after boot and on changes to * enrollment. */ - default void onEnrollmentsChanged() {} + default void onEnrollmentsChanged(@Modality int modality) {} /** * Called when the biometric prompt starts showing. diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt index 38fab8ffbfad7..fd3f6007d8a9a 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt @@ -308,7 +308,7 @@ class AuthRippleController @Inject constructor( private val authControllerCallback = object : AuthController.Callback { - override fun onAllAuthenticatorsRegistered() { + override fun onAllAuthenticatorsRegistered(modality: Int) { updateUdfpsDependentParams() updateSensorLocation() } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java index a9e310d25f9cc..7da2cf1501475 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java @@ -16,12 +16,15 @@ package com.android.systemui.doze; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; + import static com.android.systemui.doze.DozeMachine.State.DOZE; import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD; import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED; import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSING; import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSE_DONE; +import android.hardware.biometrics.BiometricAuthenticator; import android.os.Handler; import android.util.Log; import android.view.Display; @@ -232,13 +235,17 @@ public class DozeScreenState implements DozeMachine.Part { private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() { @Override - public void onAllAuthenticatorsRegistered() { - updateUdfpsController(); + public void onAllAuthenticatorsRegistered(@BiometricAuthenticator.Modality int modality) { + if (modality == TYPE_FINGERPRINT) { + updateUdfpsController(); + } } @Override - public void onEnrollmentsChanged() { - updateUdfpsController(); + public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) { + if (modality == TYPE_FINGERPRINT) { + updateUdfpsController(); + } } }; } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index da6c163b1eead..997a6e5543647 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -16,6 +16,8 @@ package com.android.systemui.doze; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; + import static com.android.systemui.doze.DozeLog.REASON_SENSOR_QUICK_PICKUP; import static com.android.systemui.doze.DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS; import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_DISPLAY; @@ -29,6 +31,7 @@ import android.hardware.Sensor; import android.hardware.SensorManager; import android.hardware.TriggerEvent; import android.hardware.TriggerEventListener; +import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.display.AmbientDisplayConfiguration; import android.net.Uri; import android.os.Handler; @@ -835,13 +838,17 @@ public class DozeSensors { private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() { @Override - public void onAllAuthenticatorsRegistered() { - updateUdfpsEnrolled(); + public void onAllAuthenticatorsRegistered(@BiometricAuthenticator.Modality int modality) { + if (modality == TYPE_FINGERPRINT) { + updateUdfpsEnrolled(); + } } @Override - public void onEnrollmentsChanged() { - updateUdfpsEnrolled(); + public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) { + if (modality == TYPE_FINGERPRINT) { + updateUdfpsEnrolled(); + } } private void updateUdfpsEnrolled() { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index c281965550e4b..3e00aec8cb281 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -16,6 +16,7 @@ package com.android.keyguard; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT; import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE; import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID; @@ -1006,7 +1007,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // WHEN udfps is now enrolled when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(true); - callback.onEnrollmentsChanged(); + callback.onEnrollmentsChanged(TYPE_FINGERPRINT); // THEN isUdfspEnrolled is TRUE assertThat(mKeyguardUpdateMonitor.isUdfpsEnrolled()).isTrue(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index d158892e4ec54..e0d1f7a191309 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -60,6 +60,9 @@ import android.hardware.biometrics.PromptInfo; import android.hardware.biometrics.SensorProperties; import android.hardware.display.DisplayManager; import android.hardware.face.FaceManager; +import android.hardware.face.FaceSensorProperties; +import android.hardware.face.FaceSensorPropertiesInternal; +import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorProperties; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; @@ -154,7 +157,9 @@ public class AuthControllerTest extends SysuiTestCase { @Mock private InteractionJankMonitor mInteractionJankMonitor; @Captor - ArgumentCaptor mAuthenticatorsRegisteredCaptor; + ArgumentCaptor mFpAuthenticatorsRegisteredCaptor; + @Captor + ArgumentCaptor mFaceAuthenticatorsRegisteredCaptor; @Captor ArgumentCaptor mBiometricStateCaptor; @Captor @@ -193,25 +198,38 @@ public class AuthControllerTest extends SysuiTestCase { when(mDisplayManager.getStableDisplaySize()).thenReturn(new Point()); when(mFingerprintManager.isHardwareDetected()).thenReturn(true); + when(mFaceManager.isHardwareDetected()).thenReturn(true); - final List componentInfo = new ArrayList<>(); - componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */, - "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */, - "00000001" /* serialNumber */, "" /* softwareVersion */)); - componentInfo.add(new ComponentInfoInternal("matchingAlgorithm" /* componentId */, - "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */, - "vendor/version/revision" /* softwareVersion */)); + final List fpComponentInfo = List.of( + new ComponentInfoInternal("faceSensor" /* componentId */, + "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */, + "00000001" /* serialNumber */, "" /* softwareVersion */)); + final List faceComponentInfo = List.of( + new ComponentInfoInternal("matchingAlgorithm" /* componentId */, + "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */, + "vendor/version/revision" /* softwareVersion */)); - FingerprintSensorPropertiesInternal prop = new FingerprintSensorPropertiesInternal( - 1 /* sensorId */, - SensorProperties.STRENGTH_STRONG, - 1 /* maxEnrollmentsPerUser */, - componentInfo, - FingerprintSensorProperties.TYPE_UDFPS_OPTICAL, - true /* resetLockoutRequireHardwareAuthToken */); - List props = new ArrayList<>(); - props.add(prop); - when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props); + final List fpProps = List.of( + new FingerprintSensorPropertiesInternal( + 1 /* sensorId */, + SensorProperties.STRENGTH_STRONG, + 1 /* maxEnrollmentsPerUser */, + fpComponentInfo, + FingerprintSensorProperties.TYPE_UDFPS_OPTICAL, + true /* resetLockoutRequireHardwareAuthToken */)); + when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(fpProps); + + final List faceProps = List.of( + new FaceSensorPropertiesInternal( + 2 /* sensorId */, + SensorProperties.STRENGTH_STRONG, + 1 /* maxEnrollmentsPerUser */, + fpComponentInfo, + FaceSensorProperties.TYPE_RGB, + true /* supportsFaceDetection */, + true /* supportsSelfIllumination */, + true /* resetLockoutRequireHardwareAuthToken */)); + when(mFaceManager.getSensorPropertiesInternal()).thenReturn(faceProps); mAuthController = new TestableAuthController(mContextSpy, mExecution, mCommandQueue, mActivityTaskManager, mWindowManager, mFingerprintManager, mFaceManager, @@ -219,12 +237,15 @@ public class AuthControllerTest extends SysuiTestCase { mAuthController.start(); verify(mFingerprintManager).addAuthenticatorsRegisteredCallback( - mAuthenticatorsRegisteredCaptor.capture()); + mFpAuthenticatorsRegisteredCaptor.capture()); + verify(mFaceManager).addAuthenticatorsRegisteredCallback( + mFaceAuthenticatorsRegisteredCaptor.capture()); when(mStatusBarStateController.isDozing()).thenReturn(false); verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture()); - mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(props); + mFpAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(fpProps); + mFaceAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(faceProps); // Ensures that the operations posted on the handler get executed. mTestableLooper.processAllMessages(); @@ -237,6 +258,7 @@ public class AuthControllerTest extends SysuiTestCase { throws RemoteException { // This test is sensitive to prior FingerprintManager interactions. reset(mFingerprintManager); + reset(mFaceManager); // This test requires an uninitialized AuthController. AuthController authController = new TestableAuthController(mContextSpy, mExecution, @@ -246,21 +268,27 @@ public class AuthControllerTest extends SysuiTestCase { authController.start(); verify(mFingerprintManager).addAuthenticatorsRegisteredCallback( - mAuthenticatorsRegisteredCaptor.capture()); + mFpAuthenticatorsRegisteredCaptor.capture()); + verify(mFaceManager).addAuthenticatorsRegisteredCallback( + mFaceAuthenticatorsRegisteredCaptor.capture()); mTestableLooper.processAllMessages(); verify(mFingerprintManager, never()).registerBiometricStateListener(any()); + verify(mFaceManager, never()).registerBiometricStateListener(any()); - mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(new ArrayList<>()); + mFpAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(List.of()); + mFaceAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(List.of()); mTestableLooper.processAllMessages(); verify(mFingerprintManager).registerBiometricStateListener(any()); + verify(mFaceManager).registerBiometricStateListener(any()); } @Test public void testDoesNotCrash_afterEnrollmentsChangedForUnknownSensor() throws RemoteException { // This test is sensitive to prior FingerprintManager interactions. reset(mFingerprintManager); + reset(mFaceManager); // This test requires an uninitialized AuthController. AuthController authController = new TestableAuthController(mContextSpy, mExecution, @@ -270,18 +298,25 @@ public class AuthControllerTest extends SysuiTestCase { authController.start(); verify(mFingerprintManager).addAuthenticatorsRegisteredCallback( - mAuthenticatorsRegisteredCaptor.capture()); + mFpAuthenticatorsRegisteredCaptor.capture()); + verify(mFaceManager).addAuthenticatorsRegisteredCallback( + mFaceAuthenticatorsRegisteredCaptor.capture()); // Emulates a device with no authenticators (empty list). - mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(new ArrayList<>()); + mFpAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(List.of()); + mFaceAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(List.of()); mTestableLooper.processAllMessages(); verify(mFingerprintManager).registerBiometricStateListener( mBiometricStateCaptor.capture()); + verify(mFaceManager).registerBiometricStateListener( + mBiometricStateCaptor.capture()); // Enrollments changed for an unknown sensor. - mBiometricStateCaptor.getValue().onEnrollmentsChanged(0 /* userId */, - 0xbeef /* sensorId */, true /* hasEnrollments */); + for (BiometricStateListener listener : mBiometricStateCaptor.getAllValues()) { + listener.onEnrollmentsChanged(0 /* userId */, + 0xbeef /* sensorId */, true /* hasEnrollments */); + } mTestableLooper.processAllMessages(); // Nothing should crash. @@ -827,4 +862,3 @@ public class AuthControllerTest extends SysuiTestCase { } } } - diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java index 9ffc5a57cef65..b33f9a7f3933e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java @@ -16,6 +16,8 @@ package com.android.systemui.doze; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; + import static com.android.systemui.doze.DozeLog.REASON_SENSOR_TAP; import static com.android.systemui.doze.DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS; import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_LOCK_SCREEN; @@ -412,7 +414,7 @@ public class DozeSensorsTest extends SysuiTestCase { // WHEN enrollment changes to TRUE when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(true); - mAuthControllerCallback.onEnrollmentsChanged(); + mAuthControllerCallback.onEnrollmentsChanged(TYPE_FINGERPRINT); // THEN mConfigured = TRUE assertTrue(triggerSensor.mConfigured); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java index 24d051508fde4..5ec6bdf3c00b6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java @@ -16,6 +16,8 @@ package com.android.systemui.keyguard; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; + import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.keyguard.LockIconView.ICON_LOCK; import static com.android.keyguard.LockIconView.ICON_UNLOCK; @@ -219,7 +221,7 @@ public class LockIconViewControllerTest extends SysuiTestCase { Pair udfps = setupUdfps(); // WHEN all authenticators are registered - mAuthControllerCallback.onAllAuthenticatorsRegistered(); + mAuthControllerCallback.onAllAuthenticatorsRegistered(TYPE_FINGERPRINT); mDelayableExecutor.runAllReady(); // THEN lock icon view location is updated with the same coordinates as auth controller vals diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceProvider.java new file mode 100644 index 0000000000000..0f1fe68ad1d79 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceProvider.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors; + +import android.annotation.NonNull; +import android.hardware.biometrics.SensorPropertiesInternal; +import android.util.proto.ProtoOutputStream; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.List; + +/** + * Common attributes for all biometric service providers. + * + * @param Internal settings type. + */ +public interface BiometricServiceProvider { + + /** Checks if the specified sensor is owned by this provider. */ + boolean containsSensor(int sensorId); + + /** All sensor properties. */ + @NonNull + List getSensorProperties(); + + /** Properties for the given sensor id. */ + @NonNull + T getSensorProperties(int sensorId); + + boolean isHardwareDetected(int sensorId); + + /** If the user has any enrollments for the given sensor. */ + boolean hasEnrollments(int sensorId, int userId); + + long getAuthenticatorId(int sensorId, int userId); + + @LockoutTracker.LockoutMode + int getLockoutModeForUser(int sensorId, int userId); + + void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto, + boolean clearSchedulerBuffer); + + void dumpProtoMetrics(int sensorId, @NonNull FileDescriptor fd); + + void dumpInternal(int sensorId, @NonNull PrintWriter pw); +} diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java new file mode 100644 index 0000000000000..4779f6f931ee4 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors; + + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.hardware.biometrics.IBiometricAuthenticator; +import android.hardware.biometrics.IBiometricService; +import android.hardware.biometrics.SensorPropertiesInternal; +import android.os.Handler; +import android.os.IInterface; +import android.os.Process; +import android.os.RemoteCallbackList; +import android.os.RemoteException; +import android.util.Pair; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.ServiceThread; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.Supplier; + +/** + * Container for all BiometricServiceProvider implementations. + * + * @param The service provider type. + * @param

The internal properties type. + * @param The registration callback for {@link #invokeRegisteredCallback(IInterface, List)}. + */ +public abstract class BiometricServiceRegistry, + P extends SensorPropertiesInternal, + C extends IInterface> { + + private static final String TAG = "BiometricServiceRegistry"; + + // Volatile so they can be read without a lock once all services are registered. + // But, ideally remove this and provide immutable copies via the callback instead. + @Nullable + private volatile List mServiceProviders; + @Nullable + private volatile List

mAllProps; + + @NonNull + private final Supplier mBiometricServiceSupplier; + @NonNull + private final RemoteCallbackList mRegisteredCallbacks = new RemoteCallbackList<>(); + + public BiometricServiceRegistry(@NonNull Supplier biometricSupplier) { + mBiometricServiceSupplier = biometricSupplier; + } + + /** + * Register an implementation by creating a new authenticator and initializing it via + * {@link IBiometricService#registerAuthenticator(int, int, int, IBiometricAuthenticator)} + * using the given properties. + * + * @param service service to register with + * @param props internal properties to initialize the authenticator + */ + protected abstract void registerService(@NonNull IBiometricService service, @NonNull P props); + + /** + * Invoke the callback to notify clients that all authenticators have been registered. + * + * @param callback callback to invoke + * @param allProps properties of all authenticators + */ + protected abstract void invokeRegisteredCallback(@NonNull C callback, + @NonNull List

allProps) throws RemoteException; + + /** + * Register all authenticators in a background thread. + * + * @param serviceProvider Supplier function that will be invoked on the background thread. + */ + public void registerAll(Supplier> serviceProvider) { + // Some HAL might not be started before the system service and will cause the code below + // to wait, and some of the operations below might take a significant amount of time to + // complete (calls to the HALs). To avoid blocking the rest of system server we put + // this on a background thread. + final ServiceThread thread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND, + true /* allowIo */); + thread.start(); + final Handler handler = new Handler(thread.getLooper()); + handler.post(() -> registerAllInBackground(serviceProvider)); + thread.quitSafely(); + } + + /** Register authenticators now, only called by {@link #registerAll(Supplier).} */ + @VisibleForTesting + public void registerAllInBackground(Supplier> serviceProvider) { + List providers = serviceProvider.get(); + if (providers == null) { + providers = new ArrayList<>(); + } + + final IBiometricService biometricService = mBiometricServiceSupplier.get(); + if (biometricService == null) { + throw new IllegalStateException("biometric service cannot be null"); + } + + // Register each sensor individually with BiometricService + final List

allProps = new ArrayList<>(); + for (T provider : providers) { + final List

props = provider.getSensorProperties(); + for (P prop : props) { + registerService(biometricService, prop); + } + allProps.addAll(props); + } + + finishRegistration(providers, allProps); + } + + private synchronized void finishRegistration( + @NonNull List providers, @NonNull List

allProps) { + mServiceProviders = Collections.unmodifiableList(providers); + mAllProps = Collections.unmodifiableList(allProps); + broadcastAllAuthenticatorsRegistered(); + } + + /** + * Add a callback that will be invoked once the work from {@link #registerAll(Supplier)} + * has finished registering all providers (executes immediately if already done). + * + * @param callback registration callback + */ + public synchronized void addAllRegisteredCallback(@Nullable C callback) { + if (callback == null) { + Slog.e(TAG, "addAllRegisteredCallback, callback is null"); + return; + } + + final boolean registered = mRegisteredCallbacks.register(callback); + final boolean allRegistered = mServiceProviders != null; + if (registered && allRegistered) { + broadcastAllAuthenticatorsRegistered(); + } else if (!registered) { + Slog.e(TAG, "addAllRegisteredCallback failed to register callback"); + } + } + + private synchronized void broadcastAllAuthenticatorsRegistered() { + final int n = mRegisteredCallbacks.beginBroadcast(); + for (int i = 0; i < n; ++i) { + final C cb = mRegisteredCallbacks.getBroadcastItem(i); + try { + invokeRegisteredCallback(cb, mAllProps); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception in broadcastAllAuthenticatorsRegistered", e); + } finally { + mRegisteredCallbacks.unregister(cb); + } + } + mRegisteredCallbacks.finishBroadcast(); + } + + /** + * Get a list of registered providers. + * + * Undefined until {@link #registerAll(Supplier)} has fired the completion callback. + */ + @NonNull + public List getProviders() { + return mServiceProviders != null ? mServiceProviders : Collections.emptyList(); + } + + /** + * Gets the provider for given sensor id or null if not registered. + * + * Undefined until {@link #registerAll(Supplier)} has fired the completion callback. + */ + @Nullable + public T getProviderForSensor(int sensorId) { + if (mServiceProviders != null) { + for (T provider : mServiceProviders) { + if (provider.containsSensor(sensorId)) { + return provider; + } + } + } + return null; + } + + /** + * Finds the provider for devices with only a single sensor. + * + * If no providers returns null. If multiple sensors are present this method + * will return the first one that is found (this is a legacy for test devices that + * use aidl/hidl concurrently and should not occur on real devices). + * + * Undefined until {@link #registerAll(Supplier)} has fired the completion callback. + */ + @Nullable + public Pair getSingleProvider() { + if (mAllProps == null || mAllProps.isEmpty()) { + Slog.e(TAG, "No sensors found"); + return null; + } + + if (mAllProps.size() > 1) { + Slog.e(TAG, "getSingleProvider() called but multiple sensors present: " + + mAllProps.size()); + } + + final int sensorId = mAllProps.get(0).sensorId; + final T provider = getProviderForSensor(sensorId); + if (provider != null) { + return new Pair<>(sensorId, provider); + } + + Slog.e(TAG, "Single sensor: " + sensorId + ", but provider not found"); + return null; + } + + /** + * Get the properties for all providers. + * + * Undefined until {@link #registerAll(Supplier)} has fired the completion callback. + */ + @NonNull + public List

getAllProperties() { + return mAllProps != null ? mAllProps : Collections.emptyList(); + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java b/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java index 0d789f7a1840c..f8543162f95e7 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java @@ -23,32 +23,64 @@ import static android.hardware.biometrics.BiometricStateListener.STATE_IDLE; import static android.hardware.biometrics.BiometricStateListener.STATE_KEYGUARD_AUTH; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricStateListener; import android.hardware.biometrics.IBiometricStateListener; +import android.hardware.biometrics.SensorPropertiesInternal; import android.os.RemoteException; +import android.os.UserManager; import android.util.Slog; import com.android.server.biometrics.Utils; +import java.util.Collections; +import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; /** * A callback for receiving notifications about biometric sensor state changes. + * + * @param service provider type + * @param

internal property type */ -public class BiometricStateCallback implements ClientMonitorCallback { +public class BiometricStateCallback, + P extends SensorPropertiesInternal> implements ClientMonitorCallback { private static final String TAG = "BiometricStateCallback"; @NonNull - private final CopyOnWriteArrayList - mBiometricStateListeners = new CopyOnWriteArrayList<>(); + private final CopyOnWriteArrayList mBiometricStateListeners = + new CopyOnWriteArrayList<>(); + @NonNull + private final UserManager mUserManager; + @BiometricStateListener.State + private int mBiometricState; + @NonNull + private List mProviders = List.of(); - private @BiometricStateListener.State int mBiometricState; - - public BiometricStateCallback() { + /** + * Create a new callback that must be {@link #start(List)}ed. + * + * @param userManager user manager + */ + public BiometricStateCallback(@NonNull UserManager userManager) { mBiometricState = STATE_IDLE; + mUserManager = userManager; } + /** + * This should be called when the service has been initialized and all providers are ready. + * + * @param allProviders all registered biometric service providers + */ + public synchronized void start(@NonNull List allProviders) { + mProviders = Collections.unmodifiableList(allProviders); + broadcastCurrentEnrollmentState(null /* listener */); + } + + /** Get the current state. */ + @BiometricStateListener.State public int getBiometricState() { return mBiometricState; } @@ -120,23 +152,43 @@ public class BiometricStateCallback implements ClientMonitorCallback { } /** - * This should be invoked when: - * 1) Enrolled --> None-enrolled - * 2) None-enrolled --> enrolled - * 3) HAL becomes ready - * 4) Listener is registered + * Enables clients to register a BiometricStateListener. For example, this is used to forward + * fingerprint sensor state changes to SideFpsEventHandler. + * + * @param listener listener to register */ - public void notifyAllEnrollmentStateChanged(int userId, int sensorId, + public synchronized void registerBiometricStateListener( + @NonNull IBiometricStateListener listener) { + mBiometricStateListeners.add(listener); + broadcastCurrentEnrollmentState(listener); + } + + private synchronized void broadcastCurrentEnrollmentState( + @Nullable IBiometricStateListener listener) { + for (T provider : mProviders) { + for (SensorPropertiesInternal prop : provider.getSensorProperties()) { + for (UserInfo userInfo : mUserManager.getAliveUsers()) { + final boolean enrolled = provider.hasEnrollments(prop.sensorId, userInfo.id); + if (listener != null) { + notifyEnrollmentStateChanged( + listener, userInfo.id, prop.sensorId, enrolled); + } else { + notifyAllEnrollmentStateChanged( + userInfo.id, prop.sensorId, enrolled); + } + } + } + } + } + + private void notifyAllEnrollmentStateChanged(int userId, int sensorId, boolean hasEnrollments) { for (IBiometricStateListener listener : mBiometricStateListeners) { notifyEnrollmentStateChanged(listener, userId, sensorId, hasEnrollments); } } - /** - * Notifies the listener of enrollment state changes. - */ - public void notifyEnrollmentStateChanged(@NonNull IBiometricStateListener listener, + private void notifyEnrollmentStateChanged(@NonNull IBiometricStateListener listener, int userId, int sensorId, boolean hasEnrollments) { try { listener.onEnrollmentsChanged(userId, sensorId, hasEnrollments); @@ -144,14 +196,4 @@ public class BiometricStateCallback implements ClientMonitorCallback { Slog.e(TAG, "Remote exception", e); } } - - /** - * Enables clients to register a BiometricStateListener. For example, this is used to forward - * fingerprint sensor state changes to SideFpsEventHandler. - * - * @param listener - */ - public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) { - mBiometricStateListeners.add(listener); - } } 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 79e65cc6d2e54..271bce9890c63 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 @@ -17,20 +17,18 @@ package com.android.server.biometrics.sensors.face; import static android.Manifest.permission.INTERACT_ACROSS_USERS; -import static android.Manifest.permission.MANAGE_BIOMETRIC; import static android.Manifest.permission.MANAGE_FACE; import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; -import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.content.Context; -import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.IBiometricSensorReceiver; import android.hardware.biometrics.IBiometricService; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; +import android.hardware.biometrics.IBiometricStateListener; import android.hardware.biometrics.IInvalidationCallback; import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.ITestSessionCallback; @@ -39,18 +37,18 @@ import android.hardware.biometrics.face.SensorProps; import android.hardware.face.Face; import android.hardware.face.FaceSensorPropertiesInternal; import android.hardware.face.FaceServiceReceiver; +import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; import android.hardware.face.IFaceService; import android.hardware.face.IFaceServiceReceiver; import android.os.Binder; -import android.os.Handler; import android.os.IBinder; import android.os.NativeHandle; -import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; import android.os.ShellCallback; import android.os.UserHandle; +import android.os.UserManager; import android.util.Pair; import android.util.Slog; import android.util.proto.ProtoOutputStream; @@ -58,10 +56,10 @@ import android.view.Surface; import com.android.internal.util.DumpUtils; import com.android.internal.widget.LockPatternUtils; -import com.android.server.ServiceThread; import com.android.server.SystemService; import com.android.server.biometrics.Utils; import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.sensors.BiometricStateCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.LockoutTracker; @@ -88,51 +86,10 @@ public class FaceService extends SystemService { private final LockoutResetDispatcher mLockoutResetDispatcher; private final LockPatternUtils mLockPatternUtils; @NonNull - private final List mServiceProviders; - - @Nullable - private ServiceProvider getProviderForSensor(int sensorId) { - for (ServiceProvider provider : mServiceProviders) { - if (provider.containsSensor(sensorId)) { - return provider; - } - } - return null; - } - - /** - * For devices with only a single provider, returns that provider. If no providers, or multiple - * providers exist, returns null. - */ - @Nullable - private Pair getSingleProvider() { - final List properties = getSensorProperties(); - if (properties.size() != 1) { - Slog.e(TAG, "Multiple sensors found: " + properties.size()); - return null; - } - - // Theoretically we can just return the first provider, but maybe this is easier to - // understand. - final int sensorId = properties.get(0).sensorId; - for (ServiceProvider provider : mServiceProviders) { - if (provider.containsSensor(sensorId)) { - return new Pair<>(sensorId, provider); - } - } - - Slog.e(TAG, "Single sensor, but provider not found"); - return null; - } - + private final FaceServiceRegistry mRegistry; @NonNull - private List getSensorProperties() { - final List properties = new ArrayList<>(); - for (ServiceProvider provider : mServiceProviders) { - properties.addAll(provider.getSensorProperties()); - } - return properties; - } + private final BiometricStateCallback + mBiometricStateCallback; /** * Receives the incoming binder calls from FaceManager. @@ -142,8 +99,7 @@ public class FaceService extends SystemService { @Override public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback, @NonNull String opPackageName) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for createTestSession, sensorId: " + sensorId); @@ -156,9 +112,8 @@ public class FaceService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public byte[] dumpSensorServiceStateProto(int sensorId, boolean clearSchedulerBuffer) { - final ProtoOutputStream proto = new ProtoOutputStream(); - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider != null) { provider.dumpProtoState(sensorId, proto, clearSchedulerBuffer); } @@ -170,16 +125,14 @@ public class FaceService extends SystemService { @Override // Binder call public List getSensorPropertiesInternal( String opPackageName) { - - return FaceService.this.getSensorProperties(); + return mRegistry.getAllProperties(); } @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public FaceSensorPropertiesInternal getSensorProperties(int sensorId, @NonNull String opPackageName) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "No matching sensor for getSensorProperties, sensorId: " + sensorId + ", caller: " + opPackageName); @@ -193,8 +146,7 @@ public class FaceService extends SystemService { @Override // Binder call public void generateChallenge(IBinder token, int sensorId, int userId, IFaceServiceReceiver receiver, String opPackageName) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "No matching sensor for generateChallenge, sensorId: " + sensorId); return; @@ -207,8 +159,7 @@ public class FaceService extends SystemService { @Override // Binder call public void revokeChallenge(IBinder token, int sensorId, int userId, String opPackageName, long challenge) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "No matching sensor for revokeChallenge, sensorId: " + sensorId); return; @@ -222,8 +173,7 @@ public class FaceService extends SystemService { public long enroll(int userId, final IBinder token, final byte[] hardwareAuthToken, final IFaceServiceReceiver receiver, final String opPackageName, final int[] disabledFeatures, Surface previewSurface, boolean debugConsent) { - - final Pair provider = getSingleProvider(); + final Pair provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for enroll"); return -1; @@ -245,8 +195,7 @@ public class FaceService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_BIOMETRIC) @Override // Binder call public void cancelEnrollment(final IBinder token, long requestId) { - - final Pair provider = getSingleProvider(); + final Pair provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for cancelEnrollment"); return; @@ -260,7 +209,6 @@ public class FaceService extends SystemService { public long authenticate(final IBinder token, final long operationId, int userId, final IFaceServiceReceiver receiver, final String opPackageName, boolean isKeyguardBypassEnabled) { - // TODO(b/152413782): If the sensor supports face detect and the device is encrypted or // lockdown, something wrong happened. See similar path in FingerprintService. @@ -273,7 +221,7 @@ public class FaceService extends SystemService { // permission. final boolean isKeyguard = Utils.isKeyguard(getContext(), opPackageName); - final Pair provider = getSingleProvider(); + final Pair provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for authenticate"); return -1; @@ -301,7 +249,7 @@ public class FaceService extends SystemService { return -1; } - final Pair provider = getSingleProvider(); + final Pair provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for detectFace"); return -1; @@ -318,8 +266,7 @@ public class FaceService extends SystemService { IBinder token, long operationId, int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName, long requestId, int cookie, boolean allowBackgroundAuthentication) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for prepareForAuthentication"); return; @@ -336,8 +283,7 @@ public class FaceService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public void startPreparedClient(int sensorId, int cookie) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for startPreparedClient"); return; @@ -350,8 +296,7 @@ public class FaceService extends SystemService { @Override // Binder call public void cancelAuthentication(final IBinder token, final String opPackageName, final long requestId) { - - final Pair provider = getSingleProvider(); + final Pair provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for cancelAuthentication"); return; @@ -370,7 +315,7 @@ public class FaceService extends SystemService { return; } - final Pair provider = getSingleProvider(); + final Pair provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for cancelFaceDetect"); return; @@ -383,8 +328,7 @@ public class FaceService extends SystemService { @Override // Binder call public void cancelAuthenticationFromService(int sensorId, final IBinder token, final String opPackageName, final long requestId) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for cancelAuthenticationFromService"); return; @@ -397,8 +341,7 @@ public class FaceService extends SystemService { @Override // Binder call public void remove(final IBinder token, final int faceId, final int userId, final IFaceServiceReceiver receiver, final String opPackageName) { - - final Pair provider = getSingleProvider(); + final Pair provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for remove"); return; @@ -412,7 +355,6 @@ public class FaceService extends SystemService { @Override // Binder call public void removeAll(final IBinder token, final int userId, final IFaceServiceReceiver receiver, final String opPackageName) { - final FaceServiceReceiver internalReceiver = new FaceServiceReceiver() { int sensorsFinishedRemoving = 0; final int numSensors = getSensorPropertiesInternal( @@ -432,7 +374,7 @@ public class FaceService extends SystemService { // This effectively iterates through all sensors, but has to do so by finding all // sensors under each provider. - for (ServiceProvider provider : mServiceProviders) { + for (ServiceProvider provider : mRegistry.getProviders()) { List props = provider.getSensorProperties(); for (FaceSensorPropertiesInternal prop : props) { provider.scheduleRemoveAll(prop.sensorId, token, userId, internalReceiver, @@ -467,27 +409,27 @@ public class FaceService extends SystemService { try { if (args.length > 1 && "--proto".equals(args[0]) && "--state".equals(args[1])) { final ProtoOutputStream proto = new ProtoOutputStream(fd); - for (ServiceProvider provider : mServiceProviders) { + for (ServiceProvider provider : mRegistry.getProviders()) { for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) { provider.dumpProtoState(props.sensorId, proto, false); } } proto.flush(); } else if (args.length > 0 && "--proto".equals(args[0])) { - for (ServiceProvider provider : mServiceProviders) { + for (ServiceProvider provider : mRegistry.getProviders()) { for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) { provider.dumpProtoMetrics(props.sensorId, fd); } } } else if (args.length > 1 && "--hal".equals(args[0])) { - for (ServiceProvider provider : mServiceProviders) { + for (ServiceProvider provider : mRegistry.getProviders()) { for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) { provider.dumpHal(props.sensorId, fd, Arrays.copyOfRange(args, 1, args.length, args.getClass())); } } } else { - for (ServiceProvider provider : mServiceProviders) { + for (ServiceProvider provider : mRegistry.getProviders()) { for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) { pw.println("Dumping for sensorId: " + props.sensorId + ", provider: " + provider.getClass().getSimpleName()); @@ -504,10 +446,9 @@ public class FaceService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public boolean isHardwareDetected(int sensorId, String opPackageName) { - final long token = Binder.clearCallingIdentity(); try { - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for isHardwareDetected, caller: " + opPackageName); return false; @@ -521,12 +462,11 @@ public class FaceService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public List getEnrolledFaces(int sensorId, int userId, String opPackageName) { - if (userId != UserHandle.getCallingUserId()) { Utils.checkPermission(getContext(), INTERACT_ACROSS_USERS); } - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for getEnrolledFaces, caller: " + opPackageName); return Collections.emptyList(); @@ -538,12 +478,11 @@ public class FaceService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public boolean hasEnrolledFaces(int sensorId, int userId, String opPackageName) { - if (userId != UserHandle.getCallingUserId()) { Utils.checkPermission(getContext(), INTERACT_ACROSS_USERS); } - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for hasEnrolledFaces, caller: " + opPackageName); return false; @@ -555,8 +494,7 @@ public class FaceService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public @LockoutTracker.LockoutMode int getLockoutModeForUser(int sensorId, int userId) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for getLockoutModeForUser"); return LockoutTracker.LOCKOUT_NONE; @@ -569,8 +507,7 @@ public class FaceService extends SystemService { @Override public void invalidateAuthenticatorId(int sensorId, int userId, IInvalidationCallback callback) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for invalidateAuthenticatorId"); return; @@ -582,7 +519,7 @@ public class FaceService extends SystemService { @Override // Binder call public long getAuthenticatorId(int sensorId, int userId) { - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for getAuthenticatorId"); return 0; @@ -595,8 +532,7 @@ public class FaceService extends SystemService { @Override // Binder call public void resetLockout(IBinder token, int sensorId, int userId, byte[] hardwareAuthToken, String opPackageName) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for resetLockout, caller: " + opPackageName); return; @@ -610,8 +546,7 @@ public class FaceService extends SystemService { public void setFeature(final IBinder token, int userId, int feature, boolean enabled, final byte[] hardwareAuthToken, IFaceServiceReceiver receiver, final String opPackageName) { - - final Pair provider = getSingleProvider(); + final Pair provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for setFeature"); return; @@ -625,8 +560,7 @@ public class FaceService extends SystemService { @Override public void getFeature(final IBinder token, int userId, int feature, IFaceServiceReceiver receiver, final String opPackageName) { - - final Pair provider = getSingleProvider(); + final Pair provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for getFeature"); return; @@ -636,18 +570,14 @@ public class FaceService extends SystemService { new ClientMonitorCallbackConverter(receiver), opPackageName); } - private void addHidlProviders(@NonNull List hidlSensors) { - for (FaceSensorPropertiesInternal hidlSensor : hidlSensors) { - mServiceProviders.add( - Face10.newInstance(getContext(), hidlSensor, mLockoutResetDispatcher)); - } - } + private List getAidlProviders() { + final List providers = new ArrayList<>(); - private void addAidlProviders() { final String[] instances = ServiceManager.getDeclaredInstances(IFace.DESCRIPTOR); if (instances == null || instances.length == 0) { - return; + return providers; } + for (String instance : instances) { final String fqName = IFace.DESCRIPTOR + "/" + instance; final IFace face = IFace.Stub.asInterface( @@ -660,53 +590,41 @@ public class FaceService extends SystemService { final SensorProps[] props = face.getSensorProps(); final FaceProvider provider = new FaceProvider(getContext(), props, instance, mLockoutResetDispatcher, BiometricContext.getInstance(getContext())); - mServiceProviders.add(provider); + providers.add(provider); } catch (RemoteException e) { Slog.e(TAG, "Remote exception in getSensorProps: " + fqName); } } + + return providers; } @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public void registerAuthenticators( @NonNull List hidlSensors) { - - // Some HAL might not be started before the system service and will cause the code below - // to wait, and some of the operations below might take a significant amount of time to - // complete (calls to the HALs). To avoid blocking the rest of system server we put - // this on a background thread. - final ServiceThread thread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND, - true /* allowIo */); - thread.start(); - final Handler handler = new Handler(thread.getLooper()); - - handler.post(() -> { - addHidlProviders(hidlSensors); - addAidlProviders(); - - final IBiometricService biometricService = IBiometricService.Stub.asInterface( - ServiceManager.getService(Context.BIOMETRIC_SERVICE)); - - // Register each sensor individually with BiometricService - for (ServiceProvider provider : mServiceProviders) { - final List props = provider.getSensorProperties(); - for (FaceSensorPropertiesInternal prop : props) { - final int sensorId = prop.sensorId; - final @BiometricManager.Authenticators.Types int strength = - Utils.propertyStrengthToAuthenticatorStrength(prop.sensorStrength); - final FaceAuthenticator authenticator = new FaceAuthenticator( - mServiceWrapper, sensorId); - try { - biometricService.registerAuthenticator(sensorId, TYPE_FACE, strength, - authenticator); - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception when registering sensorId: " + sensorId); - } - } + mRegistry.registerAll(() -> { + final List providers = new ArrayList<>(); + for (FaceSensorPropertiesInternal hidlSensor : hidlSensors) { + providers.add( + Face10.newInstance(getContext(), hidlSensor, mLockoutResetDispatcher)); } + providers.addAll(getAidlProviders()); + return providers; }); } + + @Override + public void addAuthenticatorsRegisteredCallback( + IFaceAuthenticatorsRegisteredCallback callback) { + Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); + mRegistry.addAllRegisteredCallback(callback); + } + + @Override + public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) { + mBiometricStateCallback.registerBiometricStateListener(listener); + } } public FaceService(Context context) { @@ -714,7 +632,16 @@ public class FaceService extends SystemService { mServiceWrapper = new FaceServiceWrapper(); mLockoutResetDispatcher = new LockoutResetDispatcher(context); mLockPatternUtils = new LockPatternUtils(context); - mServiceProviders = new ArrayList<>(); + mBiometricStateCallback = new BiometricStateCallback<>(UserManager.get(context)); + mRegistry = new FaceServiceRegistry(mServiceWrapper, + () -> IBiometricService.Stub.asInterface( + ServiceManager.getService(Context.BIOMETRIC_SERVICE))); + mRegistry.addAllRegisteredCallback(new IFaceAuthenticatorsRegisteredCallback.Stub() { + @Override + public void onAllAuthenticatorsRegistered(List sensors) { + mBiometricStateCallback.start(mRegistry.getProviders()); + } + }); } @Override @@ -752,7 +679,7 @@ public class FaceService extends SystemService { if (Utils.isVirtualEnabled(getContext())) { Slog.i(TAG, "Sync virtual enrollments"); final int userId = ActivityManager.getCurrentUser(); - for (ServiceProvider provider : mServiceProviders) { + for (ServiceProvider provider : mRegistry.getProviders()) { for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) { provider.scheduleInternalCleanup(props.sensorId, userId, null /* callback */, true /* favorHalEnrollments */); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceServiceRegistry.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceServiceRegistry.java new file mode 100644 index 0000000000000..0f0a81d244733 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceServiceRegistry.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.face; + +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.hardware.biometrics.BiometricManager; +import android.hardware.biometrics.IBiometricService; +import android.hardware.face.FaceSensorPropertiesInternal; +import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; +import android.hardware.face.IFaceService; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.server.biometrics.Utils; +import com.android.server.biometrics.sensors.BiometricServiceRegistry; + +import java.util.List; +import java.util.function.Supplier; + +/** Registry for {@link IFaceService} providers. */ +public class FaceServiceRegistry extends BiometricServiceRegistry { + + private static final String TAG = "FaceServiceRegistry"; + + @NonNull + private final IFaceService mService; + + /** Creates a new registry tied to the given service. */ + public FaceServiceRegistry(@NonNull IFaceService service, + @Nullable Supplier biometricSupplier) { + super(biometricSupplier); + mService = service; + } + + @Override + protected void registerService(@NonNull IBiometricService service, + @NonNull FaceSensorPropertiesInternal props) { + @BiometricManager.Authenticators.Types final int strength = + Utils.propertyStrengthToAuthenticatorStrength(props.sensorStrength); + try { + service.registerAuthenticator(props.sensorId, TYPE_FACE, strength, + new FaceAuthenticator(mService, props.sensorId)); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception when registering sensorId: " + props.sensorId); + } + } + + @Override + protected void invokeRegisteredCallback(@NonNull IFaceAuthenticatorsRegisteredCallback callback, + @NonNull List allProps) throws RemoteException { + callback.onAllAuthenticatorsRegistered(allProps); + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java index 6f98365332e22..4efaedbd55301 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java @@ -26,15 +26,13 @@ import android.hardware.face.FaceManager; import android.hardware.face.FaceSensorPropertiesInternal; import android.hardware.face.IFaceServiceReceiver; import android.os.IBinder; -import android.util.proto.ProtoOutputStream; import android.view.Surface; +import com.android.server.biometrics.sensors.BiometricServiceProvider; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; -import com.android.server.biometrics.sensors.LockoutTracker; import java.io.FileDescriptor; -import java.io.PrintWriter; import java.util.List; /** @@ -56,24 +54,11 @@ import java.util.List; * to check (e.g. via {@link FaceManager#getSensorPropertiesInternal()}) that the code path isn't * taken. ServiceProviders will provide a no-op for unsupported operations to fail safely. */ -public interface ServiceProvider { - /** - * Checks if the specified sensor is owned by this provider. - */ - boolean containsSensor(int sensorId); - - @NonNull - List getSensorProperties(); - - @NonNull - FaceSensorPropertiesInternal getSensorProperties(int sensorId); +public interface ServiceProvider extends BiometricServiceProvider { @NonNull List getEnrolledFaces(int sensorId, int userId); - @LockoutTracker.LockoutMode - int getLockoutModeForUser(int sensorId, int userId); - /** * Requests for the authenticatorId (whose source of truth is in the TEE or equivalent) to be * invalidated. See {@link com.android.server.biometrics.sensors.InvalidationRequesterClient} @@ -84,10 +69,6 @@ public interface ServiceProvider { + " this method"); } - long getAuthenticatorId(int sensorId, int userId); - - boolean isHardwareDetected(int sensorId); - void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token, @NonNull IFaceServiceReceiver receiver, String opPackageName); @@ -142,13 +123,6 @@ public interface ServiceProvider { void scheduleInternalCleanup(int sensorId, int userId, @Nullable ClientMonitorCallback callback, boolean favorHalEnrollments); - void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto, - boolean clearSchedulerBuffer); - - void dumpProtoMetrics(int sensorId, @NonNull FileDescriptor fd); - - void dumpInternal(int sensorId, @NonNull PrintWriter pw); - @NonNull ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback, @NonNull String opPackageName); 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 19d54c84c7067..6bff179e8eb75 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 @@ -284,6 +284,11 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { return FaceUtils.getInstance(sensorId).getBiometricsForUser(mContext, userId); } + @Override + public boolean hasEnrollments(int sensorId, int userId) { + return !getEnrolledFaces(sensorId, userId).isEmpty(); + } + @Override public void scheduleInvalidateAuthenticatorId(int sensorId, int userId, @NonNull IInvalidationCallback callback) { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java index 65289122747cc..c0a119ff5f1e7 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java @@ -483,6 +483,11 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { return FaceUtils.getLegacyInstance(mSensorId).getBiometricsForUser(mContext, userId); } + @Override + public boolean hasEnrollments(int sensorId, int userId) { + return !getEnrolledFaces(sensorId, userId).isEmpty(); + } + @Override @LockoutTracker.LockoutMode public int getLockoutModeForUser(int sensorId, int userId) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index 2ba449ae089e9..7e2742edd47aa 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -17,14 +17,11 @@ package com.android.server.biometrics.sensors.fingerprint; import static android.Manifest.permission.INTERACT_ACROSS_USERS; -import static android.Manifest.permission.MANAGE_BIOMETRIC; import static android.Manifest.permission.MANAGE_FINGERPRINT; -import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT; import static android.Manifest.permission.TEST_BIOMETRIC; import static android.Manifest.permission.USE_BIOMETRIC; import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; import static android.Manifest.permission.USE_FINGERPRINT; -import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_USER_CANCELED; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_VENDOR; @@ -36,8 +33,6 @@ import android.app.ActivityManager; import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; -import android.content.pm.UserInfo; -import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricPrompt; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.IBiometricSensorReceiver; @@ -65,7 +60,6 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Process; -import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; @@ -79,11 +73,9 @@ import android.util.Slog; import android.util.proto.ProtoOutputStream; import com.android.internal.R; -import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; import com.android.internal.widget.LockPatternUtils; -import com.android.server.ServiceThread; import com.android.server.SystemService; import com.android.server.biometrics.Utils; import com.android.server.biometrics.log.BiometricContext; @@ -115,74 +107,32 @@ public class FingerprintService extends SystemService { protected static final String TAG = "FingerprintService"; - private final Object mLock = new Object(); private final AppOpsManager mAppOps; private final LockoutResetDispatcher mLockoutResetDispatcher; private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher; private final LockPatternUtils mLockPatternUtils; - @NonNull private final List mServiceProviders; - @NonNull private final BiometricStateCallback mBiometricStateCallback; - @NonNull private final Handler mHandler; - @NonNull private final BiometricContext mBiometricContext; - @NonNull private final Supplier mBiometricServiceSupplier; - @NonNull private final Function mIFingerprintProvider; + @NonNull + private final BiometricContext mBiometricContext; + @NonNull + private final Supplier mAidlInstanceNameSupplier; + @NonNull + private final Function mIFingerprintProvider; + @NonNull + private final BiometricStateCallback + mBiometricStateCallback; + @NonNull + private final Handler mHandler; + @NonNull + private final FingerprintServiceRegistry mRegistry; - @GuardedBy("mLock") - @NonNull private final RemoteCallbackList - mAuthenticatorsRegisteredCallbacks; - - @GuardedBy("mLock") - @NonNull private final List mSensorProps; - - /** - * Registers BiometricStateListener in list stored by FingerprintService - * @param listener new BiometricStateListener being added - */ - public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) { - mBiometricStateCallback.registerBiometricStateListener(listener); - broadcastCurrentEnrollmentState(listener); - } - - /** - * @param listener if non-null, notifies only this listener. if null, notifies all listeners - * in {@link BiometricStateCallback}. This is slightly ugly, but reduces - * redundant code. - */ - private void broadcastCurrentEnrollmentState(@Nullable IBiometricStateListener listener) { - final UserManager um = UserManager.get(getContext()); - synchronized (mLock) { - // Update the new listener with current state of all sensors - for (FingerprintSensorPropertiesInternal prop : mSensorProps) { - final ServiceProvider provider = getProviderForSensor(prop.sensorId); - for (UserInfo userInfo : um.getAliveUsers()) { - final boolean enrolled = !provider - .getEnrolledFingerprints(prop.sensorId, userInfo.id).isEmpty(); - - // Defer this work and allow the loop to release the lock sooner - mHandler.post(() -> { - if (listener != null) { - mBiometricStateCallback.notifyEnrollmentStateChanged( - listener, userInfo.id, prop.sensorId, enrolled); - } else { - mBiometricStateCallback.notifyAllEnrollmentStateChanged( - userInfo.id, prop.sensorId, enrolled); - } - }); - } - } - } - } - - /** - * Receives the incoming binder calls from FingerprintManager. - */ - private final IFingerprintService.Stub mServiceWrapper = new IFingerprintService.Stub() { + /** Receives the incoming binder calls from FingerprintManager. */ + @VisibleForTesting + final IFingerprintService.Stub mServiceWrapper = new IFingerprintService.Stub() { @android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC) @Override public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback, @NonNull String opPackageName) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for createTestSession, sensorId: " + sensorId); @@ -195,9 +145,8 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public byte[] dumpSensorServiceStateProto(int sensorId, boolean clearSchedulerBuffer) { - final ProtoOutputStream proto = new ProtoOutputStream(); - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider != null) { provider.dumpProtoState(sensorId, proto, clearSchedulerBuffer); } @@ -212,16 +161,14 @@ public class FingerprintService extends SystemService { != PackageManager.PERMISSION_GRANTED) { Utils.checkPermission(getContext(), TEST_BIOMETRIC); } - - return FingerprintService.this.getSensorProperties(); + return mRegistry.getAllProperties(); } @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public FingerprintSensorPropertiesInternal getSensorProperties(int sensorId, @NonNull String opPackageName) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "No matching sensor for getSensorProperties, sensorId: " + sensorId + ", caller: " + opPackageName); @@ -234,8 +181,7 @@ public class FingerprintService extends SystemService { @Override // Binder call public void generateChallenge(IBinder token, int sensorId, int userId, IFingerprintServiceReceiver receiver, String opPackageName) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "No matching sensor for generateChallenge, sensorId: " + sensorId); return; @@ -248,8 +194,7 @@ public class FingerprintService extends SystemService { @Override // Binder call public void revokeChallenge(IBinder token, int sensorId, int userId, String opPackageName, long challenge) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "No matching sensor for revokeChallenge, sensorId: " + sensorId); return; @@ -264,8 +209,7 @@ public class FingerprintService extends SystemService { public long enroll(final IBinder token, @NonNull final byte[] hardwareAuthToken, final int userId, final IFingerprintServiceReceiver receiver, final String opPackageName, @FingerprintManager.EnrollReason int enrollReason) { - - final Pair provider = getSingleProvider(); + final Pair provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for enroll"); return -1; @@ -278,8 +222,7 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_FINGERPRINT) @Override // Binder call public void cancelEnrollment(final IBinder token, long requestId) { - - final Pair provider = getSingleProvider(); + final Pair provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for cancelEnrollment"); return; @@ -339,10 +282,10 @@ public class FingerprintService extends SystemService { final Pair provider; if (sensorId == FingerprintManager.SENSOR_ID_ANY) { - provider = getSingleProvider(); + provider = mRegistry.getSingleProvider(); } else { Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); - provider = new Pair<>(sensorId, getProviderForSensor(sensorId)); + provider = new Pair<>(sensorId, mRegistry.getProviderForSensor(sensorId)); } if (provider == null) { Slog.w(TAG, "Null provider for authenticate"); @@ -374,7 +317,6 @@ public class FingerprintService extends SystemService { final IFingerprintServiceReceiver receiver, final String opPackageName, boolean ignoreEnrollmentState) throws PackageManager.NameNotFoundException { - final Context context = getUiContext(); final Context promptContext = context.createPackageContextAsUser( opPackageName, 0 /* flags */, UserHandle.getUserHandleForUid(uId)); @@ -468,7 +410,7 @@ public class FingerprintService extends SystemService { return -1; } - final Pair provider = getSingleProvider(); + final Pair provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for detectFingerprint"); return -1; @@ -484,8 +426,7 @@ public class FingerprintService extends SystemService { public void prepareForAuthentication(int sensorId, IBinder token, long operationId, int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName, long requestId, int cookie, boolean allowBackgroundAuthentication) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for prepareForAuthentication"); return; @@ -501,8 +442,7 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_BIOMETRIC) @Override // Binder call public void startPreparedClient(int sensorId, int cookie) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for startPreparedClient"); return; @@ -532,7 +472,7 @@ public class FingerprintService extends SystemService { return; } - final Pair provider = getSingleProvider(); + final Pair provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for cancelAuthentication"); return; @@ -553,7 +493,7 @@ public class FingerprintService extends SystemService { // For IBiometricsFingerprint2.1, cancelling fingerprint detect is the same as // cancelling authentication. - final Pair provider = getSingleProvider(); + final Pair provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for cancelFingerprintDetect"); return; @@ -566,11 +506,9 @@ public class FingerprintService extends SystemService { @Override // Binder call public void cancelAuthenticationFromService(final int sensorId, final IBinder token, final String opPackageName, final long requestId) { - - Slog.d(TAG, "cancelAuthenticationFromService, sensorId: " + sensorId); - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for cancelAuthenticationFromService"); return; @@ -583,8 +521,7 @@ public class FingerprintService extends SystemService { @Override // Binder call public void remove(final IBinder token, final int fingerId, final int userId, final IFingerprintServiceReceiver receiver, final String opPackageName) { - - final Pair provider = getSingleProvider(); + final Pair provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for remove"); return; @@ -617,7 +554,7 @@ public class FingerprintService extends SystemService { // This effectively iterates through all sensors, but has to do so by finding all // sensors under each provider. - for (ServiceProvider provider : mServiceProviders) { + for (ServiceProvider provider : mRegistry.getProviders()) { List props = provider.getSensorProperties(); for (FingerprintSensorPropertiesInternal prop : props) { provider.scheduleRemoveAll(prop.sensorId, token, internalReceiver, userId, @@ -652,7 +589,7 @@ public class FingerprintService extends SystemService { try { if (args.length > 1 && "--proto".equals(args[0]) && "--state".equals(args[1])) { final ProtoOutputStream proto = new ProtoOutputStream(fd); - for (ServiceProvider provider : mServiceProviders) { + for (ServiceProvider provider : mRegistry.getProviders()) { for (FingerprintSensorPropertiesInternal props : provider.getSensorProperties()) { provider.dumpProtoState(props.sensorId, proto, false); @@ -660,14 +597,14 @@ public class FingerprintService extends SystemService { } proto.flush(); } else if (args.length > 0 && "--proto".equals(args[0])) { - for (ServiceProvider provider : mServiceProviders) { + for (ServiceProvider provider : mRegistry.getProviders()) { for (FingerprintSensorPropertiesInternal props : provider.getSensorProperties()) { provider.dumpProtoMetrics(props.sensorId, fd); } } } else { - for (ServiceProvider provider : mServiceProviders) { + for (ServiceProvider provider : mRegistry.getProviders()) { for (FingerprintSensorPropertiesInternal props : provider.getSensorProperties()) { pw.println("Dumping for sensorId: " + props.sensorId @@ -698,7 +635,7 @@ public class FingerprintService extends SystemService { final long token = Binder.clearCallingIdentity(); try { - final Pair provider = getSingleProvider(); + final Pair provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for isHardwareDetectedDeprecated, caller: " + opPackageName); @@ -713,8 +650,7 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public boolean isHardwareDetected(int sensorId, String opPackageName) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for isHardwareDetected, caller: " + opPackageName); return false; @@ -730,7 +666,7 @@ public class FingerprintService extends SystemService { return; } - final Pair provider = getSingleProvider(); + final Pair provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for rename"); return; @@ -781,8 +717,7 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) public boolean hasEnrolledFingerprints(int sensorId, int userId, String opPackageName) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for hasEnrolledFingerprints, caller: " + opPackageName); return false; @@ -794,8 +729,7 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public @LockoutTracker.LockoutMode int getLockoutModeForUser(int sensorId, int userId) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for getLockoutModeForUser"); return LockoutTracker.LOCKOUT_NONE; @@ -807,8 +741,7 @@ public class FingerprintService extends SystemService { @Override public void invalidateAuthenticatorId(int sensorId, int userId, IInvalidationCallback callback) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for invalidateAuthenticatorId"); return; @@ -819,8 +752,7 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public long getAuthenticatorId(int sensorId, int userId) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for getAuthenticatorId"); return 0; @@ -832,8 +764,7 @@ public class FingerprintService extends SystemService { @Override // Binder call public void resetLockout(IBinder token, int sensorId, int userId, @Nullable byte[] hardwareAuthToken, String opPackageName) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for resetLockout, caller: " + opPackageName); return; @@ -864,55 +795,38 @@ public class FingerprintService extends SystemService { @Override // Binder call public void registerAuthenticators( @NonNull List hidlSensors) { - - // Some HAL might not be started before the system service and will cause the code below - // to wait, and some of the operations below might take a significant amount of time to - // complete (calls to the HALs). To avoid blocking the rest of system server we put - // this on a background thread. - final ServiceThread thread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND, - true /* allowIo */); - thread.start(); - final Handler handler = new Handler(thread.getLooper()); - handler.post(() -> { + mRegistry.registerAll(() -> { + final List providers = new ArrayList<>(); + providers.addAll(getHidlProviders(hidlSensors)); List aidlSensors = new ArrayList<>(); - final String[] instances = - ServiceManager.getDeclaredInstances(IFingerprint.DESCRIPTOR); + final String[] instances = mAidlInstanceNameSupplier.get(); if (instances != null) { aidlSensors.addAll(Lists.newArrayList(instances)); } - registerAuthenticatorsForService(aidlSensors, hidlSensors); + providers.addAll(getAidlProviders( + Utils.filterAvailableHalInstances(getContext(), aidlSensors))); + return providers; }); - thread.quitSafely(); } @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public void addAuthenticatorsRegisteredCallback( IFingerprintAuthenticatorsRegisteredCallback callback) { - if (callback == null) { - Slog.e(TAG, "addAuthenticatorsRegisteredCallback, callback is null"); - return; - } + mRegistry.addAllRegisteredCallback(callback); + } - final boolean registered; - final boolean hasSensorProps; - synchronized (mLock) { - registered = mAuthenticatorsRegisteredCallbacks.register(callback); - hasSensorProps = !mSensorProps.isEmpty(); - } - if (registered && hasSensorProps) { - broadcastAllAuthenticatorsRegistered(); - } else if (!registered) { - Slog.e(TAG, "addAuthenticatorsRegisteredCallback failed to register callback"); - } + @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) + @Override + public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) { + mBiometricStateCallback.registerBiometricStateListener(listener); } @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public void onPointerDown(long requestId, int sensorId, int x, int y, float minor, float major) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "No matching provider for onFingerDown, sensorId: " + sensorId); return; @@ -923,8 +837,7 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public void onPointerUp(long requestId, int sensorId) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "No matching provider for onFingerUp, sensorId: " + sensorId); return; @@ -935,8 +848,7 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public void onUiReady(long requestId, int sensorId) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "No matching provider for onUiReady, sensorId: " + sensorId); return; @@ -947,8 +859,7 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller) { - - for (ServiceProvider provider : mServiceProviders) { + for (ServiceProvider provider : mRegistry.getProviders()) { provider.setUdfpsOverlayController(controller); } } @@ -956,22 +867,15 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public void setSidefpsController(@NonNull ISidefpsController controller) { - - for (ServiceProvider provider : mServiceProviders) { + for (ServiceProvider provider : mRegistry.getProviders()) { provider.setSidefpsController(controller); } } - @Override - public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) { - Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); - FingerprintService.this.registerBiometricStateListener(listener); - } - + @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public void onPowerPressed() { - Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); - for (ServiceProvider provider : mServiceProviders) { + for (ServiceProvider provider : mRegistry.getProviders()) { provider.onPowerPressed(); } } @@ -981,6 +885,7 @@ public class FingerprintService extends SystemService { this(context, BiometricContext.getInstance(context), () -> IBiometricService.Stub.asInterface( ServiceManager.getService(Context.BIOMETRIC_SERVICE)), + () -> ServiceManager.getDeclaredInstances(IFingerprint.DESCRIPTOR), (fqName) -> IFingerprint.Stub.asInterface( Binder.allowBlocking(ServiceManager.waitForDeclaredService(fqName)))); } @@ -988,61 +893,34 @@ public class FingerprintService extends SystemService { @VisibleForTesting FingerprintService(Context context, BiometricContext biometricContext, - Supplier biometricServiceProvider, + Supplier biometricServiceSupplier, + Supplier aidlInstanceNameSupplier, Function fingerprintProvider) { super(context); mBiometricContext = biometricContext; - mBiometricServiceSupplier = biometricServiceProvider; + mAidlInstanceNameSupplier = aidlInstanceNameSupplier; mIFingerprintProvider = fingerprintProvider; mAppOps = context.getSystemService(AppOpsManager.class); mGestureAvailabilityDispatcher = new GestureAvailabilityDispatcher(); mLockoutResetDispatcher = new LockoutResetDispatcher(context); mLockPatternUtils = new LockPatternUtils(context); - mServiceProviders = new ArrayList<>(); - mBiometricStateCallback = new BiometricStateCallback(); - mAuthenticatorsRegisteredCallbacks = new RemoteCallbackList<>(); - mSensorProps = new ArrayList<>(); + mBiometricStateCallback = new BiometricStateCallback<>(UserManager.get(context)); mHandler = new Handler(Looper.getMainLooper()); + mRegistry = new FingerprintServiceRegistry(mServiceWrapper, biometricServiceSupplier); + mRegistry.addAllRegisteredCallback(new IFingerprintAuthenticatorsRegisteredCallback.Stub() { + @Override + public void onAllAuthenticatorsRegistered( + List sensors) { + mBiometricStateCallback.start(mRegistry.getProviders()); + } + }); } - @VisibleForTesting - void registerAuthenticatorsForService(@NonNull List aidlInstanceNames, + @NonNull + private List getHidlProviders( @NonNull List hidlSensors) { - addHidlProviders(hidlSensors); - addAidlProviders(Utils.filterAvailableHalInstances(getContext(), aidlInstanceNames)); + final List providers = new ArrayList<>(); - final IBiometricService biometricService = mBiometricServiceSupplier.get(); - - // Register each sensor individually with BiometricService - for (ServiceProvider provider : mServiceProviders) { - final List props = - provider.getSensorProperties(); - for (FingerprintSensorPropertiesInternal prop : props) { - final int sensorId = prop.sensorId; - @BiometricManager.Authenticators.Types final int strength = - Utils.propertyStrengthToAuthenticatorStrength(prop.sensorStrength); - final FingerprintAuthenticator authenticator = new FingerprintAuthenticator( - mServiceWrapper, sensorId); - try { - biometricService.registerAuthenticator(sensorId, TYPE_FINGERPRINT, - strength, authenticator); - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception when registering sensorId: " + sensorId); - } - } - } - - synchronized (mLock) { - for (ServiceProvider provider : mServiceProviders) { - mSensorProps.addAll(provider.getSensorProperties()); - } - } - - broadcastCurrentEnrollmentState(null); // broadcasts to all listeners - broadcastAllAuthenticatorsRegistered(); - } - - private void addHidlProviders(List hidlSensors) { for (FingerprintSensorPropertiesInternal hidlSensor : hidlSensors) { final Fingerprint21 fingerprint21; if ((Build.IS_USERDEBUG || Build.IS_ENG) @@ -1059,11 +937,16 @@ public class FingerprintService extends SystemService { mBiometricStateCallback, hidlSensor, mHandler, mLockoutResetDispatcher, mGestureAvailabilityDispatcher); } - mServiceProviders.add(fingerprint21); + providers.add(fingerprint21); } + + return providers; } - private void addAidlProviders(List instances) { + @NonNull + private List getAidlProviders(@NonNull List instances) { + final List providers = new ArrayList<>(); + for (String instance : instances) { final String fqName = IFingerprint.DESCRIPTOR + "/" + instance; final IFingerprint fp = mIFingerprintProvider.apply(fqName); @@ -1075,7 +958,7 @@ public class FingerprintService extends SystemService { mLockoutResetDispatcher, mGestureAvailabilityDispatcher, mBiometricContext); Slog.i(TAG, "Adding AIDL provider: " + fqName); - mServiceProviders.add(provider); + providers.add(provider); } catch (RemoteException e) { Slog.e(TAG, "Remote exception in getSensorProps: " + fqName); } @@ -1083,38 +966,8 @@ public class FingerprintService extends SystemService { Slog.e(TAG, "Unable to get declared service: " + fqName); } } - } - // Notifies the callbacks that all of the authenticators have been registered and removes the - // invoked callbacks from the callback list. - private void broadcastAllAuthenticatorsRegistered() { - // Make a local copy of the data so it can be used outside of the synchronized block when - // making Binder calls. - final List callbacks = new ArrayList<>(); - final List props; - synchronized (mLock) { - if (!mSensorProps.isEmpty()) { - props = new ArrayList<>(mSensorProps); - } else { - Slog.e(TAG, "mSensorProps is empty"); - return; - } - final int n = mAuthenticatorsRegisteredCallbacks.beginBroadcast(); - for (int i = 0; i < n; ++i) { - final IFingerprintAuthenticatorsRegisteredCallback cb = - mAuthenticatorsRegisteredCallbacks.getBroadcastItem(i); - callbacks.add(cb); - mAuthenticatorsRegisteredCallbacks.unregister(cb); - } - mAuthenticatorsRegisteredCallbacks.finishBroadcast(); - } - for (IFingerprintAuthenticatorsRegisteredCallback cb : callbacks) { - try { - cb.onAllAuthenticatorsRegistered(props); - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception in onAllAuthenticatorsRegistered", e); - } - } + return providers; } @Override @@ -1122,51 +975,9 @@ public class FingerprintService extends SystemService { publishBinderService(Context.FINGERPRINT_SERVICE, mServiceWrapper); } - @Nullable - private ServiceProvider getProviderForSensor(int sensorId) { - for (ServiceProvider provider : mServiceProviders) { - if (provider.containsSensor(sensorId)) { - return provider; - } - } - return null; - } - - /** - * For devices with only a single provider, returns that provider. If multiple providers, - * returns the first one. If no providers, returns null. - */ - @Nullable - private Pair getSingleProvider() { - final List properties = getSensorProperties(); - if (properties.isEmpty()) { - Slog.e(TAG, "No providers found"); - return null; - } - - // Theoretically we can just return the first provider, but maybe this is easier to - // understand. - final int sensorId = properties.get(0).sensorId; - for (ServiceProvider provider : mServiceProviders) { - if (provider.containsSensor(sensorId)) { - return new Pair<>(sensorId, provider); - } - } - - Slog.e(TAG, "Provider not found"); - return null; - } - - @NonNull - private List getSensorProperties() { - synchronized (mLock) { - return mSensorProps; - } - } - @NonNull private List getEnrolledFingerprintsDeprecated(int userId, String opPackageName) { - final Pair provider = getSingleProvider(); + final Pair provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for getEnrolledFingerprintsDeprecated, caller: " + opPackageName); @@ -1229,7 +1040,7 @@ public class FingerprintService extends SystemService { if (Utils.isVirtualEnabled(getContext())) { Slog.i(TAG, "Sync virtual enrollments"); final int userId = ActivityManager.getCurrentUser(); - for (ServiceProvider provider : mServiceProviders) { + for (ServiceProvider provider : mRegistry.getProviders()) { for (FingerprintSensorPropertiesInternal props : provider.getSensorProperties()) { provider.scheduleInternalCleanup(props.sensorId, userId, null /* callback */, true /* favorHalEnrollments */); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistry.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistry.java new file mode 100644 index 0000000000000..33810b764f234 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistry.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.fingerprint; + +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.hardware.biometrics.BiometricManager; +import android.hardware.biometrics.IBiometricService; +import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; +import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback; +import android.hardware.fingerprint.IFingerprintService; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.server.biometrics.Utils; +import com.android.server.biometrics.sensors.BiometricServiceRegistry; + +import java.util.List; +import java.util.function.Supplier; + +/** Registry for {@link IFingerprintService} providers. */ +public class FingerprintServiceRegistry extends BiometricServiceRegistry { + + private static final String TAG = "FingerprintServiceRegistry"; + + @NonNull + private final IFingerprintService mService; + + /** Creates a new registry tied to the given service. */ + public FingerprintServiceRegistry(@NonNull IFingerprintService service, + @Nullable Supplier biometricSupplier) { + super(biometricSupplier); + mService = service; + } + + @Override + protected void registerService(@NonNull IBiometricService service, + @NonNull FingerprintSensorPropertiesInternal props) { + @BiometricManager.Authenticators.Types final int strength = + Utils.propertyStrengthToAuthenticatorStrength(props.sensorStrength); + try { + service.registerAuthenticator(props.sensorId, TYPE_FINGERPRINT, strength, + new FingerprintAuthenticator(mService, props.sensorId)); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception when registering sensorId: " + props.sensorId); + } + } + + @Override + protected void invokeRegisteredCallback( + @NonNull IFingerprintAuthenticatorsRegisteredCallback callback, + @NonNull List allProps) throws RemoteException { + callback.onAllAuthenticatorsRegistered(allProps); + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java index 275d7e445a75e..9075e7ec20801 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java @@ -28,14 +28,11 @@ import android.hardware.fingerprint.IFingerprintServiceReceiver; import android.hardware.fingerprint.ISidefpsController; import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.IBinder; -import android.util.proto.ProtoOutputStream; +import com.android.server.biometrics.sensors.BiometricServiceProvider; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; -import com.android.server.biometrics.sensors.LockoutTracker; -import java.io.FileDescriptor; -import java.io.PrintWriter; import java.util.List; /** @@ -59,23 +56,8 @@ import java.util.List; * fail safely. */ @SuppressWarnings("deprecation") -public interface ServiceProvider { - /** - * Checks if the specified sensor is owned by this provider. - */ - boolean containsSensor(int sensorId); - - @NonNull - List getSensorProperties(); - - /** - * Returns the internal properties of the specified sensor, if owned by this provider. - * - * @param sensorId The ID of a fingerprint sensor, or -1 for any sensor. - * @return An object representing the internal properties of the specified sensor. - */ - @Nullable - FingerprintSensorPropertiesInternal getSensorProperties(int sensorId); +public interface ServiceProvider extends + BiometricServiceProvider { void scheduleResetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken); @@ -126,16 +108,11 @@ public interface ServiceProvider { void scheduleInternalCleanup(int sensorId, int userId, @Nullable ClientMonitorCallback callback, boolean favorHalEnrollments); - boolean isHardwareDetected(int sensorId); - void rename(int sensorId, int fingerId, int userId, @NonNull String name); @NonNull List getEnrolledFingerprints(int sensorId, int userId); - @LockoutTracker.LockoutMode - int getLockoutModeForUser(int sensorId, int userId); - /** * Requests for the authenticatorId (whose source of truth is in the TEE or equivalent) to * be invalidated. See {@link com.android.server.biometrics.sensors.InvalidationRequesterClient} @@ -143,7 +120,6 @@ public interface ServiceProvider { void scheduleInvalidateAuthenticatorId(int sensorId, int userId, @NonNull IInvalidationCallback callback); - long getAuthenticatorId(int sensorId, int userId); void onPointerDown(long requestId, int sensorId, int x, int y, float minor, float major); @@ -161,13 +137,6 @@ public interface ServiceProvider { */ void setSidefpsController(@NonNull ISidefpsController controller); - void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto, - boolean clearSchedulerBuffer); - - void dumpProtoMetrics(int sensorId, @NonNull FileDescriptor fd); - - void dumpInternal(int sensorId, @NonNull PrintWriter pw); - @NonNull ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback, @NonNull String opPackageName); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index 2dc005206b42e..3fe6332fcaa06 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -564,6 +564,11 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi return FingerprintUtils.getInstance(sensorId).getBiometricsForUser(mContext, userId); } + @Override + public boolean hasEnrollments(int sensorId, int userId) { + return !getEnrolledFingerprints(sensorId, userId).isEmpty(); + } + @Override public void scheduleInvalidateAuthenticatorId(int sensorId, int userId, @NonNull IInvalidationCallback callback) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index ed482f013869f..0e6df8e0df77b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -788,6 +788,11 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider return FingerprintUtils.getLegacyInstance(mSensorId).getBiometricsForUser(mContext, userId); } + @Override + public boolean hasEnrollments(int sensorId, int userId) { + return !getEnrolledFingerprints(sensorId, userId).isEmpty(); + } + @Override @LockoutTracker.LockoutMode public int getLockoutModeForUser(int sensorId, int userId) { return mLockoutTracker.getLockoutModeForUser(userId); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceRegistryTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceRegistryTest.java new file mode 100644 index 0000000000000..903ed90824817 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceRegistryTest.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.face; + +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; +import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG; +import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_WEAK; +import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG; +import static android.hardware.biometrics.SensorProperties.STRENGTH_WEAK; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.hardware.biometrics.IBiometricService; +import android.hardware.face.FaceSensorProperties; +import android.hardware.face.FaceSensorPropertiesInternal; +import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; +import android.hardware.face.IFaceService; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.ArrayList; +import java.util.List; + +@Presubmit +@SmallTest +public class FaceServiceRegistryTest { + + private static final int SENSOR_ID_1 = 1; + private static final int SENSOR_ID_2 = 2; + + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + @Mock + private IBiometricService mBiometricService; + @Mock + private IFaceService mFaceService; + @Mock + private ServiceProvider mProvider1; + @Mock + private ServiceProvider mProvider2; + @Captor + private ArgumentCaptor mIdCaptor; + @Captor + private ArgumentCaptor mStrengthCaptor; + + private FaceSensorPropertiesInternal mProvider1Props; + private FaceSensorPropertiesInternal mProvider2Props; + private FaceServiceRegistry mRegistry; + + @Before + public void setup() { + mProvider1Props = new FaceSensorPropertiesInternal(SENSOR_ID_1, + STRENGTH_WEAK, 5 /* maxEnrollmentsPerUser */, + List.of(), FaceSensorProperties.TYPE_RGB, + true /* supportsFace Detection */, + true /* supportsSelfIllumination */, + false /* resetLockoutRequiresHardwareAuthToken */); + mProvider2Props = new FaceSensorPropertiesInternal(SENSOR_ID_2, + STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */, + List.of(), FaceSensorProperties.TYPE_IR, + true /* supportsFace Detection */, + true /* supportsSelfIllumination */, + false /* resetLockoutRequiresHardwareAuthToken */); + + when(mProvider1.getSensorProperties()).thenReturn(List.of(mProvider1Props)); + when(mProvider1.containsSensor(eq(SENSOR_ID_1))).thenReturn(true); + when(mProvider2.getSensorProperties()).thenReturn(List.of(mProvider2Props)); + when(mProvider2.containsSensor(eq(SENSOR_ID_2))).thenReturn(true); + mRegistry = new FaceServiceRegistry(mFaceService, () -> mBiometricService); + } + + @Test + public void registersAllProviders() throws Exception { + mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2)); + + assertThat(mRegistry.getProviders()).containsExactly(mProvider1, mProvider2); + assertThat(mRegistry.getAllProperties()).containsExactly(mProvider1Props, mProvider2Props); + verify(mBiometricService, times(2)).registerAuthenticator( + mIdCaptor.capture(), eq(TYPE_FACE), mStrengthCaptor.capture(), any()); + assertThat(mIdCaptor.getAllValues()).containsExactly(SENSOR_ID_1, SENSOR_ID_2); + assertThat(mStrengthCaptor.getAllValues()) + .containsExactly(BIOMETRIC_WEAK, BIOMETRIC_STRONG); + } + + @Test + public void getsProviderById() { + mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2)); + + assertThat(mRegistry.getProviderForSensor(SENSOR_ID_1)).isSameInstanceAs(mProvider1); + assertThat(mRegistry.getProviderForSensor(SENSOR_ID_2)).isSameInstanceAs(mProvider2); + assertThat(mRegistry.getProviderForSensor(500)).isNull(); + } + + @Test + public void getsSingleProvider() { + mRegistry.registerAllInBackground(() -> List.of(mProvider1)); + + assertThat(mRegistry.getSingleProvider().second).isSameInstanceAs(mProvider1); + assertThat(mRegistry.getProviders()).containsExactly(mProvider1); + assertThat(mRegistry.getProviderForSensor(SENSOR_ID_1)).isSameInstanceAs(mProvider1); + } + + @Test + public void getSingleProviderFindsFirstWhenMultiple() { + mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2)); + + assertThat(mRegistry.getSingleProvider().second).isSameInstanceAs(mProvider1); + } + + @Test + public void registersListenerBeforeAllRegistered() { + final List all = new ArrayList<>(); + mRegistry.addAllRegisteredCallback(new IFaceAuthenticatorsRegisteredCallback.Stub() { + @Override + public void onAllAuthenticatorsRegistered( + List sensors) { + all.addAll(sensors); + } + }); + + mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2)); + + assertThat(all).containsExactly(mProvider1Props, mProvider2Props); + } + + @Test + public void registersListenerAfterAllRegistered() { + mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2)); + + final List all = new ArrayList<>(); + mRegistry.addAllRegisteredCallback(new IFaceAuthenticatorsRegisteredCallback.Stub() { + @Override + public void onAllAuthenticatorsRegistered( + List sensors) { + all.addAll(sensors); + } + }); + + assertThat(all).containsExactly(mProvider1Props, mProvider2Props); + } +} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java index 5f88c99b1d1ee..0e30782eaece9 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java @@ -16,6 +16,8 @@ package com.android.server.biometrics.sensors.fingerprint; +import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG; + import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; @@ -24,37 +26,72 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricStateListener; +import android.hardware.biometrics.SensorPropertiesInternal; +import android.os.UserManager; import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; import com.android.server.biometrics.sensors.AuthenticationClient; +import com.android.server.biometrics.sensors.BiometricServiceProvider; import com.android.server.biometrics.sensors.BiometricStateCallback; import com.android.server.biometrics.sensors.EnrollClient; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.List; @Presubmit @SmallTest public class BiometricStateCallbackTest { - private BiometricStateCallback mCallback; + private static final int USER_ID = 10; + private static final int SENSOR_ID = 2; + + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + private BiometricStateCallback mCallback; @Mock - BiometricStateListener mBiometricStateListener; + private UserManager mUserManager; + @Mock + private BiometricStateListener mBiometricStateListener; + @Mock + private FakeProvider mFakeProvider; + + private SensorPropertiesInternal mFakeProviderProps; @Before public void setup() { - MockitoAnnotations.initMocks(this); + mFakeProviderProps = new SensorPropertiesInternal(SENSOR_ID, STRENGTH_STRONG, + 5 /* maxEnrollmentsPerUser */, List.of(), + false /* resetLockoutRequiresHardwareAuthToken */, + false /* resetLockoutRequiresChallenge */); + when(mFakeProvider.getSensorProperties()).thenReturn(List.of(mFakeProviderProps)); + when(mFakeProvider.getSensorProperties(eq(SENSOR_ID))).thenReturn(mFakeProviderProps); + when(mFakeProvider.hasEnrollments(eq(SENSOR_ID), eq(USER_ID))).thenReturn(true); + when(mUserManager.getAliveUsers()).thenReturn( + List.of(new UserInfo(USER_ID, "name", 0))); - mCallback = new BiometricStateCallback(); + mCallback = new BiometricStateCallback<>(mUserManager); mCallback.registerBiometricStateListener(mBiometricStateListener); } + @Test + public void startNotifiesEnrollments() { + mCallback.start(List.of(mFakeProvider)); + + verify(mBiometricStateListener).onEnrollmentsChanged(eq(USER_ID), eq(SENSOR_ID), eq(true)); + } + @Test public void testNoEnrollmentsToEnrollments_callbackNotified() { testEnrollmentCallback(true /* changed */, true /* isNowEnrolled */, @@ -102,4 +139,6 @@ public class BiometricStateCallbackTest { verify(mBiometricStateListener, never()).onEnrollmentsChanged(anyInt(), anyInt(), anyBoolean()); } + + private interface FakeProvider extends BiometricServiceProvider {} } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistryTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistryTest.java new file mode 100644 index 0000000000000..13c3f64fec933 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistryTest.java @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.fingerprint; + +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; +import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG; +import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_WEAK; +import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG; +import static android.hardware.biometrics.SensorProperties.STRENGTH_WEAK; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.hardware.biometrics.IBiometricService; +import android.hardware.fingerprint.FingerprintSensorProperties; +import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; +import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback; +import android.hardware.fingerprint.IFingerprintService; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.ArrayList; +import java.util.List; + +@Presubmit +@SmallTest +public class FingerprintServiceRegistryTest { + + private static final int SENSOR_ID_1 = 1; + private static final int SENSOR_ID_2 = 2; + + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + @Mock + private IBiometricService mBiometricService; + @Mock + private IFingerprintService mFingerprintService; + @Mock + private ServiceProvider mProvider1; + @Mock + private ServiceProvider mProvider2; + @Captor + private ArgumentCaptor mIdCaptor; + @Captor + private ArgumentCaptor mStrengthCaptor; + + private FingerprintSensorPropertiesInternal mProvider1Props; + private FingerprintSensorPropertiesInternal mProvider2Props; + private FingerprintServiceRegistry mRegistry; + + @Before + public void setup() { + mProvider1Props = new FingerprintSensorPropertiesInternal(SENSOR_ID_1, + STRENGTH_WEAK, 5 /* maxEnrollmentsPerUser */, + List.of(), FingerprintSensorProperties.TYPE_UDFPS_OPTICAL, + false /* resetLockoutRequiresHardwareAuthToken */); + mProvider2Props = new FingerprintSensorPropertiesInternal(SENSOR_ID_2, + STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */, + List.of(), FingerprintSensorProperties.TYPE_UNKNOWN, + false /* resetLockoutRequiresHardwareAuthToken */); + + when(mProvider1.getSensorProperties()).thenReturn(List.of(mProvider1Props)); + when(mProvider1.containsSensor(eq(SENSOR_ID_1))).thenReturn(true); + when(mProvider2.getSensorProperties()).thenReturn(List.of(mProvider2Props)); + when(mProvider2.containsSensor(eq(SENSOR_ID_2))).thenReturn(true); + mRegistry = new FingerprintServiceRegistry(mFingerprintService, () -> mBiometricService); + } + + @Test + public void registersAllProviders() throws Exception { + mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2)); + + assertThat(mRegistry.getProviders()).containsExactly(mProvider1, mProvider2); + assertThat(mRegistry.getAllProperties()).containsExactly(mProvider1Props, mProvider2Props); + verify(mBiometricService, times(2)).registerAuthenticator( + mIdCaptor.capture(), eq(TYPE_FINGERPRINT), mStrengthCaptor.capture(), any()); + assertThat(mIdCaptor.getAllValues()).containsExactly(SENSOR_ID_1, SENSOR_ID_2); + assertThat(mStrengthCaptor.getAllValues()) + .containsExactly(BIOMETRIC_WEAK, BIOMETRIC_STRONG); + } + + @Test + public void getsProviderById() { + mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2)); + + assertThat(mRegistry.getProviderForSensor(SENSOR_ID_1)).isSameInstanceAs(mProvider1); + assertThat(mRegistry.getProviderForSensor(SENSOR_ID_2)).isSameInstanceAs(mProvider2); + assertThat(mRegistry.getProviderForSensor(500)).isNull(); + } + + @Test + public void getsSingleProvider() { + mRegistry.registerAllInBackground(() -> List.of(mProvider1)); + + assertThat(mRegistry.getSingleProvider().second).isSameInstanceAs(mProvider1); + assertThat(mRegistry.getProviders()).containsExactly(mProvider1); + assertThat(mRegistry.getProviderForSensor(SENSOR_ID_1)).isSameInstanceAs(mProvider1); + } + + @Test + public void getSingleProviderFindsFirstWhenMultiple() { + mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2)); + + assertThat(mRegistry.getSingleProvider().second).isSameInstanceAs(mProvider1); + } + + @Test + public void registersListenerBeforeAllRegistered() { + final List all = new ArrayList<>(); + mRegistry.addAllRegisteredCallback(new IFingerprintAuthenticatorsRegisteredCallback.Stub() { + @Override + public void onAllAuthenticatorsRegistered( + List sensors) { + all.addAll(sensors); + } + }); + + mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2)); + + assertThat(all).containsExactly(mProvider1Props, mProvider2Props); + } + + @Test + public void registersListenerAfterAllRegistered() { + mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2)); + + final List all = new ArrayList<>(); + mRegistry.addAllRegisteredCallback(new IFingerprintAuthenticatorsRegisteredCallback.Stub() { + @Override + public void onAllAuthenticatorsRegistered( + List sensors) { + all.addAll(sensors); + } + }); + + assertThat(all).containsExactly(mProvider1Props, mProvider2Props); + } +} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java index ca3677e3b5ff6..a4048a27dfb53 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java @@ -32,7 +32,8 @@ import android.hardware.biometrics.fingerprint.FingerprintSensorType; import android.hardware.biometrics.fingerprint.IFingerprint; import android.hardware.biometrics.fingerprint.SensorLocation; import android.hardware.biometrics.fingerprint.SensorProps; -import android.os.RemoteException; +import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; +import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback; import android.platform.test.annotations.Presubmit; import android.provider.Settings; import android.testing.TestableContext; @@ -52,6 +53,8 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; @Presubmit @SmallTest @@ -94,9 +97,12 @@ public class FingerprintServiceTest { mContext.getTestablePermissions().setPermission( USE_BIOMETRIC_INTERNAL, PackageManager.PERMISSION_GRANTED); + } + private void initServiceWith(String... aidlInstances) { mService = new FingerprintService(mContext, mBiometricContext, () -> mIBiometricService, + () -> aidlInstances, (fqName) -> { if (fqName.endsWith(NAME_DEFAULT)) return mIFingerprintDefault; if (fqName.endsWith(NAME_VIRTUAL)) return mIFingerprintVirtual; @@ -105,29 +111,50 @@ public class FingerprintServiceTest { } @Test - public void registerAuthenticators_defaultOnly() throws RemoteException { - mService.registerAuthenticatorsForService(List.of(NAME_DEFAULT, NAME_VIRTUAL), List.of()); + public void registerAuthenticators_defaultOnly() throws Exception { + initServiceWith(NAME_DEFAULT, NAME_VIRTUAL); + + mService.mServiceWrapper.registerAuthenticators(List.of()); + waitForRegistration(); verify(mIBiometricService).registerAuthenticator(eq(ID_DEFAULT), anyInt(), anyInt(), any()); } @Test - public void registerAuthenticators_virtualOnly() throws RemoteException { + public void registerAuthenticators_virtualOnly() throws Exception { + initServiceWith(NAME_DEFAULT, NAME_VIRTUAL); Settings.Secure.putInt(mSettingsRule.mockContentResolver(mContext), Settings.Secure.BIOMETRIC_VIRTUAL_ENABLED, 1); - mService.registerAuthenticatorsForService(List.of(NAME_DEFAULT, NAME_VIRTUAL), List.of()); + mService.mServiceWrapper.registerAuthenticators(List.of()); + waitForRegistration(); verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), anyInt(), anyInt(), any()); } @Test - public void registerAuthenticators_virtualAlwaysWhenNoOther() throws RemoteException { - mService.registerAuthenticatorsForService(List.of(NAME_VIRTUAL), List.of()); + public void registerAuthenticators_virtualAlwaysWhenNoOther() throws Exception { + initServiceWith(NAME_VIRTUAL); + + mService.mServiceWrapper.registerAuthenticators(List.of()); + waitForRegistration(); verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), anyInt(), anyInt(), any()); } + private void waitForRegistration() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + mService.mServiceWrapper.addAuthenticatorsRegisteredCallback( + new IFingerprintAuthenticatorsRegisteredCallback.Stub() { + @Override + public void onAllAuthenticatorsRegistered( + List sensors) { + latch.countDown(); + } + }); + latch.await(5, TimeUnit.SECONDS); + } + private static SensorProps createProps(int id, byte strength, byte type) { final SensorProps props = new SensorProps(); props.commonProps = new CommonProps();