summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/hardware/biometrics/BiometricManager.java2
-rw-r--r--core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl58
-rw-r--r--core/java/android/hardware/biometrics/IBiometricService.aidl8
-rw-r--r--core/java/android/hardware/face/FaceManager.java3
-rw-r--r--core/java/android/hardware/face/IFaceService.aidl2
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java3
-rw-r--r--core/java/android/hardware/fingerprint/IFingerprintService.aidl2
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricService.java411
-rw-r--r--services/core/java/com/android/server/biometrics/face/FaceAuthenticator.java75
-rw-r--r--services/core/java/com/android/server/biometrics/face/FaceService.java2
-rw-r--r--services/core/java/com/android/server/biometrics/fingerprint/FingerprintAuthenticator.java75
-rw-r--r--services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java2
-rw-r--r--services/core/java/com/android/server/biometrics/iris/IrisAuthenticator.java68
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java141
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);