Loading services/core/java/com/android/server/biometrics/BiometricHandlerProvider.java 0 → 100644 +77 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; /** * This class provides the handler to process biometric operations. */ public class BiometricHandlerProvider { private static final BiometricHandlerProvider sBiometricHandlerProvider = new BiometricHandlerProvider(); private final Handler mBiometricsCallbackHandler; private final Handler mFingerprintHandler; private final Handler mFaceHandler; /** * @return an instance of {@link BiometricHandlerProvider} which contains the three * threads needed for running biometric operations */ public static BiometricHandlerProvider getInstance() { return sBiometricHandlerProvider; } private BiometricHandlerProvider() { mBiometricsCallbackHandler = getNewHandler("BiometricsCallbackHandler"); mFingerprintHandler = getNewHandler("FingerprintHandler"); mFaceHandler = getNewHandler("FaceHandler"); } /** * @return the handler to process all biometric callback operations */ public synchronized Handler getBiometricCallbackHandler() { return mBiometricsCallbackHandler; } /** * @return the handler to process all face related biometric operations */ public synchronized Handler getFaceHandler() { return mFaceHandler; } /** * @return the handler to process all fingerprint related biometric operations */ public synchronized Handler getFingerprintHandler() { return mFingerprintHandler; } private Handler getNewHandler(String tag) { if (Flags.deHidl()) { HandlerThread handlerThread = new HandlerThread(tag); handlerThread.start(); return new Handler(handlerThread.getLooper()); } return new Handler(Looper.getMainLooper()); } } services/core/java/com/android/server/biometrics/BiometricService.java +5 −4 Original line number Diff line number Diff line Loading @@ -62,7 +62,6 @@ import android.os.Build; import android.os.DeadObjectException; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceSpecificException; Loading Loading @@ -140,7 +139,7 @@ public class BiometricService extends SystemService { // The current authentication session, null if idle/done. @VisibleForTesting AuthSession mAuthSession; private final Handler mHandler = new Handler(Looper.getMainLooper()); private final Handler mHandler; private final BiometricCameraManager mBiometricCameraManager; Loading Loading @@ -1113,14 +1112,16 @@ public class BiometricService extends SystemService { * @param context The system server context. */ public BiometricService(Context context) { this(context, new Injector()); this(context, new Injector(), BiometricHandlerProvider.getInstance()); } @VisibleForTesting BiometricService(Context context, Injector injector) { BiometricService(Context context, Injector injector, BiometricHandlerProvider biometricHandlerProvider) { super(context); mInjector = injector; mHandler = biometricHandlerProvider.getBiometricCallbackHandler(); mDevicePolicyManager = mInjector.getDevicePolicyManager(context); mImpl = new BiometricServiceWrapper(); mEnabledOnKeyguardCallbacks = new ArrayList<>(); Loading services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java +26 −15 Original line number Diff line number Diff line Loading @@ -39,7 +39,6 @@ import android.hardware.face.FaceSensorPropertiesInternal; import android.hardware.face.IFaceServiceReceiver; import android.os.Binder; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; Loading @@ -53,6 +52,7 @@ import android.view.Surface; import com.android.internal.annotations.VisibleForTesting; import com.android.server.biometrics.AuthenticationStatsBroadcastReceiver; import com.android.server.biometrics.AuthenticationStatsCollector; import com.android.server.biometrics.BiometricHandlerProvider; import com.android.server.biometrics.Flags; import com.android.server.biometrics.Utils; import com.android.server.biometrics.log.BiometricContext; Loading Loading @@ -124,6 +124,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { private final BiometricContext mBiometricContext; @NonNull private final AuthSessionCoordinator mAuthSessionCoordinator; @NonNull private final BiometricHandlerProvider mBiometricHandlerProvider; @Nullable private AuthenticationStatsCollector mAuthenticationStatsCollector; @Nullable Loading Loading @@ -166,8 +168,9 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { @NonNull BiometricContext biometricContext, boolean resetLockoutRequiresChallenge) { this(context, biometricStateCallback, authenticationStateListeners, props, halInstanceName, lockoutResetDispatcher, biometricContext, null /* daemon */, getHandler(), resetLockoutRequiresChallenge, false /* testHalEnabled */); lockoutResetDispatcher, biometricContext, null /* daemon */, BiometricHandlerProvider.getInstance(), resetLockoutRequiresChallenge, false /* testHalEnabled */); } @VisibleForTesting FaceProvider(@NonNull Context context, Loading @@ -178,7 +181,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull BiometricContext biometricContext, @Nullable IFace daemon, @NonNull Handler handler, @NonNull BiometricHandlerProvider biometricHandlerProvider, boolean resetLockoutRequiresChallenge, boolean testHalEnabled) { mContext = context; Loading @@ -187,7 +190,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { mHalInstanceName = halInstanceName; mFaceSensors = new SensorList<>(ActivityManager.getService()); if (Flags.deHidl()) { mHandler = handler; mHandler = biometricHandlerProvider.getFaceHandler(); } else { mHandler = new Handler(Looper.getMainLooper()); } Loading @@ -199,18 +202,12 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { mAuthSessionCoordinator = mBiometricContext.getAuthSessionCoordinator(); mDaemon = daemon; mTestHalEnabled = testHalEnabled; mBiometricHandlerProvider = biometricHandlerProvider; initAuthenticationBroadcastReceiver(); initSensors(resetLockoutRequiresChallenge, props); } @NonNull private static Handler getHandler() { HandlerThread handlerThread = new HandlerThread(TAG); handlerThread.start(); return new Handler(handlerThread.getLooper()); } private void initAuthenticationBroadcastReceiver() { new AuthenticationStatsBroadcastReceiver( mContext, Loading Loading @@ -622,16 +619,30 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { @Override public void onClientStarted( BaseClientMonitor clientMonitor) { if (Flags.deHidl()) { mBiometricHandlerProvider.getBiometricCallbackHandler().post(() -> mAuthSessionCoordinator.authStartedFor(userId, sensorId, requestId)); } else { mAuthSessionCoordinator.authStartedFor(userId, sensorId, requestId); } } @Override public void onClientFinished( BaseClientMonitor clientMonitor, boolean success) { mAuthSessionCoordinator.authEndedFor(userId, Utils.getCurrentStrength(sensorId), if (Flags.deHidl()) { mBiometricHandlerProvider.getBiometricCallbackHandler().post(() -> mAuthSessionCoordinator.authEndedFor(userId, Utils.getCurrentStrength(sensorId), sensorId, requestId, client.wasAuthSuccessful())); } else { mAuthSessionCoordinator.authEndedFor(userId, Utils.getCurrentStrength(sensorId), sensorId, requestId, client.wasAuthSuccessful()); } } }); }); } Loading services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +24 −16 Original line number Diff line number Diff line Loading @@ -46,7 +46,6 @@ import android.hardware.fingerprint.ISidefpsController; import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.Binder; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; Loading @@ -60,6 +59,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; import com.android.server.biometrics.AuthenticationStatsBroadcastReceiver; import com.android.server.biometrics.AuthenticationStatsCollector; import com.android.server.biometrics.BiometricHandlerProvider; import com.android.server.biometrics.Flags; import com.android.server.biometrics.Utils; import com.android.server.biometrics.log.BiometricContext; Loading Loading @@ -129,11 +129,12 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi // for requests that do not use biometric prompt @NonNull private final AtomicLong mRequestCounter = new AtomicLong(0); @NonNull private final BiometricContext mBiometricContext; @NonNull private final BiometricHandlerProvider mBiometricHandlerProvider; @Nullable private IFingerprint mDaemon; @Nullable private IUdfpsOverlayController mUdfpsOverlayController; // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR @Nullable private ISidefpsController mSidefpsController; private AuthSessionCoordinator mAuthSessionCoordinator; private final AuthSessionCoordinator mAuthSessionCoordinator; @Nullable private AuthenticationStatsCollector mAuthenticationStatsCollector; private final class BiometricTaskStackListener extends TaskStackListener { Loading Loading @@ -175,8 +176,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi boolean resetLockoutRequiresHardwareAuthToken) { this(context, biometricStateCallback, authenticationStateListeners, props, halInstanceName, lockoutResetDispatcher, gestureAvailabilityDispatcher, biometricContext, null /* daemon */, getHandler(), resetLockoutRequiresHardwareAuthToken, false /* testHalEnabled */); null /* daemon */, BiometricHandlerProvider.getInstance(), resetLockoutRequiresHardwareAuthToken, false /* testHalEnabled */); } @VisibleForTesting FingerprintProvider(@NonNull Context context, Loading @@ -187,7 +188,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, @NonNull BiometricContext biometricContext, @Nullable IFingerprint daemon, @NonNull Handler handler, @NonNull BiometricHandlerProvider biometricHandlerProvider, boolean resetLockoutRequiresHardwareAuthToken, boolean testHalEnabled) { mContext = context; Loading @@ -196,7 +197,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mHalInstanceName = halInstanceName; mFingerprintSensors = new SensorList<>(ActivityManager.getService()); if (Flags.deHidl()) { mHandler = handler; mHandler = biometricHandlerProvider.getFingerprintHandler(); } else { mHandler = new Handler(Looper.getMainLooper()); } Loading @@ -207,18 +208,12 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mAuthSessionCoordinator = mBiometricContext.getAuthSessionCoordinator(); mDaemon = daemon; mTestHalEnabled = testHalEnabled; mBiometricHandlerProvider = biometricHandlerProvider; initAuthenticationBroadcastReceiver(); initSensors(resetLockoutRequiresHardwareAuthToken, props, gestureAvailabilityDispatcher); } @NonNull private static Handler getHandler() { HandlerThread handlerThread = new HandlerThread(TAG); handlerThread.start(); return new Handler(handlerThread.getLooper()); } private void initAuthenticationBroadcastReceiver() { new AuthenticationStatsBroadcastReceiver( mContext, Loading Loading @@ -620,8 +615,14 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @Override public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) { mBiometricStateCallback.onClientStarted(clientMonitor); if (Flags.deHidl()) { mBiometricHandlerProvider.getBiometricCallbackHandler().post(() -> mAuthSessionCoordinator.authStartedFor(userId, sensorId, requestId)); } else { mAuthSessionCoordinator.authStartedFor(userId, sensorId, requestId); } } @Override public void onBiometricAction(int action) { Loading @@ -632,8 +633,15 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) { mBiometricStateCallback.onClientFinished(clientMonitor, success); mAuthSessionCoordinator.authEndedFor(userId, Utils.getCurrentStrength(sensorId), sensorId, requestId, success); if (Flags.deHidl()) { mBiometricHandlerProvider.getBiometricCallbackHandler().post(() -> mAuthSessionCoordinator.authEndedFor(userId, Utils.getCurrentStrength(sensorId), sensorId, requestId, success)); } else { mAuthSessionCoordinator.authEndedFor(userId, Utils.getCurrentStrength(sensorId), sensorId, requestId, success); } } }); Loading services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java +40 −19 Original line number Diff line number Diff line Loading @@ -74,7 +74,9 @@ import android.hardware.display.DisplayManagerGlobal; import android.hardware.fingerprint.FingerprintManager; import android.hardware.keymaster.HardwareAuthenticatorType; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.UserManager; import android.platform.test.annotations.Presubmit; Loading @@ -83,6 +85,8 @@ import android.security.GateKeeper; import android.security.KeyStore; import android.security.authorization.IKeystoreAuthorization; import android.service.gatekeeper.IGateKeeperService; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.Display; import android.view.DisplayInfo; import android.view.WindowManager; Loading @@ -100,6 +104,7 @@ import com.android.server.biometrics.sensors.LockoutTracker; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.AdditionalMatchers; import org.mockito.ArgumentCaptor; import org.mockito.Mock; Loading @@ -110,6 +115,8 @@ import java.util.Random; @Presubmit @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper() public class BiometricServiceTest { @Rule Loading Loading @@ -171,6 +178,8 @@ public class BiometricServiceTest { private UserManager mUserManager; @Mock private BiometricCameraManager mBiometricCameraManager; @Mock private BiometricHandlerProvider mBiometricHandlerProvider; @Mock private IKeystoreAuthorization mKeystoreAuthService; Loading Loading @@ -235,6 +244,14 @@ public class BiometricServiceTest { when(mInjector.getGateKeeperService()).thenReturn(mGateKeeperService); when(mGateKeeperService.getSecureUserId(anyInt())).thenReturn(42L); if (com.android.server.biometrics.Flags.deHidl()) { when(mBiometricHandlerProvider.getBiometricCallbackHandler()).thenReturn( new Handler(TestableLooper.get(this).getLooper())); } else { when(mBiometricHandlerProvider.getBiometricCallbackHandler()).thenReturn( new Handler(Looper.getMainLooper())); } final String[] config = { "0:2:15", // ID0:Fingerprint:Strong "1:8:15", // ID1:Face:Strong Loading Loading @@ -312,7 +329,7 @@ public class BiometricServiceTest { when(mTrustManager.isDeviceSecure(anyInt(), anyInt())) .thenReturn(false); mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.onStart(); invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, Loading @@ -333,7 +350,7 @@ public class BiometricServiceTest { when(mTrustManager.isDeviceSecure(anyInt(), anyInt())) .thenReturn(true); mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.onStart(); invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, Loading @@ -360,7 +377,7 @@ public class BiometricServiceTest { @Test public void testAuthenticate_withoutHardware_returnsErrorHardwareNotPresent() throws Exception { mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.onStart(); invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, Loading @@ -377,7 +394,7 @@ public class BiometricServiceTest { public void testAuthenticate_withoutEnrolled_returnsErrorNoBiometrics() throws Exception { when(mFingerprintAuthenticator.isHardwareDetected(any())).thenReturn(true); mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.onStart(); mBiometricService.mImpl.registerAuthenticator(0 /* id */, TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG, Loading Loading @@ -451,7 +468,7 @@ public class BiometricServiceTest { when(mFingerprintAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true); when(mFingerprintAuthenticator.isHardwareDetected(any())).thenReturn(false); mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.onStart(); mBiometricService.mImpl.registerAuthenticator(0 /* id */, TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG, Loading Loading @@ -1374,7 +1391,7 @@ public class BiometricServiceTest { @Test public void testCanAuthenticate_onlyCredentialRequested() throws Exception { mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.onStart(); // Credential requested but not set up Loading Loading @@ -1428,7 +1445,7 @@ public class BiometricServiceTest { @Test public void testCanAuthenticate_whenNoBiometricSensor() throws Exception { mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.onStart(); // When only biometric is requested Loading Loading @@ -1515,7 +1532,7 @@ public class BiometricServiceTest { @Test public void testRegisterAuthenticator_updatesStrengths() throws Exception { mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.onStart(); verify(mBiometricService.mBiometricStrengthController).startListening(); Loading @@ -1533,7 +1550,7 @@ public class BiometricServiceTest { @Test public void testWithDowngradedAuthenticator() throws Exception { mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.onStart(); final int testId = 0; Loading Loading @@ -1639,7 +1656,7 @@ public class BiometricServiceTest { @Test(expected = IllegalStateException.class) public void testRegistrationWithDuplicateId_throwsIllegalStateException() throws Exception { mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.onStart(); mBiometricService.mImpl.registerAuthenticator( Loading @@ -1653,7 +1670,7 @@ public class BiometricServiceTest { @Test(expected = IllegalArgumentException.class) public void testRegistrationWithNullAuthenticator_throwsIllegalArgumentException() throws Exception { mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.onStart(); mBiometricService.mImpl.registerAuthenticator( Loading @@ -1665,7 +1682,7 @@ public class BiometricServiceTest { @Test public void testRegistrationHappyPath_isOk() throws Exception { // This is being tested in many of the other cases, but here's the base case. mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.onStart(); for (String s : mInjector.getConfiguration(null)) { Loading Loading @@ -1751,7 +1768,7 @@ public class BiometricServiceTest { final IBiometricEnabledOnKeyguardCallback callback = mock(IBiometricEnabledOnKeyguardCallback.class); mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); when(mUserManager.getAliveUsers()).thenReturn(aliveUsers); when(mBiometricService.mSettingObserver.getEnabledOnKeyguard(userInfo1.id)) Loading @@ -1775,7 +1792,7 @@ public class BiometricServiceTest { throws RemoteException { mSetFlagsRule.disableFlags(Flags.FLAG_LAST_AUTHENTICATION_TIME); mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.mImpl.getLastAuthenticationTime(0, Authenticators.BIOMETRIC_STRONG); } Loading @@ -1799,7 +1816,7 @@ public class BiometricServiceTest { when(mKeystoreAuthService.getLastAuthTime(eq(secureUserId), eq(hardwareAuthenticators))) .thenReturn(expectedResult); mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); final long result = mBiometricService.mImpl.getLastAuthenticationTime(userId, Authenticators.BIOMETRIC_STRONG | Authenticators.DEVICE_CREDENTIAL); Loading @@ -1822,7 +1839,7 @@ public class BiometricServiceTest { // TODO: Reconcile the registration strength with the injector private void setupAuthForOnly(int modality, int strength, boolean enrolled) throws Exception { mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.onStart(); when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true); Loading Loading @@ -1855,7 +1872,7 @@ public class BiometricServiceTest { // TODO: Reduce duplicated code, currently we cannot start the BiometricService in setUp() for // all tests. private void setupAuthForMultiple(int[] modalities, int[] strengths) throws RemoteException { mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.onStart(); when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true); Loading Loading @@ -1993,9 +2010,13 @@ public class BiometricServiceTest { return requestWrapper.eligibleSensors.get(0).getCookie(); } private static void waitForIdle() { private void waitForIdle() { if (com.android.server.biometrics.Flags.deHidl()) { TestableLooper.get(this).processAllMessages(); } else { InstrumentationRegistry.getInstrumentation().waitForIdleSync(); } } private byte[] generateRandomHAT() { byte[] HAT = new byte[69]; Loading Loading
services/core/java/com/android/server/biometrics/BiometricHandlerProvider.java 0 → 100644 +77 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; /** * This class provides the handler to process biometric operations. */ public class BiometricHandlerProvider { private static final BiometricHandlerProvider sBiometricHandlerProvider = new BiometricHandlerProvider(); private final Handler mBiometricsCallbackHandler; private final Handler mFingerprintHandler; private final Handler mFaceHandler; /** * @return an instance of {@link BiometricHandlerProvider} which contains the three * threads needed for running biometric operations */ public static BiometricHandlerProvider getInstance() { return sBiometricHandlerProvider; } private BiometricHandlerProvider() { mBiometricsCallbackHandler = getNewHandler("BiometricsCallbackHandler"); mFingerprintHandler = getNewHandler("FingerprintHandler"); mFaceHandler = getNewHandler("FaceHandler"); } /** * @return the handler to process all biometric callback operations */ public synchronized Handler getBiometricCallbackHandler() { return mBiometricsCallbackHandler; } /** * @return the handler to process all face related biometric operations */ public synchronized Handler getFaceHandler() { return mFaceHandler; } /** * @return the handler to process all fingerprint related biometric operations */ public synchronized Handler getFingerprintHandler() { return mFingerprintHandler; } private Handler getNewHandler(String tag) { if (Flags.deHidl()) { HandlerThread handlerThread = new HandlerThread(tag); handlerThread.start(); return new Handler(handlerThread.getLooper()); } return new Handler(Looper.getMainLooper()); } }
services/core/java/com/android/server/biometrics/BiometricService.java +5 −4 Original line number Diff line number Diff line Loading @@ -62,7 +62,6 @@ import android.os.Build; import android.os.DeadObjectException; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceSpecificException; Loading Loading @@ -140,7 +139,7 @@ public class BiometricService extends SystemService { // The current authentication session, null if idle/done. @VisibleForTesting AuthSession mAuthSession; private final Handler mHandler = new Handler(Looper.getMainLooper()); private final Handler mHandler; private final BiometricCameraManager mBiometricCameraManager; Loading Loading @@ -1113,14 +1112,16 @@ public class BiometricService extends SystemService { * @param context The system server context. */ public BiometricService(Context context) { this(context, new Injector()); this(context, new Injector(), BiometricHandlerProvider.getInstance()); } @VisibleForTesting BiometricService(Context context, Injector injector) { BiometricService(Context context, Injector injector, BiometricHandlerProvider biometricHandlerProvider) { super(context); mInjector = injector; mHandler = biometricHandlerProvider.getBiometricCallbackHandler(); mDevicePolicyManager = mInjector.getDevicePolicyManager(context); mImpl = new BiometricServiceWrapper(); mEnabledOnKeyguardCallbacks = new ArrayList<>(); Loading
services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java +26 −15 Original line number Diff line number Diff line Loading @@ -39,7 +39,6 @@ import android.hardware.face.FaceSensorPropertiesInternal; import android.hardware.face.IFaceServiceReceiver; import android.os.Binder; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; Loading @@ -53,6 +52,7 @@ import android.view.Surface; import com.android.internal.annotations.VisibleForTesting; import com.android.server.biometrics.AuthenticationStatsBroadcastReceiver; import com.android.server.biometrics.AuthenticationStatsCollector; import com.android.server.biometrics.BiometricHandlerProvider; import com.android.server.biometrics.Flags; import com.android.server.biometrics.Utils; import com.android.server.biometrics.log.BiometricContext; Loading Loading @@ -124,6 +124,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { private final BiometricContext mBiometricContext; @NonNull private final AuthSessionCoordinator mAuthSessionCoordinator; @NonNull private final BiometricHandlerProvider mBiometricHandlerProvider; @Nullable private AuthenticationStatsCollector mAuthenticationStatsCollector; @Nullable Loading Loading @@ -166,8 +168,9 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { @NonNull BiometricContext biometricContext, boolean resetLockoutRequiresChallenge) { this(context, biometricStateCallback, authenticationStateListeners, props, halInstanceName, lockoutResetDispatcher, biometricContext, null /* daemon */, getHandler(), resetLockoutRequiresChallenge, false /* testHalEnabled */); lockoutResetDispatcher, biometricContext, null /* daemon */, BiometricHandlerProvider.getInstance(), resetLockoutRequiresChallenge, false /* testHalEnabled */); } @VisibleForTesting FaceProvider(@NonNull Context context, Loading @@ -178,7 +181,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull BiometricContext biometricContext, @Nullable IFace daemon, @NonNull Handler handler, @NonNull BiometricHandlerProvider biometricHandlerProvider, boolean resetLockoutRequiresChallenge, boolean testHalEnabled) { mContext = context; Loading @@ -187,7 +190,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { mHalInstanceName = halInstanceName; mFaceSensors = new SensorList<>(ActivityManager.getService()); if (Flags.deHidl()) { mHandler = handler; mHandler = biometricHandlerProvider.getFaceHandler(); } else { mHandler = new Handler(Looper.getMainLooper()); } Loading @@ -199,18 +202,12 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { mAuthSessionCoordinator = mBiometricContext.getAuthSessionCoordinator(); mDaemon = daemon; mTestHalEnabled = testHalEnabled; mBiometricHandlerProvider = biometricHandlerProvider; initAuthenticationBroadcastReceiver(); initSensors(resetLockoutRequiresChallenge, props); } @NonNull private static Handler getHandler() { HandlerThread handlerThread = new HandlerThread(TAG); handlerThread.start(); return new Handler(handlerThread.getLooper()); } private void initAuthenticationBroadcastReceiver() { new AuthenticationStatsBroadcastReceiver( mContext, Loading Loading @@ -622,16 +619,30 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { @Override public void onClientStarted( BaseClientMonitor clientMonitor) { if (Flags.deHidl()) { mBiometricHandlerProvider.getBiometricCallbackHandler().post(() -> mAuthSessionCoordinator.authStartedFor(userId, sensorId, requestId)); } else { mAuthSessionCoordinator.authStartedFor(userId, sensorId, requestId); } } @Override public void onClientFinished( BaseClientMonitor clientMonitor, boolean success) { mAuthSessionCoordinator.authEndedFor(userId, Utils.getCurrentStrength(sensorId), if (Flags.deHidl()) { mBiometricHandlerProvider.getBiometricCallbackHandler().post(() -> mAuthSessionCoordinator.authEndedFor(userId, Utils.getCurrentStrength(sensorId), sensorId, requestId, client.wasAuthSuccessful())); } else { mAuthSessionCoordinator.authEndedFor(userId, Utils.getCurrentStrength(sensorId), sensorId, requestId, client.wasAuthSuccessful()); } } }); }); } Loading
services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +24 −16 Original line number Diff line number Diff line Loading @@ -46,7 +46,6 @@ import android.hardware.fingerprint.ISidefpsController; import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.Binder; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; Loading @@ -60,6 +59,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; import com.android.server.biometrics.AuthenticationStatsBroadcastReceiver; import com.android.server.biometrics.AuthenticationStatsCollector; import com.android.server.biometrics.BiometricHandlerProvider; import com.android.server.biometrics.Flags; import com.android.server.biometrics.Utils; import com.android.server.biometrics.log.BiometricContext; Loading Loading @@ -129,11 +129,12 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi // for requests that do not use biometric prompt @NonNull private final AtomicLong mRequestCounter = new AtomicLong(0); @NonNull private final BiometricContext mBiometricContext; @NonNull private final BiometricHandlerProvider mBiometricHandlerProvider; @Nullable private IFingerprint mDaemon; @Nullable private IUdfpsOverlayController mUdfpsOverlayController; // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR @Nullable private ISidefpsController mSidefpsController; private AuthSessionCoordinator mAuthSessionCoordinator; private final AuthSessionCoordinator mAuthSessionCoordinator; @Nullable private AuthenticationStatsCollector mAuthenticationStatsCollector; private final class BiometricTaskStackListener extends TaskStackListener { Loading Loading @@ -175,8 +176,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi boolean resetLockoutRequiresHardwareAuthToken) { this(context, biometricStateCallback, authenticationStateListeners, props, halInstanceName, lockoutResetDispatcher, gestureAvailabilityDispatcher, biometricContext, null /* daemon */, getHandler(), resetLockoutRequiresHardwareAuthToken, false /* testHalEnabled */); null /* daemon */, BiometricHandlerProvider.getInstance(), resetLockoutRequiresHardwareAuthToken, false /* testHalEnabled */); } @VisibleForTesting FingerprintProvider(@NonNull Context context, Loading @@ -187,7 +188,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, @NonNull BiometricContext biometricContext, @Nullable IFingerprint daemon, @NonNull Handler handler, @NonNull BiometricHandlerProvider biometricHandlerProvider, boolean resetLockoutRequiresHardwareAuthToken, boolean testHalEnabled) { mContext = context; Loading @@ -196,7 +197,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mHalInstanceName = halInstanceName; mFingerprintSensors = new SensorList<>(ActivityManager.getService()); if (Flags.deHidl()) { mHandler = handler; mHandler = biometricHandlerProvider.getFingerprintHandler(); } else { mHandler = new Handler(Looper.getMainLooper()); } Loading @@ -207,18 +208,12 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mAuthSessionCoordinator = mBiometricContext.getAuthSessionCoordinator(); mDaemon = daemon; mTestHalEnabled = testHalEnabled; mBiometricHandlerProvider = biometricHandlerProvider; initAuthenticationBroadcastReceiver(); initSensors(resetLockoutRequiresHardwareAuthToken, props, gestureAvailabilityDispatcher); } @NonNull private static Handler getHandler() { HandlerThread handlerThread = new HandlerThread(TAG); handlerThread.start(); return new Handler(handlerThread.getLooper()); } private void initAuthenticationBroadcastReceiver() { new AuthenticationStatsBroadcastReceiver( mContext, Loading Loading @@ -620,8 +615,14 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @Override public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) { mBiometricStateCallback.onClientStarted(clientMonitor); if (Flags.deHidl()) { mBiometricHandlerProvider.getBiometricCallbackHandler().post(() -> mAuthSessionCoordinator.authStartedFor(userId, sensorId, requestId)); } else { mAuthSessionCoordinator.authStartedFor(userId, sensorId, requestId); } } @Override public void onBiometricAction(int action) { Loading @@ -632,8 +633,15 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) { mBiometricStateCallback.onClientFinished(clientMonitor, success); mAuthSessionCoordinator.authEndedFor(userId, Utils.getCurrentStrength(sensorId), sensorId, requestId, success); if (Flags.deHidl()) { mBiometricHandlerProvider.getBiometricCallbackHandler().post(() -> mAuthSessionCoordinator.authEndedFor(userId, Utils.getCurrentStrength(sensorId), sensorId, requestId, success)); } else { mAuthSessionCoordinator.authEndedFor(userId, Utils.getCurrentStrength(sensorId), sensorId, requestId, success); } } }); Loading
services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java +40 −19 Original line number Diff line number Diff line Loading @@ -74,7 +74,9 @@ import android.hardware.display.DisplayManagerGlobal; import android.hardware.fingerprint.FingerprintManager; import android.hardware.keymaster.HardwareAuthenticatorType; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.UserManager; import android.platform.test.annotations.Presubmit; Loading @@ -83,6 +85,8 @@ import android.security.GateKeeper; import android.security.KeyStore; import android.security.authorization.IKeystoreAuthorization; import android.service.gatekeeper.IGateKeeperService; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.Display; import android.view.DisplayInfo; import android.view.WindowManager; Loading @@ -100,6 +104,7 @@ import com.android.server.biometrics.sensors.LockoutTracker; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.AdditionalMatchers; import org.mockito.ArgumentCaptor; import org.mockito.Mock; Loading @@ -110,6 +115,8 @@ import java.util.Random; @Presubmit @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper() public class BiometricServiceTest { @Rule Loading Loading @@ -171,6 +178,8 @@ public class BiometricServiceTest { private UserManager mUserManager; @Mock private BiometricCameraManager mBiometricCameraManager; @Mock private BiometricHandlerProvider mBiometricHandlerProvider; @Mock private IKeystoreAuthorization mKeystoreAuthService; Loading Loading @@ -235,6 +244,14 @@ public class BiometricServiceTest { when(mInjector.getGateKeeperService()).thenReturn(mGateKeeperService); when(mGateKeeperService.getSecureUserId(anyInt())).thenReturn(42L); if (com.android.server.biometrics.Flags.deHidl()) { when(mBiometricHandlerProvider.getBiometricCallbackHandler()).thenReturn( new Handler(TestableLooper.get(this).getLooper())); } else { when(mBiometricHandlerProvider.getBiometricCallbackHandler()).thenReturn( new Handler(Looper.getMainLooper())); } final String[] config = { "0:2:15", // ID0:Fingerprint:Strong "1:8:15", // ID1:Face:Strong Loading Loading @@ -312,7 +329,7 @@ public class BiometricServiceTest { when(mTrustManager.isDeviceSecure(anyInt(), anyInt())) .thenReturn(false); mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.onStart(); invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, Loading @@ -333,7 +350,7 @@ public class BiometricServiceTest { when(mTrustManager.isDeviceSecure(anyInt(), anyInt())) .thenReturn(true); mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.onStart(); invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, Loading @@ -360,7 +377,7 @@ public class BiometricServiceTest { @Test public void testAuthenticate_withoutHardware_returnsErrorHardwareNotPresent() throws Exception { mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.onStart(); invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, Loading @@ -377,7 +394,7 @@ public class BiometricServiceTest { public void testAuthenticate_withoutEnrolled_returnsErrorNoBiometrics() throws Exception { when(mFingerprintAuthenticator.isHardwareDetected(any())).thenReturn(true); mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.onStart(); mBiometricService.mImpl.registerAuthenticator(0 /* id */, TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG, Loading Loading @@ -451,7 +468,7 @@ public class BiometricServiceTest { when(mFingerprintAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true); when(mFingerprintAuthenticator.isHardwareDetected(any())).thenReturn(false); mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.onStart(); mBiometricService.mImpl.registerAuthenticator(0 /* id */, TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG, Loading Loading @@ -1374,7 +1391,7 @@ public class BiometricServiceTest { @Test public void testCanAuthenticate_onlyCredentialRequested() throws Exception { mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.onStart(); // Credential requested but not set up Loading Loading @@ -1428,7 +1445,7 @@ public class BiometricServiceTest { @Test public void testCanAuthenticate_whenNoBiometricSensor() throws Exception { mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.onStart(); // When only biometric is requested Loading Loading @@ -1515,7 +1532,7 @@ public class BiometricServiceTest { @Test public void testRegisterAuthenticator_updatesStrengths() throws Exception { mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.onStart(); verify(mBiometricService.mBiometricStrengthController).startListening(); Loading @@ -1533,7 +1550,7 @@ public class BiometricServiceTest { @Test public void testWithDowngradedAuthenticator() throws Exception { mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.onStart(); final int testId = 0; Loading Loading @@ -1639,7 +1656,7 @@ public class BiometricServiceTest { @Test(expected = IllegalStateException.class) public void testRegistrationWithDuplicateId_throwsIllegalStateException() throws Exception { mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.onStart(); mBiometricService.mImpl.registerAuthenticator( Loading @@ -1653,7 +1670,7 @@ public class BiometricServiceTest { @Test(expected = IllegalArgumentException.class) public void testRegistrationWithNullAuthenticator_throwsIllegalArgumentException() throws Exception { mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.onStart(); mBiometricService.mImpl.registerAuthenticator( Loading @@ -1665,7 +1682,7 @@ public class BiometricServiceTest { @Test public void testRegistrationHappyPath_isOk() throws Exception { // This is being tested in many of the other cases, but here's the base case. mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.onStart(); for (String s : mInjector.getConfiguration(null)) { Loading Loading @@ -1751,7 +1768,7 @@ public class BiometricServiceTest { final IBiometricEnabledOnKeyguardCallback callback = mock(IBiometricEnabledOnKeyguardCallback.class); mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); when(mUserManager.getAliveUsers()).thenReturn(aliveUsers); when(mBiometricService.mSettingObserver.getEnabledOnKeyguard(userInfo1.id)) Loading @@ -1775,7 +1792,7 @@ public class BiometricServiceTest { throws RemoteException { mSetFlagsRule.disableFlags(Flags.FLAG_LAST_AUTHENTICATION_TIME); mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.mImpl.getLastAuthenticationTime(0, Authenticators.BIOMETRIC_STRONG); } Loading @@ -1799,7 +1816,7 @@ public class BiometricServiceTest { when(mKeystoreAuthService.getLastAuthTime(eq(secureUserId), eq(hardwareAuthenticators))) .thenReturn(expectedResult); mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); final long result = mBiometricService.mImpl.getLastAuthenticationTime(userId, Authenticators.BIOMETRIC_STRONG | Authenticators.DEVICE_CREDENTIAL); Loading @@ -1822,7 +1839,7 @@ public class BiometricServiceTest { // TODO: Reconcile the registration strength with the injector private void setupAuthForOnly(int modality, int strength, boolean enrolled) throws Exception { mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.onStart(); when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true); Loading Loading @@ -1855,7 +1872,7 @@ public class BiometricServiceTest { // TODO: Reduce duplicated code, currently we cannot start the BiometricService in setUp() for // all tests. private void setupAuthForMultiple(int[] modalities, int[] strengths) throws RemoteException { mBiometricService = new BiometricService(mContext, mInjector); mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.onStart(); when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true); Loading Loading @@ -1993,9 +2010,13 @@ public class BiometricServiceTest { return requestWrapper.eligibleSensors.get(0).getCookie(); } private static void waitForIdle() { private void waitForIdle() { if (com.android.server.biometrics.Flags.deHidl()) { TestableLooper.get(this).processAllMessages(); } else { InstrumentationRegistry.getInstrumentation().waitForIdleSync(); } } private byte[] generateRandomHAT() { byte[] HAT = new byte[69]; Loading