diff options
14 files changed, 548 insertions, 304 deletions
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java index cbe8a052db2f..9d427c8d8bab 100644 --- a/core/java/android/hardware/biometrics/BiometricManager.java +++ b/core/java/android/hardware/biometrics/BiometricManager.java @@ -137,7 +137,7 @@ public class BiometricManager { public boolean hasEnrolledBiometrics(int userId) { if (mService != null) { try { - return mService.hasEnrolledBiometrics(userId); + return mService.hasEnrolledBiometrics(userId, mContext.getOpPackageName()); } catch (RemoteException e) { Slog.w(TAG, "Remote exception in hasEnrolledBiometrics(): " + e); return false; diff --git a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl new file mode 100644 index 000000000000..987d19799644 --- /dev/null +++ b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2019 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.biometrics; + +import android.hardware.biometrics.IBiometricServiceReceiverInternal; +import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; +import android.hardware.face.IFaceServiceReceiver; +import android.hardware.face.Face; + +/** + * This interface encapsulates fingerprint, face, iris, etc. authenticators. + * Implementations of this interface are meant to be registered with BiometricService. + * @hide + */ +interface IBiometricAuthenticator { + + // This method prepares the service to start authenticating, but doesn't start authentication. + // This is protected by the MANAGE_BIOMETRIC signature permission. This method should only be + // called from BiometricService. The additional uid, pid, userId arguments should be determined + // by BiometricService. To start authentication after the clients are ready, use + // startPreparedClient(). + void prepareForAuthentication(boolean requireConfirmation, IBinder token, long sessionId, + int userId, IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName, + int cookie, int callingUid, int callingPid, int callingUserId); + + // Starts authentication with the previously prepared client. + void startPreparedClient(int cookie); + + // Same as above, with extra arguments. + void cancelAuthenticationFromService(IBinder token, String opPackageName, + int callingUid, int callingPid, int callingUserId, boolean fromClient); + + // Determine if HAL is loaded and ready + boolean isHardwareDetected(String opPackageName); + + // Determine if a user has at least one enrolled face + boolean hasEnrolledTemplates(int userId, String opPackageName); + + // Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password) + void resetLockout(in byte [] token); + + // Explicitly set the active user (for enrolling work profile) + void setActiveUser(int uid); +} diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl index 6a3bf38a97e1..06336a55ac5e 100644 --- a/core/java/android/hardware/biometrics/IBiometricService.aidl +++ b/core/java/android/hardware/biometrics/IBiometricService.aidl @@ -19,6 +19,7 @@ package android.hardware.biometrics; import android.os.Bundle; import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback; import android.hardware.biometrics.IBiometricServiceReceiver; +import android.hardware.biometrics.IBiometricAuthenticator; /** * Communication channel from BiometricPrompt and BiometricManager to BiometricService. The @@ -40,7 +41,12 @@ interface IBiometricService { int canAuthenticate(String opPackageName, int userId); // Checks if any biometrics are enrolled. - boolean hasEnrolledBiometrics(int userId); + boolean hasEnrolledBiometrics(int userId, String opPackageName); + + // Registers an authenticator (e.g. face, fingerprint, iris). + // Id must be unique, whereas strength and modality don't need to be. + // TODO(b/123321528): Turn strength and modality into enums. + void registerAuthenticator(int id, int strength, int modality, IBiometricAuthenticator authenticator); // Register callback for when keyguard biometric eligibility changes. void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback); diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index 12b285a0f0ab..55ebe285af1e 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -504,8 +504,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan public boolean isHardwareDetected() { if (mService != null) { try { - long deviceId = 0; /* TODO: plumb hardware id to FPMS */ - return mService.isHardwareDetected(deviceId, mContext.getOpPackageName()); + return mService.isHardwareDetected(mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl index b6a0afbf716c..68a4aef7af08 100644 --- a/core/java/android/hardware/face/IFaceService.aidl +++ b/core/java/android/hardware/face/IFaceService.aidl @@ -67,7 +67,7 @@ interface IFaceService { List<Face> getEnrolledFaces(int userId, String opPackageName); // Determine if HAL is loaded and ready - boolean isHardwareDetected(long deviceId, String opPackageName); + boolean isHardwareDetected(String opPackageName); // Get a pre-enrollment authentication token long generateChallenge(IBinder token); diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index d0622c88b4ca..22f8590a4e97 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -691,8 +691,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing public boolean isHardwareDetected() { if (mService != null) { try { - long deviceId = 0; /* TODO: plumb hardware id to FPMS */ - return mService.isHardwareDetected(deviceId, mContext.getOpPackageName()); + return mService.isHardwareDetected(mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index dd6b29d87d67..1a7e12856753 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -71,7 +71,7 @@ interface IFingerprintService { List<Fingerprint> getEnrolledFingerprints(int groupId, String opPackageName); // Determine if HAL is loaded and ready - boolean isHardwareDetected(long deviceId, String opPackageName); + boolean isHardwareDetected(String opPackageName); // Get a pre-enrollment authentication token long preEnroll(IBinder token); diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index 4f1db3c96faf..2b45f1970ef4 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -37,13 +37,12 @@ import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricPrompt; import android.hardware.biometrics.BiometricSourceType; import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.IBiometricAuthenticator; import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback; import android.hardware.biometrics.IBiometricService; import android.hardware.biometrics.IBiometricServiceReceiver; import android.hardware.biometrics.IBiometricServiceReceiverInternal; -import android.hardware.face.FaceManager; import android.hardware.face.IFaceService; -import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.IFingerprintService; import android.net.Uri; import android.os.Binder; @@ -68,6 +67,8 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.SomeArgs; import com.android.internal.statusbar.IStatusBarService; import com.android.server.SystemService; +import com.android.server.biometrics.face.FaceAuthenticator; +import com.android.server.biometrics.fingerprint.FingerprintAuthenticator; import java.util.ArrayList; import java.util.HashMap; @@ -95,11 +96,6 @@ public class BiometricService extends SystemService { private static final int MSG_CANCEL_AUTHENTICATION = 10; private static final int MSG_ON_AUTHENTICATION_TIMED_OUT = 11; private static final int MSG_ON_DEVICE_CREDENTIAL_PRESSED = 12; - private static final int[] FEATURE_ID = { - TYPE_FINGERPRINT, - TYPE_IRIS, - TYPE_FACE - }; /** * Authentication either just called and we have not transitioned to the CALLED state, or @@ -219,19 +215,15 @@ public class BiometricService extends SystemService { private final Injector mInjector; @VisibleForTesting final IBiometricService.Stub mImpl; + private final boolean mHasFeatureFace; private final boolean mHasFeatureFingerprint; private final boolean mHasFeatureIris; - private final boolean mHasFeatureFace; @VisibleForTesting final SettingObserver mSettingObserver; private final List<EnabledOnKeyguardCallback> mEnabledOnKeyguardCallbacks; private final Random mRandom = new Random(); @VisibleForTesting - IFingerprintService mFingerprintService; - @VisibleForTesting - IFaceService mFaceService; - @VisibleForTesting IStatusBarService mStatusBarService; @VisibleForTesting KeyStore mKeyStore; @@ -347,21 +339,23 @@ public class BiometricService extends SystemService { } }; - private final class AuthenticatorWrapper { - final int mType; - final BiometricAuthenticator mAuthenticator; - - AuthenticatorWrapper(int type, BiometricAuthenticator authenticator) { - mType = type; - mAuthenticator = authenticator; - } - - int getType() { - return mType; - } - - BiometricAuthenticator getAuthenticator() { - return mAuthenticator; + /** + * Wraps IBiometricAuthenticator implementation and stores information about the authenticator. + * TODO(b/141025588): Consider refactoring the tests to not rely on this implementation detail. + */ + @VisibleForTesting + public static final class AuthenticatorWrapper { + public final int id; + public final int strength; + public final int modality; + public final IBiometricAuthenticator impl; + + AuthenticatorWrapper(int id, int strength, int modality, + IBiometricAuthenticator impl) { + this.id = id; + this.strength = strength; + this.modality = modality; + this.impl = impl; } } @@ -666,7 +660,8 @@ public class BiometricService extends SystemService { final long ident = Binder.clearCallingIdentity(); int error; try { - final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId); + final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId, + opPackageName); error = result.second; } finally { Binder.restoreCallingIdentity(ident); @@ -675,22 +670,30 @@ public class BiometricService extends SystemService { } @Override - public boolean hasEnrolledBiometrics(int userId) { + public boolean hasEnrolledBiometrics(int userId, String opPackageName) { checkInternalPermission(); final long ident = Binder.clearCallingIdentity(); try { - for (int i = 0; i < mAuthenticators.size(); i++) { - if (mAuthenticators.get(i).mAuthenticator.hasEnrolledTemplates(userId)) { + for (AuthenticatorWrapper authenticator : mAuthenticators) { + if (authenticator.impl.hasEnrolledTemplates(userId, opPackageName)) { return true; } } + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); } finally { Binder.restoreCallingIdentity(ident); } return false; } + @Override + public void registerAuthenticator(int id, int strength, int modality, + IBiometricAuthenticator authenticator) { + mAuthenticators.add(new AuthenticatorWrapper(id, strength, modality, authenticator)); + } + @Override // Binder call public void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback) throws RemoteException { @@ -710,9 +713,11 @@ public class BiometricService extends SystemService { checkInternalPermission(); final long ident = Binder.clearCallingIdentity(); try { - for (int i = 0; i < mAuthenticators.size(); i++) { - mAuthenticators.get(i).getAuthenticator().setActiveUser(userId); + for (AuthenticatorWrapper authenticator : mAuthenticators) { + authenticator.impl.setActiveUser(userId); } + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); } finally { Binder.restoreCallingIdentity(ident); } @@ -723,11 +728,8 @@ public class BiometricService extends SystemService { checkInternalPermission(); final long ident = Binder.clearCallingIdentity(); try { - if (mFingerprintService != null) { - mFingerprintService.resetTimeout(token); - } - if (mFaceService != null) { - mFaceService.resetLockout(token); + for (AuthenticatorWrapper authenticator : mAuthenticators) { + authenticator.impl.resetLockout(token); } } catch (RemoteException e) { Slog.e(TAG, "Remote exception", e); @@ -750,40 +752,69 @@ public class BiometricService extends SystemService { } } + /** + * Class for injecting dependencies into BiometricService. + * TODO(b/141025588): Replace with a dependency injection framework (e.g. Guice, Dagger). + */ @VisibleForTesting - static class Injector { - IActivityManager getActivityManagerService() { + public static class Injector { + + @VisibleForTesting + public IActivityManager getActivityManagerService() { return ActivityManager.getService(); } - IStatusBarService getStatusBarService() { + @VisibleForTesting + public IStatusBarService getStatusBarService() { return IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); } - IFingerprintService getFingerprintService() { - return IFingerprintService.Stub.asInterface( - ServiceManager.getService(Context.FINGERPRINT_SERVICE)); + /** + * Allows to mock FaceAuthenticator for testing. + */ + @VisibleForTesting + public IBiometricAuthenticator getFingerprintAuthenticator() { + return new FingerprintAuthenticator(IFingerprintService.Stub.asInterface( + ServiceManager.getService(Context.FINGERPRINT_SERVICE))); } - IFaceService getFaceService() { - return IFaceService.Stub.asInterface(ServiceManager.getService(Context.FACE_SERVICE)); + /** + * Allows to mock FaceAuthenticator for testing. + */ + @VisibleForTesting + public IBiometricAuthenticator getFaceAuthenticator() { + return new FaceAuthenticator( + IFaceService.Stub.asInterface(ServiceManager.getService(Context.FACE_SERVICE))); } - SettingObserver getSettingObserver(Context context, Handler handler, + /** + * Allows to mock SettingObserver for testing. + */ + @VisibleForTesting + public SettingObserver getSettingObserver(Context context, Handler handler, List<EnabledOnKeyguardCallback> callbacks) { return new SettingObserver(context, handler, callbacks); } - KeyStore getKeyStore() { + @VisibleForTesting + public KeyStore getKeyStore() { return KeyStore.getInstance(); } - boolean isDebugEnabled(Context context, int userId) { + /** + * Allows to enable/disable debug logs. + */ + @VisibleForTesting + public boolean isDebugEnabled(Context context, int userId) { return Utils.isDebugEnabled(context, userId); } - void publishBinderService(BiometricService service, IBiometricService.Stub impl) { + /** + * Allows to stub publishBinderService(...) for testing. + */ + @VisibleForTesting + public void publishBinderService(BiometricService service, IBiometricService.Stub impl) { service.publishBinderService(Context.BIOMETRIC_SERVICE, impl); } } @@ -812,9 +843,9 @@ public class BiometricService extends SystemService { mEnabledOnKeyguardCallbacks); final PackageManager pm = context.getPackageManager(); + mHasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE); mHasFeatureFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT); mHasFeatureIris = pm.hasSystemFeature(PackageManager.FEATURE_IRIS); - mHasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE); try { injector.getActivityManagerService().registerUserSwitchObserver( @@ -833,26 +864,30 @@ public class BiometricService extends SystemService { @Override public void onStart() { - // TODO: maybe get these on-demand - if (mHasFeatureFingerprint) { - mFingerprintService = mInjector.getFingerprintService(); - } - if (mHasFeatureFace) { - mFaceService = mInjector.getFaceService(); + // TODO(b/141025588): remove this code block once AuthService is integrated. + { + if (mHasFeatureFace) { + try { + mImpl.registerAuthenticator(0, 0, TYPE_FACE, mInjector.getFaceAuthenticator()); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + } + if (mHasFeatureFingerprint) { + try { + mImpl.registerAuthenticator(0, 0, TYPE_FINGERPRINT, + mInjector.getFingerprintAuthenticator()); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + } + if (mHasFeatureIris) { + Slog.e(TAG, "Iris is not supported"); + } } mKeyStore = mInjector.getKeyStore(); mStatusBarService = mInjector.getStatusBarService(); - - // Cache the authenticators - for (int featureId : FEATURE_ID) { - if (hasFeature(featureId)) { - AuthenticatorWrapper authenticator = - new AuthenticatorWrapper(featureId, getAuthenticator(featureId)); - mAuthenticators.add(authenticator); - } - } - mInjector.publishBinderService(this, mImpl); } @@ -868,7 +903,7 @@ public class BiometricService extends SystemService { * {@link BiometricAuthenticator#TYPE_FACE} * and the error containing one of the {@link BiometricConstants} errors. */ - private Pair<Integer, Integer> checkAndGetBiometricModality(int userId) { + private Pair<Integer, Integer> checkAndGetBiometricModality(int userId, String opPackageName) { // No biometric features, send error if (mAuthenticators.isEmpty()) { return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT); @@ -886,23 +921,26 @@ public class BiometricService extends SystemService { int modality = TYPE_NONE; int firstHwAvailable = TYPE_NONE; - for (AuthenticatorWrapper authenticatorWrapper : mAuthenticators) { - modality = authenticatorWrapper.getType(); - BiometricAuthenticator authenticator = authenticatorWrapper.getAuthenticator(); - if (authenticator.isHardwareDetected()) { - isHardwareDetected = true; - if (firstHwAvailable == TYPE_NONE) { - // Store the first one since we want to return the error in correct priority - // order. - firstHwAvailable = modality; - } - if (authenticator.hasEnrolledTemplates(userId)) { - hasTemplatesEnrolled = true; - if (isEnabledForApp(modality, userId)) { - enabledForApps = true; - break; + for (AuthenticatorWrapper authenticator : mAuthenticators) { + modality = authenticator.modality; + try { + if (authenticator.impl.isHardwareDetected(opPackageName)) { + isHardwareDetected = true; + if (firstHwAvailable == TYPE_NONE) { + // Store the first one since we want to return the error in correct priority + // order. + firstHwAvailable = modality; + } + if (authenticator.impl.hasEnrolledTemplates(userId, opPackageName)) { + hasTemplatesEnrolled = true; + if (isEnabledForApp(modality, userId)) { + enabledForApps = true; + break; + } } } + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); } } @@ -926,7 +964,7 @@ public class BiometricService extends SystemService { } private boolean isEnabledForApp(int modality, int userId) { - switch(modality) { + switch (modality) { case TYPE_FINGERPRINT: return true; case TYPE_IRIS: @@ -939,47 +977,16 @@ public class BiometricService extends SystemService { } } - private String getErrorString(int type, int error, int vendorCode) { - switch (type) { - case TYPE_FINGERPRINT: - return FingerprintManager.getErrorString(getContext(), error, vendorCode); - case TYPE_IRIS: - Slog.w(TAG, "Modality not supported"); - return null; // not supported - case TYPE_FACE: - return FaceManager.getErrorString(getContext(), error, vendorCode); - default: - Slog.w(TAG, "Unable to get error string for modality: " + type); - return null; - } - } - - private BiometricAuthenticator getAuthenticator(int type) { - switch (type) { - case TYPE_FINGERPRINT: - return (FingerprintManager) - getContext().getSystemService(Context.FINGERPRINT_SERVICE); - case TYPE_IRIS: - return null; - case TYPE_FACE: - return (FaceManager) - getContext().getSystemService(Context.FACE_SERVICE); - default: - return null; - } - } - - private boolean hasFeature(int type) { - switch (type) { - case TYPE_FINGERPRINT: - return mHasFeatureFingerprint; - case TYPE_IRIS: - return mHasFeatureIris; - case TYPE_FACE: - return mHasFeatureFace; - default: - return false; + private String getErrorString(int modality, int error, int vendorCode) { + for (AuthenticatorWrapper authenticator : mAuthenticators) { + if (authenticator.modality == modality) { + // TODO(b/141025588): Refactor IBiometricServiceReceiver.aidl#onError(...) to not + // ask for a String error message, but derive it from the error code instead. + return ""; + } } + Slog.w(TAG, "Unable to get error string for modality: " + modality); + return null; } private void logDialogDismissed(int reason) { @@ -1354,30 +1361,35 @@ public class BiometricService extends SystemService { mPendingAuthSession = null; mCurrentAuthSession.mState = STATE_AUTH_STARTED; - try { - int modality = TYPE_NONE; - it = mCurrentAuthSession.mModalitiesMatched.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry<Integer, Integer> pair = (Map.Entry) it.next(); - if (pair.getKey() == TYPE_FINGERPRINT) { - mFingerprintService.startPreparedClient(pair.getValue()); - } else if (pair.getKey() == TYPE_IRIS) { - Slog.e(TAG, "Iris unsupported"); - } else if (pair.getKey() == TYPE_FACE) { - mFaceService.startPreparedClient(pair.getValue()); - } else { - Slog.e(TAG, "Unknown modality: " + pair.getKey()); + int modality = TYPE_NONE; + it = mCurrentAuthSession.mModalitiesMatched.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry<Integer, Integer> pair = (Map.Entry) it.next(); + boolean foundAuthenticator = false; + for (AuthenticatorWrapper authenticator : mAuthenticators) { + if (authenticator.modality == pair.getKey()) { + foundAuthenticator = true; + try { + authenticator.impl.startPreparedClient(pair.getValue()); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } } - modality |= pair.getKey(); } + if (!foundAuthenticator) { + Slog.e(TAG, "Unknown modality: " + pair.getKey()); + } + modality |= pair.getKey(); + } - if (!continuing) { + if (!continuing) { + try { mStatusBarService.showAuthenticationDialog(mCurrentAuthSession.mBundle, mInternalReceiver, modality, requireConfirmation, userId, mCurrentAuthSession.mOpPackageName); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); } - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception", e); } } } @@ -1387,7 +1399,8 @@ public class BiometricService extends SystemService { int callingUid, int callingPid, int callingUserId) { mHandler.post(() -> { - final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId); + final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId, + opPackageName); final int modality = result.first; final int error = result.second; @@ -1435,41 +1448,41 @@ public class BiometricService extends SystemService { * modality/modalities to start authenticating with. authenticateInternal() should only be * used for: * 1) Preparing <Biometric>Services for authentication when BiometricPrompt#authenticate is, - * invoked, shortly after which BiometricPrompt is shown and authentication starts + * invoked, shortly after which BiometricPrompt is shown and authentication starts * 2) Preparing <Biometric>Services for authentication when BiometricPrompt is already shown - * and the user has pressed "try again" + * and the user has pressed "try again" */ private void authenticateInternal(IBinder token, long sessionId, int userId, IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle, int callingUid, int callingPid, int callingUserId, int modality) { - try { - boolean requireConfirmation = bundle.getBoolean( - BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true /* default */); - if ((modality & TYPE_FACE) != 0) { - // Check if the user has forced confirmation to be required in Settings. - requireConfirmation = requireConfirmation - || mSettingObserver.getFaceAlwaysRequireConfirmation(userId); - } - // Generate random cookies to pass to the services that should prepare to start - // authenticating. Store the cookie here and wait for all services to "ack" - // with the cookie. Once all cookies are received, we can show the prompt - // and let the services start authenticating. The cookie should be non-zero. - final int cookie = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1; - final int authenticators = bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED); - Slog.d(TAG, "Creating auth session. Modality: " + modality - + ", cookie: " + cookie - + ", authenticators: " + authenticators); - final HashMap<Integer, Integer> modalities = new HashMap<>(); - - // If it's only device credential, we don't need to wait - LockSettingsService is - // always ready to check credential (SystemUI invokes that path). - if ((authenticators & ~Authenticator.TYPE_CREDENTIAL) != 0) { - modalities.put(modality, cookie); - } - mPendingAuthSession = new AuthSession(modalities, token, sessionId, userId, - receiver, opPackageName, bundle, callingUid, callingPid, callingUserId, - modality, requireConfirmation); + boolean requireConfirmation = bundle.getBoolean( + BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true /* default */); + if ((modality & TYPE_FACE) != 0) { + // Check if the user has forced confirmation to be required in Settings. + requireConfirmation = requireConfirmation + || mSettingObserver.getFaceAlwaysRequireConfirmation(userId); + } + // Generate random cookies to pass to the services that should prepare to start + // authenticating. Store the cookie here and wait for all services to "ack" + // with the cookie. Once all cookies are received, we can show the prompt + // and let the services start authenticating. The cookie should be non-zero. + final int cookie = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1; + final int authenticators = bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED); + Slog.d(TAG, "Creating auth session. Modality: " + modality + + ", cookie: " + cookie + + ", authenticators: " + authenticators); + final HashMap<Integer, Integer> modalities = new HashMap<>(); + + // If it's only device credential, we don't need to wait - LockSettingsService is + // always ready to check credential (SystemUI invokes that path). + if ((authenticators & ~Authenticator.TYPE_CREDENTIAL) != 0) { + modalities.put(modality, cookie); + } + mPendingAuthSession = new AuthSession(modalities, token, sessionId, userId, + receiver, opPackageName, bundle, callingUid, callingPid, callingUserId, + modality, requireConfirmation); + try { if (authenticators == Authenticator.TYPE_CREDENTIAL) { mPendingAuthSession.mState = STATE_SHOWING_DEVICE_CREDENTIAL; mCurrentAuthSession = mPendingAuthSession; @@ -1484,19 +1497,10 @@ public class BiometricService extends SystemService { mCurrentAuthSession.mOpPackageName); } else { mPendingAuthSession.mState = STATE_AUTH_CALLED; - // No polymorphism :( - if ((modality & TYPE_FINGERPRINT) != 0) { - mFingerprintService.prepareForAuthentication(token, sessionId, userId, - mInternalReceiver, opPackageName, cookie, - callingUid, callingPid, callingUserId); - } - if ((modality & TYPE_IRIS) != 0) { - Slog.w(TAG, "Iris unsupported"); - } - if ((modality & TYPE_FACE) != 0) { - mFaceService.prepareForAuthentication(requireConfirmation, - token, sessionId, userId, mInternalReceiver, opPackageName, - cookie, callingUid, callingPid, callingUserId); + for (AuthenticatorWrapper authenticator : mAuthenticators) { + authenticator.impl.prepareForAuthentication(requireConfirmation, token, + sessionId, userId, mInternalReceiver, opPackageName, cookie, callingUid, + callingPid, callingUserId); } } } catch (RemoteException e) { @@ -1537,30 +1541,25 @@ public class BiometricService extends SystemService { final int callingPid = Binder.getCallingPid(); final int callingUserId = UserHandle.getCallingUserId(); - try { - if (mCurrentAuthSession == null) { - Slog.w(TAG, "Skipping cancelInternal"); - return; - } else if (mCurrentAuthSession.mState != STATE_AUTH_STARTED) { - Slog.w(TAG, "Skipping cancelInternal, state: " + mCurrentAuthSession.mState); - return; - } + if (mCurrentAuthSession == null) { + Slog.w(TAG, "Skipping cancelInternal"); + return; + } else if (mCurrentAuthSession.mState != STATE_AUTH_STARTED) { + Slog.w(TAG, "Skipping cancelInternal, state: " + mCurrentAuthSession.mState); + return; + } - // TODO: For multiple modalities, send a single ERROR_CANCELED only when all - // drivers have canceled authentication. - if ((mCurrentAuthSession.mModality & TYPE_FINGERPRINT) != 0) { - mFingerprintService.cancelAuthenticationFromService(token, opPackageName, - callingUid, callingPid, callingUserId, fromClient); - } - if ((mCurrentAuthSession.mModality & TYPE_IRIS) != 0) { - Slog.w(TAG, "Iris unsupported"); - } - if ((mCurrentAuthSession.mModality & TYPE_FACE) != 0) { - mFaceService.cancelAuthenticationFromService(token, opPackageName, - callingUid, callingPid, callingUserId, fromClient); + // TODO: For multiple modalities, send a single ERROR_CANCELED only when all + // drivers have canceled authentication. + for (AuthenticatorWrapper authenticator : mAuthenticators) { + if ((authenticator.modality & mCurrentAuthSession.mModality) != 0) { + try { + authenticator.impl.cancelAuthenticationFromService(token, opPackageName, + callingUid, callingPid, callingUserId, fromClient); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to cancel authentication"); + } } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to cancel authentication"); } } } diff --git a/services/core/java/com/android/server/biometrics/face/FaceAuthenticator.java b/services/core/java/com/android/server/biometrics/face/FaceAuthenticator.java new file mode 100644 index 000000000000..5df5b29d58a8 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/face/FaceAuthenticator.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2019 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.face; + +import android.hardware.biometrics.IBiometricAuthenticator; +import android.hardware.biometrics.IBiometricServiceReceiverInternal; +import android.hardware.face.IFaceService; +import android.os.IBinder; +import android.os.RemoteException; + +/** + * TODO(b/141025588): Add JavaDoc. + */ +public final class FaceAuthenticator extends IBiometricAuthenticator.Stub { + private final IFaceService mFaceService; + + public FaceAuthenticator(IFaceService faceService) { + mFaceService = faceService; + } + + @Override + public void prepareForAuthentication(boolean requireConfirmation, IBinder token, + long sessionId, int userId, IBiometricServiceReceiverInternal wrapperReceiver, + String opPackageName, int cookie, int callingUid, int callingPid, int callingUserId) + throws RemoteException { + mFaceService.prepareForAuthentication(requireConfirmation, token, sessionId, userId, + wrapperReceiver, opPackageName, cookie, callingUid, callingPid, callingUserId); + } + + @Override + public void startPreparedClient(int cookie) throws RemoteException { + mFaceService.startPreparedClient(cookie); + } + + @Override + public void cancelAuthenticationFromService(IBinder token, String opPackageName, int callingUid, + int callingPid, int callingUserId, boolean fromClient) throws RemoteException { + mFaceService.cancelAuthenticationFromService(token, opPackageName, callingUid, callingPid, + callingUserId, fromClient); + } + + @Override + public boolean isHardwareDetected(String opPackageName) throws RemoteException { + return mFaceService.isHardwareDetected(opPackageName); + } + + @Override + public boolean hasEnrolledTemplates(int userId, String opPackageName) throws RemoteException { + return mFaceService.hasEnrolledFaces(userId, opPackageName); + } + + @Override + public void resetLockout(byte[] token) throws RemoteException { + mFaceService.resetLockout(token); + } + + @Override + public void setActiveUser(int uid) throws RemoteException { + mFaceService.setActiveUser(uid); + } +} diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java index 1b132120f709..de6ee8b20c0e 100644 --- a/services/core/java/com/android/server/biometrics/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/face/FaceService.java @@ -538,7 +538,7 @@ public class FaceService extends BiometricServiceBase { // TODO: refactor out common code here @Override // Binder call - public boolean isHardwareDetected(long deviceId, String opPackageName) { + public boolean isHardwareDetected(String opPackageName) { checkPermission(USE_BIOMETRIC_INTERNAL); if (!canUseBiometric(opPackageName, false /* foregroundOnly */, Binder.getCallingUid(), Binder.getCallingPid(), diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintAuthenticator.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintAuthenticator.java new file mode 100644 index 000000000000..6150de151ccc --- /dev/null +++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintAuthenticator.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2019 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.fingerprint; + +import android.hardware.biometrics.IBiometricAuthenticator; +import android.hardware.biometrics.IBiometricServiceReceiverInternal; +import android.hardware.fingerprint.IFingerprintService; +import android.os.IBinder; +import android.os.RemoteException; + +/** + * TODO(b/141025588): Add JavaDoc. + */ +public final class FingerprintAuthenticator extends IBiometricAuthenticator.Stub { + private final IFingerprintService mFingerprintService; + + public FingerprintAuthenticator(IFingerprintService fingerprintService) { + mFingerprintService = fingerprintService; + } + + @Override + public void prepareForAuthentication(boolean requireConfirmation, IBinder token, + long sessionId, int userId, IBiometricServiceReceiverInternal wrapperReceiver, + String opPackageName, int cookie, int callingUid, int callingPid, int callingUserId) + throws RemoteException { + mFingerprintService.prepareForAuthentication(token, sessionId, userId, wrapperReceiver, + opPackageName, cookie, callingUid, callingPid, callingUserId); + } + + @Override + public void startPreparedClient(int cookie) throws RemoteException { + mFingerprintService.startPreparedClient(cookie); + } + + @Override + public void cancelAuthenticationFromService(IBinder token, String opPackageName, int callingUid, + int callingPid, int callingUserId, boolean fromClient) throws RemoteException { + mFingerprintService.cancelAuthenticationFromService(token, opPackageName, callingUid, + callingPid, callingUserId, fromClient); + } + + @Override + public boolean isHardwareDetected(String opPackageName) throws RemoteException { + return mFingerprintService.isHardwareDetected(opPackageName); + } + + @Override + public boolean hasEnrolledTemplates(int userId, String opPackageName) throws RemoteException { + return mFingerprintService.hasEnrolledFingerprints(userId, opPackageName); + } + + @Override + public void resetLockout(byte[] token) throws RemoteException { + mFingerprintService.resetTimeout(token); + } + + @Override + public void setActiveUser(int uid) throws RemoteException { + mFingerprintService.setActiveUser(uid); + } +} diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java index d85af2eb0b32..d59fcbf7c4d8 100644 --- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java @@ -350,7 +350,7 @@ public class FingerprintService extends BiometricServiceBase { // TODO: refactor out common code here @Override // Binder call - public boolean isHardwareDetected(long deviceId, String opPackageName) { + public boolean isHardwareDetected(String opPackageName) { if (!canUseBiometric(opPackageName, false /* foregroundOnly */, Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.getCallingUserId())) { diff --git a/services/core/java/com/android/server/biometrics/iris/IrisAuthenticator.java b/services/core/java/com/android/server/biometrics/iris/IrisAuthenticator.java new file mode 100644 index 000000000000..c44b8e7227e3 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/iris/IrisAuthenticator.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2019 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.iris; + +import android.hardware.biometrics.IBiometricAuthenticator; +import android.hardware.biometrics.IBiometricServiceReceiverInternal; +import android.hardware.iris.IIrisService; +import android.os.IBinder; +import android.os.RemoteException; + +/** + * TODO(b/141025588): Add JavaDoc. + */ +public final class IrisAuthenticator extends IBiometricAuthenticator.Stub { + private final IIrisService mIrisService; + + public IrisAuthenticator(IIrisService irisService) { + mIrisService = irisService; + } + + @Override + public void prepareForAuthentication(boolean requireConfirmation, IBinder token, + long sessionId, int userId, IBiometricServiceReceiverInternal wrapperReceiver, + String opPackageName, int cookie, int callingUid, int callingPid, int callingUserId) + throws RemoteException { + } + + @Override + public void startPreparedClient(int cookie) throws RemoteException { + } + + @Override + public void cancelAuthenticationFromService(IBinder token, String opPackageName, int callingUid, + int callingPid, int callingUserId, boolean fromClient) throws RemoteException { + } + + @Override + public boolean isHardwareDetected(String opPackageName) throws RemoteException { + return false; + } + + @Override + public boolean hasEnrolledTemplates(int userId, String opPackageName) throws RemoteException { + return false; + } + + @Override + public void resetLockout(byte[] token) throws RemoteException { + } + + @Override + public void setActiveUser(int uid) throws RemoteException { + } +} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java index 4aeeb0af1bf8..dd86123ec423 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java @@ -33,7 +33,6 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.app.AppOpsManager; import android.app.IActivityManager; import android.content.ContentResolver; import android.content.Context; @@ -43,17 +42,15 @@ import android.hardware.biometrics.Authenticator; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricPrompt; +import android.hardware.biometrics.IBiometricAuthenticator; import android.hardware.biometrics.IBiometricService; import android.hardware.biometrics.IBiometricServiceReceiver; import android.hardware.biometrics.IBiometricServiceReceiverInternal; -import android.hardware.face.FaceManager; -import android.hardware.face.IFaceService; import android.hardware.fingerprint.FingerprintManager; -import android.hardware.fingerprint.IFingerprintService; import android.os.Binder; import android.os.Bundle; -import android.os.Handler; import android.os.IBinder; +import android.os.RemoteException; import android.security.KeyStore; import androidx.test.InstrumentationRegistry; @@ -68,8 +65,6 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.util.List; - @SmallTest public class BiometricServiceTest { @@ -98,71 +93,33 @@ public class BiometricServiceTest { @Mock private PackageManager mPackageManager; @Mock - private AppOpsManager mAppOpsManager; - @Mock IBiometricServiceReceiver mReceiver1; @Mock IBiometricServiceReceiver mReceiver2; @Mock - FingerprintManager mFingerprintManager; + BiometricService.Injector mInjector; @Mock - FaceManager mFaceManager; - - private static class MockInjector extends BiometricService.Injector { - @Override - IActivityManager getActivityManagerService() { - return mock(IActivityManager.class); - } - - @Override - IStatusBarService getStatusBarService() { - return mock(IStatusBarService.class); - } - - @Override - IFingerprintService getFingerprintService() { - return mock(IFingerprintService.class); - } - - @Override - IFaceService getFaceService() { - return mock(IFaceService.class); - } - - @Override - BiometricService.SettingObserver getSettingObserver(Context context, Handler handler, - List<BiometricService.EnabledOnKeyguardCallback> callbacks) { - return mock(BiometricService.SettingObserver.class); - } - - @Override - KeyStore getKeyStore() { - return mock(KeyStore.class); - } - - @Override - boolean isDebugEnabled(Context context, int userId) { - return false; - } - - @Override - void publishBinderService(BiometricService service, IBiometricService.Stub impl) { - // no-op for test - } - } + IBiometricAuthenticator mFingerprintAuthenticator; + @Mock + IBiometricAuthenticator mFaceAuthenticator; @Before public void setUp() { MockitoAnnotations.initMocks(this); when(mContext.getPackageManager()).thenReturn(mPackageManager); - when(mContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager); - when(mContext.getSystemService(Context.FINGERPRINT_SERVICE)) - .thenReturn(mFingerprintManager); - when(mContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mFaceManager); when(mContext.getContentResolver()).thenReturn(mContentResolver); when(mContext.getResources()).thenReturn(mResources); + when(mInjector.getActivityManagerService()).thenReturn(mock(IActivityManager.class)); + when(mInjector.getStatusBarService()).thenReturn(mock(IStatusBarService.class)); + when(mInjector.getFingerprintAuthenticator()).thenReturn(mFingerprintAuthenticator); + when(mInjector.getFaceAuthenticator()).thenReturn(mFaceAuthenticator); + when(mInjector.getSettingObserver(any(), any(), any())).thenReturn( + mock(BiometricService.SettingObserver.class)); + when(mInjector.getKeyStore()).thenReturn(mock(KeyStore.class)); + when(mInjector.isDebugEnabled(any(), anyInt())).thenReturn(false); + when(mResources.getString(R.string.biometric_error_hw_unavailable)) .thenReturn(ERROR_HW_UNAVAILABLE); when(mResources.getString(R.string.biometric_not_recognized)) @@ -172,13 +129,14 @@ public class BiometricServiceTest { } @Test - public void testAuthenticate_withoutHardware_returnsErrorHardwareNotPresent() throws Exception { + public void testAuthenticate_withoutHardware_returnsErrorHardwareNotPresent() throws + Exception { when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) .thenReturn(false); when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_IRIS)).thenReturn(false); when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(false); - mBiometricService = new BiometricService(mContext, new MockInjector()); + mBiometricService = new BiometricService(mContext, mInjector); mBiometricService.onStart(); invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, @@ -191,9 +149,9 @@ public class BiometricServiceTest { @Test public void testAuthenticate_withoutEnrolled_returnsErrorNoBiometrics() throws Exception { when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true); - when(mFingerprintManager.isHardwareDetected()).thenReturn(true); + when(mFingerprintAuthenticator.isHardwareDetected(any())).thenReturn(true); - mBiometricService = new BiometricService(mContext, new MockInjector()); + mBiometricService = new BiometricService(mContext, mInjector); mBiometricService.onStart(); invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, @@ -204,12 +162,13 @@ public class BiometricServiceTest { } @Test - public void testAuthenticate_whenHalIsDead_returnsErrorHardwareUnavailable() throws Exception { + public void testAuthenticate_whenHalIsDead_returnsErrorHardwareUnavailable() throws + Exception { when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true); - when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true); - when(mFingerprintManager.isHardwareDetected()).thenReturn(false); + when(mFingerprintAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true); + when(mFingerprintAuthenticator.isHardwareDetected(any())).thenReturn(false); - mBiometricService = new BiometricService(mContext, new MockInjector()); + mBiometricService = new BiometricService(mContext, mInjector); mBiometricService.onStart(); invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, @@ -223,10 +182,10 @@ public class BiometricServiceTest { public void testAuthenticateFace_respectsUserSetting() throws Exception { when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true); - when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true); - when(mFaceManager.isHardwareDetected()).thenReturn(true); + when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true); + when(mFaceAuthenticator.isHardwareDetected(any())).thenReturn(true); - mBiometricService = new BiometricService(mContext, new MockInjector()); + mBiometricService = new BiometricService(mContext, mInjector); mBiometricService.onStart(); // Disabled in user settings receives onError @@ -246,7 +205,7 @@ public class BiometricServiceTest { false /* allowDeviceCredential */); waitForIdle(); verify(mReceiver1, never()).onError(anyInt(), any(String.class)); - verify(mBiometricService.mFaceService).prepareForAuthentication( + verify(mBiometricService.mAuthenticators.get(0).impl).prepareForAuthentication( eq(true) /* requireConfirmation */, any(IBinder.class), anyLong() /* sessionId */, @@ -265,7 +224,7 @@ public class BiometricServiceTest { invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, false /* allowDeviceCredential */); waitForIdle(); - verify(mBiometricService.mFaceService).prepareForAuthentication( + verify(mBiometricService.mAuthenticators.get(0).impl).prepareForAuthentication( eq(false) /* requireConfirmation */, any(IBinder.class), anyLong() /* sessionId */, @@ -281,7 +240,7 @@ public class BiometricServiceTest { @Test public void testAuthenticate_happyPathWithoutConfirmation() throws Exception { setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT); - mBiometricService = new BiometricService(mContext, new MockInjector()); + mBiometricService = new BiometricService(mContext, mInjector); mBiometricService.onStart(); // Start testing the happy path @@ -296,7 +255,8 @@ public class BiometricServiceTest { // Invokes <Modality>Service#prepareForAuthentication ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class); verify(mReceiver1, never()).onError(anyInt(), any(String.class)); - verify(mBiometricService.mFingerprintService).prepareForAuthentication( + verify(mBiometricService.mAuthenticators.get(0).impl).prepareForAuthentication( + anyBoolean() /* requireConfirmation */, any(IBinder.class), anyLong() /* sessionId */, anyInt() /* userId */, @@ -316,7 +276,7 @@ public class BiometricServiceTest { BiometricService.STATE_AUTH_STARTED); // startPreparedClient invoked - verify(mBiometricService.mFingerprintService) + verify(mBiometricService.mAuthenticators.get(0).impl) .startPreparedClient(cookieCaptor.getValue()); // StatusBar showBiometricDialog invoked @@ -355,7 +315,7 @@ public class BiometricServiceTest { @Test public void testAuthenticate_noBiometrics_credentialAllowed() throws Exception { setupAuthForOnly(BiometricAuthenticator.TYPE_FACE); - when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false); + when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(false); invokeAuthenticate(mBiometricService.mImpl, mReceiver1, true /* requireConfirmation */, true /* allowDeviceCredential */); waitForIdle(); @@ -434,7 +394,8 @@ public class BiometricServiceTest { } @Test - public void testErrorCanceled_whenAuthenticating_notifiesSystemUIAndClient() throws Exception { + public void testErrorCanceled_whenAuthenticating_notifiesSystemUIAndClient() throws + Exception { setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT); invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, false /* allowDeviceCredential */); @@ -524,7 +485,7 @@ public class BiometricServiceTest { public void testErrorFromHal_whenPaused_notifiesSystemUIAndClient() throws Exception { setupAuthForOnly(BiometricAuthenticator.TYPE_FACE); invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1, - false /* requireCOnfirmation */, false /* allowDeviceCredential */); + false /* requireConfirmation */, false /* allowDeviceCredential */); mBiometricService.mInternalReceiver.onError( getCookieForCurrentSession(mBiometricService.mCurrentAuthSession), @@ -760,7 +721,7 @@ public class BiometricServiceTest { verify(mReceiver1).onError( eq(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED), eq(ERROR_USER_CANCELED)); - verify(mBiometricService.mFingerprintService).cancelAuthenticationFromService( + verify(mBiometricService.mAuthenticators.get(0).impl).cancelAuthenticationFromService( any(), any(), anyInt(), @@ -784,7 +745,8 @@ public class BiometricServiceTest { BiometricPrompt.DISMISSED_REASON_NEGATIVE); waitForIdle(); - verify(mBiometricService.mFaceService, never()).cancelAuthenticationFromService( + verify(mBiometricService.mAuthenticators.get(0).impl, + never()).cancelAuthenticationFromService( any(), any(), anyInt(), @@ -794,7 +756,8 @@ public class BiometricServiceTest { } @Test - public void testDismissedReasonUserCancel_whilePaused_doesntInvokeHalCancel() throws Exception { + public void testDismissedReasonUserCancel_whilePaused_doesntInvokeHalCancel() throws + Exception { setupAuthForOnly(BiometricAuthenticator.TYPE_FACE); invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, false /* allowDeviceCredential */); @@ -807,7 +770,8 @@ public class BiometricServiceTest { BiometricPrompt.DISMISSED_REASON_USER_CANCEL); waitForIdle(); - verify(mBiometricService.mFaceService, never()).cancelAuthenticationFromService( + verify(mBiometricService.mAuthenticators.get(0).impl, + never()).cancelAuthenticationFromService( any(), any(), anyInt(), @@ -830,7 +794,8 @@ public class BiometricServiceTest { waitForIdle(); // doesn't send cancel to HAL - verify(mBiometricService.mFaceService, never()).cancelAuthenticationFromService( + verify(mBiometricService.mAuthenticators.get(0).impl, + never()).cancelAuthenticationFromService( any(), any(), anyInt(), @@ -863,7 +828,7 @@ public class BiometricServiceTest { // Helper methods - private void setupAuthForOnly(int modality) { + private void setupAuthForOnly(int modality) throws RemoteException { when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) .thenReturn(false); when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(false); @@ -871,17 +836,17 @@ public class BiometricServiceTest { if (modality == BiometricAuthenticator.TYPE_FINGERPRINT) { when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) .thenReturn(true); - when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true); - when(mFingerprintManager.isHardwareDetected()).thenReturn(true); + when(mFingerprintAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true); + when(mFingerprintAuthenticator.isHardwareDetected(any())).thenReturn(true); } else if (modality == BiometricAuthenticator.TYPE_FACE) { when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true); - when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true); - when(mFaceManager.isHardwareDetected()).thenReturn(true); + when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true); + when(mFaceAuthenticator.isHardwareDetected(any())).thenReturn(true); } else { fail("Unknown modality: " + modality); } - mBiometricService = new BiometricService(mContext, new MockInjector()); + mBiometricService = new BiometricService(mContext, mInjector); mBiometricService.onStart(); when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(true); |